Commit 8a81f8aa13

Andrew Kelley <superjoe30@gmail.com>
2016-11-19 07:39:51
IR: implement compileVar builtin and more
* implicit array to slice cast * fix if statements at global scope * implement array type IR
1 parent 1903701
src/all_types.hpp
@@ -1451,6 +1451,7 @@ enum IrInstructionId {
     IrInstructionIdArrayType,
     IrInstructionIdSliceType,
     IrInstructionIdAsm,
+    IrInstructionIdCompileVar,
 };
 
 struct IrInstruction {
@@ -1735,6 +1736,12 @@ struct IrInstructionAsm {
     bool has_side_effects;
 };
 
+struct IrInstructionCompileVar {
+    IrInstruction base;
+
+    IrInstruction *name;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/analyze.cpp
@@ -869,12 +869,9 @@ static IrInstruction *analyze_const_value(CodeGen *g, BlockContext *scope, AstNo
     return result;
 }
 
-static TypeTableEntry *analyze_type_expr_pointer_only(CodeGen *g, ImportTableEntry *import,
-        BlockContext *context, AstNode *node, bool pointer_only)
+static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node)
 {
-    if (pointer_only)
-        zig_panic("TODO");
-
     IrInstruction *result = analyze_const_value(g, context, node, g->builtin_types.entry_type);
     if (result->type_entry->id == TypeTableEntryIdInvalid)
         return g->builtin_types.entry_invalid;
@@ -883,13 +880,6 @@ static TypeTableEntry *analyze_type_expr_pointer_only(CodeGen *g, ImportTableEnt
     return result->static_value.data.x_type;
 }
 
-// Calls analyze_expression on node, and then resolve_type.
-static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node)
-{
-    return analyze_type_expr_pointer_only(g, import, context, node, false);
-}
-
 static bool fn_wants_full_static_eval(FnTableEntry *fn_table_entry) {
     assert(fn_table_entry);
     AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
src/ast_render.cpp
@@ -696,7 +696,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 fprintf(ar->f, ") ");
                 render_node_grouped(ar, node->data.if_bool_expr.then_block);
                 if (node->data.if_bool_expr.else_node) {
-                    fprintf(ar->f, "else ");
+                    fprintf(ar->f, " else ");
                     render_node_grouped(ar, node->data.if_bool_expr.else_node);
                 }
                 break;
src/codegen.cpp
@@ -526,240 +526,6 @@ static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddS
     return result;
 }
 
-static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
-        LLVMValueRef val1, LLVMValueRef val2)
-{
-    // for unsigned left shifting, we do the wrapping shift, then logically shift
-    // right the same number of bits
-    // if the values don't match, we have an overflow
-    // for signed left shifting we do the same except arithmetic shift right
-
-    assert(type_entry->id == TypeTableEntryIdInt);
-
-    LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
-    LLVMValueRef orig_val;
-    if (type_entry->data.integral.is_signed) {
-        orig_val = LLVMBuildAShr(g->builder, result, val2, "");
-    } else {
-        orig_val = LLVMBuildLShr(g->builder, result, val2, "");
-    }
-    LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, "");
-
-    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
-    LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
-    LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, fail_block);
-    gen_debug_safety_crash(g);
-
-    LLVMPositionBuilderAtEnd(g->builder, ok_block);
-    return result;
-}
-
-static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
-        TypeTableEntry *type_entry, bool exact)
-{
-
-    if (want_debug_safety(g, source_node)) {
-        LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
-        LLVMValueRef is_zero_bit;
-        if (type_entry->id == TypeTableEntryIdInt) {
-            is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
-        } else if (type_entry->id == TypeTableEntryIdFloat) {
-            is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
-        } else {
-            zig_unreachable();
-        }
-        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk");
-        LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail");
-        LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block);
-
-        LLVMPositionBuilderAtEnd(g->builder, fail_block);
-        gen_debug_safety_crash(g);
-
-        LLVMPositionBuilderAtEnd(g->builder, ok_block);
-    }
-
-    if (type_entry->id == TypeTableEntryIdFloat) {
-        assert(!exact);
-        return LLVMBuildFDiv(g->builder, val1, val2, "");
-    }
-
-    assert(type_entry->id == TypeTableEntryIdInt);
-
-    if (exact) {
-        if (want_debug_safety(g, source_node)) {
-            LLVMValueRef remainder_val;
-            if (type_entry->data.integral.is_signed) {
-                remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
-            } else {
-                remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
-            }
-            LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
-            LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
-
-            LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactOk");
-            LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactFail");
-            LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
-
-            LLVMPositionBuilderAtEnd(g->builder, fail_block);
-            gen_debug_safety_crash(g);
-
-            LLVMPositionBuilderAtEnd(g->builder, ok_block);
-        }
-        if (type_entry->data.integral.is_signed) {
-            return LLVMBuildExactSDiv(g->builder, val1, val2, "");
-        } else {
-            return ZigLLVMBuildExactUDiv(g->builder, val1, val2, "");
-        }
-    } else {
-        if (type_entry->data.integral.is_signed) {
-            return LLVMBuildSDiv(g->builder, val1, val2, "");
-        } else {
-            return LLVMBuildUDiv(g->builder, val1, val2, "");
-        }
-    }
-}
-
-static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
-    LLVMValueRef val1, LLVMValueRef val2,
-    TypeTableEntry *op1_type, TypeTableEntry *op2_type,
-    BinOpType bin_op)
-{
-    assert(op1_type == op2_type);
-
-    switch (bin_op) {
-        case BinOpTypeBinOr:
-        case BinOpTypeAssignBitOr:
-            return LLVMBuildOr(g->builder, val1, val2, "");
-        case BinOpTypeBinXor:
-        case BinOpTypeAssignBitXor:
-            return LLVMBuildXor(g->builder, val1, val2, "");
-        case BinOpTypeBinAnd:
-        case BinOpTypeAssignBitAnd:
-            return LLVMBuildAnd(g->builder, val1, val2, "");
-        case BinOpTypeBitShiftLeft:
-        case BinOpTypeBitShiftLeftWrap:
-        case BinOpTypeAssignBitShiftLeft:
-        case BinOpTypeAssignBitShiftLeftWrap:
-            {
-                assert(op1_type->id == TypeTableEntryIdInt);
-                bool is_wrapping = (bin_op == BinOpTypeBitShiftLeftWrap) ||
-                    (bin_op == BinOpTypeAssignBitShiftLeftWrap);
-                if (is_wrapping) {
-                    return LLVMBuildShl(g->builder, val1, val2, "");
-                } else if (want_debug_safety(g, source_node)) {
-                    return gen_overflow_shl_op(g, op1_type, val1, val2);
-                } else if (op1_type->data.integral.is_signed) {
-                    return ZigLLVMBuildNSWShl(g->builder, val1, val2, "");
-                } else {
-                    return ZigLLVMBuildNUWShl(g->builder, val1, val2, "");
-                }
-            }
-        case BinOpTypeBitShiftRight:
-        case BinOpTypeAssignBitShiftRight:
-            assert(op1_type->id == TypeTableEntryIdInt);
-            assert(op2_type->id == TypeTableEntryIdInt);
-
-            if (op1_type->data.integral.is_signed) {
-                return LLVMBuildAShr(g->builder, val1, val2, "");
-            } else {
-                return LLVMBuildLShr(g->builder, val1, val2, "");
-            }
-        case BinOpTypeAdd:
-        case BinOpTypeAddWrap:
-        case BinOpTypeAssignPlus:
-        case BinOpTypeAssignPlusWrap:
-            if (op1_type->id == TypeTableEntryIdFloat) {
-                return LLVMBuildFAdd(g->builder, val1, val2, "");
-            } else if (op1_type->id == TypeTableEntryIdInt) {
-                bool is_wrapping = (bin_op == BinOpTypeAddWrap) || (bin_op == BinOpTypeAssignPlusWrap);
-                if (is_wrapping) {
-                    return LLVMBuildAdd(g->builder, val1, val2, "");
-                } else if (want_debug_safety(g, source_node)) {
-                    return gen_overflow_op(g, op1_type, AddSubMulAdd, val1, val2);
-                } else if (op1_type->data.integral.is_signed) {
-                    return LLVMBuildNSWAdd(g->builder, val1, val2, "");
-                } else {
-                    return LLVMBuildNUWAdd(g->builder, val1, val2, "");
-                }
-            } else {
-                zig_unreachable();
-            }
-        case BinOpTypeSub:
-        case BinOpTypeSubWrap:
-        case BinOpTypeAssignMinus:
-        case BinOpTypeAssignMinusWrap:
-            if (op1_type->id == TypeTableEntryIdFloat) {
-                return LLVMBuildFSub(g->builder, val1, val2, "");
-            } else if (op1_type->id == TypeTableEntryIdInt) {
-                bool is_wrapping = (bin_op == BinOpTypeSubWrap || bin_op == BinOpTypeAssignMinusWrap);
-                if (is_wrapping) {
-                    return LLVMBuildSub(g->builder, val1, val2, "");
-                } else if (want_debug_safety(g, source_node)) {
-                    return gen_overflow_op(g, op1_type, AddSubMulSub, val1, val2);
-                } else if (op1_type->data.integral.is_signed) {
-                    return LLVMBuildNSWSub(g->builder, val1, val2, "");
-                } else {
-                    return LLVMBuildNUWSub(g->builder, val1, val2, "");
-                }
-            } else {
-                zig_unreachable();
-            }
-        case BinOpTypeMult:
-        case BinOpTypeMultWrap:
-        case BinOpTypeAssignTimes:
-        case BinOpTypeAssignTimesWrap:
-            if (op1_type->id == TypeTableEntryIdFloat) {
-                return LLVMBuildFMul(g->builder, val1, val2, "");
-            } else if (op1_type->id == TypeTableEntryIdInt) {
-                bool is_wrapping = (bin_op == BinOpTypeMultWrap || bin_op == BinOpTypeAssignTimesWrap);
-                if (is_wrapping) {
-                    return LLVMBuildMul(g->builder, val1, val2, "");
-                } else if (want_debug_safety(g, source_node)) {
-                    return gen_overflow_op(g, op1_type, AddSubMulMul, val1, val2);
-                } else if (op1_type->data.integral.is_signed) {
-                    return LLVMBuildNSWMul(g->builder, val1, val2, "");
-                } else {
-                    return LLVMBuildNUWMul(g->builder, val1, val2, "");
-                }
-            } else {
-                zig_unreachable();
-            }
-        case BinOpTypeDiv:
-        case BinOpTypeAssignDiv:
-            return gen_div(g, source_node, val1, val2, op1_type, false);
-        case BinOpTypeMod:
-        case BinOpTypeAssignMod:
-            if (op1_type->id == TypeTableEntryIdFloat) {
-                return LLVMBuildFRem(g->builder, val1, val2, "");
-            } else {
-                assert(op1_type->id == TypeTableEntryIdInt);
-                if (op1_type->data.integral.is_signed) {
-                    return LLVMBuildSRem(g->builder, val1, val2, "");
-                } else {
-                    return LLVMBuildURem(g->builder, val1, val2, "");
-                }
-            }
-        case BinOpTypeBoolOr:
-        case BinOpTypeBoolAnd:
-        case BinOpTypeCmpEq:
-        case BinOpTypeCmpNotEq:
-        case BinOpTypeCmpLessThan:
-        case BinOpTypeCmpGreaterThan:
-        case BinOpTypeCmpLessOrEq:
-        case BinOpTypeCmpGreaterOrEq:
-        case BinOpTypeInvalid:
-        case BinOpTypeAssign:
-        case BinOpTypeAssignBoolAnd:
-        case BinOpTypeAssignBoolOr:
-        case BinOpTypeUnwrapMaybe:
-        case BinOpTypeArrayCat:
-        case BinOpTypeArrayMult:
-            zig_unreachable();
-    }
-    zig_unreachable();
-}
 static LLVMIntPredicate cmp_op_to_int_predicate(IrBinOp cmp_op, bool is_signed) {
     switch (cmp_op) {
         case IrBinOpCmpEq:
@@ -825,7 +591,7 @@ static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef
     return LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, "");
 }
 
-static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
+static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node,
         LLVMValueRef target_ref, LLVMValueRef value,
         TypeTableEntry *op1_type, TypeTableEntry *op2_type)
 {
@@ -834,18 +600,10 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b
     }
     if (handle_is_ptr(op1_type)) {
         assert(op1_type == op2_type);
-        assert(bin_op == BinOpTypeAssign);
 
         return gen_struct_memcpy(g, value, target_ref, op1_type);
     }
 
-    if (bin_op != BinOpTypeAssign) {
-        assert(source_node->type == NodeTypeBinOpExpr);
-        LLVMValueRef left_value = LLVMBuildLoad(g->builder, target_ref, "");
-
-        value = gen_arithmetic_bin_op(g, source_node, left_value, value, op1_type, op2_type, bin_op);
-    }
-
     LLVMBuildStore(g->builder, value, target_ref);
     return nullptr;
 }
@@ -1036,7 +794,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
                     return expr_val;
                 } else {
                     LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, "");
-                    gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign,
+                    gen_assign_raw(g, cast_instruction->base.source_node,
                             val_ptr, expr_val, child_type, actual_type);
 
                     LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
@@ -1065,7 +823,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
                     LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
 
                     LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, "");
-                    gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign,
+                    gen_assign_raw(g, cast_instruction->base.source_node,
                             payload_ptr, expr_val, child_type, actual_type);
 
                     return cast_instruction->tmp_ptr;
@@ -1414,7 +1172,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
         want_zeroes = true;
 
     if (have_init_expr) {
-        gen_assign_raw(g, init_value->source_node, BinOpTypeAssign, var->value_ref,
+        gen_assign_raw(g, init_value->source_node, var->value_ref,
                 ir_llvm_value(g, init_value), var->type, init_value->type_entry);
     } else {
         bool ignore_uninit = false;
@@ -1685,6 +1443,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdSetDebugSafety:
         case IrInstructionIdArrayType:
         case IrInstructionIdSliceType:
+        case IrInstructionIdCompileVar:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
src/eval.cpp
@@ -318,8 +318,23 @@ void eval_const_expr_implicit_cast(CastOp cast_op,
             // can't do it
             break;
         case CastOpToUnknownSizeArray:
-            zig_panic("TODO compile time implicit to unknown size array");
-            break;
+            {
+                assert(other_type->id == TypeTableEntryIdArray);
+                assert(other_val->data.x_array.size == other_type->data.array.len);
+
+                const_val->data.x_struct.fields = allocate<ConstExprValue>(2);
+                ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index];
+                ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index];
+
+                ptr_field->special = ConstValSpecialStatic;
+                ptr_field->data.x_ptr.base_ptr = other_val;
+
+                len_field->special = ConstValSpecialStatic;
+                bignum_init_unsigned(&len_field->data.x_bignum, other_type->data.array.len);
+
+                const_val->special = ConstValSpecialStatic;
+                break;
+            }
         case CastOpMaybeWrap:
             const_val->data.x_maybe = other_val;
             const_val->special = ConstValSpecialStatic;
src/ir.cpp
@@ -218,6 +218,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {
     return IrInstructionIdAsm;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileVar *) {
+    return IrInstructionIdCompileVar;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -507,14 +511,6 @@ static IrInstruction *ir_build_field_ptr(IrBuilder *irb, AstNode *source_node,
     return &instruction->base;
 }
 
-//static IrInstruction *ir_build_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
-//        IrInstruction *container_ptr, Buf *field_name)
-//{
-//    IrInstruction *new_instruction = ir_build_field_ptr(irb, old_instruction->source_node, container_ptr, field_name);
-//    ir_link_new_instruction(new_instruction, old_instruction);
-//    return new_instruction;
-//}
-
 static IrInstruction *ir_build_read_field(IrBuilder *irb, AstNode *source_node,
     IrInstruction *container_ptr, Buf *field_name)
 {
@@ -527,14 +523,6 @@ static IrInstruction *ir_build_read_field(IrBuilder *irb, AstNode *source_node,
     return &instruction->base;
 }
 
-//static IrInstruction *ir_build_read_field_from(IrBuilder *irb, IrInstruction *old_instruction,
-//        IrInstruction *container_ptr, Buf *field_name)
-//{
-//    IrInstruction *new_instruction = ir_build_read_field(irb, old_instruction->source_node, container_ptr, field_name);
-//    ir_link_new_instruction(new_instruction, old_instruction);
-//    return new_instruction;
-//}
-
 static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, AstNode *source_node,
     IrInstruction *struct_ptr, TypeStructField *field)
 {
@@ -861,6 +849,15 @@ static IrInstruction *ir_build_asm_from(IrBuilder *irb, IrInstruction *old_instr
     return new_instruction;
 }
 
+static IrInstruction *ir_build_compile_var(IrBuilder *irb, AstNode *source_node, IrInstruction *name) {
+    IrInstructionCompileVar *instruction = ir_build_instruction<IrInstructionCompileVar>(irb, source_node);
+    instruction->name = name;
+
+    ir_ref_instruction(name);
+
+    return &instruction->base;
+}
+
 static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
         bool gen_error_defers, bool gen_maybe_defers)
 {
@@ -1336,6 +1333,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
 
                 return ir_build_set_debug_safety(irb, node, arg0_value, arg1_value);
             }
+        case BuiltinFnIdCompileVar:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                return ir_build_compile_var(irb, node, arg0_value);
+            }
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
         case BuiltinFnIdSizeof:
@@ -1350,7 +1356,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
         case BuiltinFnIdCInclude:
         case BuiltinFnIdCDefine:
         case BuiltinFnIdCUndef:
-        case BuiltinFnIdCompileVar:
         case BuiltinFnIdCompileErr:
         case BuiltinFnIdConstEval:
         case BuiltinFnIdCtz:
@@ -1410,14 +1415,15 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, AstNode *node) {
     IrBasicBlock *else_block = ir_build_basic_block(irb, "Else");
     IrBasicBlock *endif_block = ir_build_basic_block(irb, "EndIf");
 
-    ir_build_cond_br(irb, condition->source_node, condition, then_block, else_block, false);
+    bool is_inline = (node->block_context->fn_entry == nullptr);
+    ir_build_cond_br(irb, condition->source_node, condition, then_block, else_block, is_inline);
 
     ir_set_cursor_at_end(irb, then_block);
     IrInstruction *then_expr_result = ir_gen_node(irb, then_node, node->block_context);
     if (then_expr_result == irb->codegen->invalid_instruction)
         return then_expr_result;
     IrBasicBlock *after_then_block = irb->current_basic_block;
-    ir_build_br(irb, node, endif_block, false);
+    ir_build_br(irb, node, endif_block, is_inline);
 
     ir_set_cursor_at_end(irb, else_block);
     IrInstruction *else_expr_result;
@@ -1429,7 +1435,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, AstNode *node) {
         else_expr_result = ir_build_const_void(irb, node);
     }
     IrBasicBlock *after_else_block = irb->current_basic_block;
