From 5d9f2c3de760ba6a45097e6f23188a1a0cf85efd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Oct 2018 17:35:16 -0400 Subject: [PATCH 01/17] copy elision for variable declarations See #287 --- src/all_types.hpp | 27 +++++++++- src/codegen.cpp | 43 ++++++++++----- src/ir.cpp | 130 ++++++++++++++++++++++++++++++---------------- 3 files changed, 142 insertions(+), 58 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index a14401394780..f55929bc3747 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -40,6 +40,7 @@ struct ZigWindowsSDK; struct Tld; struct TldExport; struct IrAnalyze; +struct IrResultLocationAlloca; enum X64CABIClass { X64CABIClass_Unknown, @@ -1321,6 +1322,7 @@ struct ZigFn { AstNode *fn_static_eval_set_node; ZigList alloca_list; + ZigList result_loc_alloca_list; ZigList variable_list; Buf *section_name; @@ -2175,8 +2177,27 @@ enum IrInstructionId { IrInstructionIdCheckRuntimeScope, }; +enum IrResultLocationId { + IrResultLocationIdVar, + IrResultLocationIdAlloca, +}; + +struct IrResultLocation { + IrResultLocationId id; +}; + +struct IrResultLocationVar { + IrResultLocation base; + ZigVar *var; +}; + +struct IrResultLocationAlloca { + IrResultLocation base; + LLVMValueRef alloca; + ZigType *ty; +}; + struct IrInstruction { - IrInstructionId id; Scope *scope; AstNode *source_node; ConstExprValue value; @@ -2190,6 +2211,8 @@ struct IrInstruction { // with this child field. IrInstruction *child; IrBasicBlock *owner_bb; + + IrInstructionId id; // true if this instruction was generated by zig and not from user code bool is_gen; }; @@ -2379,9 +2402,9 @@ struct IrInstructionCall { size_t arg_count; IrInstruction **args; bool is_comptime; - LLVMValueRef tmp_ptr; FnInline fn_inline; bool is_async; + IrResultLocation *result_location; IrInstruction *async_allocator; IrInstruction *new_stack; diff --git a/src/codegen.cpp b/src/codegen.cpp index 02105b4d2a74..25711b76eb4d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3197,7 +3197,9 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, PtrLenSingle, var->align_bytes, 0, 0); LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); - gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); + if (var->value_ref != llvm_init_val) { + gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); + } } else { bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); if (want_safe) { @@ -3421,6 +3423,19 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); } +static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { + if (base == nullptr) + return nullptr; + switch (base->id) { + case IrResultLocationIdAlloca: + return reinterpret_cast(base)->alloca; + case IrResultLocationIdVar: + // TODO check next_var field? + return reinterpret_cast(base)->var->value_ref; + } + zig_unreachable(); +} + static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { LLVMValueRef fn_val; ZigType *fn_type; @@ -3433,6 +3448,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr fn_type = instruction->fn_ref->value.type; } + LLVMValueRef tmp_ptr = gen_result_location(g, instruction->result_location); + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; ZigType *src_return_type = fn_type_id->return_type; @@ -3445,7 +3462,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool is_var_args = fn_type_id->is_var_args; ZigList gen_param_values = {}; if (first_arg_ret) { - gen_param_values.append(instruction->tmp_ptr); + gen_param_values.append(tmp_ptr); } if (prefix_arg_err_ret_stack) { gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); @@ -3453,7 +3470,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { gen_param_values.append(ir_llvm_value(g, instruction->async_allocator)); - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, tmp_ptr, err_union_err_index, ""); gen_param_values.append(err_val_ptr); } FnWalk fn_walk = {}; @@ -3496,9 +3513,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, tmp_ptr, err_union_payload_index, ""); LLVMBuildStore(g->builder, result, payload_ptr); - return instruction->tmp_ptr; + return tmp_ptr; } if (src_return_type->id == ZigTypeIdUnreachable) { @@ -3507,11 +3524,11 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr return nullptr; } else if (first_arg_ret) { set_call_instr_sret(g, result); - return instruction->tmp_ptr; + return tmp_ptr; } else if (handle_is_ptr(src_return_type)) { - auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr); - LLVMSetAlignment(store_instr, LLVMGetAlignment(instruction->tmp_ptr)); - return instruction->tmp_ptr; + auto store_instr = LLVMBuildStore(g->builder, result, tmp_ptr); + LLVMSetAlignment(store_instr, LLVMGetAlignment(tmp_ptr)); + return tmp_ptr; } else { return result; } @@ -6197,6 +6214,11 @@ static void do_code_gen(CodeGen *g) { } // allocate temporary stack data + for (size_t alloca_i = 0; alloca_i < fn_table_entry->result_loc_alloca_list.length; alloca_i += 1) { + IrResultLocationAlloca *alloca_loc = fn_table_entry->result_loc_alloca_list.at(alloca_i); + alloca_loc->alloca = build_alloca(g, alloca_loc->ty, "", get_abi_alignment(g, alloca_loc->ty)); + } + // TODO migrate these to use the Result Location mechanism 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; @@ -6218,9 +6240,6 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdUnionInit) { IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction; slot = &union_init_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCall) { - IrInstructionCall *call_instruction = (IrInstructionCall *)instruction; - slot = &call_instruction->tmp_ptr; } else if (instruction->id == IrInstructionIdSlice) { IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; slot = &slice_instruction->tmp_ptr; diff --git a/src/ir.cpp b/src/ir.cpp index c48b56255d1c..999598000789 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -139,7 +139,9 @@ struct ConstCastErrSetMismatch { }; static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); -static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); +static IrInstruction *ir_gen_node_lval(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); +static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, + IrResultLocation *result_location); static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type); static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr); @@ -1169,7 +1171,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, - IrInstruction *new_stack) + IrInstruction *new_stack, IrResultLocation *result_location) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; @@ -1181,6 +1183,7 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->is_async = is_async; call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; + call_instruction->result_location = result_location; if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); @@ -3017,7 +3020,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *err_union_ptr = ir_gen_node_lval(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); @@ -3218,7 +3221,7 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no } static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); + IrInstruction *lvalue = ir_gen_node_lval(irb, node->data.bin_op_expr.op1, scope, LValPtr); IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) @@ -3229,7 +3232,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) } static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); + IrInstruction *lvalue = ir_gen_node_lval(irb, node->data.bin_op_expr.op1, scope, LValPtr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); @@ -3331,7 +3334,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); + IrInstruction *maybe_ptr = ir_gen_node_lval(irb, op1_node, parent_scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3567,7 +3570,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; - IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr); + IrInstruction *array_ref_instruction = ir_gen_node_lval(irb, array_ref_node, scope, LValPtr); if (array_ref_instruction == irb->codegen->invalid_instruction) return array_ref_instruction; @@ -3590,7 +3593,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode AstNode *container_ref_node = node->data.field_access_expr.struct_expr; Buf *field_name = node->data.field_access_expr.field_name; - IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr); + IrInstruction *container_ref_instruction = ir_gen_node_lval(irb, container_ref_node, scope, LValPtr); if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; @@ -4260,7 +4263,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr); + IrInstruction *arg0_value = ir_gen_node_lval(irb, arg0_node, scope, LValPtr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4490,7 +4493,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr, nullptr); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdNewStackCall: @@ -4520,7 +4523,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return args[i]; } - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack, nullptr); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: @@ -4730,7 +4733,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo zig_unreachable(); } -static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrResultLocation *result_location) +{ assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) @@ -4760,7 +4765,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + FnInlineAuto, is_async, async_allocator, nullptr, result_location); return ir_lval_wrap(irb, scope, fn_call, lval); } @@ -4825,7 +4831,7 @@ static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, Ast assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); + IrInstruction *value = ir_gen_node_lval(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) return value; @@ -4906,7 +4912,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *err_union_ptr = ir_gen_node_lval(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -4951,7 +4957,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), lval); + return ir_lval_wrap(irb, scope, ir_gen_node_lval(irb, expr_node, scope, LValPtr), lval); } } zig_unreachable(); @@ -5050,11 +5056,16 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } + IrResultLocationVar *result_loc = allocate(1); + result_loc->base.id = IrResultLocationIdVar; + result_loc->var = var; + // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. Buf *old_exec_name = irb->exec->name; irb->exec->name = variable_declaration->symbol; - IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); + IrInstruction *init_value = ir_gen_node_extra(irb, variable_declaration->expr, scope, LValNone, + &result_loc->base); irb->exec->name = old_exec_name; if (init_value == irb->codegen->invalid_instruction) @@ -5098,7 +5109,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } else { payload_scope = subexpr_scope; } - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); + IrInstruction *err_val_ptr = ir_gen_node_lval(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); @@ -5185,7 +5196,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); + IrInstruction *maybe_val_ptr = ir_gen_node_lval(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); @@ -5335,7 +5346,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo } assert(elem_node->type == NodeTypeSymbol); - IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr); + IrInstruction *array_val_ptr = ir_gen_node_lval(irb, array_node, parent_scope, LValPtr); if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; @@ -5597,7 +5608,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no AstNode *else_node = node->data.test_expr.else_node; bool var_is_ptr = node->data.test_expr.var_is_ptr; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *maybe_val_ptr = ir_gen_node_lval(irb, expr_node, scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; @@ -5676,7 +5687,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); + IrInstruction *err_val_ptr = ir_gen_node_lval(irb, target_node, scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; @@ -5803,7 +5814,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; - IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); + IrInstruction *target_value_ptr = ir_gen_node_lval(irb, target_node, scope, LValPtr); if (target_value_ptr == irb->codegen->invalid_instruction) return target_value_ptr; IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); @@ -5995,7 +6006,7 @@ static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNo assert(node->type == NodeTypeCompTime); Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope); - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval); + return ir_gen_node_lval(irb, node->data.comptime_expr.expr, child_scope, lval); } static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { @@ -6174,7 +6185,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; - IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr); + IrInstruction *ptr_value = ir_gen_node_lval(irb, array_node, scope, LValPtr); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6212,7 +6223,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN } - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); + IrInstruction *err_union_ptr = ir_gen_node_lval(irb, op1_node, parent_scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6949,7 +6960,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod } static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, - LVal lval) + LVal lval, IrResultLocation *result_location) { assert(scope); switch (node->type) { @@ -6966,7 +6977,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeBlock: return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval); case NodeTypeGroupedExpr: - return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval); + return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_location); case NodeTypeBinOpExpr: return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval); case NodeTypeIntLiteral: @@ -6978,7 +6989,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval); case NodeTypeFnCallExpr: - return ir_gen_fn_call(irb, scope, node, lval); + return ir_gen_fn_call(irb, scope, node, lval, result_location); case NodeTypeIfBoolExpr: return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: @@ -7007,7 +7018,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; - IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); + IrInstruction *value = ir_gen_node_lval(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) return value; @@ -7016,7 +7027,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *maybe_ptr = ir_gen_node_lval(irb, expr_node, scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -7082,14 +7093,20 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop zig_unreachable(); } -static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { - IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval); +static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, + IrResultLocation *result_location) +{ + IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval, result_location); irb->exec->invalid = irb->exec->invalid || (result == irb->codegen->invalid_instruction); return result; } +static IrInstruction *ir_gen_node_lval(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { + return ir_gen_node_extra(irb, node, scope, lval, nullptr); +} + static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { - return ir_gen_node_extra(irb, node, scope, LValNone); + return ir_gen_node_extra(irb, node, scope, LValNone, nullptr); } static void invalidate_exec(IrExecutable *exec) { @@ -7219,7 +7236,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup"); } - IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone); + IrInstruction *result = ir_gen_node_lval(irb, node, scope, LValNone); assert(result); if (irb->exec->invalid) return false; @@ -7317,7 +7334,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction **args = allocate(arg_count); args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem - ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); + ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr, nullptr); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); @@ -9118,6 +9135,7 @@ static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *t if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry != nullptr) { + assert(instruction->id != IrInstructionIdCall); fn_entry->alloca_list.append(instruction); } } @@ -12822,6 +12840,24 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i zig_unreachable(); } +static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLocation *result_location, + ZigType *ty) +{ + if (result_location != nullptr) + return result_location; + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + if (fn_entry == nullptr) + return nullptr; + if (type_has_bits(ty) && handle_is_ptr(ty)) { + IrResultLocationAlloca *alloca_loc = allocate(1); + alloca_loc->base.id = IrResultLocationIdAlloca; + alloca_loc->ty = ty; + fn_entry->result_loc_alloca_list.append(alloca_loc); + return &alloca_loc->base; + } + return nullptr; +} + static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) { @@ -12855,8 +12891,12 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *promise_type = get_promise_type(ira->codegen, return_type); ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); + IrResultLocation *result_location = ir_analyze_result_location(ira, call_instruction->result_location, + async_return_type); + IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr); + fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr, + result_location); result->value.type = async_return_type; return result; } @@ -13500,19 +13540,19 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); - ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result); } assert(async_allocator_inst == nullptr); + IrResultLocation *result_location = ir_analyze_result_location(ira, call_instruction->result_location, + return_type); + IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, - call_instruction->is_async, nullptr, casted_new_stack); + call_instruction->is_async, nullptr, casted_new_stack, result_location); new_call_instruction->value.type = return_type; - ir_add_alloca(ira, new_call_instruction, return_type); - return ir_finish_anal(ira, new_call_instruction); } @@ -13596,7 +13636,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, casted_args, call_param_count, async_allocator_inst); - ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result); } @@ -13606,11 +13645,14 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } + IrResultLocation *result_location = ir_analyze_result_location(ira, call_instruction->result_location, + return_type); + IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack); + fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack, + result_location); new_call_instruction->value.type = return_type; - ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, new_call_instruction); } From f1890e438974f2cf5d8ed72f004da75a1242d7dc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Oct 2018 18:05:31 -0400 Subject: [PATCH 02/17] more consistently set undefined structs to 0xaa in debug mode --- src/codegen.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 25711b76eb4d..477642a4fd77 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3173,6 +3173,26 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildICmp(g->builder, LLVMIntEQ, value, zero, ""); } +static bool value_is_all_undef(ConstExprValue *const_val) { + switch (const_val->special) { + case ConstValSpecialRuntime: + return false; + case ConstValSpecialUndef: + return true; + case ConstValSpecialStatic: + if (const_val->type->id == ZigTypeIdStruct) { + for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) { + if (!value_is_all_undef(&const_val->data.x_struct.fields[i])) + return false; + } + return true; + } else { + return false; + } + } + zig_unreachable(); +} + static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstructionDeclVar *decl_var_instruction) { @@ -3186,11 +3206,8 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstruction *init_value = decl_var_instruction->init_value; - bool have_init_expr = false; - ConstExprValue *const_val = &init_value->value; - if (const_val->special == ConstValSpecialRuntime || const_val->special == ConstValSpecialStatic) - have_init_expr = true; + bool have_init_expr = !value_is_all_undef(const_val); if (have_init_expr) { assert(var->value->type == init_value->value.type); From 06354979338a648d505420ae8c6509383e5b8b13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Oct 2018 19:01:56 -0400 Subject: [PATCH 03/17] return location mechanism works with optional wrap cast --- src/all_types.hpp | 13 +++ src/codegen.cpp | 50 ++++++++--- src/ir.cpp | 214 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 260 insertions(+), 17 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index f55929bc3747..a6316e392e6e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -607,6 +607,7 @@ struct AstNodeUnwrapOptional { enum CastOp { CastOpNoCast, // signifies the function call expression is not a cast CastOpNoop, // fn call expr is a cast, but does nothing + CastOpNoopResultLoc, // don't copy the value; it's handled by result location mechanism CastOpIntToFloat, CastOpFloatToInt, CastOpBoolToInt, @@ -2180,6 +2181,8 @@ enum IrInstructionId { enum IrResultLocationId { IrResultLocationIdVar, IrResultLocationIdAlloca, + IrResultLocationIdLVal, + IrResultLocationIdOptionalUnwrap, }; struct IrResultLocation { @@ -2197,6 +2200,16 @@ struct IrResultLocationAlloca { ZigType *ty; }; +struct IrResultLocationLVal { + IrResultLocation base; + IrInstruction *parent_instruction; +}; + +struct IrResultLocationOptionalUnwrap { + IrResultLocation base; + IrResultLocation *parent; +}; + struct IrInstruction { Scope *scope; AstNode *source_node; diff --git a/src/codegen.cpp b/src/codegen.cpp index 477642a4fd77..23b6d1ee7978 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2815,6 +2815,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, case CastOpNumLitToConcrete: zig_unreachable(); case CastOpNoop: + case CastOpNoopResultLoc: return expr_val; case CastOpResizeSlice: { @@ -3213,8 +3214,10 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, assert(var->value->type == init_value->value.type); ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, PtrLenSingle, var->align_bytes, 0, 0); - LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); - if (var->value_ref != llvm_init_val) { + bool elide_assign = init_value->id == IrInstructionIdCast && + reinterpret_cast(init_value)->cast_op == CastOpNoopResultLoc; + if (!elide_assign) { + LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); } } else { @@ -3270,6 +3273,11 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { + bool elide_assign = (instruction->value->id == IrInstructionIdCast && + reinterpret_cast(instruction->value)->cast_op == CastOpNoopResultLoc); + if (elide_assign) + return nullptr; + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); LLVMValueRef value = ir_llvm_value(g, instruction->value); @@ -3441,14 +3449,29 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { } static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { - if (base == nullptr) - return nullptr; + assert(base != nullptr); switch (base->id) { - case IrResultLocationIdAlloca: - return reinterpret_cast(base)->alloca; - case IrResultLocationIdVar: - // TODO check next_var field? - return reinterpret_cast(base)->var->value_ref; + case IrResultLocationIdAlloca: { + IrResultLocationAlloca *alloca_loc = reinterpret_cast(base); + return alloca_loc->alloca; + } + case IrResultLocationIdVar: { + IrResultLocationVar *var_loc = reinterpret_cast(base); + return var_loc->var->value_ref; + } + case IrResultLocationIdLVal: { + IrResultLocationLVal *lval_loc = reinterpret_cast(base); + IrInstruction *ptr = lval_loc->parent_instruction->child; + return ir_llvm_value(g, ptr); + } + case IrResultLocationIdOptionalUnwrap: { + IrResultLocationOptionalUnwrap *opt_unwrap_loc = reinterpret_cast(base); + LLVMValueRef parent = gen_result_location(g, opt_unwrap_loc->parent); + + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, parent, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); + return LLVMBuildStructGEP(g->builder, parent, maybe_child_index, ""); + } } zig_unreachable(); } @@ -3465,8 +3488,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr fn_type = instruction->fn_ref->value.type; } - LLVMValueRef tmp_ptr = gen_result_location(g, instruction->result_location); - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; ZigType *src_return_type = fn_type_id->return_type; @@ -3477,6 +3498,13 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id); bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; + + LLVMValueRef tmp_ptr; + if (first_arg_ret || instruction->is_async || handle_is_ptr(src_return_type)) { + tmp_ptr = gen_result_location(g, instruction->result_location); + assert(tmp_ptr != nullptr); + } + ZigList gen_param_values = {}; if (first_arg_ret) { gen_param_values.append(tmp_ptr); diff --git a/src/ir.cpp b/src/ir.cpp index 999598000789..e231410dc604 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3222,7 +3222,11 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { IrInstruction *lvalue = ir_gen_node_lval(irb, node->data.bin_op_expr.op1, scope, LValPtr); - IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrResultLocationLVal *lval_result_loc = allocate(1); + lval_result_loc->base.id = IrResultLocationIdLVal; + lval_result_loc->parent_instruction = lvalue; + IrInstruction *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone, + &lval_result_loc->base); if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -9169,6 +9173,7 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: + case CastOpNoopResultLoc: { bool same_global_refs = other_val->special == ConstValSpecialStatic; copy_const_val(const_val, other_val, same_global_refs); @@ -9644,6 +9649,160 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } +static IrResultLocation **ir_get_result_location(IrInstruction *base) { + switch (base->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdCall: { + IrInstructionCall *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdBr: + case IrInstructionIdCondBr: + case IrInstructionIdSwitchBr: + case IrInstructionIdDeclVar: + case IrInstructionIdStorePtr: + case IrInstructionIdReturn: + case IrInstructionIdUnreachable: + case IrInstructionIdSetCold: + case IrInstructionIdSetRuntimeSafety: + case IrInstructionIdSetFloatMode: + case IrInstructionIdImport: + case IrInstructionIdCompileErr: + case IrInstructionIdCompileLog: + case IrInstructionIdCImport: + case IrInstructionIdCInclude: + case IrInstructionIdCDefine: + case IrInstructionIdCUndef: + case IrInstructionIdCmpxchg: + case IrInstructionIdFence: + case IrInstructionIdMemset: + case IrInstructionIdMemcpy: + case IrInstructionIdBreakpoint: + case IrInstructionIdOverflowOp: + case IrInstructionIdCheckSwitchProngs: + case IrInstructionIdCheckStatementIsVoid: + case IrInstructionIdCheckRuntimeScope: + case IrInstructionIdPanic: + case IrInstructionIdSetEvalBranchQuota: + case IrInstructionIdPtrType: + case IrInstructionIdSetAlignStack: + case IrInstructionIdExport: + case IrInstructionIdCancel: + case IrInstructionIdCoroId: + case IrInstructionIdCoroBegin: + case IrInstructionIdCoroAllocFail: + case IrInstructionIdCoroEnd: + case IrInstructionIdCoroResume: + case IrInstructionIdCoroSave: + case IrInstructionIdCoroAllocHelper: + case IrInstructionIdAwaitBookkeeping: + case IrInstructionIdSaveErrRetAddr: + case IrInstructionIdAddImplicitReturnType: + case IrInstructionIdMergeErrRetTraces: + case IrInstructionIdMarkErrRetTracePtr: + case IrInstructionIdAtomicRmw: + case IrInstructionIdPhi: + case IrInstructionIdUnOp: + case IrInstructionIdBinOp: + case IrInstructionIdLoadPtr: + case IrInstructionIdConst: + case IrInstructionIdCast: + case IrInstructionIdContainerInitList: + case IrInstructionIdContainerInitFields: + case IrInstructionIdStructInit: + case IrInstructionIdUnionInit: + case IrInstructionIdFieldPtr: + case IrInstructionIdElemPtr: + case IrInstructionIdVarPtr: + case IrInstructionIdTypeOf: + case IrInstructionIdToPtrType: + case IrInstructionIdPtrTypeChild: + case IrInstructionIdArrayLen: + case IrInstructionIdStructFieldPtr: + case IrInstructionIdUnionFieldPtr: + case IrInstructionIdArrayType: + case IrInstructionIdPromiseType: + case IrInstructionIdSliceType: + case IrInstructionIdSizeOf: + case IrInstructionIdTestNonNull: + case IrInstructionIdUnwrapOptional: + case IrInstructionIdClz: + case IrInstructionIdCtz: + case IrInstructionIdPopCount: + case IrInstructionIdSwitchVar: + case IrInstructionIdSwitchTarget: + case IrInstructionIdUnionTag: + case IrInstructionIdRef: + case IrInstructionIdMinValue: + case IrInstructionIdMaxValue: + case IrInstructionIdEmbedFile: + case IrInstructionIdTruncate: + case IrInstructionIdIntType: + case IrInstructionIdBoolNot: + case IrInstructionIdSlice: + case IrInstructionIdMemberCount: + case IrInstructionIdMemberType: + case IrInstructionIdMemberName: + case IrInstructionIdAlignOf: + case IrInstructionIdReturnAddress: + case IrInstructionIdFrameAddress: + case IrInstructionIdHandle: + case IrInstructionIdTestErr: + case IrInstructionIdUnwrapErrCode: + case IrInstructionIdOptionalWrap: + case IrInstructionIdErrWrapCode: + case IrInstructionIdErrWrapPayload: + case IrInstructionIdFnProto: + case IrInstructionIdTestComptime: + case IrInstructionIdPtrCast: + case IrInstructionIdBitCast: + case IrInstructionIdWidenOrShorten: + case IrInstructionIdPtrToInt: + case IrInstructionIdIntToPtr: + case IrInstructionIdIntToEnum: + case IrInstructionIdIntToErr: + case IrInstructionIdErrToInt: + case IrInstructionIdDeclRef: + case IrInstructionIdErrName: + case IrInstructionIdTypeName: + case IrInstructionIdTagName: + case IrInstructionIdFieldParentPtr: + case IrInstructionIdByteOffsetOf: + case IrInstructionIdBitOffsetOf: + case IrInstructionIdTypeInfo: + case IrInstructionIdTypeId: + case IrInstructionIdAlignCast: + case IrInstructionIdOpaqueType: + case IrInstructionIdArgType: + case IrInstructionIdTagType: + case IrInstructionIdErrorReturnTrace: + case IrInstructionIdErrorUnion: + case IrInstructionIdGetImplicitAllocator: + case IrInstructionIdCoroAlloc: + case IrInstructionIdCoroSize: + case IrInstructionIdCoroSuspend: + case IrInstructionIdCoroFree: + case IrInstructionIdCoroPromise: + case IrInstructionIdPromiseResultType: + case IrInstructionIdSqrt: + case IrInstructionIdAtomicLoad: + case IrInstructionIdIntCast: + case IrInstructionIdFloatCast: + case IrInstructionIdErrSetCast: + case IrInstructionIdIntToFloat: + case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: + case IrInstructionIdFromBytes: + case IrInstructionIdToBytes: + case IrInstructionIdEnumToInt: + case IrInstructionIdAsm: + case IrInstructionIdUnwrapErrPayload: + return nullptr; + } + zig_unreachable(); +} + static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); @@ -9669,6 +9828,22 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc return &const_instruction->base; } + // If we have a result location, we push a special OptionalWrap result location value + // to the result location stack. + IrResultLocation **result_location = ir_get_result_location(value); + if (result_location != nullptr) { + IrResultLocation *prev_result_location = *result_location; + IrResultLocationOptionalUnwrap *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdOptionalUnwrap; + new_result_location->parent = prev_result_location; + *result_location = &new_result_location->base; + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpNoopResultLoc); + result->value.data.rh_maybe = RuntimeHintOptionalNonNull; + result->value.type = wanted_type; + return result; + } + IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; @@ -12840,11 +13015,7 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i zig_unreachable(); } -static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLocation *result_location, - ZigType *ty) -{ - if (result_location != nullptr) - return result_location; +static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry == nullptr) return nullptr; @@ -12858,6 +13029,37 @@ static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLoca return nullptr; } +static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLocation *result_location, + ZigType *ty) +{ + if (result_location != nullptr) { + if (result_location->id == IrResultLocationIdLVal) { + IrResultLocationLVal *lval_loc = reinterpret_cast(result_location); + if (lval_loc->parent_instruction->value.special == ConstValSpecialStatic && + lval_loc->parent_instruction->value.type->id == ZigTypeIdPointer && + lval_loc->parent_instruction->value.data.x_ptr.special == ConstPtrSpecialDiscard) + { + // We have to convert this to an Alloca because the function is sret + // and so even though we want to discard the result, we need a stack allocation + // to pass as the sret pointer. + return create_alloca_result_loc(ira, ty); + } + } else if (result_location->id == IrResultLocationIdVar) { + // We have to resolve the variable in case of an inline loop. + IrResultLocationVar *var_loc = reinterpret_cast(result_location); + IrResultLocationVar *new_var_loc = allocate(1); + new_var_loc->base.id = IrResultLocationIdVar; + new_var_loc->var = var_loc->var; + while (new_var_loc->var->next_var != nullptr) { + new_var_loc->var = new_var_loc->var->next_var; + } + return &var_loc->base; + } + return result_location; + } + return create_alloca_result_loc(ira, ty); +} + static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) { From 0a3e1b7c5dfaff16193083cf3809dc3088f0670e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Oct 2018 18:35:34 -0400 Subject: [PATCH 04/17] move all automatic stack allocations to result location mechanism --- src/all_types.hpp | 27 +++-- src/codegen.cpp | 280 ++++++++++++++++++++-------------------------- src/ir.cpp | 111 +++++++++--------- 3 files changed, 193 insertions(+), 225 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index a6316e392e6e..d7ecbb498f50 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1322,7 +1322,6 @@ struct ZigFn { AstNode *fn_no_inline_set_node; AstNode *fn_static_eval_set_node; - ZigList alloca_list; ZigList result_loc_alloca_list; ZigList variable_list; @@ -2183,6 +2182,7 @@ enum IrResultLocationId { IrResultLocationIdAlloca, IrResultLocationIdLVal, IrResultLocationIdOptionalUnwrap, + IrResultLocationIdRet, }; struct IrResultLocation { @@ -2210,6 +2210,10 @@ struct IrResultLocationOptionalUnwrap { IrResultLocation *parent; }; +struct IrResultLocationRet { + IrResultLocation base; +}; + struct IrInstruction { Scope *scope; AstNode *source_node; @@ -2443,7 +2447,7 @@ struct IrInstructionCast { IrInstruction *value; ZigType *dest_type; CastOp cast_op; - LLVMValueRef tmp_ptr; + IrResultLocation *result_location; }; struct IrInstructionContainerInitList { @@ -2452,7 +2456,7 @@ struct IrInstructionContainerInitList { IrInstruction *container_type; size_t item_count; IrInstruction **items; - LLVMValueRef tmp_ptr; + IrResultLocation *result_location; }; struct IrInstructionContainerInitFieldsField { @@ -2481,7 +2485,7 @@ struct IrInstructionStructInit { ZigType *struct_type; size_t field_count; IrInstructionStructInitField *fields; - LLVMValueRef tmp_ptr; + IrResultLocation *result_location; }; struct IrInstructionUnionInit { @@ -2490,7 +2494,7 @@ struct IrInstructionUnionInit { ZigType *union_type; TypeUnionField *field; IrInstruction *init_value; - LLVMValueRef tmp_ptr; + IrResultLocation *result_location; }; struct IrInstructionUnreachable { @@ -2640,9 +2644,9 @@ struct IrInstructionRef { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; bool is_const; bool is_volatile; + IrResultLocation *result_location; }; struct IrInstructionMinValue { @@ -2714,6 +2718,7 @@ struct IrInstructionCmpxchg { IrInstruction *new_value; IrInstruction *success_order_value; IrInstruction *failure_order_value; + IrResultLocation *result_location; // if this instruction gets to runtime then we know these values: ZigType *type; @@ -2721,8 +2726,6 @@ struct IrInstructionCmpxchg { AtomicOrder failure_order; bool is_weak; - - LLVMValueRef tmp_ptr; }; struct IrInstructionFence { @@ -2830,8 +2833,8 @@ struct IrInstructionSlice { IrInstruction *ptr; IrInstruction *start; IrInstruction *end; + IrResultLocation *result_location; bool safety_check_on; - LLVMValueRef tmp_ptr; }; struct IrInstructionMemberCount { @@ -2919,21 +2922,21 @@ struct IrInstructionOptionalWrap { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrResultLocation *result_location; }; struct IrInstructionErrWrapPayload { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrResultLocation *result_location; }; struct IrInstructionErrWrapCode { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrResultLocation *result_location; }; struct IrInstructionFnProto { diff --git a/src/codegen.cpp b/src/codegen.cpp index 23b6d1ee7978..02e29d38597b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1785,7 +1785,7 @@ static LLVMRealPredicate cmp_op_to_real_predicate(IrBinOp cmp_op) { } static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, - LLVMValueRef value) + LLVMValueRef value, bool is_global_const) { assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; @@ -1794,6 +1794,12 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty return nullptr; if (handle_is_ptr(child_type)) { + // If the handle is a pointer, the result location mechanism caused the value to be + // populated in place, and this assignment should be elided, unless this + // is a global constant. + if (!is_global_const) + return nullptr; + assert(LLVMGetTypeKind(LLVMTypeOf(value)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMTypeOf(ptr)) == LLVMPointerTypeKind); @@ -2238,17 +2244,18 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut } static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { - LLVMValueRef value = ir_llvm_value(g, return_instruction->value); ZigType *return_type = return_instruction->value->value.type; if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + // assume that result location mechanism took care of it LLVMBuildRetVoid(g->builder); } else if (handle_is_ptr(return_type)) { + LLVMValueRef value = ir_llvm_value(g, return_instruction->value); LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); LLVMBuildRet(g->builder, by_val_value); } else { + LLVMValueRef value = ir_llvm_value(g, return_instruction->value); LLVMBuildRet(g->builder, value); } return nullptr; @@ -2802,6 +2809,38 @@ static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *in } } +static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { + assert(base != nullptr); + switch (base->id) { + case IrResultLocationIdAlloca: { + IrResultLocationAlloca *alloca_loc = reinterpret_cast(base); + return alloca_loc->alloca; + } + case IrResultLocationIdVar: { + IrResultLocationVar *var_loc = reinterpret_cast(base); + return var_loc->var->value_ref; + } + case IrResultLocationIdLVal: { + IrResultLocationLVal *lval_loc = reinterpret_cast(base); + IrInstruction *ptr = lval_loc->parent_instruction->child; + return ir_llvm_value(g, ptr); + } + case IrResultLocationIdOptionalUnwrap: { + IrResultLocationOptionalUnwrap *opt_unwrap_loc = reinterpret_cast(base); + LLVMValueRef parent = gen_result_location(g, opt_unwrap_loc->parent); + + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, parent, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); + return LLVMBuildStructGEP(g->builder, parent, maybe_child_index, ""); + } + case IrResultLocationIdRet: { + //IrResultLocationRet *ret_loc = reinterpret_cast(base); + return g->cur_ret_ptr; + } + } + zig_unreachable(); +} + static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, IrInstructionCast *cast_instruction) { @@ -2819,12 +2858,13 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return expr_val; case CastOpResizeSlice: { - assert(cast_instruction->tmp_ptr); assert(wanted_type->id == ZigTypeIdStruct); assert(wanted_type->data.structure.is_slice); assert(actual_type->id == ZigTypeIdStruct); assert(actual_type->data.structure.is_slice); + LLVMValueRef result_loc = gen_result_location(g, cast_instruction->result_location); + ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; @@ -2840,7 +2880,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, wanted_type->data.structure.fields[0].type_entry->type_ref, ""); - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)wanted_ptr_index, ""); gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); @@ -2873,38 +2913,39 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); } - LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)wanted_len_index, ""); gen_store_untyped(g, new_len, dest_len_ptr, 0, false); - return cast_instruction->tmp_ptr; + return result_loc; } case CastOpBytesToSlice: { - assert(cast_instruction->tmp_ptr); assert(wanted_type->id == ZigTypeIdStruct); assert(wanted_type->data.structure.is_slice); assert(actual_type->id == ZigTypeIdArray); + LLVMValueRef result_loc = gen_result_location(g, cast_instruction->result_location); + ZigType *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)wanted_ptr_index, ""); LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, ""); gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)wanted_len_index, ""); LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, actual_type->data.array.len / type_size(g, wanted_child_type), false); gen_store_untyped(g, len_val, len_ptr, 0, false); - return cast_instruction->tmp_ptr; + return result_loc; } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); @@ -2960,12 +3001,13 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, case CastOpBitCast: return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); case CastOpPtrOfArrayToSlice: { - assert(cast_instruction->tmp_ptr); + LLVMValueRef result_loc = gen_result_location(g, cast_instruction->result_location); + assert(actual_type->id == ZigTypeIdPointer); ZigType *array_type = actual_type->data.pointer.child_type; assert(array_type->id == ZigTypeIdArray); - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, slice_ptr_index, ""); LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), @@ -2974,13 +3016,13 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, slice_len_index, ""); LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return cast_instruction->tmp_ptr; + return result_loc; } } zig_unreachable(); @@ -3194,6 +3236,20 @@ static bool value_is_all_undef(ConstExprValue *const_val) { zig_unreachable(); } +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { + ZigType *usize = g->builtin_types.entry_usize; + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); + assert(size_bytes > 0); + assert(ptr_align_bytes > 0); + + // memset uninitialized memory to 0xaa + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); + ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); +} + static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstructionDeclVar *decl_var_instruction) { @@ -3214,28 +3270,11 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, assert(var->value->type == init_value->value.type); ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, PtrLenSingle, var->align_bytes, 0, 0); - bool elide_assign = init_value->id == IrInstructionIdCast && - reinterpret_cast(init_value)->cast_op == CastOpNoopResultLoc; - if (!elide_assign) { - LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); - gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); - } - } else { - bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); - if (want_safe) { - ZigType *usize = g->builtin_types.entry_usize; - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref); - assert(size_bytes > 0); - - assert(var->align_bytes > 0); - - // memset uninitialized memory to 0xa - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, ""); - LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); - ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, var->align_bytes, false); - } + LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); + gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val, + init_value->value.special == ConstValSpecialStatic); + } else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) { + gen_undef_init(g, var->align_bytes, var->value->type, var->value_ref); } gen_var_debug_decl(g, var); @@ -3273,19 +3312,24 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { - bool elide_assign = (instruction->value->id == IrInstructionIdCast && - reinterpret_cast(instruction->value)->cast_op == CastOpNoopResultLoc); - if (elide_assign) - return nullptr; - - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); + bool have_init_expr = !value_is_all_undef(&instruction->value->value); + if (have_init_expr) { + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef value = ir_llvm_value(g, instruction->value); - assert(instruction->ptr->value.type->id == ZigTypeIdPointer); - ZigType *ptr_type = instruction->ptr->value.type; + assert(instruction->ptr->value.type->id == ZigTypeIdPointer); + ZigType *ptr_type = instruction->ptr->value.type; - gen_assign_raw(g, ptr, ptr_type, value); + gen_assign_raw(g, ptr, ptr_type, value, instruction->value->value.special == ConstValSpecialStatic); + return nullptr; + } + if (ir_want_runtime_safety(g, &instruction->base)) { + gen_undef_init(g, + get_ptr_align(g, instruction->ptr->value.type), + instruction->value->value.type, + ir_llvm_value(g, instruction->ptr)); + } return nullptr; } @@ -3448,34 +3492,6 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); } -static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { - assert(base != nullptr); - switch (base->id) { - case IrResultLocationIdAlloca: { - IrResultLocationAlloca *alloca_loc = reinterpret_cast(base); - return alloca_loc->alloca; - } - case IrResultLocationIdVar: { - IrResultLocationVar *var_loc = reinterpret_cast(base); - return var_loc->var->value_ref; - } - case IrResultLocationIdLVal: { - IrResultLocationLVal *lval_loc = reinterpret_cast(base); - IrInstruction *ptr = lval_loc->parent_instruction->child; - return ir_llvm_value(g, ptr); - } - case IrResultLocationIdOptionalUnwrap: { - IrResultLocationOptionalUnwrap *opt_unwrap_loc = reinterpret_cast(base); - LLVMValueRef parent = gen_result_location(g, opt_unwrap_loc->parent); - - LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, parent, maybe_null_index, ""); - gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); - return LLVMBuildStructGEP(g->builder, parent, maybe_child_index, ""); - } - } - zig_unreachable(); -} - static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { LLVMValueRef fn_val; ZigType *fn_type; @@ -3945,9 +3961,9 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru if (handle_is_ptr(instruction->value->value.type)) { return value; } else { - assert(instruction->tmp_ptr); - gen_store_untyped(g, value, instruction->tmp_ptr, 0, false); - return instruction->tmp_ptr; + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); + gen_store_untyped(g, value, result_loc, 0, false); + return result_loc; } } @@ -4249,18 +4265,18 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); } - assert(instruction->tmp_ptr != nullptr); + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); assert(type_has_bits(instruction->type)); LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val, false); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); - return instruction->tmp_ptr; + return result_loc; } static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) { @@ -4323,7 +4339,7 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns } static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInstructionSlice *instruction) { - assert(instruction->tmp_ptr); + LLVMValueRef tmp_struct_ptr = gen_result_location(g, instruction->result_location); LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); ZigType *array_ptr_type = instruction->ptr->value.type; @@ -4331,8 +4347,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst ZigType *array_type = array_ptr_type->data.pointer.child_type; LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; - bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); if (array_type->id == ZigTypeIdArray || @@ -4665,15 +4679,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I return payload_val; } - assert(instruction->tmp_ptr); - - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); - // child_type and instruction->value->value.type may differ by constness - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); - gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); - - return instruction->tmp_ptr; + zig_unreachable(); } static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapCode *instruction) { @@ -4689,12 +4695,12 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable if (!type_has_bits(payload_type) || !type_has_bits(err_set_type)) return err_val; - assert(instruction->tmp_ptr); + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_err_index, ""); gen_store_untyped(g, err_val, err_tag_ptr, 0, false); - return instruction->tmp_ptr; + return result_loc; } static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapPayload *instruction) { @@ -4714,17 +4720,17 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa if (!type_has_bits(payload_type)) return ok_err_val; - assert(instruction->tmp_ptr); + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_err_index, ""); gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); - gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val); + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_payload_index, ""); + gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val, false); - return instruction->tmp_ptr; + return result_loc; } static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, IrInstructionUnionTag *instruction) { @@ -4746,13 +4752,16 @@ static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, Ir } static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, IrInstructionStructInit *instruction) { + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); + for (size_t i = 0; i < instruction->field_count; i += 1) { IrInstructionStructInitField *field = &instruction->fields[i]; TypeStructField *type_struct_field = field->type_struct_field; if (!type_has_bits(type_struct_field->type_entry)) continue; - LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + + LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)type_struct_field->gen_index, ""); LLVMValueRef value = ir_llvm_value(g, field->value); @@ -4763,9 +4772,9 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, false, false, PtrLenSingle, field_align_bytes, (uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes); - gen_assign_raw(g, field_ptr, ptr_type, value); + gen_assign_raw(g, field_ptr, ptr_type, value, false); } - return instruction->tmp_ptr; + return result_loc; } static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) { @@ -4774,6 +4783,8 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I if (!type_has_bits(type_union_field->type_entry)) return nullptr; + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); + uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry); ZigType *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry, false, false, PtrLenSingle, field_align_bytes, @@ -4784,25 +4795,25 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I // correctly. Otherwise safety code somewhere other than here could fail. ZigType *union_type = instruction->union_type; if (union_type->data.unionation.gen_tag_index != SIZE_MAX) { - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, union_type->data.unionation.gen_tag_index, ""); LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, &type_union_field->enum_field->value); gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + uncasted_union_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)union_type->data.unionation.gen_union_index, ""); } else { - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, ""); + uncasted_union_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)0, ""); } LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, ""); LLVMValueRef value = ir_llvm_value(g, instruction->init_value); - gen_assign_raw(g, field_ptr, ptr_type, value); + gen_assign_raw(g, field_ptr, ptr_type, value, false); - return instruction->tmp_ptr; + return result_loc; } static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable, @@ -4810,7 +4821,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec { ZigType *array_type = instruction->base.value.type; assert(array_type->id == ZigTypeIdArray); - LLVMValueRef tmp_array_ptr = instruction->tmp_ptr; + LLVMValueRef tmp_array_ptr = gen_result_location(g, instruction->result_location); assert(tmp_array_ptr); size_t field_count = instruction->item_count; @@ -4823,7 +4834,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec LLVMConstInt(g->builtin_types.entry_usize->type_ref, i, false), }; LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, ""); - gen_assign_raw(g, elem_ptr, get_pointer_to_type(g, child_type, false), elem_val); + gen_assign_raw(g, elem_ptr, get_pointer_to_type(g, child_type, false), elem_val, false); } return tmp_array_ptr; @@ -6263,49 +6274,6 @@ static void do_code_gen(CodeGen *g) { IrResultLocationAlloca *alloca_loc = fn_table_entry->result_loc_alloca_list.at(alloca_i); alloca_loc->alloca = build_alloca(g, alloca_loc->ty, "", get_abi_alignment(g, alloca_loc->ty)); } - // TODO migrate these to use the Result Location mechanism - 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; - ZigType *slot_type = instruction->value.type; - 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; - assert(instruction->value.type->id == ZigTypeIdPointer); - slot_type = instruction->value.type->data.pointer.child_type; - } else if (instruction->id == IrInstructionIdContainerInitList) { - IrInstructionContainerInitList *container_init_list_instruction = (IrInstructionContainerInitList *)instruction; - slot = &container_init_list_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdStructInit) { - IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction; - slot = &struct_init_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdUnionInit) { - IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction; - slot = &union_init_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdSlice) { - IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; - slot = &slice_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdOptionalWrap) { - IrInstructionOptionalWrap *maybe_wrap_instruction = (IrInstructionOptionalWrap *)instruction; - slot = &maybe_wrap_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdErrWrapPayload) { - IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction; - slot = &err_wrap_payload_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdErrWrapCode) { - IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; - slot = &err_wrap_code_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCmpxchg) { - IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; - slot = &cmpxchg_instruction->tmp_ptr; - } else { - zig_unreachable(); - } - *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); - } - ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); unsigned gen_i_init = want_first_arg_sret(g, fn_type_id) ? 1 : 0; diff --git a/src/ir.cpp b/src/ir.cpp index e231410dc604..c0997ce12956 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2958,12 +2958,15 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, switch (node->data.return_expr.kind) { case ReturnKindUnconditional: { + IrResultLocationRet *result_loc_ret = allocate(1); + result_loc_ret->base.id = IrResultLocationIdRet; + IrInstruction *return_value; if (expr_node) { // Temporarily set this so that if we return a type it gets the name of the function ZigFn *prev_name_fn = irb->exec->name_fn; irb->exec->name_fn = exec_fn_entry(irb->exec); - return_value = ir_gen_node(irb, expr_node, scope); + return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base); irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -9135,16 +9138,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } } -static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry) { - if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry != nullptr) { - assert(instruction->id != IrInstructionIdCall); - fn_entry->alloca_list.append(instruction); - } - } -} - static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) { ConstGlobalRefs *global_refs = dest->global_refs; *dest = *src; @@ -9261,7 +9254,7 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ return true; } static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, - ZigType *wanted_type, CastOp cast_op, bool need_alloca) + ZigType *wanted_type, CastOp cast_op) { if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice) @@ -9277,9 +9270,6 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); result->value.type = wanted_type; - if (need_alloca) { - ir_add_alloca(ira, result, wanted_type); - } return result; } } @@ -9358,7 +9348,6 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpPtrOfArrayToSlice); result->value.type = wanted_type; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9657,6 +9646,46 @@ static IrResultLocation **ir_get_result_location(IrInstruction *base) { IrInstructionCall *inst = reinterpret_cast(base); return &inst->result_location; } + case IrInstructionIdCast: { + IrInstructionCast *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdContainerInitList: { + IrInstructionContainerInitList *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdStructInit: { + IrInstructionStructInit *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdUnionInit: { + IrInstructionUnionInit *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdRef: { + IrInstructionRef *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdCmpxchg: { + IrInstructionCmpxchg *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdSlice: { + IrInstructionSlice *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdOptionalWrap: { + IrInstructionOptionalWrap *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdErrWrapPayload: { + IrInstructionErrWrapPayload *inst = reinterpret_cast(base); + return &inst->result_location; + } + case IrInstructionIdErrWrapCode: { + IrInstructionErrWrapCode *inst = reinterpret_cast(base); + return &inst->result_location; + } case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: @@ -9674,7 +9703,6 @@ static IrResultLocation **ir_get_result_location(IrInstruction *base) { case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: - case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: @@ -9707,11 +9735,7 @@ static IrResultLocation **ir_get_result_location(IrInstruction *base) { case IrInstructionIdBinOp: case IrInstructionIdLoadPtr: case IrInstructionIdConst: - case IrInstructionIdCast: - case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: - case IrInstructionIdStructInit: - case IrInstructionIdUnionInit: case IrInstructionIdFieldPtr: case IrInstructionIdElemPtr: case IrInstructionIdVarPtr: @@ -9733,14 +9757,12 @@ static IrResultLocation **ir_get_result_location(IrInstruction *base) { case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: case IrInstructionIdUnionTag: - case IrInstructionIdRef: case IrInstructionIdMinValue: case IrInstructionIdMaxValue: case IrInstructionIdEmbedFile: case IrInstructionIdTruncate: case IrInstructionIdIntType: case IrInstructionIdBoolNot: - case IrInstructionIdSlice: case IrInstructionIdMemberCount: case IrInstructionIdMemberType: case IrInstructionIdMemberName: @@ -9750,9 +9772,6 @@ static IrResultLocation **ir_get_result_location(IrInstruction *base) { case IrInstructionIdHandle: case IrInstructionIdTestErr: case IrInstructionIdUnwrapErrCode: - case IrInstructionIdOptionalWrap: - case IrInstructionIdErrWrapCode: - case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: case IrInstructionIdPtrCast: @@ -9847,7 +9866,6 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9878,7 +9896,6 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9947,7 +9964,6 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionError; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9980,14 +9996,6 @@ static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_ IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instr->scope, source_instr->source_node, value, true, false); new_instruction->value.type = wanted_type; - - ZigType *child_type = wanted_type->data.pointer.child_type; - if (type_has_bits(child_type)) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - assert(fn_entry); - fn_entry->alloca_list.append(new_instruction); - } - ir_add_alloca(ira, new_instruction, child_type); return new_instruction; } @@ -10035,11 +10043,6 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; - if (type_has_bits(ptr_type)) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - assert(fn_entry); - fn_entry->alloca_list.append(new_instruction); - } return new_instruction; } @@ -10084,7 +10087,6 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s result->value.type = wanted_type; result->value.data.rh_slice.id = RuntimeHintSliceIdLen; result->value.data.rh_slice.len = array_type->data.array.len; - ir_add_alloca(ira, result, result->value.type); return result; } @@ -10656,7 +10658,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (const_cast_result.id == ConstCastResultIdInvalid) return ira->codegen->invalid_instruction; if (const_cast_result.id == ConstCastResultIdOk) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); + return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); } // widening conversion @@ -10981,7 +10983,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } else { zig_unreachable(); } - return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false); + return ir_resolve_cast(ira, source_instr, value, wanted_type, op); } else { return ira->codegen->invalid_instruction; } @@ -16499,7 +16501,6 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI instruction->scope, instruction->source_node, container_type, type_field, casted_field_value); new_instruction->value.type = container_type; - ir_add_alloca(ira, new_instruction, container_type); return new_instruction; } @@ -16623,7 +16624,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc instruction->scope, instruction->source_node, container_type, actual_field_count, new_fields); new_instruction->value.type = container_type; - ir_add_alloca(ira, new_instruction, container_type); return new_instruction; } @@ -16728,7 +16728,6 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, instruction->base.scope, instruction->base.source_node, container_type_value, elem_count, new_items); new_instruction->value.type = fixed_size_array_type; - ir_add_alloca(ira, new_instruction, fixed_size_array_type); return new_instruction; } else if (container_type->id == ZigTypeIdVoid) { if (elem_count != 0) { @@ -18354,7 +18353,6 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, operand_type, success_order, failure_order); result->value.type = get_optional_type(ira->codegen, operand_type); - ir_add_alloca(ira, result, result->value.type); return result; } @@ -18437,7 +18435,7 @@ static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstruct if (target->value.type->id == ZigTypeIdComptimeInt) { if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { - return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpNumLitToConcrete, false); + return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpNumLitToConcrete); } else { return ira->codegen->invalid_instruction; } @@ -18477,7 +18475,7 @@ static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstru } else { op = CastOpNumLitToConcrete; } - return ir_resolve_cast(ira, &instruction->base, target, dest_type, op, false); + return ir_resolve_cast(ira, &instruction->base, target, dest_type, op); } else { return ira->codegen->invalid_instruction; } @@ -18616,7 +18614,7 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru } } - return ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); + return ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice); } static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { @@ -18643,7 +18641,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct alignment, 0, 0); ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); - return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); + return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice); } static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { @@ -18661,7 +18659,7 @@ static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInst return ira->codegen->invalid_instruction; } - return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat, false); + return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat); } static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { @@ -18683,7 +18681,7 @@ static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInst return ira->codegen->invalid_instruction; } - return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt, false); + return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt); } static IrInstruction *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) { @@ -18735,7 +18733,7 @@ static IrInstruction *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstr } ZigType *u1_type = get_int_type(ira->codegen, false, 1); - return ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt, false); + return ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt); } static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) { @@ -19293,7 +19291,6 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction instruction->base.scope, instruction->base.source_node, ptr_ptr, casted_start, end, instruction->safety_check_on); new_instruction->value.type = return_type; - ir_add_alloca(ira, new_instruction, return_type); return new_instruction; } From 52cbaead9f2efcb47f8b8555b8e61c230589a49e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 12 Oct 2018 13:04:05 -0400 Subject: [PATCH 05/17] copy elision implicit cast optional wrap when payload is integer --- src/codegen.cpp | 11 +++++++- src/ir.cpp | 67 ++++++++++++++++++++++++++++--------------------- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 02e29d38597b..5733d48744c3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4679,7 +4679,16 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I return payload_val; } - zig_unreachable(); + + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); + + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); + // child_type and instruction->value->value.type may differ by constness + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val, false); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); + + return result_loc; } static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapCode *instruction) { diff --git a/src/ir.cpp b/src/ir.cpp index c0997ce12956..a583678ad0c2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1551,9 +1551,12 @@ static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { +static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrResultLocation *result_location) +{ IrInstructionOptionalWrap *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; + instruction->result_location = result_location; ir_ref_instruction(value, irb->current_basic_block); @@ -9822,6 +9825,20 @@ static IrResultLocation **ir_get_result_location(IrInstruction *base) { zig_unreachable(); } +static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty) { + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + if (fn_entry == nullptr) + return nullptr; + if (type_has_bits(ty) && handle_is_ptr(ty)) { + IrResultLocationAlloca *alloca_loc = allocate(1); + alloca_loc->base.id = IrResultLocationIdAlloca; + alloca_loc->ty = ty; + fn_entry->result_loc_alloca_list.append(alloca_loc); + return &alloca_loc->base; + } + return nullptr; +} + static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); @@ -9847,23 +9864,31 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc return &const_instruction->base; } - // If we have a result location, we push a special OptionalWrap result location value - // to the result location stack. + // If we have a result location and the type handle is pointer, we push a special + // OptionalWrap result location value to the result location stack. IrResultLocation **result_location = ir_get_result_location(value); + IrResultLocation *final_result_location; if (result_location != nullptr) { - IrResultLocation *prev_result_location = *result_location; - IrResultLocationOptionalUnwrap *new_result_location = allocate(1); - new_result_location->base.id = IrResultLocationIdOptionalUnwrap; - new_result_location->parent = prev_result_location; - *result_location = &new_result_location->base; - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpNoopResultLoc); - result->value.data.rh_maybe = RuntimeHintOptionalNonNull; - result->value.type = wanted_type; - return result; + if (handle_is_ptr(value->value.type)) { + IrResultLocation *prev_result_location = *result_location; + IrResultLocationOptionalUnwrap *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdOptionalUnwrap; + new_result_location->parent = prev_result_location; + *result_location = &new_result_location->base; + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpNoopResultLoc); + result->value.data.rh_maybe = RuntimeHintOptionalNonNull; + result->value.type = wanted_type; + return result; + } else { + final_result_location = *result_location; + } + } else { + final_result_location = create_alloca_result_loc(ira, wanted_type); } - IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); + IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, + value, final_result_location); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; return result; @@ -13017,20 +13042,6 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i zig_unreachable(); } -static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry == nullptr) - return nullptr; - if (type_has_bits(ty) && handle_is_ptr(ty)) { - IrResultLocationAlloca *alloca_loc = allocate(1); - alloca_loc->base.id = IrResultLocationIdAlloca; - alloca_loc->ty = ty; - fn_entry->result_loc_alloca_list.append(alloca_loc); - return &alloca_loc->base; - } - return nullptr; -} - static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLocation *result_location, ZigType *ty) { From 64e0afc3091782c8ff0b876d349cccf5ea2e8c08 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 12 Oct 2018 13:14:36 -0400 Subject: [PATCH 06/17] copy elision - returning const struct --- src/codegen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 5733d48744c3..4fd9ce9f27a4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2244,18 +2244,18 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut } static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { + LLVMValueRef value = ir_llvm_value(g, return_instruction->value); ZigType *return_type = return_instruction->value->value.type; if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { assert(g->cur_ret_ptr); - // assume that result location mechanism took care of it + gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value, + return_instruction->value->value.special == ConstValSpecialStatic); LLVMBuildRetVoid(g->builder); } else if (handle_is_ptr(return_type)) { - LLVMValueRef value = ir_llvm_value(g, return_instruction->value); LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); LLVMBuildRet(g->builder, by_val_value); } else { - LLVMValueRef value = ir_llvm_value(g, return_instruction->value); LLVMBuildRet(g->builder, value); } return nullptr; From 6d67d1613c02b3cb41b3a46c9f54f206e89aa275 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 12 Oct 2018 14:16:35 -0400 Subject: [PATCH 07/17] copy elision implicit cast payload to error union --- src/all_types.hpp | 6 ++++++ src/codegen.cpp | 9 +++++++++ src/ir.cpp | 29 +++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index d7ecbb498f50..aebbba561797 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2182,6 +2182,7 @@ enum IrResultLocationId { IrResultLocationIdAlloca, IrResultLocationIdLVal, IrResultLocationIdOptionalUnwrap, + IrResultLocationIdErrorUnionPayload, IrResultLocationIdRet, }; @@ -2210,6 +2211,11 @@ struct IrResultLocationOptionalUnwrap { IrResultLocation *parent; }; +struct IrResultLocationErrorUnionPayload { + IrResultLocation base; + IrResultLocation *parent; +}; + struct IrResultLocationRet { IrResultLocation base; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4fd9ce9f27a4..ec7ff80d9bc7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2833,6 +2833,15 @@ static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); return LLVMBuildStructGEP(g->builder, parent, maybe_child_index, ""); } + case IrResultLocationIdErrorUnionPayload: { + IrResultLocationErrorUnionPayload *loc = reinterpret_cast(base); + LLVMValueRef parent = gen_result_location(g, loc->parent); + + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, parent, err_union_err_index, ""); + LLVMTypeRef err_type_ref = g->builtin_types.entry_global_error_set->type_ref; + gen_store_untyped(g, LLVMConstInt(err_type_ref, 0, false), err_val_ptr, 0, false); + return LLVMBuildStructGEP(g->builder, parent, err_union_payload_index, ""); + } case IrResultLocationIdRet: { //IrResultLocationRet *ret_loc = reinterpret_cast(base); return g->cur_ret_ptr; diff --git a/src/ir.cpp b/src/ir.cpp index a583678ad0c2..92f65b3ee649 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1563,9 +1563,12 @@ static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_err_wrap_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { +static IrInstruction *ir_build_err_wrap_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrResultLocation *result_location) +{ IrInstructionErrWrapPayload *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; + instruction->result_location = result_location; ir_ref_instruction(value, irb->current_basic_block); @@ -9918,7 +9921,29 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction return &const_instruction->base; } - IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value); + IrResultLocation **result_location = ir_get_result_location(value); + IrResultLocation *final_result_location; + if (result_location != nullptr) { + if (handle_is_ptr(value->value.type)) { + IrResultLocation *prev_result_location = *result_location; + IrResultLocationErrorUnionPayload *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdErrorUnionPayload; + new_result_location->parent = prev_result_location; + *result_location = &new_result_location->base; + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpNoopResultLoc); + result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; + result->value.type = wanted_type; + return result; + } else { + final_result_location = *result_location; + } + } else { + final_result_location = create_alloca_result_loc(ira, wanted_type); + } + + IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, + source_instr->source_node, value, final_result_location); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; return result; From da99ee0515dc1cfe65020b9dba2be50c3ba913b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 12 Oct 2018 15:04:29 -0400 Subject: [PATCH 08/17] copy elision implicit cast error code to error union --- src/ir.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 92f65b3ee649..a10c512d146b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1575,9 +1575,12 @@ static IrInstruction *ir_build_err_wrap_payload(IrBuilder *irb, Scope *scope, As return &instruction->base; } -static IrInstruction *ir_build_err_wrap_code(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { +static IrInstruction *ir_build_err_wrap_code(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrResultLocation *result_location) +{ IrInstructionErrWrapCode *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; + instruction->result_location = result_location; ir_ref_instruction(value, irb->current_basic_block); @@ -10011,7 +10014,16 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so return &const_instruction->base; } - IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value); + IrResultLocation **result_location = ir_get_result_location(value); + IrResultLocation *final_result_location; + if (result_location != nullptr) { + final_result_location = *result_location; + } else { + final_result_location = create_alloca_result_loc(ira, wanted_type); + } + + IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, + value, final_result_location); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionError; return result; From 3e0c9cefb6ab7f226f0784569796881aedd8e3d3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Oct 2018 13:42:36 -0400 Subject: [PATCH 09/17] copy elision - double implicit cast --- src/all_types.hpp | 95 +++++++++++++++++++++++++---------------------- src/codegen.cpp | 81 ++++++++++++++++++++++++++++------------ src/ir.cpp | 91 ++++++++++++++++++++++++--------------------- src/ir_print.cpp | 7 ++++ 4 files changed, 163 insertions(+), 111 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index aebbba561797..085c10e42c3d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -607,7 +607,6 @@ struct AstNodeUnwrapOptional { enum CastOp { CastOpNoCast, // signifies the function call expression is not a cast CastOpNoop, // fn call expr is a cast, but does nothing - CastOpNoopResultLoc, // don't copy the value; it's handled by result location mechanism CastOpIntToFloat, CastOpFloatToInt, CastOpBoolToInt, @@ -2031,6 +2030,50 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; +enum IrResultLocationId { + IrResultLocationIdVar, + IrResultLocationIdAlloca, + IrResultLocationIdLVal, + IrResultLocationIdOptionalUnwrap, + IrResultLocationIdErrorUnionPayload, + IrResultLocationIdRet, +}; + +struct IrResultLocation { + IrResultLocation *child; + IrResultLocationId id; +}; + +struct IrResultLocationVar { + IrResultLocation base; + ZigVar *var; +}; + +struct IrResultLocationAlloca { + IrResultLocation base; + LLVMValueRef alloca; + ZigType *ty; +}; + +struct IrResultLocationLVal { + IrResultLocation base; + IrInstruction *parent_instruction; +}; + +struct IrResultLocationOptionalUnwrap { + IrResultLocation base; + LLVMValueRef result; +}; + +struct IrResultLocationErrorUnionPayload { + IrResultLocation base; + LLVMValueRef result; +}; + +struct IrResultLocationRet { + IrResultLocation base; +}; + enum IrInstructionId { IrInstructionIdInvalid, IrInstructionIdBr, @@ -2175,49 +2218,7 @@ enum IrInstructionId { IrInstructionIdToBytes, IrInstructionIdFromBytes, IrInstructionIdCheckRuntimeScope, -}; - -enum IrResultLocationId { - IrResultLocationIdVar, - IrResultLocationIdAlloca, - IrResultLocationIdLVal, - IrResultLocationIdOptionalUnwrap, - IrResultLocationIdErrorUnionPayload, - IrResultLocationIdRet, -}; - -struct IrResultLocation { - IrResultLocationId id; -}; - -struct IrResultLocationVar { - IrResultLocation base; - ZigVar *var; -}; - -struct IrResultLocationAlloca { - IrResultLocation base; - LLVMValueRef alloca; - ZigType *ty; -}; - -struct IrResultLocationLVal { - IrResultLocation base; - IrInstruction *parent_instruction; -}; - -struct IrResultLocationOptionalUnwrap { - IrResultLocation base; - IrResultLocation *parent; -}; - -struct IrResultLocationErrorUnionPayload { - IrResultLocation base; - IrResultLocation *parent; -}; - -struct IrResultLocationRet { - IrResultLocation base; + IrInstructionIdResultLoc, }; struct IrInstruction { @@ -3325,6 +3326,12 @@ struct IrInstructionCheckRuntimeScope { IrInstruction *is_comptime; }; +struct IrInstructionResultLoc { + IrInstruction base; + + IrResultLocation *result_location; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index ec7ff80d9bc7..c7b7e822a5ae 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2809,45 +2809,71 @@ static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *in } } -static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { - assert(base != nullptr); +static LLVMValueRef gen_result_location_recur(CodeGen *g, IrResultLocation *base, LLVMValueRef prev) { + LLVMValueRef result; switch (base->id) { case IrResultLocationIdAlloca: { - IrResultLocationAlloca *alloca_loc = reinterpret_cast(base); - return alloca_loc->alloca; + IrResultLocationAlloca *loc = reinterpret_cast(base); + assert(prev == nullptr); + result = loc->alloca; + break; } case IrResultLocationIdVar: { - IrResultLocationVar *var_loc = reinterpret_cast(base); - return var_loc->var->value_ref; + IrResultLocationVar *loc = reinterpret_cast(base); + assert(prev == nullptr); + result = loc->var->value_ref; + break; } case IrResultLocationIdLVal: { IrResultLocationLVal *lval_loc = reinterpret_cast(base); + assert(prev == nullptr); IrInstruction *ptr = lval_loc->parent_instruction->child; - return ir_llvm_value(g, ptr); + result = ir_llvm_value(g, ptr); + break; + } + case IrResultLocationIdRet: { + //IrResultLocationRet *ret_loc = reinterpret_cast(base); + assert(prev == nullptr); + result = g->cur_ret_ptr; + break; } case IrResultLocationIdOptionalUnwrap: { - IrResultLocationOptionalUnwrap *opt_unwrap_loc = reinterpret_cast(base); - LLVMValueRef parent = gen_result_location(g, opt_unwrap_loc->parent); - - LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, parent, maybe_null_index, ""); + IrResultLocationOptionalUnwrap *loc = reinterpret_cast(base); + if (loc->result != nullptr) + return loc->result; + assert(prev != nullptr); + if (base->child != nullptr) { + prev = gen_result_location_recur(g, base->child, prev); + } + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev, maybe_null_index, ""); gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, false), nonnull_ptr, 0, false); - return LLVMBuildStructGEP(g->builder, parent, maybe_child_index, ""); + loc->result = LLVMBuildStructGEP(g->builder, prev, maybe_child_index, ""); + return loc->result; } case IrResultLocationIdErrorUnionPayload: { IrResultLocationErrorUnionPayload *loc = reinterpret_cast(base); - LLVMValueRef parent = gen_result_location(g, loc->parent); - - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, parent, err_union_err_index, ""); + if (loc->result != nullptr) + return loc->result; + assert(prev != nullptr); + if (base->child != nullptr) { + prev = gen_result_location_recur(g, base->child, prev); + } + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, prev, err_union_err_index, ""); LLVMTypeRef err_type_ref = g->builtin_types.entry_global_error_set->type_ref; gen_store_untyped(g, LLVMConstInt(err_type_ref, 0, false), err_val_ptr, 0, false); - return LLVMBuildStructGEP(g->builder, parent, err_union_payload_index, ""); - } - case IrResultLocationIdRet: { - //IrResultLocationRet *ret_loc = reinterpret_cast(base); - return g->cur_ret_ptr; + loc->result = LLVMBuildStructGEP(g->builder, prev, err_union_payload_index, ""); + return loc->result; } } - zig_unreachable(); + if (base->child != nullptr) { + return gen_result_location_recur(g, base->child, result); + } + return result; +} + +static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { + assert(base != nullptr); + return gen_result_location_recur(g, base, nullptr); } static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, @@ -2863,7 +2889,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, case CastOpNumLitToConcrete: zig_unreachable(); case CastOpNoop: - case CastOpNoopResultLoc: return expr_val; case CastOpResizeSlice: { @@ -3279,9 +3304,9 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, assert(var->value->type == init_value->value.type); ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, PtrLenSingle, var->align_bytes, 0, 0); + bool is_global_const = init_value->value.special == ConstValSpecialStatic; LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); - gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val, - init_value->value.special == ConstValSpecialStatic); + gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val, is_global_const); } else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) { gen_undef_init(g, var->align_bytes, var->value->type, var->value_ref); } @@ -5161,6 +5186,12 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); } +static LLVMValueRef ir_render_result_loc(CodeGen *g, IrExecutable *executable, + IrInstructionResultLoc *instruction) +{ + return gen_result_location(g, instruction->result_location); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5401,6 +5432,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); + case IrInstructionIdResultLoc: + return ir_render_result_loc(g, executable, (IrInstructionResultLoc *)instruction); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index a10c512d146b..e81825192d86 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -865,6 +865,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop return IrInstructionIdCheckRuntimeScope; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultLoc *) { + return IrInstructionIdResultLoc; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2758,6 +2762,15 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, return &instruction->base; } +static IrInstruction *ir_build_result_loc(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrResultLocation *result_location) +{ + IrInstructionResultLoc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->result_location = result_location; + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -9175,7 +9188,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: - case CastOpNoopResultLoc: { bool same_global_refs = other_val->special == ConstValSpecialStatic; copy_const_val(const_val, other_val, same_global_refs); @@ -9647,53 +9659,57 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrResultLocation **ir_get_result_location(IrInstruction *base) { +static IrResultLocation *ir_get_result_location(IrInstruction *base) { switch (base->id) { case IrInstructionIdInvalid: zig_unreachable(); case IrInstructionIdCall: { IrInstructionCall *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdCast: { IrInstructionCast *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdContainerInitList: { IrInstructionContainerInitList *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdStructInit: { IrInstructionStructInit *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdUnionInit: { IrInstructionUnionInit *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdRef: { IrInstructionRef *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdCmpxchg: { IrInstructionCmpxchg *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdSlice: { IrInstructionSlice *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdOptionalWrap: { IrInstructionOptionalWrap *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdErrWrapPayload: { IrInstructionErrWrapPayload *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; } case IrInstructionIdErrWrapCode: { IrInstructionErrWrapCode *inst = reinterpret_cast(base); - return &inst->result_location; + return inst->result_location; + } + case IrInstructionIdResultLoc: { + IrInstructionResultLoc *inst = reinterpret_cast(base); + return inst->result_location; } case IrInstructionIdBr: case IrInstructionIdCondBr: @@ -9872,29 +9888,25 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc // If we have a result location and the type handle is pointer, we push a special // OptionalWrap result location value to the result location stack. - IrResultLocation **result_location = ir_get_result_location(value); - IrResultLocation *final_result_location; + IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { if (handle_is_ptr(value->value.type)) { - IrResultLocation *prev_result_location = *result_location; IrResultLocationOptionalUnwrap *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdOptionalUnwrap; - new_result_location->parent = prev_result_location; - *result_location = &new_result_location->base; - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpNoopResultLoc); + assert(result_location->child == nullptr); + result_location->child = &new_result_location->base; + IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, + source_instr->source_node, &new_result_location->base); result->value.data.rh_maybe = RuntimeHintOptionalNonNull; result->value.type = wanted_type; return result; - } else { - final_result_location = *result_location; } } else { - final_result_location = create_alloca_result_loc(ira, wanted_type); + result_location = create_alloca_result_loc(ira, wanted_type); } IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, - value, final_result_location); + value, result_location); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; return result; @@ -9924,29 +9936,25 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction return &const_instruction->base; } - IrResultLocation **result_location = ir_get_result_location(value); - IrResultLocation *final_result_location; + IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { if (handle_is_ptr(value->value.type)) { - IrResultLocation *prev_result_location = *result_location; IrResultLocationErrorUnionPayload *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdErrorUnionPayload; - new_result_location->parent = prev_result_location; - *result_location = &new_result_location->base; - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpNoopResultLoc); + assert(result_location->child == nullptr); + result_location->child = &new_result_location->base; + IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, + source_instr->source_node, &new_result_location->base); result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; result->value.type = wanted_type; return result; - } else { - final_result_location = *result_location; } } else { - final_result_location = create_alloca_result_loc(ira, wanted_type); + result_location = create_alloca_result_loc(ira, wanted_type); } IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, - source_instr->source_node, value, final_result_location); + source_instr->source_node, value, result_location); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; return result; @@ -10014,16 +10022,11 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so return &const_instruction->base; } - IrResultLocation **result_location = ir_get_result_location(value); - IrResultLocation *final_result_location; - if (result_location != nullptr) { - final_result_location = *result_location; - } else { - final_result_location = create_alloca_result_loc(ira, wanted_type); - } + IrResultLocation *result_location = ir_get_result_location(value); + if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type); IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, - value, final_result_location); + value, result_location); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionError; return result; @@ -21368,6 +21371,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdResultLoc: zig_unreachable(); case IrInstructionIdReturn: @@ -21859,6 +21863,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFromBytes: case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: + case IrInstructionIdResultLoc: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 3bed716756b0..7198de9ffba0 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -965,6 +965,10 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime fprintf(irp->f, ")"); } +static void ir_print_result_loc(IrPrint *irp, IrInstructionResultLoc *instruction) { + fprintf(irp->f, "resultloc (TODO print details)"); +} + static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { fprintf(irp->f, "inttoerr "); ir_print_other_instruction(irp, instruction->target); @@ -1771,6 +1775,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCheckRuntimeScope: ir_print_check_runtime_scope(irp, (IrInstructionCheckRuntimeScope *)instruction); break; + case IrInstructionIdResultLoc: + ir_print_result_loc(irp, (IrInstructionResultLoc *)instruction); + break; } fprintf(irp->f, "\n"); } From f0a13bb06a533954def2c429aa32297a83c036ae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 14 Oct 2018 16:38:24 -0400 Subject: [PATCH 10/17] copy elision - cast pointer of array to slice --- src/all_types.hpp | 21 +- src/codegen.cpp | 92 ++++---- src/ir.cpp | 545 ++++++++++++++++++++++++---------------------- src/ir_print.cpp | 8 + 4 files changed, 368 insertions(+), 298 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 085c10e42c3d..e2b162111e29 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -615,7 +615,6 @@ enum CastOp { CastOpNumLitToConcrete, CastOpErrSet, CastOpBitCast, - CastOpPtrOfArrayToSlice, }; struct AstNodeFnCallExpr { @@ -2034,9 +2033,10 @@ enum IrResultLocationId { IrResultLocationIdVar, IrResultLocationIdAlloca, IrResultLocationIdLVal, + IrResultLocationIdRet, + IrResultLocationIdOptionalUnwrap, IrResultLocationIdErrorUnionPayload, - IrResultLocationIdRet, }; struct IrResultLocation { @@ -2060,18 +2060,18 @@ struct IrResultLocationLVal { IrInstruction *parent_instruction; }; -struct IrResultLocationOptionalUnwrap { +struct IrResultLocationRet { IrResultLocation base; - LLVMValueRef result; }; -struct IrResultLocationErrorUnionPayload { +struct IrResultLocationOptionalUnwrap { IrResultLocation base; LLVMValueRef result; }; -struct IrResultLocationRet { +struct IrResultLocationErrorUnionPayload { IrResultLocation base; + LLVMValueRef result; }; enum IrInstructionId { @@ -2219,6 +2219,7 @@ enum IrInstructionId { IrInstructionIdFromBytes, IrInstructionIdCheckRuntimeScope, IrInstructionIdResultLoc, + IrInstructionIdPtrOfArrayToSlice, }; struct IrInstruction { @@ -2416,6 +2417,7 @@ struct IrInstructionVarPtr { ZigVar *var; ScopeFnDef *crossed_fndef_scope; + IrResultLocation *result_location; }; struct IrInstructionCall { @@ -3332,6 +3334,13 @@ struct IrInstructionResultLoc { IrResultLocation *result_location; }; +struct IrInstructionPtrOfArrayToSlice { + IrInstruction base; + + IrInstruction *value; + IrResultLocation *result_location; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/codegen.cpp b/src/codegen.cpp index c7b7e822a5ae..cfe66a4836c7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2810,33 +2810,45 @@ static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *in } static LLVMValueRef gen_result_location_recur(CodeGen *g, IrResultLocation *base, LLVMValueRef prev) { - LLVMValueRef result; switch (base->id) { case IrResultLocationIdAlloca: { IrResultLocationAlloca *loc = reinterpret_cast(base); assert(prev == nullptr); - result = loc->alloca; - break; + LLVMValueRef result = loc->alloca; + if (base->child != nullptr) { + return gen_result_location_recur(g, base->child, result); + } + return result; } case IrResultLocationIdVar: { IrResultLocationVar *loc = reinterpret_cast(base); assert(prev == nullptr); - result = loc->var->value_ref; - break; + LLVMValueRef result = loc->var->value_ref; + if (base->child != nullptr) { + return gen_result_location_recur(g, base->child, result); + } + return result; } case IrResultLocationIdLVal: { IrResultLocationLVal *lval_loc = reinterpret_cast(base); assert(prev == nullptr); IrInstruction *ptr = lval_loc->parent_instruction->child; - result = ir_llvm_value(g, ptr); - break; + LLVMValueRef result = ir_llvm_value(g, ptr); + if (base->child != nullptr) { + return gen_result_location_recur(g, base->child, result); + } + return result; } case IrResultLocationIdRet: { //IrResultLocationRet *ret_loc = reinterpret_cast(base); assert(prev == nullptr); - result = g->cur_ret_ptr; - break; + LLVMValueRef result = g->cur_ret_ptr; + if (base->child != nullptr) { + return gen_result_location_recur(g, base->child, result); + } + return result; } + case IrResultLocationIdOptionalUnwrap: { IrResultLocationOptionalUnwrap *loc = reinterpret_cast(base); if (loc->result != nullptr) @@ -2865,10 +2877,7 @@ static LLVMValueRef gen_result_location_recur(CodeGen *g, IrResultLocation *base return loc->result; } } - if (base->child != nullptr) { - return gen_result_location_recur(g, base->child, result); - } - return result; + zig_unreachable(); } static LLVMValueRef gen_result_location(CodeGen *g, IrResultLocation *base) { @@ -3034,34 +3043,41 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return expr_val; case CastOpBitCast: return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpPtrOfArrayToSlice: { - LLVMValueRef result_loc = gen_result_location(g, cast_instruction->result_location); - - assert(actual_type->id == ZigTypeIdPointer); - ZigType *array_type = actual_type->data.pointer.child_type; - assert(array_type->id == ZigTypeIdArray); - - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, - slice_ptr_index, ""); - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->type_ref), - LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), - }; - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, - slice_len_index, ""); - LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - array_type->data.array.len, false); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - - return result_loc; - } } zig_unreachable(); } +static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *executable, + IrInstructionPtrOfArrayToSlice *instruction) +{ + ZigType *actual_type = instruction->value->value.type; + LLVMValueRef expr_val = ir_llvm_value(g, instruction->value); + assert(expr_val); + + LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); + + assert(actual_type->id == ZigTypeIdPointer); + ZigType *array_type = actual_type->data.pointer.child_type; + assert(array_type->id == ZigTypeIdArray); + + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, + slice_ptr_index, ""); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->type_ref), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), + }; + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, + slice_len_index, ""); + LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + array_type->data.array.len, false); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return result_loc; +} + static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, IrInstructionPtrCast *instruction) { @@ -5434,6 +5450,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); case IrInstructionIdResultLoc: return ir_render_result_loc(g, executable, (IrInstructionResultLoc *)instruction); + case IrInstructionIdPtrOfArrayToSlice: + return ir_render_ptr_of_array_to_slice(g, executable, (IrInstructionPtrOfArrayToSlice *)instruction); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index e81825192d86..8b5bda317db6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -869,6 +869,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionResultLoc *) { return IrInstructionIdResultLoc; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrOfArrayToSlice *) { + return IrInstructionIdPtrOfArrayToSlice; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1091,11 +1095,12 @@ static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_var_ptr_x(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, - ScopeFnDef *crossed_fndef_scope) + ScopeFnDef *crossed_fndef_scope, IrResultLocation *result_location) { IrInstructionVarPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->var = var; instruction->crossed_fndef_scope = crossed_fndef_scope; + instruction->result_location = result_location; ir_ref_var(var); @@ -1103,7 +1108,7 @@ static IrInstruction *ir_build_var_ptr_x(IrBuilder *irb, Scope *scope, AstNode * } static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) { - return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr); + return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr, nullptr); } static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr, @@ -2771,6 +2776,18 @@ static IrInstruction *ir_build_result_loc(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_ptr_of_array_to_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrResultLocation *result_location) +{ + IrInstructionPtrOfArrayToSlice *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + instruction->result_location = result_location; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3545,7 +3562,9 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode return ir_build_const_null(irb, scope, node); } -static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrResultLocation *result_location) +{ assert(node->type == NodeTypeSymbol); Buf *variable_name = node->data.symbol_expr.symbol; @@ -3572,7 +3591,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, ScopeFnDef *crossed_fndef_scope; ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope); if (var) { - IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); + IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope, result_location); if (lval == LValPtr) return var_ptr; else @@ -4966,7 +4985,9 @@ static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_bool_not(irb, scope, node, value); } -static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrResultLocation *result_location) +{ assert(node->type == NodeTypePrefixOpExpr); PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; @@ -4986,7 +5007,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node_lval(irb, expr_node, scope, LValPtr), lval); + return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, result_location), lval); } } zig_unreachable(); @@ -7016,13 +7037,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeCharLiteral: return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval); case NodeTypeSymbol: - return ir_gen_symbol(irb, scope, node, lval); + return ir_gen_symbol(irb, scope, node, lval, result_location); case NodeTypeFnCallExpr: return ir_gen_fn_call(irb, scope, node, lval, result_location); case NodeTypeIfBoolExpr: return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: - return ir_gen_prefix_op_expr(irb, scope, node, lval); + return ir_gen_prefix_op_expr(irb, scope, node, lval, result_location); case NodeTypeContainerInitExpr: return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); case NodeTypeVariableDeclaration: @@ -9185,7 +9206,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ zig_unreachable(); case CastOpErrSet: case CastOpBitCast: - case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: { @@ -9333,14 +9353,255 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, return result; } +static IrResultLocation *ir_get_result_location(IrInstruction *base) { + switch (base->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdCall: { + IrInstructionCall *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdCast: { + IrInstructionCast *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdContainerInitList: { + IrInstructionContainerInitList *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdStructInit: { + IrInstructionStructInit *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdUnionInit: { + IrInstructionUnionInit *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdRef: { + IrInstructionRef *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdCmpxchg: { + IrInstructionCmpxchg *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdSlice: { + IrInstructionSlice *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdOptionalWrap: { + IrInstructionOptionalWrap *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdErrWrapPayload: { + IrInstructionErrWrapPayload *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdErrWrapCode: { + IrInstructionErrWrapCode *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdResultLoc: { + IrInstructionResultLoc *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdPtrOfArrayToSlice: { + IrInstructionPtrOfArrayToSlice *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdVarPtr: { + IrInstructionVarPtr *inst = reinterpret_cast(base); + return inst->result_location; + } + case IrInstructionIdBr: + case IrInstructionIdCondBr: + case IrInstructionIdSwitchBr: + case IrInstructionIdDeclVar: + case IrInstructionIdStorePtr: + case IrInstructionIdReturn: + case IrInstructionIdUnreachable: + case IrInstructionIdSetCold: + case IrInstructionIdSetRuntimeSafety: + case IrInstructionIdSetFloatMode: + case IrInstructionIdImport: + case IrInstructionIdCompileErr: + case IrInstructionIdCompileLog: + case IrInstructionIdCImport: + case IrInstructionIdCInclude: + case IrInstructionIdCDefine: + case IrInstructionIdCUndef: + case IrInstructionIdFence: + case IrInstructionIdMemset: + case IrInstructionIdMemcpy: + case IrInstructionIdBreakpoint: + case IrInstructionIdOverflowOp: + case IrInstructionIdCheckSwitchProngs: + case IrInstructionIdCheckStatementIsVoid: + case IrInstructionIdCheckRuntimeScope: + case IrInstructionIdPanic: + case IrInstructionIdSetEvalBranchQuota: + case IrInstructionIdPtrType: + case IrInstructionIdSetAlignStack: + case IrInstructionIdExport: + case IrInstructionIdCancel: + case IrInstructionIdCoroId: + case IrInstructionIdCoroBegin: + case IrInstructionIdCoroAllocFail: + case IrInstructionIdCoroEnd: + case IrInstructionIdCoroResume: + case IrInstructionIdCoroSave: + case IrInstructionIdCoroAllocHelper: + case IrInstructionIdAwaitBookkeeping: + case IrInstructionIdSaveErrRetAddr: + case IrInstructionIdAddImplicitReturnType: + case IrInstructionIdMergeErrRetTraces: + case IrInstructionIdMarkErrRetTracePtr: + case IrInstructionIdAtomicRmw: + case IrInstructionIdPhi: + case IrInstructionIdUnOp: + case IrInstructionIdBinOp: + case IrInstructionIdLoadPtr: + case IrInstructionIdConst: + case IrInstructionIdContainerInitFields: + case IrInstructionIdFieldPtr: + case IrInstructionIdElemPtr: + case IrInstructionIdTypeOf: + case IrInstructionIdToPtrType: + case IrInstructionIdPtrTypeChild: + case IrInstructionIdArrayLen: + case IrInstructionIdStructFieldPtr: + case IrInstructionIdUnionFieldPtr: + case IrInstructionIdArrayType: + case IrInstructionIdPromiseType: + case IrInstructionIdSliceType: + case IrInstructionIdSizeOf: + case IrInstructionIdTestNonNull: + case IrInstructionIdUnwrapOptional: + case IrInstructionIdClz: + case IrInstructionIdCtz: + case IrInstructionIdPopCount: + case IrInstructionIdSwitchVar: + case IrInstructionIdSwitchTarget: + case IrInstructionIdUnionTag: + case IrInstructionIdMinValue: + case IrInstructionIdMaxValue: + case IrInstructionIdEmbedFile: + case IrInstructionIdTruncate: + case IrInstructionIdIntType: + case IrInstructionIdBoolNot: + case IrInstructionIdMemberCount: + case IrInstructionIdMemberType: + case IrInstructionIdMemberName: + case IrInstructionIdAlignOf: + case IrInstructionIdReturnAddress: + case IrInstructionIdFrameAddress: + case IrInstructionIdHandle: + case IrInstructionIdTestErr: + case IrInstructionIdUnwrapErrCode: + case IrInstructionIdFnProto: + case IrInstructionIdTestComptime: + case IrInstructionIdPtrCast: + case IrInstructionIdBitCast: + case IrInstructionIdWidenOrShorten: + case IrInstructionIdPtrToInt: + case IrInstructionIdIntToPtr: + case IrInstructionIdIntToEnum: + case IrInstructionIdIntToErr: + case IrInstructionIdErrToInt: + case IrInstructionIdDeclRef: + case IrInstructionIdErrName: + case IrInstructionIdTypeName: + case IrInstructionIdTagName: + case IrInstructionIdFieldParentPtr: + case IrInstructionIdByteOffsetOf: + case IrInstructionIdBitOffsetOf: + case IrInstructionIdTypeInfo: + case IrInstructionIdTypeId: + case IrInstructionIdAlignCast: + case IrInstructionIdOpaqueType: + case IrInstructionIdArgType: + case IrInstructionIdTagType: + case IrInstructionIdErrorReturnTrace: + case IrInstructionIdErrorUnion: + case IrInstructionIdGetImplicitAllocator: + case IrInstructionIdCoroAlloc: + case IrInstructionIdCoroSize: + case IrInstructionIdCoroSuspend: + case IrInstructionIdCoroFree: + case IrInstructionIdCoroPromise: + case IrInstructionIdPromiseResultType: + case IrInstructionIdSqrt: + case IrInstructionIdAtomicLoad: + case IrInstructionIdIntCast: + case IrInstructionIdFloatCast: + case IrInstructionIdErrSetCast: + case IrInstructionIdIntToFloat: + case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: + case IrInstructionIdFromBytes: + case IrInstructionIdToBytes: + case IrInstructionIdEnumToInt: + case IrInstructionIdAsm: + case IrInstructionIdUnwrapErrPayload: + return nullptr; + } + zig_unreachable(); +} + +static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty) { + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + if (fn_entry == nullptr) + return nullptr; + if (type_has_bits(ty) && handle_is_ptr(ty)) { + IrResultLocationAlloca *alloca_loc = allocate(1); + alloca_loc->base.id = IrResultLocationIdAlloca; + alloca_loc->ty = ty; + fn_entry->result_loc_alloca_list.append(alloca_loc); + return &alloca_loc->base; + } + return nullptr; +} + +static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLocation *result_location, + ZigType *ty) +{ + if (result_location != nullptr) { + if (result_location->id == IrResultLocationIdLVal) { + IrResultLocationLVal *lval_loc = reinterpret_cast(result_location); + if (lval_loc->parent_instruction->value.special == ConstValSpecialStatic && + lval_loc->parent_instruction->value.type->id == ZigTypeIdPointer && + lval_loc->parent_instruction->value.data.x_ptr.special == ConstPtrSpecialDiscard) + { + // We have to convert this to an Alloca because the function is sret + // and so even though we want to discard the result, we need a stack allocation + // to pass as the sret pointer. + return create_alloca_result_loc(ira, ty); + } + } else if (result_location->id == IrResultLocationIdVar) { + // We have to resolve the variable in case of an inline loop. + IrResultLocationVar *var_loc = reinterpret_cast(result_location); + IrResultLocationVar *new_var_loc = allocate(1); + new_var_loc->base.id = IrResultLocationIdVar; + new_var_loc->var = var_loc->var; + while (new_var_loc->var->next_var != nullptr) { + new_var_loc->var = new_var_loc->var->next_var; + } + return &var_loc->base; + } + return result_location; + } + return create_alloca_result_loc(ira, ty); +} + static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { Error err; - if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { + assert(value->value.type->id == ZigTypeIdPointer); + ZigType *array_type = value->value.type->data.pointer.child_type; + + if ((err = type_resolve(ira->codegen, array_type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_instruction; } @@ -9351,8 +9612,6 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - assert(value->value.type->id == ZigTypeIdPointer); - ZigType *array_type = value->value.type->data.pointer.child_type; assert(is_slice(wanted_type)); bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; @@ -9366,8 +9625,11 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc } } - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpPtrOfArrayToSlice); + IrResultLocation *result_location = ir_get_result_location(value); + if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type); + + IrInstruction *result = ir_build_ptr_of_array_to_slice(&ira->new_irb, source_instr->scope, + source_instr->source_node, value, result_location); result->value.type = wanted_type; return result; } @@ -9659,208 +9921,6 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrResultLocation *ir_get_result_location(IrInstruction *base) { - switch (base->id) { - case IrInstructionIdInvalid: - zig_unreachable(); - case IrInstructionIdCall: { - IrInstructionCall *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdCast: { - IrInstructionCast *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdContainerInitList: { - IrInstructionContainerInitList *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdStructInit: { - IrInstructionStructInit *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdUnionInit: { - IrInstructionUnionInit *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdRef: { - IrInstructionRef *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdCmpxchg: { - IrInstructionCmpxchg *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdSlice: { - IrInstructionSlice *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdOptionalWrap: { - IrInstructionOptionalWrap *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdErrWrapPayload: { - IrInstructionErrWrapPayload *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdErrWrapCode: { - IrInstructionErrWrapCode *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdResultLoc: { - IrInstructionResultLoc *inst = reinterpret_cast(base); - return inst->result_location; - } - case IrInstructionIdBr: - case IrInstructionIdCondBr: - case IrInstructionIdSwitchBr: - case IrInstructionIdDeclVar: - case IrInstructionIdStorePtr: - case IrInstructionIdReturn: - case IrInstructionIdUnreachable: - case IrInstructionIdSetCold: - case IrInstructionIdSetRuntimeSafety: - case IrInstructionIdSetFloatMode: - case IrInstructionIdImport: - case IrInstructionIdCompileErr: - case IrInstructionIdCompileLog: - case IrInstructionIdCImport: - case IrInstructionIdCInclude: - case IrInstructionIdCDefine: - case IrInstructionIdCUndef: - case IrInstructionIdFence: - case IrInstructionIdMemset: - case IrInstructionIdMemcpy: - case IrInstructionIdBreakpoint: - case IrInstructionIdOverflowOp: - case IrInstructionIdCheckSwitchProngs: - case IrInstructionIdCheckStatementIsVoid: - case IrInstructionIdCheckRuntimeScope: - case IrInstructionIdPanic: - case IrInstructionIdSetEvalBranchQuota: - case IrInstructionIdPtrType: - case IrInstructionIdSetAlignStack: - case IrInstructionIdExport: - case IrInstructionIdCancel: - case IrInstructionIdCoroId: - case IrInstructionIdCoroBegin: - case IrInstructionIdCoroAllocFail: - case IrInstructionIdCoroEnd: - case IrInstructionIdCoroResume: - case IrInstructionIdCoroSave: - case IrInstructionIdCoroAllocHelper: - case IrInstructionIdAwaitBookkeeping: - case IrInstructionIdSaveErrRetAddr: - case IrInstructionIdAddImplicitReturnType: - case IrInstructionIdMergeErrRetTraces: - case IrInstructionIdMarkErrRetTracePtr: - case IrInstructionIdAtomicRmw: - case IrInstructionIdPhi: - case IrInstructionIdUnOp: - case IrInstructionIdBinOp: - case IrInstructionIdLoadPtr: - case IrInstructionIdConst: - case IrInstructionIdContainerInitFields: - case IrInstructionIdFieldPtr: - case IrInstructionIdElemPtr: - case IrInstructionIdVarPtr: - case IrInstructionIdTypeOf: - case IrInstructionIdToPtrType: - case IrInstructionIdPtrTypeChild: - case IrInstructionIdArrayLen: - case IrInstructionIdStructFieldPtr: - case IrInstructionIdUnionFieldPtr: - case IrInstructionIdArrayType: - case IrInstructionIdPromiseType: - case IrInstructionIdSliceType: - case IrInstructionIdSizeOf: - case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapOptional: - case IrInstructionIdClz: - case IrInstructionIdCtz: - case IrInstructionIdPopCount: - case IrInstructionIdSwitchVar: - case IrInstructionIdSwitchTarget: - case IrInstructionIdUnionTag: - case IrInstructionIdMinValue: - case IrInstructionIdMaxValue: - case IrInstructionIdEmbedFile: - case IrInstructionIdTruncate: - case IrInstructionIdIntType: - case IrInstructionIdBoolNot: - case IrInstructionIdMemberCount: - case IrInstructionIdMemberType: - case IrInstructionIdMemberName: - case IrInstructionIdAlignOf: - case IrInstructionIdReturnAddress: - case IrInstructionIdFrameAddress: - case IrInstructionIdHandle: - case IrInstructionIdTestErr: - case IrInstructionIdUnwrapErrCode: - case IrInstructionIdFnProto: - case IrInstructionIdTestComptime: - case IrInstructionIdPtrCast: - case IrInstructionIdBitCast: - case IrInstructionIdWidenOrShorten: - case IrInstructionIdPtrToInt: - case IrInstructionIdIntToPtr: - case IrInstructionIdIntToEnum: - case IrInstructionIdIntToErr: - case IrInstructionIdErrToInt: - case IrInstructionIdDeclRef: - case IrInstructionIdErrName: - case IrInstructionIdTypeName: - case IrInstructionIdTagName: - case IrInstructionIdFieldParentPtr: - case IrInstructionIdByteOffsetOf: - case IrInstructionIdBitOffsetOf: - case IrInstructionIdTypeInfo: - case IrInstructionIdTypeId: - case IrInstructionIdAlignCast: - case IrInstructionIdOpaqueType: - case IrInstructionIdArgType: - case IrInstructionIdTagType: - case IrInstructionIdErrorReturnTrace: - case IrInstructionIdErrorUnion: - case IrInstructionIdGetImplicitAllocator: - case IrInstructionIdCoroAlloc: - case IrInstructionIdCoroSize: - case IrInstructionIdCoroSuspend: - case IrInstructionIdCoroFree: - case IrInstructionIdCoroPromise: - case IrInstructionIdPromiseResultType: - case IrInstructionIdSqrt: - case IrInstructionIdAtomicLoad: - case IrInstructionIdIntCast: - case IrInstructionIdFloatCast: - case IrInstructionIdErrSetCast: - case IrInstructionIdIntToFloat: - case IrInstructionIdFloatToInt: - case IrInstructionIdBoolToInt: - case IrInstructionIdFromBytes: - case IrInstructionIdToBytes: - case IrInstructionIdEnumToInt: - case IrInstructionIdAsm: - case IrInstructionIdUnwrapErrPayload: - return nullptr; - } - zig_unreachable(); -} - -static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry == nullptr) - return nullptr; - if (type_has_bits(ty) && handle_is_ptr(ty)) { - IrResultLocationAlloca *alloca_loc = allocate(1); - alloca_loc->base.id = IrResultLocationIdAlloca; - alloca_loc->ty = ty; - fn_entry->result_loc_alloca_list.append(alloca_loc); - return &alloca_loc->base; - } - return nullptr; -} - static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { assert(wanted_type->id == ZigTypeIdOptional); @@ -13082,37 +13142,6 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i zig_unreachable(); } -static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLocation *result_location, - ZigType *ty) -{ - if (result_location != nullptr) { - if (result_location->id == IrResultLocationIdLVal) { - IrResultLocationLVal *lval_loc = reinterpret_cast(result_location); - if (lval_loc->parent_instruction->value.special == ConstValSpecialStatic && - lval_loc->parent_instruction->value.type->id == ZigTypeIdPointer && - lval_loc->parent_instruction->value.data.x_ptr.special == ConstPtrSpecialDiscard) - { - // We have to convert this to an Alloca because the function is sret - // and so even though we want to discard the result, we need a stack allocation - // to pass as the sret pointer. - return create_alloca_result_loc(ira, ty); - } - } else if (result_location->id == IrResultLocationIdVar) { - // We have to resolve the variable in case of an inline loop. - IrResultLocationVar *var_loc = reinterpret_cast(result_location); - IrResultLocationVar *new_var_loc = allocate(1); - new_var_loc->base.id = IrResultLocationIdVar; - new_var_loc->var = var_loc->var; - while (new_var_loc->var->next_var != nullptr) { - new_var_loc->var = new_var_loc->var->next_var; - } - return &var_loc->base; - } - return result_location; - } - return create_alloca_result_loc(ira, ty); -} - static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) { @@ -13289,9 +13318,7 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { return fn_entry->variable_list.at(next_var_i); } -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - ZigVar *var) -{ +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } @@ -13345,10 +13372,16 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, no_mem_slot: - IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, - instruction->scope, instruction->source_node, var); - var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, var->value->type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); + IrResultLocation *result_location = nullptr; + if (instruction->id == IrInstructionIdVarPtr) { + IrInstructionVarPtr *var_inst = reinterpret_cast(instruction); + result_location = ir_analyze_result_location(ira, var_inst->result_location, result_type); + } + IrInstruction *var_ptr_instruction = ir_build_var_ptr_x(&ira->new_irb, + instruction->scope, instruction->source_node, var, nullptr, result_location); + var_ptr_instruction->value.type = result_type; bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; @@ -21372,6 +21405,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: case IrInstructionIdResultLoc: + case IrInstructionIdPtrOfArrayToSlice: zig_unreachable(); case IrInstructionIdReturn: @@ -21864,6 +21898,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: case IrInstructionIdResultLoc: + case IrInstructionIdPtrOfArrayToSlice: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7198de9ffba0..076781038701 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -969,6 +969,11 @@ static void ir_print_result_loc(IrPrint *irp, IrInstructionResultLoc *instructio fprintf(irp->f, "resultloc (TODO print details)"); } +static void ir_print_ptr_of_array_to_slice(IrPrint *irp, IrInstructionPtrOfArrayToSlice *instruction) { + fprintf(irp->f, "PtrOfArrayToSlice "); + ir_print_other_instruction(irp, instruction->value); +} + static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { fprintf(irp->f, "inttoerr "); ir_print_other_instruction(irp, instruction->target); @@ -1778,6 +1783,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdResultLoc: ir_print_result_loc(irp, (IrInstructionResultLoc *)instruction); break; + case IrInstructionIdPtrOfArrayToSlice: + ir_print_ptr_of_array_to_slice(irp, (IrInstructionPtrOfArrayToSlice *)instruction); + break; } fprintf(irp->f, "\n"); } From 7d81743e00a375f37c1e67a15396804167b054e8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Oct 2018 14:57:12 -0400 Subject: [PATCH 11/17] copy elision - assignment of non function call --- src/all_types.hpp | 5 ++ src/codegen.cpp | 72 ++++++++++++++++--------- src/ir.cpp | 134 ++++++++++++++++++++++++++-------------------- src/ir.hpp | 2 + 4 files changed, 130 insertions(+), 83 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index e2b162111e29..5bcdd140d08d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2041,7 +2041,11 @@ enum IrResultLocationId { struct IrResultLocation { IrResultLocation *child; + IrResultLocation *parent; IrResultLocationId id; + bool from_call; + bool sret; + bool copy_generated; }; struct IrResultLocationVar { @@ -2368,6 +2372,7 @@ struct IrInstructionLoadPtr { IrInstruction base; IrInstruction *ptr; + IrResultLocation *result_location; }; struct IrInstructionStorePtr { diff --git a/src/codegen.cpp b/src/codegen.cpp index cfe66a4836c7..9915b7344141 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1785,7 +1785,7 @@ static LLVMRealPredicate cmp_op_to_real_predicate(IrBinOp cmp_op) { } static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, - LLVMValueRef value, bool is_global_const) + LLVMValueRef value) { assert(ptr_type->id == ZigTypeIdPointer); ZigType *child_type = ptr_type->data.pointer.child_type; @@ -1794,12 +1794,8 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty return nullptr; if (handle_is_ptr(child_type)) { - // If the handle is a pointer, the result location mechanism caused the value to be - // populated in place, and this assignment should be elided, unless this - // is a global constant. - if (!is_global_const) + if (ptr == value) return nullptr; - assert(LLVMGetTypeKind(LLVMTypeOf(value)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMTypeOf(ptr)) == LLVMPointerTypeKind); @@ -1882,6 +1878,34 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { return instruction->llvm_value; } +static LLVMValueRef gen_assign(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, + IrInstruction *value) +{ + IrResultLocation *result_location = ir_get_result_location(value); + bool elide_copy; + if (result_location != nullptr) { + switch (result_location->id) { + case IrResultLocationIdAlloca: + elide_copy = false; + break; + case IrResultLocationIdLVal: + case IrResultLocationIdVar: + case IrResultLocationIdRet: + case IrResultLocationIdOptionalUnwrap: + case IrResultLocationIdErrorUnionPayload: + elide_copy = handle_is_ptr(value->value.type) && result_location->from_call; + break; + } + } else { + elide_copy = false; + } + + if (elide_copy) + return nullptr; + + return gen_assign_raw(g, ptr, ptr_type, ir_llvm_value(g, value)); +} + ATTRIBUTE_NORETURN static void report_errors_and_exit(CodeGen *g) { assert(g->errors.length != 0); @@ -2249,8 +2273,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value, - return_instruction->value->value.special == ConstValSpecialStatic); + gen_assign(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), return_instruction->value); LLVMBuildRetVoid(g->builder); } else if (handle_is_ptr(return_type)) { LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); @@ -3320,9 +3343,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, assert(var->value->type == init_value->value.type); ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, PtrLenSingle, var->align_bytes, 0, 0); - bool is_global_const = init_value->value.special == ConstValSpecialStatic; - LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); - gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val, is_global_const); + gen_assign(g, var->value_ref, var_ptr_type, init_value); } else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) { gen_undef_init(g, var->align_bytes, var->value->type, var->value_ref); } @@ -3365,12 +3386,11 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir bool have_init_expr = !value_is_all_undef(&instruction->value->value); if (have_init_expr) { LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); assert(instruction->ptr->value.type->id == ZigTypeIdPointer); ZigType *ptr_type = instruction->ptr->value.type; - gen_assign_raw(g, ptr, ptr_type, value, instruction->value->value.special == ConstValSpecialStatic); + gen_assign(g, ptr, ptr_type, instruction->value); return nullptr; } @@ -4320,7 +4340,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val, false); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); @@ -4724,9 +4744,8 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I return LLVMConstInt(LLVMInt1Type(), 1, false); } - LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); if (type_is_codegen_pointer(child_type)) { - return payload_val; + return ir_llvm_value(g, instruction->value); } @@ -4734,7 +4753,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); // child_type and instruction->value->value.type may differ by constness - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val, false); + gen_assign(g, val_ptr, get_pointer_to_type(g, child_type, false), instruction->value); LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); @@ -4781,13 +4800,11 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa LLVMValueRef result_loc = gen_result_location(g, instruction->result_location); - LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_err_index, ""); gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_payload_index, ""); - gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val, false); + gen_assign(g, payload_ptr, get_pointer_to_type(g, payload_type, false), instruction->value); return result_loc; } @@ -4822,7 +4839,6 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, result_loc, (unsigned)type_struct_field->gen_index, ""); - LLVMValueRef value = ir_llvm_value(g, field->value); uint32_t field_align_bytes = get_abi_alignment(g, type_struct_field->type_entry); uint32_t host_int_bytes = get_host_int_bytes(g, instruction->struct_type, type_struct_field); @@ -4831,7 +4847,7 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, false, false, PtrLenSingle, field_align_bytes, (uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes); - gen_assign_raw(g, field_ptr, ptr_type, value, false); + gen_assign(g, field_ptr, ptr_type, field->value); } return result_loc; } @@ -4868,9 +4884,8 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I } LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, ""); - LLVMValueRef value = ir_llvm_value(g, instruction->init_value); - gen_assign_raw(g, field_ptr, ptr_type, value, false); + gen_assign(g, field_ptr, ptr_type, instruction->init_value); return result_loc; } @@ -4887,13 +4902,12 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec ZigType *child_type = array_type->data.array.child_type; for (size_t i = 0; i < field_count; i += 1) { - LLVMValueRef elem_val = ir_llvm_value(g, instruction->items[i]); LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), LLVMConstInt(g->builtin_types.entry_usize->type_ref, i, false), }; LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, ""); - gen_assign_raw(g, elem_ptr, get_pointer_to_type(g, child_type, false), elem_val, false); + gen_assign(g, elem_ptr, get_pointer_to_type(g, child_type, false), instruction->items[i]); } return tmp_array_ptr; @@ -5205,6 +5219,12 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr static LLVMValueRef ir_render_result_loc(CodeGen *g, IrExecutable *executable, IrInstructionResultLoc *instruction) { + // Generate the ancestor so that it caches our result + IrResultLocation *result_loc = instruction->result_location; + while (result_loc->parent != nullptr) { + result_loc = result_loc->parent; + } + gen_result_location(g, result_loc); return gen_result_location(g, instruction->result_location); } diff --git a/src/ir.cpp b/src/ir.cpp index 8b5bda317db6..0aa4acfe63ea 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1397,9 +1397,12 @@ static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *sou return &export_instruction->base; } -static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { +static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, + IrResultLocation *result_location) +{ IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; + instruction->result_location = result_location; ir_ref_instruction(ptr, irb->current_basic_block); @@ -3065,7 +3068,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *err_union_ptr = ir_gen_node_lval(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); + IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr, nullptr); IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); @@ -3093,7 +3096,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, if (lval == LValPtr) return unwrapped_ptr; else - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, nullptr); } } zig_unreachable(); @@ -3281,7 +3284,7 @@ static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *no IrInstruction *lvalue = ir_gen_node_lval(irb, node->data.bin_op_expr.op1, scope, LValPtr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; - IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); + IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue, nullptr); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (op2 == irb->codegen->invalid_instruction) return op2; @@ -3384,7 +3387,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val); IrInstruction *is_comptime; @@ -3409,7 +3412,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); - IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); + IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr, nullptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -3595,7 +3598,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, if (lval == LValPtr) return var_ptr; else - return ir_build_load_ptr(irb, scope, node, var_ptr); + return ir_build_load_ptr(irb, scope, node, var_ptr, result_location); } Tld *tld = find_decl(irb->codegen, scope, variable_name); @@ -3632,7 +3635,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_load_ptr(irb, scope, node, ptr_instruction, nullptr); } static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -4325,7 +4328,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_load_ptr(irb, scope, node, ptr_instruction, nullptr); } case BuiltinFnIdTypeInfo: { @@ -4971,7 +4974,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode if (lval == LValPtr) return payload_ptr; - return ir_build_load_ptr(irb, scope, source_node, payload_ptr); + return ir_build_load_ptr(irb, scope, source_node, payload_ptr, nullptr); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5162,7 +5165,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *err_val_ptr = ir_gen_node_lval(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5176,7 +5179,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? - var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); + var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value, nullptr); ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } @@ -5249,7 +5252,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *maybe_val_ptr = ir_gen_node_lval(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); @@ -5261,7 +5264,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? - var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); + var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value, nullptr); ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; @@ -5400,7 +5403,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; - IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); + IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr, nullptr); IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val); IrInstruction *elem_var_type; @@ -5451,7 +5454,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_build_br(irb, child_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr); + IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr, nullptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -5463,7 +5466,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; } else { - elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); + elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr, nullptr); } ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val)); @@ -5662,7 +5665,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); @@ -5689,7 +5692,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no var_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); + IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value, nullptr); ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { @@ -5741,7 +5744,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); + IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk"); @@ -5764,7 +5767,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); + IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value, nullptr); ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { @@ -5840,9 +5843,9 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit IrInstruction *var_value; if (prong_value) { IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); - var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value); + var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value, nullptr); } else { - var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); + var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr, nullptr); } IrInstruction *var_type = nullptr; // infer the type ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); @@ -6277,7 +6280,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr); + IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr, nullptr); IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val); IrInstruction *is_comptime; @@ -6316,7 +6319,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN ir_set_cursor_at_end_and_append_block(irb, ok_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false); - IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); + IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr, nullptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -6829,7 +6832,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. - IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); + IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr, nullptr); ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -6889,7 +6892,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, scope, node, my_result_var_ptr); + return ir_build_load_ptr(irb, scope, node, my_result_var_ptr, nullptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -7064,7 +7067,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_load_ptr(irb, scope, node, ptr_instruction, nullptr); } case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; @@ -7085,7 +7088,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (lval == LValPtr) return unwrapped_ptr; - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr, nullptr); } case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); @@ -7232,7 +7235,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); - IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); + IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr, nullptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); @@ -7322,7 +7325,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); + IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, nullptr); IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); @@ -7334,7 +7337,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); - IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); + IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, nullptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } // Before we destroy the coroutine frame, we need to load the target promise into @@ -7342,7 +7345,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // otherwise llvm tries to access memory inside the destroyed frame. IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, irb->exec->await_handle_var_ptr, false); - IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); + IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr, nullptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); @@ -7369,7 +7372,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); - IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); + IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr, nullptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, @@ -7378,7 +7381,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); - IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); + IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); size_t arg_count = 2; IrInstruction **args = allocate(arg_count); @@ -9353,7 +9356,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, return result; } -static IrResultLocation *ir_get_result_location(IrInstruction *base) { +IrResultLocation *ir_get_result_location(IrInstruction *base) { switch (base->id) { case IrInstructionIdInvalid: zig_unreachable(); @@ -9413,6 +9416,10 @@ static IrResultLocation *ir_get_result_location(IrInstruction *base) { IrInstructionVarPtr *inst = reinterpret_cast(base); return inst->result_location; } + case IrInstructionIdLoadPtr: { + IrInstructionLoadPtr *inst = reinterpret_cast(base); + return inst->result_location; + } case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: @@ -9460,7 +9467,6 @@ static IrResultLocation *ir_get_result_location(IrInstruction *base) { case IrInstructionIdPhi: case IrInstructionIdUnOp: case IrInstructionIdBinOp: - case IrInstructionIdLoadPtr: case IrInstructionIdConst: case IrInstructionIdContainerInitFields: case IrInstructionIdFieldPtr: @@ -9548,13 +9554,15 @@ static IrResultLocation *ir_get_result_location(IrInstruction *base) { zig_unreachable(); } -static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty) { +static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty, bool from_call) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry == nullptr) return nullptr; if (type_has_bits(ty) && handle_is_ptr(ty)) { IrResultLocationAlloca *alloca_loc = allocate(1); alloca_loc->base.id = IrResultLocationIdAlloca; + alloca_loc->base.from_call = from_call; + if (from_call && handle_is_ptr(ty)) alloca_loc->base.sret = true; alloca_loc->ty = ty; fn_entry->result_loc_alloca_list.append(alloca_loc); return &alloca_loc->base; @@ -9563,9 +9571,11 @@ static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty) { } static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLocation *result_location, - ZigType *ty) + ZigType *ty, bool from_call) { if (result_location != nullptr) { + result_location->from_call = from_call; + if (from_call && handle_is_ptr(ty)) result_location->sret = true; if (result_location->id == IrResultLocationIdLVal) { IrResultLocationLVal *lval_loc = reinterpret_cast(result_location); if (lval_loc->parent_instruction->value.special == ConstValSpecialStatic && @@ -9575,13 +9585,15 @@ static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLoca // We have to convert this to an Alloca because the function is sret // and so even though we want to discard the result, we need a stack allocation // to pass as the sret pointer. - return create_alloca_result_loc(ira, ty); + return create_alloca_result_loc(ira, ty, from_call); } } else if (result_location->id == IrResultLocationIdVar) { // We have to resolve the variable in case of an inline loop. IrResultLocationVar *var_loc = reinterpret_cast(result_location); IrResultLocationVar *new_var_loc = allocate(1); new_var_loc->base.id = IrResultLocationIdVar; + new_var_loc->base.from_call = from_call; + if (from_call && handle_is_ptr(ty)) new_var_loc->base.sret = true; new_var_loc->var = var_loc->var; while (new_var_loc->var->next_var != nullptr) { new_var_loc->var = new_var_loc->var->next_var; @@ -9590,7 +9602,7 @@ static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLoca } return result_location; } - return create_alloca_result_loc(ira, ty); + return create_alloca_result_loc(ira, ty, from_call); } static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, @@ -9626,7 +9638,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc } IrResultLocation *result_location = ir_get_result_location(value); - if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type); + if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type, false); IrInstruction *result = ir_build_ptr_of_array_to_slice(&ira->new_irb, source_instr->scope, source_instr->source_node, value, result_location); @@ -9950,9 +9962,11 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc // OptionalWrap result location value to the result location stack. IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { - if (handle_is_ptr(value->value.type)) { + if (handle_is_ptr(value->value.type) && result_location->from_call) { IrResultLocationOptionalUnwrap *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdOptionalUnwrap; + new_result_location->base.parent = result_location; + new_result_location->base.from_call = true; assert(result_location->child == nullptr); result_location->child = &new_result_location->base; IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, @@ -9962,7 +9976,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc return result; } } else { - result_location = create_alloca_result_loc(ira, wanted_type); + result_location = create_alloca_result_loc(ira, wanted_type, false); } IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, @@ -9998,9 +10012,11 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { - if (handle_is_ptr(value->value.type)) { + if (handle_is_ptr(value->value.type) && result_location->from_call) { IrResultLocationErrorUnionPayload *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdErrorUnionPayload; + new_result_location->base.parent = result_location; + new_result_location->base.from_call = true; assert(result_location->child == nullptr); result_location->child = &new_result_location->base; IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, @@ -10010,7 +10026,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction return result; } } else { - result_location = create_alloca_result_loc(ira, wanted_type); + result_location = create_alloca_result_loc(ira, wanted_type, false); } IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, @@ -10083,7 +10099,7 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so } IrResultLocation *result_location = ir_get_result_location(value); - if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type); + if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type, false); IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value, result_location); @@ -11318,9 +11334,13 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } } } - // TODO if the instruction is a const ref instruction we can skip it + IrResultLocation *result_location = nullptr; + if (source_instruction->id == IrInstructionIdLoadPtr) { + IrInstructionLoadPtr *load_ptr = reinterpret_cast(source_instruction); + result_location = ir_analyze_result_location(ira, load_ptr->result_location, child_type, false); + } IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, ptr); + source_instruction->source_node, ptr, result_location); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; } else { @@ -13176,7 +13196,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); IrResultLocation *result_location = ir_analyze_result_location(ira, call_instruction->result_location, - async_return_type); + async_return_type, false); IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr, @@ -13377,7 +13397,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, IrResultLocation *result_location = nullptr; if (instruction->id == IrInstructionIdVarPtr) { IrInstructionVarPtr *var_inst = reinterpret_cast(instruction); - result_location = ir_analyze_result_location(ira, var_inst->result_location, result_type); + result_location = ir_analyze_result_location(ira, var_inst->result_location, result_type, false); } IrInstruction *var_ptr_instruction = ir_build_var_ptr_x(&ira->new_irb, instruction->scope, instruction->source_node, var, nullptr, result_location); @@ -13833,7 +13853,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call assert(async_allocator_inst == nullptr); IrResultLocation *result_location = ir_analyze_result_location(ira, call_instruction->result_location, - return_type); + return_type, true); IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, @@ -13934,7 +13954,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } IrResultLocation *result_location = ir_analyze_result_location(ira, call_instruction->result_location, - return_type); + return_type, true); IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, @@ -16281,7 +16301,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstruction *result = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr); + target_value_ptr, nullptr); result->value.type = target_type; return result; } @@ -16312,7 +16332,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + switch_target_instruction->base.source_node, target_value_ptr, nullptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16337,7 +16357,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, } IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + switch_target_instruction->base.source_node, target_value_ptr, nullptr); enum_value->value.type = target_type; return enum_value; } @@ -16504,7 +16524,7 @@ static IrInstruction *ir_analyze_instruction_array_len(IrAnalyze *ira, array_len_instruction->base.source_node, array_value, field); len_ptr->value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_usize, true); IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr); + array_len_instruction->base.scope, array_len_instruction->base.source_node, len_ptr, nullptr); result->value.type = ira->codegen->builtin_types.entry_usize; return result; } else { diff --git a/src/ir.hpp b/src/ir.hpp index b298750decdd..91d76c7676cd 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -24,4 +24,6 @@ ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_ bool ir_has_side_effects(IrInstruction *instruction); ConstExprValue *const_ptr_pointee(CodeGen *codegen, ConstExprValue *const_val); +IrResultLocation *ir_get_result_location(IrInstruction *base); + #endif From 3ac09f8e16e9a498d6b823997b935fd278ffd393 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Oct 2018 16:40:30 -0400 Subject: [PATCH 12/17] copy elision - double implicit cast when payload is integer --- src/all_types.hpp | 2 -- src/codegen.cpp | 26 +++----------------------- src/ir.cpp | 7 ++----- 3 files changed, 5 insertions(+), 30 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 5bcdd140d08d..f60f52fd9224 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2044,8 +2044,6 @@ struct IrResultLocation { IrResultLocation *parent; IrResultLocationId id; bool from_call; - bool sret; - bool copy_generated; }; struct IrResultLocationVar { diff --git a/src/codegen.cpp b/src/codegen.cpp index 9915b7344141..2eea29842663 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1882,23 +1882,7 @@ static LLVMValueRef gen_assign(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, IrInstruction *value) { IrResultLocation *result_location = ir_get_result_location(value); - bool elide_copy; - if (result_location != nullptr) { - switch (result_location->id) { - case IrResultLocationIdAlloca: - elide_copy = false; - break; - case IrResultLocationIdLVal: - case IrResultLocationIdVar: - case IrResultLocationIdRet: - case IrResultLocationIdOptionalUnwrap: - case IrResultLocationIdErrorUnionPayload: - elide_copy = handle_is_ptr(value->value.type) && result_location->from_call; - break; - } - } else { - elide_copy = false; - } + bool elide_copy = result_location != nullptr && result_location->from_call; if (elide_copy) return nullptr; @@ -3585,11 +3569,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; - LLVMValueRef tmp_ptr; - if (first_arg_ret || instruction->is_async || handle_is_ptr(src_return_type)) { - tmp_ptr = gen_result_location(g, instruction->result_location); - assert(tmp_ptr != nullptr); - } + LLVMValueRef tmp_ptr = gen_result_location(g, instruction->result_location); ZigList gen_param_values = {}; if (first_arg_ret) { @@ -3656,7 +3636,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } else if (first_arg_ret) { set_call_instr_sret(g, result); return tmp_ptr; - } else if (handle_is_ptr(src_return_type)) { + } else if (tmp_ptr != nullptr) { auto store_instr = LLVMBuildStore(g->builder, result, tmp_ptr); LLVMSetAlignment(store_instr, LLVMGetAlignment(tmp_ptr)); return tmp_ptr; diff --git a/src/ir.cpp b/src/ir.cpp index 0aa4acfe63ea..748dcd45bafb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9562,7 +9562,6 @@ static IrResultLocation *create_alloca_result_loc(IrAnalyze *ira, ZigType *ty, b IrResultLocationAlloca *alloca_loc = allocate(1); alloca_loc->base.id = IrResultLocationIdAlloca; alloca_loc->base.from_call = from_call; - if (from_call && handle_is_ptr(ty)) alloca_loc->base.sret = true; alloca_loc->ty = ty; fn_entry->result_loc_alloca_list.append(alloca_loc); return &alloca_loc->base; @@ -9575,7 +9574,6 @@ static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLoca { if (result_location != nullptr) { result_location->from_call = from_call; - if (from_call && handle_is_ptr(ty)) result_location->sret = true; if (result_location->id == IrResultLocationIdLVal) { IrResultLocationLVal *lval_loc = reinterpret_cast(result_location); if (lval_loc->parent_instruction->value.special == ConstValSpecialStatic && @@ -9593,7 +9591,6 @@ static IrResultLocation *ir_analyze_result_location(IrAnalyze *ira, IrResultLoca IrResultLocationVar *new_var_loc = allocate(1); new_var_loc->base.id = IrResultLocationIdVar; new_var_loc->base.from_call = from_call; - if (from_call && handle_is_ptr(ty)) new_var_loc->base.sret = true; new_var_loc->var = var_loc->var; while (new_var_loc->var->next_var != nullptr) { new_var_loc->var = new_var_loc->var->next_var; @@ -9962,7 +9959,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc // OptionalWrap result location value to the result location stack. IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { - if (handle_is_ptr(value->value.type) && result_location->from_call) { + if (result_location->from_call) { IrResultLocationOptionalUnwrap *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdOptionalUnwrap; new_result_location->base.parent = result_location; @@ -10012,7 +10009,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { - if (handle_is_ptr(value->value.type) && result_location->from_call) { + if (result_location->from_call) { IrResultLocationErrorUnionPayload *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdErrorUnionPayload; new_result_location->base.parent = result_location; From f7cf6307ee19aadae7bc460cdb5ea76da53a95eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 15 Oct 2018 16:41:31 -0400 Subject: [PATCH 13/17] copy elision - remove hack --- src/all_types.hpp | 1 - src/codegen.cpp | 6 ------ src/ir.cpp | 2 -- 3 files changed, 9 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index f60f52fd9224..79396415d7a1 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2041,7 +2041,6 @@ enum IrResultLocationId { struct IrResultLocation { IrResultLocation *child; - IrResultLocation *parent; IrResultLocationId id; bool from_call; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 2eea29842663..18e7e219b98d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5199,12 +5199,6 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr static LLVMValueRef ir_render_result_loc(CodeGen *g, IrExecutable *executable, IrInstructionResultLoc *instruction) { - // Generate the ancestor so that it caches our result - IrResultLocation *result_loc = instruction->result_location; - while (result_loc->parent != nullptr) { - result_loc = result_loc->parent; - } - gen_result_location(g, result_loc); return gen_result_location(g, instruction->result_location); } diff --git a/src/ir.cpp b/src/ir.cpp index 748dcd45bafb..0e15f72fd69d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9962,7 +9962,6 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc if (result_location->from_call) { IrResultLocationOptionalUnwrap *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdOptionalUnwrap; - new_result_location->base.parent = result_location; new_result_location->base.from_call = true; assert(result_location->child == nullptr); result_location->child = &new_result_location->base; @@ -10012,7 +10011,6 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction if (result_location->from_call) { IrResultLocationErrorUnionPayload *new_result_location = allocate(1); new_result_location->base.id = IrResultLocationIdErrorUnionPayload; - new_result_location->base.parent = result_location; new_result_location->base.from_call = true; assert(result_location->child == nullptr); result_location->child = &new_result_location->base; From 435d9841442915cac92566335b1c3404f71205a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 16 Oct 2018 11:05:41 -0400 Subject: [PATCH 14/17] copy elision - array to slice --- src/ir.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 5112aeaaf25d..e36c51ccca12 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1994,12 +1994,14 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on) + IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, + IrResultLocation *result_location) { IrInstructionSlice *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->start = start; instruction->end = end; + instruction->result_location = result_location; instruction->safety_check_on = safety_check_on; ir_ref_instruction(ptr, irb->current_basic_block); @@ -6230,7 +6232,7 @@ static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode return ir_build_const_void(irb, parent_scope, node); } -static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, IrResultLocation *result_location) { assert(node->type == NodeTypeSliceExpr); AstNodeSliceExpr *slice_expr = &node->data.slice_expr; @@ -6255,7 +6257,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) end_value = nullptr; } - return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); + return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, result_location); } static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -7125,7 +7127,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeDefer: return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval); case NodeTypeSliceExpr: - return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node, result_location), lval); case NodeTypeUnwrapErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); case NodeTypeContainerDecl: @@ -7278,7 +7280,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name); - IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false); + IrResultLocationLVal *addrs_slice_ptr_loc = allocate(1); + addrs_slice_ptr_loc->base.id = IrResultLocationIdLVal; + addrs_slice_ptr_loc->parent_instruction = addrs_slice_ptr; + IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false, &addrs_slice_ptr_loc->base); ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value); } @@ -7382,7 +7387,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); - IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); + IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, nullptr); size_t arg_count = 2; IrInstruction **args = allocate(arg_count); args[0] = implicit_allocator_ptr; // self @@ -10186,8 +10191,11 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); + IrResultLocation *result_location = ir_get_result_location(array_arg); + if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type, false); + IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, - source_instr->source_node, array_ptr, start, end, false); + source_instr->source_node, array_ptr, start, end, false, result_location); result->value.type = wanted_type; result->value.data.rh_slice.id = RuntimeHintSliceIdLen; result->value.data.rh_slice.len = array_type->data.array.len; @@ -19348,7 +19356,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction IrInstruction *new_instruction = ir_build_slice(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - ptr_ptr, casted_start, end, instruction->safety_check_on); + ptr_ptr, casted_start, end, instruction->safety_check_on, nullptr); new_instruction->value.type = return_type; return new_instruction; } From a8dce973ca9a1e9ffd02d16689a3ba13d3ab8366 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 16 Oct 2018 14:43:35 -0400 Subject: [PATCH 15/17] copy elision - double implicit cast when payload is integer, no fn call --- src/all_types.hpp | 9 ++++++ src/codegen.cpp | 39 ++++++++++++++++++++++-- src/ir.cpp | 76 ++++++++++++++++++++++++++++------------------- src/ir_print.cpp | 3 +- 4 files changed, 93 insertions(+), 34 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 79396415d7a1..8b3d7fdceba9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2037,10 +2037,12 @@ enum IrResultLocationId { IrResultLocationIdOptionalUnwrap, IrResultLocationIdErrorUnionPayload, + IrResultLocationIdPtrOfArrayToSlice, }; struct IrResultLocation { IrResultLocation *child; + IrResultLocation *parent; IrResultLocationId id; bool from_call; }; @@ -2075,6 +2077,12 @@ struct IrResultLocationErrorUnionPayload { LLVMValueRef result; }; +struct IrResultLocationPtrOfArrayToSlice { + IrResultLocation base; + LLVMValueRef result; + uint64_t len; +}; + enum IrInstructionId { IrInstructionIdInvalid, IrInstructionIdBr, @@ -3333,6 +3341,7 @@ struct IrInstructionCheckRuntimeScope { struct IrInstructionResultLoc { IrInstruction base; + IrInstruction *value; IrResultLocation *result_location; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 46e6884a77b4..bd679f5dbd0f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1882,7 +1882,8 @@ static LLVMValueRef gen_assign(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, IrInstruction *value) { IrResultLocation *result_location = ir_get_result_location(value); - bool elide_copy = result_location != nullptr && result_location->from_call; + bool elide_copy = value->id == IrInstructionIdResultLoc || + (result_location != nullptr && result_location->from_call); if (elide_copy) return nullptr; @@ -2883,6 +2884,22 @@ static LLVMValueRef gen_result_location_recur(CodeGen *g, IrResultLocation *base loc->result = LLVMBuildStructGEP(g->builder, prev, err_union_payload_index, ""); return loc->result; } + case IrResultLocationIdPtrOfArrayToSlice: { + IrResultLocationPtrOfArrayToSlice *loc = reinterpret_cast(base); + if (loc->result != nullptr) + return loc->result; + assert(prev != nullptr); + if (base->child != nullptr) { + prev = gen_result_location_recur(g, base->child, prev); + } + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, prev, slice_len_index, ""); + LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, loc->len, false); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + loc->result = LLVMBuildStructGEP(g->builder, prev, slice_ptr_index, ""); + return loc->result; + } } zig_unreachable(); } @@ -5199,7 +5216,25 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr static LLVMValueRef ir_render_result_loc(CodeGen *g, IrExecutable *executable, IrInstructionResultLoc *instruction) { - return gen_result_location(g, instruction->result_location); + // Generate the ancestor so that it caches our result + IrResultLocation *result_loc = instruction->result_location; + while (result_loc->parent != nullptr) { + result_loc = result_loc->parent; + } + LLVMValueRef ancestor_ptr = gen_result_location(g, result_loc); + + LLVMValueRef ptr = gen_result_location(g, instruction->result_location); + if (instruction->result_location->child == nullptr && !instruction->result_location->from_call) { + IrInstructionResultLoc *ancestor = instruction; + while (ancestor->value->id == IrInstructionIdResultLoc) { + ancestor = reinterpret_cast(ancestor->value); + } + + LLVMValueRef val = ir_llvm_value(g, ancestor->value); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, LLVMGetElementType(LLVMTypeOf(ancestor_ptr)), ""); + gen_store_untyped(g, bitcasted, ancestor_ptr, 0, false); + } + return ptr; } static void set_debug_location(CodeGen *g, IrInstruction *instruction) { diff --git a/src/ir.cpp b/src/ir.cpp index e36c51ccca12..5457c04f4743 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2773,10 +2773,13 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, } static IrInstruction *ir_build_result_loc(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrResultLocation *result_location) + IrResultLocation *result_location, IrInstruction *value) { IrInstructionResultLoc *instruction = ir_build_instruction(irb, scope, source_node); instruction->result_location = result_location; + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } @@ -9640,12 +9643,25 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc } IrResultLocation *result_location = ir_get_result_location(value); - if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type, false); - - IrInstruction *result = ir_build_ptr_of_array_to_slice(&ira->new_irb, source_instr->scope, - source_instr->source_node, value, result_location); - result->value.type = wanted_type; - return result; + if (result_location == nullptr) { + result_location = create_alloca_result_loc(ira, wanted_type, false); + IrInstruction *result = ir_build_ptr_of_array_to_slice(&ira->new_irb, source_instr->scope, + source_instr->source_node, value, result_location); + result->value.type = wanted_type; + return result; + } else { + IrResultLocationPtrOfArrayToSlice *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdPtrOfArrayToSlice; + new_result_location->base.parent = result_location; + new_result_location->base.from_call = result_location->from_call; + new_result_location->len = array_type->data.array.len; + assert(result_location->child == nullptr); + result_location->child = &new_result_location->base; + IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, + source_instr->source_node, &new_result_location->base, value); + result->value.type = wanted_type; + return result; + } } static bool is_container(ZigType *type) { @@ -9964,18 +9980,17 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc // OptionalWrap result location value to the result location stack. IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { - if (result_location->from_call) { - IrResultLocationOptionalUnwrap *new_result_location = allocate(1); - new_result_location->base.id = IrResultLocationIdOptionalUnwrap; - new_result_location->base.from_call = true; - assert(result_location->child == nullptr); - result_location->child = &new_result_location->base; - IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, - source_instr->source_node, &new_result_location->base); - result->value.data.rh_maybe = RuntimeHintOptionalNonNull; - result->value.type = wanted_type; - return result; - } + IrResultLocationOptionalUnwrap *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdOptionalUnwrap; + new_result_location->base.parent = result_location; + new_result_location->base.from_call = result_location->from_call; + assert(result_location->child == nullptr); + result_location->child = &new_result_location->base; + IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, + source_instr->source_node, &new_result_location->base, value); + result->value.data.rh_maybe = RuntimeHintOptionalNonNull; + result->value.type = wanted_type; + return result; } else { result_location = create_alloca_result_loc(ira, wanted_type, false); } @@ -10013,18 +10028,17 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction IrResultLocation *result_location = ir_get_result_location(value); if (result_location != nullptr) { - if (result_location->from_call) { - IrResultLocationErrorUnionPayload *new_result_location = allocate(1); - new_result_location->base.id = IrResultLocationIdErrorUnionPayload; - new_result_location->base.from_call = true; - assert(result_location->child == nullptr); - result_location->child = &new_result_location->base; - IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, - source_instr->source_node, &new_result_location->base); - result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; - result->value.type = wanted_type; - return result; - } + IrResultLocationErrorUnionPayload *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdErrorUnionPayload; + new_result_location->base.parent = result_location; + new_result_location->base.from_call = result_location->from_call; + assert(result_location->child == nullptr); + result_location->child = &new_result_location->base; + IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, + source_instr->source_node, &new_result_location->base, value); + result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; + result->value.type = wanted_type; + return result; } else { result_location = create_alloca_result_loc(ira, wanted_type, false); } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 076781038701..9c70b6713bee 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -966,7 +966,8 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime } static void ir_print_result_loc(IrPrint *irp, IrInstructionResultLoc *instruction) { - fprintf(irp->f, "resultloc (TODO print details)"); + fprintf(irp->f, "resultloc "); + ir_print_other_instruction(irp, instruction->value); } static void ir_print_ptr_of_array_to_slice(IrPrint *irp, IrInstructionPtrOfArrayToSlice *instruction) { From acb31c3c08af96035aac0dbee780ca992c2a8c00 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 16 Oct 2018 14:56:29 -0400 Subject: [PATCH 16/17] copy elision - error code to error union --- src/all_types.hpp | 6 ++++++ src/codegen.cpp | 12 ++++++++++++ src/ir.cpp | 26 ++++++++++++++++++++------ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 8b3d7fdceba9..3263af4d025e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2037,6 +2037,7 @@ enum IrResultLocationId { IrResultLocationIdOptionalUnwrap, IrResultLocationIdErrorUnionPayload, + IrResultLocationIdErrorUnionCode, IrResultLocationIdPtrOfArrayToSlice, }; @@ -2077,6 +2078,11 @@ struct IrResultLocationErrorUnionPayload { LLVMValueRef result; }; +struct IrResultLocationErrorUnionCode { + IrResultLocation base; + LLVMValueRef result; +}; + struct IrResultLocationPtrOfArrayToSlice { IrResultLocation base; LLVMValueRef result; diff --git a/src/codegen.cpp b/src/codegen.cpp index bd679f5dbd0f..0605709907af 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2884,6 +2884,18 @@ static LLVMValueRef gen_result_location_recur(CodeGen *g, IrResultLocation *base loc->result = LLVMBuildStructGEP(g->builder, prev, err_union_payload_index, ""); return loc->result; } + case IrResultLocationIdErrorUnionCode: { + IrResultLocationErrorUnionCode *loc = reinterpret_cast(base); + if (loc->result != nullptr) + return loc->result; + assert(prev != nullptr); + if (base->child != nullptr) { + prev = gen_result_location_recur(g, base->child, prev); + } + // TODO write 0xaa in debug mode to the undefined payload + loc->result = LLVMBuildStructGEP(g->builder, prev, err_union_err_index, ""); + return loc->result; + } case IrResultLocationIdPtrOfArrayToSlice: { IrResultLocationPtrOfArrayToSlice *loc = reinterpret_cast(base); if (loc->result != nullptr) diff --git a/src/ir.cpp b/src/ir.cpp index 5457c04f4743..a28e1c49b877 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10113,13 +10113,27 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so } IrResultLocation *result_location = ir_get_result_location(value); - if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type, false); + if (result_location == nullptr) { + result_location = create_alloca_result_loc(ira, wanted_type, false); + IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, + value, result_location); + result->value.type = wanted_type; + result->value.data.rh_error_union = RuntimeHintErrorUnionError; + return result; + } else { + IrResultLocationErrorUnionCode *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdErrorUnionCode; + new_result_location->base.parent = result_location; + new_result_location->base.from_call = result_location->from_call; + assert(result_location->child == nullptr); + result_location->child = &new_result_location->base; + IrInstruction *result = ir_build_result_loc(&ira->new_irb, source_instr->scope, + source_instr->source_node, &new_result_location->base, value); + result->value.type = wanted_type; + result->value.data.rh_error_union = RuntimeHintErrorUnionError; + return result; + } - IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, - value, result_location); - result->value.type = wanted_type; - result->value.data.rh_error_union = RuntimeHintErrorUnionError; - return result; } static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { From e304d1bc5f69d156c5f30b6da6378eb7f6e469df Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 16 Oct 2018 15:13:48 -0400 Subject: [PATCH 17/17] copy elision - array to slice --- src/all_types.hpp | 4 ++-- src/codegen.cpp | 4 ++-- src/ir.cpp | 23 ++++++++++++++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 3263af4d025e..56da5198372e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2038,7 +2038,7 @@ enum IrResultLocationId { IrResultLocationIdOptionalUnwrap, IrResultLocationIdErrorUnionPayload, IrResultLocationIdErrorUnionCode, - IrResultLocationIdPtrOfArrayToSlice, + IrResultLocationIdArrayToSlice, }; struct IrResultLocation { @@ -2083,7 +2083,7 @@ struct IrResultLocationErrorUnionCode { LLVMValueRef result; }; -struct IrResultLocationPtrOfArrayToSlice { +struct IrResultLocationArrayToSlice { IrResultLocation base; LLVMValueRef result; uint64_t len; diff --git a/src/codegen.cpp b/src/codegen.cpp index 0605709907af..677291f31f71 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2896,8 +2896,8 @@ static LLVMValueRef gen_result_location_recur(CodeGen *g, IrResultLocation *base loc->result = LLVMBuildStructGEP(g->builder, prev, err_union_err_index, ""); return loc->result; } - case IrResultLocationIdPtrOfArrayToSlice: { - IrResultLocationPtrOfArrayToSlice *loc = reinterpret_cast(base); + case IrResultLocationIdArrayToSlice: { + IrResultLocationArrayToSlice *loc = reinterpret_cast(base); if (loc->result != nullptr) return loc->result; assert(prev != nullptr); diff --git a/src/ir.cpp b/src/ir.cpp index a28e1c49b877..0b6ac12f55ca 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9650,8 +9650,8 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc result->value.type = wanted_type; return result; } else { - IrResultLocationPtrOfArrayToSlice *new_result_location = allocate(1); - new_result_location->base.id = IrResultLocationIdPtrOfArrayToSlice; + IrResultLocationArrayToSlice *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdArrayToSlice; new_result_location->base.parent = result_location; new_result_location->base.from_call = result_location->from_call; new_result_location->len = array_type->data.array.len; @@ -10220,10 +10220,23 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); IrResultLocation *result_location = ir_get_result_location(array_arg); - if (result_location == nullptr) result_location = create_alloca_result_loc(ira, wanted_type, false); - - IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, + IrInstruction *result; + if (result_location == nullptr) { + result_location = create_alloca_result_loc(ira, wanted_type, false); + result = ir_build_slice(&ira->new_irb, source_instr->scope, source_instr->source_node, array_ptr, start, end, false, result_location); + } else { + IrResultLocationArrayToSlice *new_result_location = allocate(1); + new_result_location->base.id = IrResultLocationIdArrayToSlice; + new_result_location->base.parent = result_location; + new_result_location->base.from_call = result_location->from_call; + new_result_location->len = array_type->data.array.len; + assert(result_location->child == nullptr); + result_location->child = &new_result_location->base; + result = ir_build_result_loc(&ira->new_irb, source_instr->scope, + source_instr->source_node, &new_result_location->base, array_arg); + } + result->value.type = wanted_type; result->value.data.rh_slice.id = RuntimeHintSliceIdLen; result->value.data.rh_slice.len = array_type->data.array.len;