diff --git a/src/all_types.hpp b/src/all_types.hpp index 5ff3ce933..124eb81ba 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1081,8 +1081,7 @@ struct FnTableEntry { AstNode *fn_test_set_node; AstNode *fn_static_eval_set_node; - ZigList cast_alloca_list; - ZigList struct_val_expr_alloca_list; + ZigList alloca_list; ZigList 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, diff --git a/src/codegen.cpp b/src/codegen.cpp index 07110f9bf..c6e00419e 100644 --- a/src/codegen.cpp +++ b/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 diff --git a/src/ir.cpp b/src/ir.cpp index f74ed39e7..20fb30cbc 100644 --- a/src/ir.cpp +++ b/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 static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { T *special_instruction = allocate(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(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: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 46e8a91ad..98ed8ebcc 100644 --- a/src/ir_print.cpp +++ b/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"); }