-    ir_build_br(irb, node, endif_block, false);
+    ir_build_br(irb, node, endif_block, is_inline);
 
     ir_set_cursor_at_end(irb, endif_block);
     IrInstruction **incoming_values = allocate<IrInstruction *>(2);
@@ -2281,7 +2287,19 @@ static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *ins
     return ira->codegen->builtin_types.entry_usize;
 }
 
-static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) {
+static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) {
+    if (value->static_value.special != ConstValSpecialStatic) {
+        add_node_error(ira->codegen, value->source_node,
+                buf_sprintf("unable to evaluate constant expression"));
+        return nullptr;
+    }
+    return &value->static_value;
+}
+
+static TypeTableEntry *ir_resolve_type_lval(IrAnalyze *ira, IrInstruction *type_value, LValPurpose lval) {
+    if (lval != LValPurposeNone)
+        zig_panic("TODO");
+
     if (type_value == ira->codegen->invalid_instruction)
         return ira->codegen->builtin_types.entry_invalid;
 
@@ -2294,44 +2312,15 @@ static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value
         return ira->codegen->builtin_types.entry_invalid;
     }
 
-    ConstExprValue *const_val = &type_value->static_value;
-    if (const_val->special == ConstValSpecialRuntime) {
-        add_node_error(ira->codegen, type_value->source_node,
-                buf_sprintf("unable to evaluate constant expression"));
+    ConstExprValue *const_val = ir_resolve_const(ira, type_value);
+    if (!const_val)
         return ira->codegen->builtin_types.entry_invalid;
-    }
 
     return const_val->data.x_type;
 }
 
