Commit d9329ed389

Andrew Kelley <superjoe30@gmail.com>
2016-11-27 07:22:30
IR: add ref instruction
1 parent bd4d4ee
src/all_types.hpp
@@ -1081,8 +1081,7 @@ struct FnTableEntry {
     AstNode *fn_test_set_node;
     AstNode *fn_static_eval_set_node;
 
-    ZigList<IrInstructionCast *> cast_alloca_list;
-    ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
+    ZigList<IrInstruction *> alloca_list;
     ZigList<VariableTableEntry *> variable_list;
 };
 
@@ -1414,6 +1413,7 @@ enum IrInstructionId {
     IrInstructionIdStaticEval,
     IrInstructionIdImport,
     IrInstructionIdArrayLen,
+    IrInstructionIdRef,
 };
 
 struct IrInstruction {
@@ -1771,6 +1771,13 @@ struct IrInstructionArrayLen {
     IrInstruction *array_value;
 };
 
+struct IrInstructionRef {
+    IrInstruction base;
+
+    IrInstruction *value;
+    LLVMValueRef tmp_ptr;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/codegen.cpp
@@ -1661,6 +1661,16 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstru
     return phi;
 }
 
+static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstructionRef *instruction) {
+    LLVMValueRef value = ir_llvm_value(g, instruction->value);
+    if (handle_is_ptr(instruction->value->type_entry)) {
+        return value;
+    } else {
+        LLVMBuildStore(g->builder, value, instruction->tmp_ptr);
+        return instruction->tmp_ptr;
+    }
+}
+
 static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
     set_debug_source_node(g, instruction->source_node);
 
@@ -1724,6 +1734,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction);
         case IrInstructionIdPhi:
             return ir_render_phi(g, executable, (IrInstructionPhi *)instruction);
+        case IrInstructionIdRef:
+            return ir_render_ref(g, executable, (IrInstructionRef *)instruction);
         case IrInstructionIdSwitchVar:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
@@ -2317,17 +2329,20 @@ static void do_code_gen(CodeGen *g) {
 
         clear_debug_source_node(g);
 
-        // allocate structs which are the result of casts
-        for (size_t cea_i = 0; cea_i < fn_table_entry->cast_alloca_list.length; cea_i += 1) {
-            IrInstructionCast *cast_instruction = fn_table_entry->cast_alloca_list.at(cea_i);
-            cast_instruction->tmp_ptr = LLVMBuildAlloca(g->builder, cast_instruction->base.type_entry->type_ref, "");
-        }
-
-        // allocate structs which are struct value expressions
-        for (size_t alloca_i = 0; alloca_i < fn_table_entry->struct_val_expr_alloca_list.length; alloca_i += 1) {
-            StructValExprCodeGen *struct_val_expr_node = fn_table_entry->struct_val_expr_alloca_list.at(alloca_i);
-            struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder,
-                    struct_val_expr_node->type_entry->type_ref, "");
+        // allocate temporary stack data
+        for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) {
+            IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
+            LLVMValueRef *slot;
+            if (instruction->id == IrInstructionIdCast) {
+                IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction;
+                slot = &cast_instruction->tmp_ptr;
+            } else if (instruction->id == IrInstructionIdRef) {
+                IrInstructionRef *ref_instruction = (IrInstructionRef *)instruction;
+                slot = &ref_instruction->tmp_ptr;
+            } else {
+                zig_unreachable();
+            }
+            *slot = LLVMBuildAlloca(g->builder, instruction->type_entry->type_ref, "");
         }
 
         // create debug variable declarations for variables and allocate all local variables
