diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-12-13 04:30:41 -0500 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-12-13 04:30:41 -0500 |
| commit | 3f3630d7e349361116416dce36d2f69d7c3e318c (patch) | |
| tree | 836ce54c606706f23a0a7524a3628b6e464c051c /src | |
| parent | 8bb5f54b292efacc03ff8d7cc6f59ae36c24305d (diff) | |
| download | zig-3f3630d7e349361116416dce36d2f69d7c3e318c.tar.gz zig-3f3630d7e349361116416dce36d2f69d7c3e318c.zip | |
IR: implement the rest of the builtin functions
* returnAddress
* frameAddress
* addWithOverflow
* subWithOverflow
* mulWithOverflow
* shlWithOverflow
* alignOf
Diffstat (limited to 'src')
| -rw-r--r-- | src/all_types.hpp | 39 | ||||
| -rw-r--r-- | src/analyze.cpp | 2 | ||||
| -rw-r--r-- | src/codegen.cpp | 85 | ||||
| -rw-r--r-- | src/ir.cpp | 484 | ||||
| -rw-r--r-- | src/ir.hpp | 2 | ||||
| -rw-r--r-- | src/ir_print.cpp | 51 |
6 files changed, 475 insertions, 188 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index 1bd4c2917b..40d6b73589 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1187,6 +1187,8 @@ struct CodeGen { LLVMValueRef memcpy_fn_val; LLVMValueRef memset_fn_val; LLVMValueRef trap_fn_val; + LLVMValueRef return_address_fn_val; + LLVMValueRef frame_address_fn_val; bool error_during_imports; uint32_t next_node_index; TypeTableEntry *err_tag_type; @@ -1420,6 +1422,10 @@ enum IrInstructionId { IrInstructionIdSlice, IrInstructionIdMemberCount, IrInstructionIdBreakpoint, + IrInstructionIdReturnAddress, + IrInstructionIdFrameAddress, + IrInstructionIdAlignOf, + IrInstructionIdOverflowOp, }; struct IrInstruction { @@ -1965,6 +1971,39 @@ struct IrInstructionBreakpoint { IrInstruction base; }; +struct IrInstructionReturnAddress { + IrInstruction base; +}; + +struct IrInstructionFrameAddress { + IrInstruction base; +}; + +enum IrOverflowOp { + IrOverflowOpAdd, + IrOverflowOpSub, + IrOverflowOpMul, + IrOverflowOpShl, +}; + +struct IrInstructionOverflowOp { + IrInstruction base; + + IrOverflowOp op; + IrInstruction *type_value; + IrInstruction *op1; + IrInstruction *op2; + IrInstruction *result_ptr; + + TypeTableEntry *result_ptr_type; +}; + +struct IrInstructionAlignOf { + IrInstruction base; + + IrInstruction *type_value; +}; + enum LValPurpose { LValPurposeNone, LValPurposeAssign, diff --git a/src/analyze.cpp b/src/analyze.cpp index 03864ec85c..e0211169ae 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -876,7 +876,7 @@ static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *nod size_t backward_branch_count = 0; return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota, - nullptr, nullptr); + nullptr, nullptr, node); } TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { diff --git a/src/codegen.cpp b/src/codegen.cpp index f89fb84f69..78a07ed3d1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2058,6 +2058,80 @@ static LLVMValueRef ir_render_breakpoint(CodeGen *g, IrExecutable *executable, I return nullptr; } +static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutable *executable, + IrInstructionReturnAddress *instruction) +{ + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); + return LLVMBuildCall(g->builder, g->return_address_fn_val, &zero, 1, ""); +} + +static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable, + IrInstructionFrameAddress *instruction) +{ + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); + return LLVMBuildCall(g->builder, g->frame_address_fn_val, &zero, 1, ""); +} + +static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { + TypeTableEntry *int_type = get_underlying_type(instruction->result_ptr_type); + assert(int_type->id == TypeTableEntryIdInt); + + LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); + LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); + LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr); + + LLVMValueRef result = LLVMBuildShl(g->builder, op1, op2, ""); + LLVMValueRef orig_val; + if (int_type->data.integral.is_signed) { + orig_val = LLVMBuildAShr(g->builder, result, op2, ""); + } else { + orig_val = LLVMBuildLShr(g->builder, result, op2, ""); + } + LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, op1, orig_val, ""); + + LLVMBuildStore(g->builder, result, ptr_result); + + return overflow_bit; +} + +static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable, IrInstructionOverflowOp *instruction) { + AddSubMul add_sub_mul; + switch (instruction->op) { + case IrOverflowOpAdd: + add_sub_mul = AddSubMulAdd; + break; + case IrOverflowOpSub: + add_sub_mul = AddSubMulAdd; + break; + case IrOverflowOpMul: + add_sub_mul = AddSubMulAdd; + break; + case IrOverflowOpShl: + return render_shl_with_overflow(g, instruction); + } + + TypeTableEntry *int_type = get_underlying_type(instruction->result_ptr_type); + assert(int_type->id == TypeTableEntryIdInt); + + LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul); + + LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); + LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); + LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr); + + LLVMValueRef params[] = { + op1, + op2, + }; + + LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); + LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); + LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); + LLVMBuildStore(g->builder, result, ptr_result); + + return overflow_bit; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -2100,6 +2174,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdEmbedFile: case IrInstructionIdIntType: case IrInstructionIdMemberCount: + case IrInstructionIdAlignOf: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -2169,6 +2244,12 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_slice(g, executable, (IrInstructionSlice *)instruction); case IrInstructionIdBreakpoint: return ir_render_breakpoint(g, executable, (IrInstructionBreakpoint *)instruction); + case IrInstructionIdReturnAddress: + return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction); + case IrInstructionIdFrameAddress: + return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdOverflowOp: + return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); case IrInstructionIdSwitchVar: case IrInstructionIdContainerInitList: case IrInstructionIdStructInit: @@ -3300,6 +3381,8 @@ static void define_builtin_fns(CodeGen *g) { &g->builtin_types.entry_i32->type_ref, 1, false); builtin_fn->fn_val = LLVMAddFunction(g->module, "llvm.returnaddress", fn_type); assert(LLVMGetIntrinsicID(builtin_fn->fn_val)); + + g->return_address_fn_val = builtin_fn->fn_val; } { BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdFrameAddress, @@ -3310,6 +3393,8 @@ static void define_builtin_fns(CodeGen *g) { &g->builtin_types.entry_i32->type_ref, 1, false); builtin_fn->fn_val = LLVMAddFunction(g->module, "llvm.frameaddress", fn_type); assert(LLVMGetIntrinsicID(builtin_fn->fn_val)); + + g->frame_address_fn_val = builtin_fn->fn_val; } { BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); diff --git a/src/ir.cpp b/src/ir.cpp index ccbd10aa39..1ae05e4b64 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -395,6 +395,22 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) { return IrInstructionIdBreakpoint; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionReturnAddress *) { + return IrInstructionIdReturnAddress; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) { + return IrInstructionIdFrameAddress; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { + return IrInstructionIdAlignOf; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) { + return IrInstructionIdOverflowOp; +} + template<typename T> static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) { T *special_instruction = allocate<T>(1); @@ -1632,6 +1648,67 @@ static IrInstruction *ir_build_breakpoint_from(IrBuilder *irb, IrInstruction *ol return new_instruction; } +static IrInstruction *ir_build_return_address(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionReturnAddress *instruction = ir_build_instruction<IrInstructionReturnAddress>(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_return_address_from(IrBuilder *irb, IrInstruction *old_instruction) { + IrInstruction *new_instruction = ir_build_return_address(irb, old_instruction->scope, old_instruction->source_node); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + +static IrInstruction *ir_build_frame_address(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionFrameAddress *instruction = ir_build_instruction<IrInstructionFrameAddress>(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction *old_instruction) { + IrInstruction *new_instruction = ir_build_frame_address(irb, old_instruction->scope, old_instruction->source_node); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + +static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, + IrInstruction *result_ptr, TypeTableEntry *result_ptr_type) +{ + IrInstructionOverflowOp *instruction = ir_build_instruction<IrInstructionOverflowOp>(irb, scope, source_node); + instruction->op = op; + instruction->type_value = type_value; + instruction->op1 = op1; + instruction->op2 = op2; + instruction->result_ptr = result_ptr; + instruction->result_ptr_type = result_ptr_type; + + ir_ref_instruction(type_value); + ir_ref_instruction(op1); + ir_ref_instruction(op2); + ir_ref_instruction(result_ptr); + + return &instruction->base; +} + +static IrInstruction *ir_build_overflow_op_from(IrBuilder *irb, IrInstruction *old_instruction, + IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, + IrInstruction *result_ptr, TypeTableEntry *result_ptr_type) +{ + IrInstruction *new_instruction = ir_build_overflow_op(irb, old_instruction->scope, old_instruction->source_node, + op, type_value, op1, op2, result_ptr, result_ptr_type); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + +static IrInstruction *ir_build_alignof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { + IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node); + instruction->type_value = type_value; + + ir_ref_instruction(type_value); + + return &instruction->base; +} + static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers, bool gen_maybe_defers) { @@ -2142,6 +2219,34 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode return ir_build_load_ptr(irb, scope, node, ptr_instruction); } +static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) { + assert(node->type == NodeTypeFnCallExpr); + + AstNode *type_node = node->data.fn_call_expr.params.at(0); + AstNode *op1_node = node->data.fn_call_expr.params.at(1); + AstNode *op2_node = node->data.fn_call_expr.params.at(2); + AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3); + + + IrInstruction *type_value = ir_gen_node(irb, type_node, scope); + if (type_value == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *op1 = ir_gen_node(irb, op1_node, scope); + if (op1 == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *op2 = ir_gen_node(irb, op2_node, scope); + if (op2 == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope); + if (result_ptr == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); +} + static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); @@ -2522,14 +2627,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } case BuiltinFnIdBreakpoint: return ir_build_breakpoint(irb, scope, node); + case BuiltinFnIdReturnAddress: + return ir_build_return_address(irb, scope, node); + case BuiltinFnIdFrameAddress: + return ir_build_frame_address(irb, scope, node); case BuiltinFnIdAlignof: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + return ir_build_alignof(irb, scope, node, arg0_value); + } case BuiltinFnIdAddWithOverflow: + return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd); case BuiltinFnIdSubWithOverflow: + return ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub); case BuiltinFnIdMulWithOverflow: + return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul); case BuiltinFnIdShlWithOverflow: - case BuiltinFnIdReturnAddress: - case BuiltinFnIdFrameAddress: - zig_panic("TODO IR gen more builtin functions"); + return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl); } zig_unreachable(); } @@ -2749,10 +2867,6 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod type_instruction = nullptr; } - IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); - if (init_value == irb->codegen->invalid_instruction) - return init_value; - bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; @@ -2768,6 +2882,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod return irb->codegen->invalid_instruction; } + IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); + if (init_value == irb->codegen->invalid_instruction) + return init_value; + return ir_build_var_decl(irb, scope, node, var, type_instruction, init_value); } @@ -4057,9 +4175,9 @@ static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instructio static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, ConstExprValue *pointee, TypeTableEntry *pointee_type, bool depends_on_compile_var, - ConstPtrSpecial special) + ConstPtrSpecial special, bool ptr_is_const) { - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, pointee_type, true); + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, pointee_type, ptr_is_const); ConstExprValue *const_val = ir_build_const_from(ira, instruction, depends_on_compile_var || pointee->depends_on_compile_var); const_val->data.x_ptr.base_ptr = pointee; @@ -4086,7 +4204,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) { IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, - FnTableEntry *fn_entry, Buf *c_import_buf) + FnTableEntry *fn_entry, Buf *c_import_buf, AstNode *source_node) { IrExecutable ir_executable = {0}; ir_executable.is_inline = true; @@ -4122,7 +4240,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node IrInstruction *result = ir_exec_const_result(&analyzed_executable); if (!result) { - add_node_error(codegen, node, buf_sprintf("unable to evaluate constant expression")); + add_node_error(codegen, source_node, buf_sprintf("unable to evaluate constant expression")); return codegen->invalid_instruction; } @@ -4499,7 +4617,9 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst ConstExprValue *val = ir_resolve_const(ira, value); if (!val) return ira->codegen->builtin_types.entry_invalid; - return ir_analyze_const_ptr(ira, source_instruction, val, value->type_entry, false, ConstPtrSpecialNone); + bool ptr_is_const = true; + return ir_analyze_const_ptr(ira, source_instruction, val, value->type_entry, + false, ConstPtrSpecialNone, ptr_is_const); } TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true); @@ -5230,11 +5350,16 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc var->type = result_type; assert(var->type); - if (casted_init_value->static_value.special == ConstValSpecialStatic) { + if (casted_init_value->static_value.special != ConstValSpecialRuntime) { if (var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_count); ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; *mem_slot = casted_init_value->static_value; + + if (var->is_inline) { + ir_build_const_from(ira, &decl_var_instruction->base, false); + return ira->codegen->builtin_types.entry_void; + } } } else if (var->is_inline) { ir_add_error(ira, &decl_var_instruction->base, @@ -5403,7 +5528,8 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal // Analyze the fn body block like any other constant expression. AstNode *body_node = fn_entry->fn_def_node->data.fn_def.body; IrInstruction *result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type, - ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, nullptr); + ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, + nullptr, call_instruction->base.source_node); if (result->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; @@ -6055,7 +6181,7 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc if (mem_slot && mem_slot->special != ConstValSpecialRuntime) { ConstPtrSpecial ptr_special = var->is_inline ? ConstPtrSpecialInline : ConstPtrSpecialNone; - return ir_analyze_const_ptr(ira, instruction, mem_slot, var->type, false, ptr_special); + return ir_analyze_const_ptr(ira, instruction, mem_slot, var->type, false, ptr_special, var->src_is_const); } else { ir_build_var_ptr_from(&ira->new_irb, instruction, var); return get_pointer_to_type(ira->codegen, var->type, false); @@ -6270,7 +6396,9 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source const_val->special = ConstValSpecialStatic; const_val->data.x_fn = fn_entry; - return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, depends_on_compile_var, ConstPtrSpecialNone); + bool ptr_is_const = true; + return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, + depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const); } case TldIdContainer: { @@ -6283,7 +6411,9 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source const_val->special = ConstValSpecialStatic; const_val->data.x_type = tld_container->type_entry; - return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_container->type_entry, depends_on_compile_var, ConstPtrSpecialNone); + bool ptr_is_const = true; + return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_container->type_entry, + depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const); } case TldIdTypeDef: { @@ -6296,7 +6426,9 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source const_val->special = ConstValSpecialStatic; const_val->data.x_type = tld_typedef->type_entry; - return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_typedef->type_entry, depends_on_compile_var, ConstPtrSpecialNone); + bool ptr_is_const = true; + return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_typedef->type_entry, + depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const); } } zig_unreachable(); @@ -6332,7 +6464,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru bignum_init_unsigned(&len_val->data.x_bignum, container_type->data.array.len); TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; - return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, usize, false, ConstPtrSpecialNone); + bool ptr_is_const = true; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, + usize, false, ConstPtrSpecialNone, ptr_is_const); } else { add_node_error(ira->codegen, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), @@ -6363,8 +6497,10 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru TypeEnumField *field = find_enum_type_field(child_type, field_name); if (field) { if (field->type_entry->id == TypeTableEntryIdVoid) { - return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_enum_tag(field->value), - child_type, depends_on_compile_var, ConstPtrSpecialNone); + bool ptr_is_const = true; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_enum_tag(field->value), child_type, depends_on_compile_var, + ConstPtrSpecialNone, ptr_is_const); } else { zig_panic("TODO enum tag type"); } @@ -6387,8 +6523,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru const_val->special = ConstValSpecialStatic; const_val->data.x_pure_err = err_table_entry->value; + bool ptr_is_const = true; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val, - child_type, depends_on_compile_var, ConstPtrSpecialNone); + child_type, depends_on_compile_var, ConstPtrSpecialNone, ptr_is_const); } ir_add_error(ira, &field_ptr_instruction->base, @@ -6396,13 +6533,17 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru return ira->codegen->builtin_types.entry_invalid; } else if (child_type->id == TypeTableEntryIdInt) { if (buf_eql_str(field_name, "bit_count")) { + bool ptr_is_const = true; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_unsigned_negative(child_type->data.integral.bit_count, false), - ira->codegen->builtin_types.entry_num_lit_int, depends_on_compile_var, ConstPtrSpecialNone); + ira->codegen->builtin_types.entry_num_lit_int, depends_on_compile_var, + ConstPtrSpecialNone, ptr_is_const); } else if (buf_eql_str(field_name, "is_signed")) { + bool ptr_is_const = true; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_bool(child_type->data.integral.is_signed), - ira->codegen->builtin_types.entry_bool, depends_on_compile_var, ConstPtrSpecialNone); + ira->codegen->builtin_types.entry_bool, depends_on_compile_var, + ConstPtrSpecialNone, ptr_is_const); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", @@ -7713,7 +7854,8 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc // Execute the C import block like an inline function TypeTableEntry *void_type = ira->codegen->builtin_types.entry_void; IrInstruction *result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, - ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf); + ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, + &cimport_scope->buf, block_node); if (result->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; @@ -8492,6 +8634,125 @@ static TypeTableEntry *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstr return ira->codegen->builtin_types.entry_void; } +static TypeTableEntry *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) { + ir_build_return_address_from(&ira->new_irb, &instruction->base); + + TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; + TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true); + return u8_ptr_const; +} + +static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) { + ir_build_frame_address_from(&ira->new_irb, &instruction->base); + + TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; + TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true); + return u8_ptr_const; +} + +static TypeTableEntry *ir_analyze_instruction_alignof(IrAnalyze *ira, IrInstructionAlignOf *instruction) { + IrInstruction *type_value = instruction->type_value->other; + if (type_value->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + + if (type_entry->id == TypeTableEntryIdInvalid) { + return ira->codegen->builtin_types.entry_invalid; + } else if (type_entry->id == TypeTableEntryIdUnreachable) { + add_node_error(ira->codegen, first_executing_node(instruction->type_value->source_node), + buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } else { + uint64_t align_in_bytes = LLVMABISizeOfType(ira->codegen->target_data_ref, type_entry->type_ref); + bool depends_on_compile_var = type_value->static_value.depends_on_compile_var; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); + bignum_init_unsigned(&out_val->data.x_bignum, align_in_bytes); + return ira->codegen->builtin_types.entry_num_lit_int; + } +} + +static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) { + IrInstruction *type_value = instruction->type_value->other; + if (type_value->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + TypeTableEntry *dest_type = ir_resolve_type(ira, type_value); + TypeTableEntry *canon_type = get_underlying_type(dest_type); + if (canon_type->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + if (canon_type->id != TypeTableEntryIdInt) { + ir_add_error(ira, type_value, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + // TODO if this is a typedecl, add error note showing the declaration of the type decl + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *op1 = instruction->op1->other; + if (op1->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type); + if (casted_op1->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *op2 = instruction->op2->other; + if (op2->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, dest_type); + if (casted_op2->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result_ptr = instruction->result_ptr->other; + if (result_ptr->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); + IrInstruction *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type); + if (casted_result_ptr->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + if (casted_op1->static_value.special == ConstValSpecialStatic && + casted_op2->static_value.special == ConstValSpecialStatic && + casted_result_ptr->static_value.special == ConstValSpecialStatic) + { + bool depends_on_compile_var = type_value->static_value.depends_on_compile_var || + casted_op1->static_value.depends_on_compile_var || casted_op2->static_value.depends_on_compile_var || + casted_result_ptr->static_value.depends_on_compile_var; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); + BigNum *op1_bignum = &casted_op1->static_value.data.x_bignum; + BigNum *op2_bignum = &casted_op2->static_value.data.x_bignum; + ConstExprValue *pointee_val = const_ptr_pointee(&casted_result_ptr->static_value); + BigNum *dest_bignum = &pointee_val->data.x_bignum; + switch (instruction->op) { + case IrOverflowOpAdd: + out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + break; + case IrOverflowOpSub: + out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + break; + case IrOverflowOpMul: + out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + break; + case IrOverflowOpShl: + out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + break; + } + if (!bignum_fits_in_bits(dest_bignum, canon_type->data.integral.bit_count, + canon_type->data.integral.is_signed)) + { + out_val->data.x_bool = true; + bignum_truncate(dest_bignum, canon_type->data.integral.bit_count); + } + pointee_val->special = ConstValSpecialStatic; + return ira->codegen->builtin_types.entry_bool; + } + + ir_build_overflow_op_from(&ira->new_irb, &instruction->base, instruction->op, type_value, + casted_op1, casted_op2, casted_result_ptr, dest_type); + return ira->codegen->builtin_types.entry_bool; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -8618,6 +8879,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction); case IrInstructionIdBreakpoint: return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction); + case IrInstructionIdReturnAddress: + return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); + case IrInstructionIdFrameAddress: + return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdAlignOf: + return ir_analyze_instruction_alignof(ira, (IrInstructionAlignOf *)instruction); + case IrInstructionIdOverflowOp: + return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction); case IrInstructionIdCast: case IrInstructionIdStructFieldPtr: case IrInstructionIdEnumFieldPtr: @@ -8722,6 +8991,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMemset: case IrInstructionIdMemcpy: case IrInstructionIdBreakpoint: + case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free return true; case IrInstructionIdPhi: case IrInstructionIdUnOp: @@ -8765,6 +9035,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAlloca: case IrInstructionIdSlice: case IrInstructionIdMemberCount: + case IrInstructionIdAlignOf: + case IrInstructionIdReturnAddress: + case IrInstructionIdFrameAddress: return false; case IrInstructionIdAsm: { @@ -8776,63 +9049,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { } // TODO port over all this commented out code into new IR way of doing things -//static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, -// TypeTableEntry *expected_type, AstNode *node) -//{ -// -// switch (builtin_fn->id) { -// case BuiltinFnIdInvalid: -// zig_unreachable(); -// case BuiltinFnIdAddWithOverflow: -// case BuiltinFnIdSubWithOverflow: -// case BuiltinFnIdMulWithOverflow: -// case BuiltinFnIdShlWithOverflow: -// { -// AstNode *type_node = node->data.fn_call_expr.params.at(0); -// TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node); -// if (int_type->id == TypeTableEntryIdInvalid) { -// return g->builtin_types.entry_bool; -// } else if (int_type->id == TypeTableEntryIdInt) { -// AstNode *op1_node = node->data.fn_call_expr.params.at(1); -// AstNode *op2_node = node->data.fn_call_expr.params.at(2); -// AstNode *result_node = node->data.fn_call_expr.params.at(3); -// -// analyze_expression(g, import, context, int_type, op1_node); -// analyze_expression(g, import, context, int_type, op2_node); -// analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false), -// result_node); -// } else { -// add_node_error(g, type_node, -// buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name))); -// } -// -// // TODO constant expression evaluation -// -// return g->builtin_types.entry_bool; -// } -// case BuiltinFnIdAlignof: -// { -// AstNode *type_node = node->data.fn_call_expr.params.at(0); -// TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); -// if (type_entry->id == TypeTableEntryIdInvalid) { -// return g->builtin_types.entry_invalid; -// } else if (type_entry->id == TypeTableEntryIdUnreachable) { -// add_node_error(g, first_executing_node(type_node), -// buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name))); -// return g->builtin_types.entry_invalid; -// } else { -// uint64_t align_in_bytes = LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref); -// return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, -// align_in_bytes, false); -// } -// } -// case BuiltinFnIdReturnAddress: -// case BuiltinFnIdFrameAddress: -// mark_impure_fn(g, context, node); -// return builtin_fn->return_type; -// } -// zig_unreachable(); -//} //static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, // TypeTableEntry *expected_type, AstNode *node) @@ -8948,110 +9164,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { // } //} // -//static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) { -// assert(node->type == NodeTypeFnCallExpr); -// -// size_t fn_call_param_count = node->data.fn_call_expr.params.length; -// assert(fn_call_param_count == 4); -// -// TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0)); -// assert(int_type->id == TypeTableEntryIdInt); -// -// LLVMValueRef val1 = gen_expr(g, node->data.fn_call_expr.params.at(1)); -// LLVMValueRef val2 = gen_expr(g, node->data.fn_call_expr.params.at(2)); -// LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3)); -// -// LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); -// LLVMValueRef orig_val; -// if (int_type->data.integral.is_signed) { -// orig_val = LLVMBuildAShr(g->builder, result, val2, ""); -// } else { -// orig_val = LLVMBuildLShr(g->builder, result, val2, ""); -// } -// LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, val1, orig_val, ""); -// -// LLVMBuildStore(g->builder, result, ptr_result); -// -// return overflow_bit; -//} -// -//static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { -// assert(node->type == NodeTypeFnCallExpr); -// AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; -// assert(fn_ref_expr->type == NodeTypeSymbol); -// BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn; -// -// switch (builtin_fn->id) { -// case BuiltinFnIdInvalid: -// case BuiltinFnIdTypeof: -// zig_unreachable(); -// case BuiltinFnIdAddWithOverflow: -// case BuiltinFnIdSubWithOverflow: -// case BuiltinFnIdMulWithOverflow: -// { -// size_t fn_call_param_count = node->data.fn_call_expr.params.length; -// assert(fn_call_param_count == 4); -// -// TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0)); -// AddSubMul add_sub_mul; -// if (builtin_fn->id == BuiltinFnIdAddWithOverflow) { -// add_sub_mul = AddSubMulAdd; -// } else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) { -// add_sub_mul = AddSubMulSub; -// } else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) { -// add_sub_mul = AddSubMulMul; -// } else { -// zig_unreachable(); -// } -// LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul); -// -// LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1)); -// LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2)); -// LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3)); -// -// LLVMValueRef params[] = { -// op1, -// op2, -// }; -// -// LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); -// LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); -// LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); -// LLVMBuildStore(g->builder, result, ptr_result); -// -// return overflow_bit; -// } -// case BuiltinFnIdShlWithOverflow: -// return gen_shl_with_overflow(g, node); -// case BuiltinFnIdAlignof: -// case BuiltinFnIdMinValue: -// case BuiltinFnIdMaxValue: -// // caught by constant expression eval codegen -// zig_unreachable(); -// case BuiltinFnIdCompileVar: -// return nullptr; -// case BuiltinFnIdFrameAddress: -// case BuiltinFnIdReturnAddress: -// { -// LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); -// return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, ""); -// } -// case BuiltinFnIdCmpExchange: -// return gen_cmp_exchange(g, node); -// case BuiltinFnIdFence: -// return gen_fence(g, node); -// case BuiltinFnIdUnreachable: -// zig_panic("moved to ir render"); -// case BuiltinFnIdSetFnTest: -// case BuiltinFnIdSetFnStaticEval: -// case BuiltinFnIdSetFnNoInline: -// case BuiltinFnIdSetDebugSafety: -// // do nothing -// return nullptr; -// } -// zig_unreachable(); -//} -// //static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntry *enum_type, // AstNode *arg_node) //{ diff --git a/src/ir.hpp b/src/ir.hpp index d3445e3364..bb79a6ed53 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -15,7 +15,7 @@ IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry); IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, - FnTableEntry *fn_entry, Buf *c_import_buf); + FnTableEntry *fn_entry, Buf *c_import_buf, AstNode *source_node); TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, TypeTableEntry *expected_type, AstNode *expected_type_source_node); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 186de8b449..d00ba372b4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -816,6 +816,45 @@ static void ir_print_breakpoint(IrPrint *irp, IrInstructionBreakpoint *instructi fprintf(irp->f, "@breakpoint()"); } +static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *instruction) { + fprintf(irp->f, "@frameAddress()"); +} + +static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { + fprintf(irp->f, "@returnAddress()"); +} + +static void ir_print_alignof(IrPrint *irp, IrInstructionAlignOf *instruction) { + fprintf(irp->f, "@alignOf("); + ir_print_other_instruction(irp, instruction->type_value); + fprintf(irp->f, ")"); +} + +static void ir_print_overflow_op(IrPrint *irp, IrInstructionOverflowOp *instruction) { + switch (instruction->op) { + case IrOverflowOpAdd: + fprintf(irp->f, "@addWithOverflow("); + break; + case IrOverflowOpSub: + fprintf(irp->f, "@subWithOverflow("); + break; + case IrOverflowOpMul: + fprintf(irp->f, "@mulWithOverflow("); + break; + case IrOverflowOpShl: + fprintf(irp->f, "@shlWithOverflow("); + break; + } + ir_print_other_instruction(irp, instruction->type_value); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->op1); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->op2); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->result_ptr); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1016,6 +1055,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBreakpoint: ir_print_breakpoint(irp, (IrInstructionBreakpoint *)instruction); break; + case IrInstructionIdReturnAddress: + ir_print_return_address(irp, (IrInstructionReturnAddress *)instruction); + break; + case IrInstructionIdFrameAddress: + ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction); + break; + case IrInstructionIdAlignOf: + ir_print_alignof(irp, (IrInstructionAlignOf *)instruction); + break; + case IrInstructionIdOverflowOp: + ir_print_overflow_op(irp, (IrInstructionOverflowOp *)instruction); + break; } fprintf(irp->f, "\n"); } |