-static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) {
-    if (value->static_value.special != ConstValSpecialStatic) {
-        add_node_error(ira->codegen, value->source_node,
-                buf_sprintf("unable to evaluate constant expression"));
-        return nullptr;
-    }
-    return &value->static_value;
-}
-
-static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out) {
-    if (bool_value == ira->codegen->invalid_instruction)
-        return false;
-
-    if (bool_value->type_entry->id == TypeTableEntryIdInvalid)
-        return false;
-
-    if (bool_value->type_entry->id != TypeTableEntryIdBool) {
-        add_node_error(ira->codegen, bool_value->source_node,
-                buf_sprintf("expected type 'bool', found '%s'", buf_ptr(&bool_value->type_entry->name)));
-        return false;
-    }
-
-    ConstExprValue *const_val = ir_resolve_const(ira, bool_value);
-    if (!const_val)
-        return false;
-
-    *out = const_val->data.x_bool;
-    return true;
+static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) {
+    return ir_resolve_type_lval(ira, type_value, LValPurposeNone);
 }
 
 static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
@@ -2347,12 +2336,9 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
         return nullptr;
     }
 
-    ConstExprValue *const_val = &fn_value->static_value;
-    if (const_val->special == ConstValSpecialRuntime) {
-        add_node_error(ira->codegen, fn_value->source_node,
-                buf_sprintf("unable to evaluate constant expression"));
+    ConstExprValue *const_val = ir_resolve_const(ira, fn_value);
+    if (!const_val)
         return nullptr;
-    }
 
     return const_val->data.x_fn;
 }
@@ -2654,6 +2640,69 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
     zig_unreachable();
 }
 
+static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) {
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return false;
+
+    IrInstruction *casted_value = ir_get_casted_value(ira, value, ira->codegen->builtin_types.entry_usize);
+    if (casted_value->type_entry->id == TypeTableEntryIdInvalid)
+        return false;
+
+    ConstExprValue *const_val = ir_resolve_const(ira, casted_value);
+    if (!const_val)
+        return false;
+
+    *out = const_val->data.x_bignum.data.x_uint;
+    return true;
+}
+
+static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) {
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return false;
+
+    IrInstruction *casted_value = ir_get_casted_value(ira, value, ira->codegen->builtin_types.entry_bool);
+    if (casted_value->type_entry->id == TypeTableEntryIdInvalid)
+        return false;
+
+    ConstExprValue *const_val = ir_resolve_const(ira, casted_value);
+    if (!const_val)
+        return false;
+
+    *out = const_val->data.x_bool;
+    return true;
+}
+
+static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return nullptr;
+
+    TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
+    IrInstruction *casted_value = ir_get_casted_value(ira, value, str_type);
+    if (casted_value->type_entry->id == TypeTableEntryIdInvalid)
+        return nullptr;
+
+    ConstExprValue *const_val = ir_resolve_const(ira, casted_value);
+    if (!const_val)
+        return nullptr;
+
+    ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index];
+    ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index];
+    ConstExprValue *array_val = ptr_field->data.x_ptr.base_ptr;
+    assert(ptr_field->data.x_ptr.index != SIZE_MAX);
+    size_t len = len_field->data.x_bignum.data.x_uint;
+    Buf *result = buf_alloc();
+    buf_resize(result, len);
+    for (size_t i = 0; i < len; i += 1) {
+        size_t new_index = ptr_field->data.x_ptr.index + i;
+        ConstExprValue *char_val = &array_val->data.x_array.elements[new_index];
+        uint64_t big_c = char_val->data.x_bignum.data.x_uint;
+        assert(big_c <= UINT8_MAX);
+        uint8_t c = big_c;
+        buf_ptr(result)[i] = c;
+    }
+    return result;
+}
+
 static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
     IrInstructionReturn *return_instruction)
 {
@@ -3528,29 +3577,33 @@ static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr
 }
 
 static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) {
-    TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
-    IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type);
-    if (condition == ira->codegen->invalid_instruction)
-        return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
+    IrInstruction *condition = cond_br_instruction->condition->other;
 
     // TODO detect backward jumps