src/ir.cpp
@@ -272,6 +272,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayLen *) {
     return IrInstructionIdArrayLen;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) {
+    return IrInstructionIdRef;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -1112,6 +1116,21 @@ static IrInstruction *ir_build_array_len_from(IrBuilder *irb, IrInstruction *old
     return new_instruction;
 }
 
+static IrInstruction *ir_build_ref(IrBuilder *irb, AstNode *source_node, IrInstruction *value) {
+    IrInstructionRef *instruction = ir_build_instruction<IrInstructionRef>(irb, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_ref_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
+    IrInstruction *new_instruction = ir_build_ref(irb, old_instruction->source_node, value);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
         bool gen_error_defers, bool gen_maybe_defers)
 {
@@ -2493,6 +2512,17 @@ static IrInstruction *ir_gen_goto(IrBuilder *irb, AstNode *node) {
     return ir_build_unreachable(irb, node);
 }
 
+static IrInstruction *ir_lval_wrap(IrBuilder *irb, IrInstruction *value, LValPurpose lval) {
+    if (lval == LValPurposeNone)
+        return value;
+    if (value == irb->codegen->invalid_instruction)
+        return value;
+
+    // We needed a pointer to a value, but we got a value. So we create
+    // an instruction which just makes a const pointer of it.
+    return ir_build_ref(irb, value->source_node, value);
+}
+
 static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContext *block_context,
         LValPurpose lval)
 {
@@ -2509,7 +2539,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContex
         case NodeTypeSymbol:
             return ir_gen_symbol(irb, node, lval);
         case NodeTypeFnCallExpr:
-            return ir_gen_fn_call(irb, node);
+            return ir_lval_wrap(irb, ir_gen_fn_call(irb, node), lval);
         case NodeTypeIfBoolExpr:
             return ir_gen_if_bool_expr(irb, node);
         case NodeTypePrefixOpExpr:
@@ -2898,8 +2928,7 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst
                 dest_type, value, cast_op);
         result->type_entry = wanted_type;
         if (need_alloca && source_instr->source_node->block_context->fn_entry) {
-            IrInstructionCast *cast_instruction = (IrInstructionCast *)result;
-            source_instr->source_node->block_context->fn_entry->cast_alloca_list.append(cast_instruction);
+            source_instr->source_node->block_context->fn_entry->alloca_list.append(result);
         }
         return result;
     }
@@ -5700,6 +5729,31 @@ static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira,
     }
 }
 
+static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) {
+    IrInstruction *value = ref_instruction->value->other;
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    FnTableEntry *fn_entry = ref_instruction->base.source_node->block_context->fn_entry;
+    if (!fn_entry || value->static_value.special != ConstValSpecialRuntime) {
+        ConstExprValue *val = ir_resolve_const(ira, value);
+        if (!val)
+            return ira->codegen->builtin_types.entry_invalid;
+        return ir_analyze_const_ptr(ira, &ref_instruction->base, val, value->type_entry, false);
+    }
+
+    TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true);
+    if (handle_is_ptr(value->type_entry)) {
+        // this instruction is a noop - codegen can pass the pointer we already have as the result
+        ir_link_new_instruction(value, &ref_instruction->base);
+        return ptr_type;
+    } else {
+        fn_entry->alloca_list.append(&ref_instruction->base);
+        ir_build_ref_from(&ira->new_irb, &ref_instruction->base, value);
+        return ptr_type;
+    }
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -5778,6 +5832,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction);
         case IrInstructionIdArrayLen:
             return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction);
+        case IrInstructionIdRef:
+            return ir_analyze_instruction_ref(ira, (IrInstructionRef *)instruction);
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
@@ -5901,6 +5957,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdSwitchTarget:
         case IrInstructionIdEnumTag:
         case IrInstructionIdStaticEval:
+        case IrInstructionIdRef:
             return false;
         case IrInstructionIdAsm:
             {
src/ir_print.cpp
@@ -586,6 +586,11 @@ static void ir_print_array_len(IrPrint *irp, IrInstructionArrayLen *instruction)
     fprintf(irp->f, ".len");
 }
 
+static void ir_print_ref(IrPrint *irp, IrInstructionRef *instruction) {
+    fprintf(irp->f, "ref ");
+    ir_print_other_instruction(irp, instruction->value);
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -714,6 +719,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdArrayLen:
             ir_print_array_len(irp, (IrInstructionArrayLen *)instruction);
             break;
+        case IrInstructionIdRef:
+            ir_print_ref(irp, (IrInstructionRef *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }