authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-07 20:53:16-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-07 20:53:16-07:00
log404defd99b76ebf2cfe46ea26248a8813e40136f
tree9a1ddc547f3f278642c5cd7feb5e1006cbf11895
parenteb83111f0258ee41af021091bb78b3b5e5f6f3d3

add div_exact builtin fn

closes #149

8 files changed, 153 insertions(+), 3 deletions(-)

src/all_types.hpp+1
...@@ -1128,6 +1128,7 @@ enum BuiltinFnId {...@@ -1128,6 +1128,7 @@ enum BuiltinFnId {
1128 BuiltinFnIdEmbedFile,1128 BuiltinFnIdEmbedFile,
1129 BuiltinFnIdCmpExchange,1129 BuiltinFnIdCmpExchange,
1130 BuiltinFnIdFence,1130 BuiltinFnIdFence,
1131 BuiltinFnIdDivExact,
1131};1132};
11321133
1133struct BuiltinFnEntry {1134struct BuiltinFnEntry {
src/analyze.cpp+33
...@@ -4657,6 +4657,37 @@ static TypeTableEntry *analyze_fence(CodeGen *g, ImportTableEntry *import,...@@ -4657,6 +4657,37 @@ static TypeTableEntry *analyze_fence(CodeGen *g, ImportTableEntry *import,
4657 return g->builtin_types.entry_void;4657 return g->builtin_types.entry_void;
4658}4658}
46594659
4660static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import,
4661 BlockContext *context, AstNode *node)
4662{
4663 assert(node->type == NodeTypeFnCallExpr);
4664
4665 AstNode **op1 = &node->data.fn_call_expr.params.at(0);
4666 AstNode **op2 = &node->data.fn_call_expr.params.at(1);
4667
4668 TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
4669 TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
4670
4671 AstNode *op_nodes[] = {*op1, *op2};
4672 TypeTableEntry *op_types[] = {op1_type, op2_type};
4673 TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node,
4674 op_nodes, op_types, 2);
4675
4676 if (result_type->id == TypeTableEntryIdInvalid) {
4677 return g->builtin_types.entry_invalid;
4678 } else if (result_type->id == TypeTableEntryIdInt) {
4679 return result_type;
4680 } else if (result_type->id == TypeTableEntryIdNumLitInt) {
4681 // check for division by zero
4682 // check for non exact division
4683 zig_panic("TODO");
4684 } else {
4685 add_node_error(g, node,
4686 buf_sprintf("expected integer type, got '%s'", buf_ptr(&result_type->name)));
4687 return g->builtin_types.entry_invalid;
4688 }
4689}
4690
4660static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,4691static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
4661 TypeTableEntry *expected_type, AstNode *node)4692 TypeTableEntry *expected_type, AstNode *node)
4662{4693{
...@@ -4997,6 +5028,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry...@@ -4997,6 +5028,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
4997 return analyze_cmpxchg(g, import, context, node);5028 return analyze_cmpxchg(g, import, context, node);
4998 case BuiltinFnIdFence:5029 case BuiltinFnIdFence:
4999 return analyze_fence(g, import, context, node);5030 return analyze_fence(g, import, context, node);
5031 case BuiltinFnIdDivExact:
5032 return analyze_div_exact(g, import, context, node);
5000 }5033 }
5001 zig_unreachable();5034 zig_unreachable();
5002}5035}
src/codegen.cpp+49-3
...@@ -217,6 +217,8 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b...@@ -217,6 +217,8 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b
217 LLVMValueRef target_ref, LLVMValueRef value,217 LLVMValueRef target_ref, LLVMValueRef value,
218 TypeTableEntry *op1_type, TypeTableEntry *op2_type);218 TypeTableEntry *op1_type, TypeTableEntry *op2_type);
219static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref);219static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref);
220static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
221 TypeTableEntry *type_entry, bool exact);
220222
221static TypeTableEntry *get_type_for_type_node(AstNode *node) {223static TypeTableEntry *get_type_for_type_node(AstNode *node) {
222 Expr *expr = get_resolved_expr(node);224 Expr *expr = get_resolved_expr(node);
...@@ -459,6 +461,18 @@ static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) {...@@ -459,6 +461,18 @@ static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) {
459 return nullptr;461 return nullptr;
460}462}
461463
464static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) {
465 assert(node->type == NodeTypeFnCallExpr);
466
467 AstNode *op1_node = node->data.fn_call_expr.params.at(0);
468 AstNode *op2_node = node->data.fn_call_expr.params.at(1);
469
470 LLVMValueRef op1_val = gen_expr(g, op1_node);
471 LLVMValueRef op2_val = gen_expr(g, op2_node);
472
473 return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true);
474}
475
462static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {476static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
463 assert(node->type == NodeTypeFnCallExpr);477 assert(node->type == NodeTypeFnCallExpr);
464478
...@@ -645,6 +659,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {...@@ -645,6 +659,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
645 return gen_cmp_exchange(g, node);659 return gen_cmp_exchange(g, node);
646 case BuiltinFnIdFence:660 case BuiltinFnIdFence:
647 return gen_fence(g, node);661 return gen_fence(g, node);
662 case BuiltinFnIdDivExact:
663 return gen_div_exact(g, node);
648 }664 }
649 zig_unreachable();665 zig_unreachable();
650}666}
...@@ -1566,7 +1582,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {...@@ -1566,7 +1582,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
1566}1582}
15671583
1568static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,1584static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
1569 TypeTableEntry *type_entry)1585 TypeTableEntry *type_entry, bool exact)
1570{1586{
1571 set_debug_source_node(g, source_node);1587 set_debug_source_node(g, source_node);
15721588
...@@ -1591,9 +1607,38 @@ static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1,...@@ -1591,9 +1607,38 @@ static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1,
1591 }1607 }
15921608
1593 if (type_entry->id == TypeTableEntryIdFloat) {1609 if (type_entry->id == TypeTableEntryIdFloat) {
1610 assert(!exact);
1594 return LLVMBuildFDiv(g->builder, val1, val2, "");1611 return LLVMBuildFDiv(g->builder, val1, val2, "");
1612 }
1613
1614 assert(type_entry->id == TypeTableEntryIdInt);
1615
1616 if (exact) {
1617 if (want_debug_safety(g, source_node)) {
1618 LLVMValueRef remainder_val;
1619 if (type_entry->data.integral.is_signed) {
1620 remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
1621 } else {
1622 remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
1623 }
1624 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
1625 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
1626
1627 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactOk");
1628 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactFail");
1629 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
1630
1631 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1632 gen_debug_safety_crash(g);
1633
1634 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1635 }
1636 if (type_entry->data.integral.is_signed) {
1637 return LLVMBuildExactSDiv(g->builder, val1, val2, "");
1638 } else {
1639 return ZigLLVMBuildExactUDiv(g->builder, val1, val2, "");
1640 }
1595 } else {1641 } else {
1596 assert(type_entry->id == TypeTableEntryIdInt);
1597 if (type_entry->data.integral.is_signed) {1642 if (type_entry->data.integral.is_signed) {
1598 return LLVMBuildSDiv(g->builder, val1, val2, "");1643 return LLVMBuildSDiv(g->builder, val1, val2, "");
1599 } else {1644 } else {
...@@ -1702,7 +1747,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,...@@ -1702,7 +1747,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
1702 }1747 }
1703 case BinOpTypeDiv:1748 case BinOpTypeDiv:
1704 case BinOpTypeAssignDiv:1749 case BinOpTypeAssignDiv:
1705 return gen_div(g, source_node, val1, val2, op1_type);1750 return gen_div(g, source_node, val1, val2, op1_type, false);
1706 case BinOpTypeMod:1751 case BinOpTypeMod:
1707 case BinOpTypeAssignMod:1752 case BinOpTypeAssignMod:
1708 set_debug_source_node(g, source_node);1753 set_debug_source_node(g, source_node);
...@@ -4492,6 +4537,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4492,6 +4537,7 @@ static void define_builtin_fns(CodeGen *g) {
4492 create_builtin_fn_with_arg_count(g, BuiltinFnIdEmbedFile, "embed_file", 1);4537 create_builtin_fn_with_arg_count(g, BuiltinFnIdEmbedFile, "embed_file", 1);
4493 create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);4538 create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
4494 create_builtin_fn_with_arg_count(g, BuiltinFnIdFence, "fence", 1);4539 create_builtin_fn_with_arg_count(g, BuiltinFnIdFence, "fence", 1);
4540 create_builtin_fn_with_arg_count(g, BuiltinFnIdDivExact, "div_exact", 2);
4495}4541}
44964542
4497static void init(CodeGen *g, Buf *source_path) {4543static void init(CodeGen *g, Buf *source_path) {
src/eval.cpp+42
...@@ -709,6 +709,46 @@ static bool eval_min_max(EvalFn *ef, AstNode *node, ConstExprValue *out_val, boo...@@ -709,6 +709,46 @@ static bool eval_min_max(EvalFn *ef, AstNode *node, ConstExprValue *out_val, boo
709 return false;709 return false;
710}710}
711711
712static bool eval_div_exact(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
713 assert(node->type == NodeTypeFnCallExpr);
714 AstNode *op1_node = node->data.fn_call_expr.params.at(0);
715 AstNode *op2_node = node->data.fn_call_expr.params.at(1);
716
717 TypeTableEntry *type_entry = get_resolved_expr(op1_node)->type_entry;
718 assert(type_entry->id == TypeTableEntryIdInt);
719
720 ConstExprValue op1_val = {0};
721 if (eval_expr(ef, op1_node, &op1_val)) return true;
722
723 ConstExprValue op2_val = {0};
724 if (eval_expr(ef, op2_node, &op2_val)) return true;
725
726 if (op2_val.data.x_bignum.data.x_uint == 0) {
727 ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
728 buf_sprintf("function evaluation caused division by zero"));
729 add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
730 add_error_note(ef->root->codegen, msg, node, buf_sprintf("division by zero here"));
731 return true;
732 }
733
734 bignum_div(&out_val->data.x_bignum, &op1_val.data.x_bignum, &op2_val.data.x_bignum);
735
736 BigNum orig_bn;
737 bignum_mul(&orig_bn, &out_val->data.x_bignum, &op2_val.data.x_bignum);
738
739 if (bignum_cmp_neq(&orig_bn, &op1_val.data.x_bignum)) {
740 ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
741 buf_sprintf("function evaluation violated exact division"));
742 add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
743 add_error_note(ef->root->codegen, msg, node, buf_sprintf("exact division violation here"));
744 return true;
745 }
746
747 out_val->ok = true;
748 out_val->depends_on_compile_var = op1_val.depends_on_compile_var || op2_val.depends_on_compile_var;
749 return false;
750}
751
712static bool eval_fn_with_overflow(EvalFn *ef, AstNode *node, ConstExprValue *out_val,752static bool eval_fn_with_overflow(EvalFn *ef, AstNode *node, ConstExprValue *out_val,
713 bool (*bignum_fn)(BigNum *dest, BigNum *op1, BigNum *op2))753 bool (*bignum_fn)(BigNum *dest, BigNum *op1, BigNum *op2))
714{754{
...@@ -767,6 +807,8 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_...@@ -767,6 +807,8 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_
767 return eval_fn_with_overflow(ef, node, out_val, bignum_shl);807 return eval_fn_with_overflow(ef, node, out_val, bignum_shl);
768 case BuiltinFnIdFence:808 case BuiltinFnIdFence:
769 return false;809 return false;
810 case BuiltinFnIdDivExact:
811 return eval_div_exact(ef, node, out_val);
770 case BuiltinFnIdMemcpy:812 case BuiltinFnIdMemcpy:
771 case BuiltinFnIdMemset:813 case BuiltinFnIdMemset:
772 case BuiltinFnIdSizeof:814 case BuiltinFnIdSizeof:
src/zig_llvm.cpp+7
...@@ -680,6 +680,13 @@ LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa...@@ -680,6 +680,13 @@ LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa
680 return wrap(unwrap(builder)->CreateShl(unwrap(LHS), unwrap(RHS), name, false, true));680 return wrap(unwrap(builder)->CreateShl(unwrap(LHS), unwrap(RHS), name, false, true));
681}681}
682682
683LLVMValueRef ZigLLVMBuildExactUDiv(LLVMBuilderRef B, LLVMValueRef LHS,
684 LLVMValueRef RHS, const char *Name)
685{
686 return wrap(unwrap(B)->CreateExactUDiv(unwrap(LHS), unwrap(RHS), Name));
687}
688
689
683690
684//------------------------------------691//------------------------------------
685692
src/zig_llvm.hpp+2
...@@ -47,6 +47,8 @@ LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa...@@ -47,6 +47,8 @@ LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa
47 const char *name);47 const char *name);
48LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,48LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
49 const char *name);49 const char *name);
50LLVMValueRef ZigLLVMBuildExactUDiv(LLVMBuilderRef B, LLVMValueRef LHS,
51 LLVMValueRef RHS, const char *Name);
5052
51// 0 is return value, 1 is first arg53// 0 is return value, 1 is first arg
52void LLVMZigAddNonNullAttr(LLVMValueRef fn, unsigned i);54void LLVMZigAddNonNullAttr(LLVMValueRef fn, unsigned i);
test/run_tests.cpp+10
...@@ -1456,6 +1456,16 @@ fn div0(a: i32, b: i32) -> i32 {...@@ -1456,6 +1456,16 @@ fn div0(a: i32, b: i32) -> i32 {
1456}1456}
1457 )SOURCE");1457 )SOURCE");
14581458
1459 add_debug_safety_case("exact division failure", R"SOURCE(
1460pub fn main(args: [][]u8) -> %void {
1461 div_exact(10, 3);
1462}
1463#static_eval_enable(false)
1464fn div_exact(a: i32, b: i32) -> i32 {
1465 @div_exact(a, b)
1466}
1467 )SOURCE");
1468
1459}1469}
14601470
1461//////////////////////////////////////////////////////////////////////////////1471//////////////////////////////////////////////////////////////////////////////
test/self_hosted.zig+9
...@@ -1603,3 +1603,12 @@ fn float_division() {...@@ -1603,3 +1603,12 @@ fn float_division() {
1603fn fdiv32(a: f32, b: f32) -> f32 {1603fn fdiv32(a: f32, b: f32) -> f32 {
1604 a / b1604 a / b
1605}1605}
1606
1607#attribute("test")
1608fn exact_division() {
1609 assert(div_exact(55, 11) == 5);
1610}
1611#static_eval_enable(false)
1612fn div_exact(a: u32, b: u32) -> u32 {
1613 @div_exact(a, b)
1614}