-    if (condition->static_value.special != ConstValSpecialRuntime) {
-        IrBasicBlock *old_dest_block = condition->static_value.data.x_bool ?
+
+    if (cond_br_instruction->is_inline || condition->static_value.special != ConstValSpecialRuntime) {
+        bool cond_is_true;
+        if (!ir_resolve_bool(ira, condition, &cond_is_true))
+            return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
+
+        IrBasicBlock *old_dest_block = cond_is_true ?
             cond_br_instruction->then_block : cond_br_instruction->else_block;
 
         if (cond_br_instruction->is_inline || old_dest_block->ref_count == 1) {
             ir_inline_bb(ira, old_dest_block);
             return ira->codegen->builtin_types.entry_unreachable;
         }
-    } else if (cond_br_instruction->is_inline) {
-        add_node_error(ira->codegen, condition->source_node,
-                buf_sprintf("unable to evaluate constant expression"));
-        return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
     }
 
+    TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
+    IrInstruction *casted_condition = ir_get_casted_value(ira, condition, bool_type);
+    if (casted_condition == ira->codegen->invalid_instruction)
+        return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
+
     IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block);
     IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block);
-    ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, condition, new_then_block, new_else_block, false);
+    ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base,
+            casted_condition, new_then_block, new_else_block, false);
     return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
 }
 
@@ -4255,6 +4308,100 @@ static TypeTableEntry *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionA
     return return_type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
