From 433c17aeb192740053b1b9aea6220dca760303e2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 11 Dec 2016 14:27:37 -0500 Subject: [PATCH] IR: implement divExact builtin --- src/all_types.hpp | 8 +++ src/codegen.cpp | 10 +++ src/ir.cpp | 156 +++++++++++++++++++++++++++++------------- src/ir_print.cpp | 11 +++ test/self_hosted2.zig | 8 +++ 5 files changed, 145 insertions(+), 48 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index c1e8bc18fc9527d8c044edf9644ddbf8964efbaa..91f81b3f6ac8283cdd91585a0f265104041f804a 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1409,6 +1409,7 @@ enum IrInstructionId { IrInstructionIdEmbedFile, IrInstructionIdCmpxchg, IrInstructionIdFence, + IrInstructionIdDivExact, }; struct IrInstruction { @@ -1884,6 +1885,13 @@ struct IrInstructionFence { AtomicOrder order; }; +struct IrInstructionDivExact { + IrInstruction base; + + IrInstruction *op1; + IrInstruction *op2; +}; + enum LValPurpose { LValPurposeNone, LValPurposeAssign, diff --git a/src/codegen.cpp b/src/codegen.cpp index dca316e84e48d27fe4c95dd7cc3074d1b9fa9ce2..e4eb8dd3230ea4f9a8c3eab12257c21d1253f183 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1871,6 +1871,14 @@ static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInst return nullptr; } +static LLVMValueRef ir_render_div_exact(CodeGen *g, IrExecutable *executable, IrInstructionDivExact *instruction) { + LLVMValueRef op1_val = ir_llvm_value(g, instruction->op1); + LLVMValueRef op2_val = ir_llvm_value(g, instruction->op2); + + bool want_debug_safety = ir_want_debug_safety(g, &instruction->base); + return gen_div(g, want_debug_safety, op1_val, op2_val, instruction->base.type_entry, true); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -1964,6 +1972,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction); case IrInstructionIdFence: return ir_render_fence(g, executable, (IrInstructionFence *)instruction); + case IrInstructionIdDivExact: + return ir_render_div_exact(g, executable, (IrInstructionDivExact *)instruction); case IrInstructionIdSwitchVar: case IrInstructionIdContainerInitList: case IrInstructionIdStructInit: diff --git a/src/ir.cpp b/src/ir.cpp index 1ed4173f3ac055e370f49a375f6b889108815d0c..c9ab9262af88a65cd9fc410c68dc77df8d8c28a4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -355,6 +355,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { return IrInstructionIdFence; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionDivExact *) { + return IrInstructionIdDivExact; +} + template static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -1413,6 +1417,23 @@ static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_ins return new_instruction; } +static IrInstruction *ir_build_div_exact(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *op1, IrInstruction *op2) { + IrInstructionDivExact *instruction = ir_build_instruction(irb, scope, source_node); + instruction->op1 = op1; + instruction->op2 = op2; + + ir_ref_instruction(op1); + ir_ref_instruction(op2); + + return &instruction->base; +} + +static IrInstruction *ir_build_div_exact_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *op1, IrInstruction *op2) { + IrInstruction *new_instruction = ir_build_div_exact(irb, old_instruction->scope, old_instruction->source_node, op1, op2); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers, bool gen_maybe_defers) { @@ -2192,6 +2213,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); } + case BuiltinFnIdDivExact: + { + 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; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + return ir_build_div_exact(irb, scope, node, arg0_value, arg1_value); + } case BuiltinFnIdMemcpy: case BuiltinFnIdMemset: case BuiltinFnIdAlignof: @@ -2203,7 +2238,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdBreakpoint: case BuiltinFnIdReturnAddress: case BuiltinFnIdFrameAddress: - case BuiltinFnIdDivExact: case BuiltinFnIdTruncate: case BuiltinFnIdIntType: zig_panic("TODO IR gen more builtin functions"); @@ -7426,6 +7460,76 @@ static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructio return ira->codegen->builtin_types.entry_void; } +static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstructionDivExact *instruction) { + IrInstruction *op1 = instruction->op1->other; + if (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 *peer_instructions[] = { op1, op2 }; + TypeTableEntry *result_type = ir_resolve_peer_types(ira, instruction->base.source_node, peer_instructions, 2); + + if (result_type->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *canon_type = get_underlying_type(result_type); + + if (canon_type->id != TypeTableEntryIdInt && + canon_type->id != TypeTableEntryIdNumLitInt) + { + ir_add_error(ira, &instruction->base, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&result_type->name))); + // TODO if meta_type is type decl, add note pointing to type decl declaration + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *casted_op1 = ir_get_casted_value(ira, op1, result_type); + if (casted_op1->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_op2 = ir_get_casted_value(ira, op2, result_type); + if (casted_op2->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + if (casted_op1->static_value.special == ConstValSpecialStatic && + casted_op2->static_value.special == ConstValSpecialStatic) + { + ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1); + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2); + assert(op1_val); + assert(op2_val); + + if (op1_val->data.x_bignum.data.x_uint == 0) { + ir_add_error(ira, &instruction->base, buf_sprintf("division by zero")); + return ira->codegen->builtin_types.entry_invalid; + } + + BigNum remainder; + if (bignum_mod(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { + ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow")); + return ira->codegen->builtin_types.entry_invalid; + } + + if (remainder.data.x_uint != 0) { + ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder")); + return ira->codegen->builtin_types.entry_invalid; + } + + bool depends_on_compile_var = casted_op1->static_value.depends_on_compile_var || + casted_op2->static_value.depends_on_compile_var; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); + bignum_div(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); + return result_type; + } + + ir_build_div_exact_from(&ira->new_irb, &instruction->base, casted_op1, casted_op2); + return result_type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -7532,6 +7636,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); case IrInstructionIdFence: return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); + case IrInstructionIdDivExact: + return ir_analyze_instruction_div_exact(ira, (IrInstructionDivExact *)instruction); case IrInstructionIdCast: case IrInstructionIdStructFieldPtr: case IrInstructionIdEnumFieldPtr: @@ -7669,6 +7775,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMaxValue: case IrInstructionIdErrName: case IrInstructionIdEmbedFile: + case IrInstructionIdDivExact: return false; case IrInstructionIdAsm: { @@ -7682,37 +7789,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_div_exact(CodeGen *g, ImportTableEntry *import, -// BlockContext *context, AstNode *node) -//{ -// assert(node->type == NodeTypeFnCallExpr); -// -// AstNode **op1 = &node->data.fn_call_expr.params.at(0); -// AstNode **op2 = &node->data.fn_call_expr.params.at(1); -// -// TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1); -// TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2); -// -// AstNode *op_nodes[] = {*op1, *op2}; -// TypeTableEntry *op_types[] = {op1_type, op2_type}; -// TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node, -// op_nodes, op_types, 2); -// -// if (result_type->id == TypeTableEntryIdInvalid) { -// return g->builtin_types.entry_invalid; -// } else if (result_type->id == TypeTableEntryIdInt) { -// return result_type; -// } else if (result_type->id == TypeTableEntryIdNumLitInt) { -// // check for division by zero -// // check for non exact division -// zig_panic("TODO"); -// } else { -// add_node_error(g, node, -// buf_sprintf("expected integer type, found '%s'", buf_ptr(&result_type->name))); -// return g->builtin_types.entry_invalid; -// } -//} -// //static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import, // BlockContext *context, AstNode *node) //{ @@ -7920,8 +7996,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { // case BuiltinFnIdFrameAddress: // mark_impure_fn(g, context, node); // return builtin_fn->return_type; -// case BuiltinFnIdDivExact: -// return analyze_div_exact(g, import, context, node); // case BuiltinFnIdTruncate: // return analyze_truncate(g, import, context, node); // case BuiltinFnIdIntType: @@ -8243,18 +8317,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { // -//static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) { -// assert(node->type == NodeTypeFnCallExpr); -// -// AstNode *op1_node = node->data.fn_call_expr.params.at(0); -// AstNode *op2_node = node->data.fn_call_expr.params.at(1); -// -// LLVMValueRef op1_val = gen_expr(g, op1_node); -// LLVMValueRef op2_val = gen_expr(g, op2_node); -// -// return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true); -//} -// //static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeFnCallExpr); // @@ -8421,8 +8483,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { // return gen_cmp_exchange(g, node); // case BuiltinFnIdFence: // return gen_fence(g, node); -// case BuiltinFnIdDivExact: -// return gen_div_exact(g, node); // case BuiltinFnIdTruncate: // return gen_truncate(g, node); // case BuiltinFnIdUnreachable: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5ef934c61f838e5095cd813530e052e796700980..00197531231452e0cb70bf249eee831989a15375 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -739,6 +739,14 @@ static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) { fprintf(irp->f, ")"); } +static void ir_print_div_exact(IrPrint *irp, IrInstructionDivExact *instruction) { + fprintf(irp->f, "@divExact("); + ir_print_other_instruction(irp, instruction->op1); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->op2); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -909,6 +917,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFence: ir_print_fence(irp, (IrInstructionFence *)instruction); break; + case IrInstructionIdDivExact: + ir_print_div_exact(irp, (IrInstructionDivExact *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/self_hosted2.zig b/test/self_hosted2.zig index ab9a3a717bdaf18b8cd21e578c23364c72720de9..76890d419487160cfa12ce4cdede10dfc8a7ba79 100644 --- a/test/self_hosted2.zig +++ b/test/self_hosted2.zig @@ -285,6 +285,13 @@ fn fence() { x = 5678; } +fn exactDivision() { + assert(divExact(55, 11) == 5); +} +fn divExact(a: u32, b: u32) -> u32 { + @divExact(a, b) +} + fn assert(ok: bool) { if (!ok) @unreachable(); @@ -314,6 +321,7 @@ fn runAllTests() { testErrorName(); cmpxchg(); fence(); + exactDivision(); } export nakedcc fn _start() -> unreachable { -- 2.54.0