+        IrInstructionArrayType *array_type_instruction)
+{
+    IrInstruction *size_value = array_type_instruction->size->other;
+    uint64_t size;
+    if (!ir_resolve_usize(ira, size_value, &size))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    IrInstruction *child_type_value = array_type_instruction->child_type->other;
+    TypeTableEntry *child_type = ir_resolve_type(ira, child_type_value);
+    TypeTableEntry *canon_child_type = get_underlying_type(child_type);
+    switch (canon_child_type->id) {
+        case TypeTableEntryIdTypeDecl:
+            zig_unreachable();
+        case TypeTableEntryIdInvalid:
+            return ira->codegen->builtin_types.entry_invalid;
+        case TypeTableEntryIdVar:
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
+        case TypeTableEntryIdBlock:
+            add_node_error(ira->codegen, array_type_instruction->base.source_node,
+                    buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_type->name)));
+            // TODO if this is a typedecl, add error note showing the declaration of the type decl
+            return ira->codegen->builtin_types.entry_invalid;
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+        case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdErrorUnion:
+        case TypeTableEntryIdPureError:
+        case TypeTableEntryIdEnum:
+        case TypeTableEntryIdUnion:
+        case TypeTableEntryIdFn:
+        case TypeTableEntryIdNamespace:
+        case TypeTableEntryIdGenericFn:
+            {
+                TypeTableEntry *result_type = get_array_type(ira->codegen, child_type, size);
+                bool depends_on_compile_var = child_type_value->static_value.depends_on_compile_var ||
+                    size_value->static_value.depends_on_compile_var;
+                ConstExprValue *out_val = ir_build_const_from(ira, &array_type_instruction->base,
+                        depends_on_compile_var);
+                out_val->data.x_type = result_type;
+                return ira->codegen->builtin_types.entry_type;
+            }
+    }
+    zig_unreachable();
+}
+
+static TypeTableEntry *ir_analyze_instruction_compile_var(IrAnalyze *ira,
+        IrInstructionCompileVar *compile_var_instruction)
+{
+    IrInstruction *name_value = compile_var_instruction->name->other;
+    Buf *var_name = ir_resolve_str(ira, name_value);
+    if (!var_name)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    ConstExprValue *out_val = ir_build_const_from(ira, &compile_var_instruction->base, true);
+    if (buf_eql_str(var_name, "is_big_endian")) {
+        out_val->data.x_bool = ira->codegen->is_big_endian;
+        return ira->codegen->builtin_types.entry_bool;
+    } else if (buf_eql_str(var_name, "is_release")) {
+        out_val->data.x_bool = ira->codegen->is_release_build;
+        return ira->codegen->builtin_types.entry_bool;
+    } else if (buf_eql_str(var_name, "is_test")) {
+        out_val->data.x_bool = ira->codegen->is_test_build;
+        return ira->codegen->builtin_types.entry_bool;
+    } else if (buf_eql_str(var_name, "os")) {
+        out_val->data.x_enum.tag = ira->codegen->target_os_index;
+        return ira->codegen->builtin_types.entry_os_enum;
+    } else if (buf_eql_str(var_name, "arch")) {
+        out_val->data.x_enum.tag = ira->codegen->target_arch_index;
+        return ira->codegen->builtin_types.entry_arch_enum;
+    } else if (buf_eql_str(var_name, "environ")) {
+        out_val->data.x_enum.tag = ira->codegen->target_environ_index;
+        return ira->codegen->builtin_types.entry_environ_enum;
+    } else if (buf_eql_str(var_name, "object_format")) {
+        out_val->data.x_enum.tag = ira->codegen->target_oformat_index;
+        return ira->codegen->builtin_types.entry_oformat_enum;
+    } else {
+        add_node_error(ira->codegen, name_value->source_node,
+            buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -4305,12 +4452,15 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
         case IrInstructionIdAsm:
             return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
+        case IrInstructionIdArrayType:
+            return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
+        case IrInstructionIdCompileVar:
+            return ir_analyze_instruction_compile_var(ira, (IrInstructionCompileVar *)instruction);
         case IrInstructionIdSwitchBr:
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
         case IrInstructionIdStructFieldPtr:
-        case IrInstructionIdArrayType:
             zig_panic("TODO analyze more instructions");
     }
     zig_unreachable();
@@ -4414,6 +4564,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdArrayType:
         case IrInstructionIdSliceType:
+        case IrInstructionIdCompileVar:
             return false;
         case IrInstructionIdAsm:
             {
@@ -5180,43 +5331,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //        case BuiltinFnIdCUndef:
 //            zig_panic("TODO");
 //
-//        case BuiltinFnIdCompileVar:
-//            {
-//                AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field;
-//
-//                Buf *var_name = resolve_const_expr_str(g, import, context, str_node);
-//                if (!var_name) {
-//                    return g->builtin_types.entry_invalid;
-//                }
-//
-//                ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-//                const_val->ok = true;
-//                const_val->depends_on_compile_var = true;
-//
-//                if (buf_eql_str(var_name, "is_big_endian")) {
-//                    return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true);
-//                } else if (buf_eql_str(var_name, "is_release")) {
-//                    return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true);
-//                } else if (buf_eql_str(var_name, "is_test")) {
-//                    return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true);
-//                } else if (buf_eql_str(var_name, "os")) {
-//                    const_val->data.x_enum.tag = g->target_os_index;
-//                    return g->builtin_types.entry_os_enum;
-//                } else if (buf_eql_str(var_name, "arch")) {
-//                    const_val->data.x_enum.tag = g->target_arch_index;
-//                    return g->builtin_types.entry_arch_enum;
-//                } else if (buf_eql_str(var_name, "environ")) {
-//                    const_val->data.x_enum.tag = g->target_environ_index;
-//                    return g->builtin_types.entry_environ_enum;
-//                } else if (buf_eql_str(var_name, "object_format")) {
-//                    const_val->data.x_enum.tag = g->target_oformat_index;
-//                    return g->builtin_types.entry_oformat_enum;
-//                } else {
-//                    add_node_error(g, *str_node,
-//                        buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name)));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//            }
 //        case BuiltinFnIdConstEval:
 //            {
 //                AstNode **expr_node = node->data.fn_call_expr.params.at(0)->parent_field;
@@ -7605,53 +7719,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    }
 //}
 //
-//static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    AstNode *size_node = node->data.array_type.size;
-//
-//    TypeTableEntry *child_type = analyze_type_expr_pointer_only(g, import, context,
-//            node->data.array_type.child_type, true);
-//
-//    if (child_type->id == TypeTableEntryIdUnreachable) {
-//        add_node_error(g, node, buf_create_from_str("array of unreachable not allowed"));
-//        return g->builtin_types.entry_invalid;
-//    } else if (child_type->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (size_node) {
-//        child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type);
-//        TypeTableEntry *size_type = analyze_expression(g, import, context,
-//                g->builtin_types.entry_usize, size_node);
-//        if (size_type->id == TypeTableEntryIdInvalid) {
-//            return g->builtin_types.entry_invalid;
-//        }
-//
-//        ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
-//        if (const_val->ok) {
-//            if (const_val->data.x_bignum.is_negative) {
-//                add_node_error(g, size_node,
-//                    buf_sprintf("array size %s is negative",
-//                        buf_ptr(bignum_to_buf(&const_val->data.x_bignum))));
-//                return g->builtin_types.entry_invalid;
-//            } else {
-//                return resolve_expr_const_val_as_type(g, node,
-//                        get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint), false);
-//            }
-//        } else if (context->fn_entry) {
-//            return resolve_expr_const_val_as_type(g, node,
-//                    get_slice_type(g, child_type, node->data.array_type.is_const), false);
-//        } else {
-//            add_node_error(g, first_executing_node(size_node),
-//                    buf_sprintf("unable to evaluate constant expression"));
-//            return g->builtin_types.entry_invalid;
-//        }
-//    } else {
-//        TypeTableEntry *slice_type = get_slice_type(g, child_type, node->data.array_type.is_const);
-//        return resolve_expr_const_val_as_type(g, node, slice_type, false);
-//    }
-//}
 //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
 //    size_t result = 0;
 //    while (inner_block != outer_block) {
@@ -9572,3 +9639,97 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
 //    LLVMPositionBuilderAtEnd(g->builder, basic_block);
 //    return nullptr;
 //}
+//static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
+//        LLVMValueRef val1, LLVMValueRef val2)
+//{
+//    // for unsigned left shifting, we do the wrapping shift, then logically shift
+//    // right the same number of bits
+//    // if the values don't match, we have an overflow
+//    // for signed left shifting we do the same except arithmetic shift right
+//
+//    assert(type_entry->id == TypeTableEntryIdInt);
+//
+//    LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
+//    LLVMValueRef orig_val;
+//    if (type_entry->data.integral.is_signed) {
+//        orig_val = LLVMBuildAShr(g->builder, result, val2, "");
+//    } else {
+//        orig_val = LLVMBuildLShr(g->builder, result, val2, "");
+//    }
+//    LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, "");
+//
+//    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
+//    LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
+//    LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, fail_block);
+//    gen_debug_safety_crash(g);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, ok_block);
+//    return result;
+//}
+//
+//static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
+//        TypeTableEntry *type_entry, bool exact)
+//{
+//
+//    if (want_debug_safety(g, source_node)) {
+//        LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
+//        LLVMValueRef is_zero_bit;
+//        if (type_entry->id == TypeTableEntryIdInt) {
+//            is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
+//        } else if (type_entry->id == TypeTableEntryIdFloat) {
+//            is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
+//        } else {
+//            zig_unreachable();
+//        }
+//        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk");
+//        LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail");
+//        LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block);
+//
+//        LLVMPositionBuilderAtEnd(g->builder, fail_block);
+//        gen_debug_safety_crash(g);
+//
+//        LLVMPositionBuilderAtEnd(g->builder, ok_block);
+//    }
+//
+//    if (type_entry->id == TypeTableEntryIdFloat) {
+//        assert(!exact);
+//        return LLVMBuildFDiv(g->builder, val1, val2, "");
+//    }
+//
+//    assert(type_entry->id == TypeTableEntryIdInt);
+//
+//    if (exact) {
+//        if (want_debug_safety(g, source_node)) {
+//            LLVMValueRef remainder_val;
+//            if (type_entry->data.integral.is_signed) {
+//                remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
+//            } else {
+//                remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
+//            }
+//            LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
+//            LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
+//
+//            LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactOk");
+//            LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactFail");
+//            LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+//
+//            LLVMPositionBuilderAtEnd(g->builder, fail_block);
+//            gen_debug_safety_crash(g);
+//
+//            LLVMPositionBuilderAtEnd(g->builder, ok_block);
+//        }
+//        if (type_entry->data.integral.is_signed) {
+//            return LLVMBuildExactSDiv(g->builder, val1, val2, "");
+//        } else {
+//            return ZigLLVMBuildExactUDiv(g->builder, val1, val2, "");
+//        }
+//    } else {
+//        if (type_entry->data.integral.is_signed) {
+//            return LLVMBuildSDiv(g->builder, val1, val2, "");
+//        } else {
+//            return LLVMBuildUDiv(g->builder, val1, val2, "");
+//        }
+//    }
+//}
src/ir_print.cpp
@@ -472,6 +472,12 @@ static void ir_print_asm(IrPrint *irp, IrInstructionAsm *instruction) {
     fprintf(irp->f, ")");
 }
 
+static void ir_print_compile_var(IrPrint *irp, IrInstructionCompileVar *instruction) {
+    fprintf(irp->f, "@compileVar(");
+    ir_print_other_instruction(irp, instruction->name);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -561,6 +567,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdAsm:
             ir_print_asm(irp, (IrInstructionAsm *)instruction);
             break;
+        case IrInstructionIdCompileVar:
+            ir_print_compile_var(irp, (IrInstructionCompileVar *)instruction);
+            break;
         case IrInstructionIdSwitchBr:
             zig_panic("TODO print more IR instructions");
     }