| author | |
| committer | |
| log | 404defd99b76ebf2cfe46ea26248a8813e40136f |
| tree | 9a1ddc547f3f278642c5cd7feb5e1006cbf11895 |
| parent | eb83111f0258ee41af021091bb78b3b5e5f6f3d3 |
closes #1498 files changed, 153 insertions(+), 3 deletions(-)
src/all_types.hpp+1| ... | ... | @@ -1128,6 +1128,7 @@ enum BuiltinFnId { |
| 1128 | 1128 | BuiltinFnIdEmbedFile, |
| 1129 | 1129 | BuiltinFnIdCmpExchange, |
| 1130 | 1130 | BuiltinFnIdFence, |
| 1131 | BuiltinFnIdDivExact, | |
| 1131 | 1132 | }; |
| 1132 | 1133 | |
| 1133 | 1134 | struct BuiltinFnEntry { |
src/analyze.cpp+33| ... | ... | @@ -4657,6 +4657,37 @@ static TypeTableEntry *analyze_fence(CodeGen *g, ImportTableEntry *import, |
| 4657 | 4657 | return g->builtin_types.entry_void; |
| 4658 | 4658 | } |
| 4659 | 4659 | |
| 4660 | static 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 | ||
| 4660 | 4691 | static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 4661 | 4692 | TypeTableEntry *expected_type, AstNode *node) |
| 4662 | 4693 | { |
| ... | ... | @@ -4997,6 +5028,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4997 | 5028 | return analyze_cmpxchg(g, import, context, node); |
| 4998 | 5029 | case BuiltinFnIdFence: |
| 4999 | 5030 | return analyze_fence(g, import, context, node); |
| 5031 | case BuiltinFnIdDivExact: | |
| 5032 | return analyze_div_exact(g, import, context, node); | |
| 5000 | 5033 | } |
| 5001 | 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 | 217 | LLVMValueRef target_ref, LLVMValueRef value, |
| 218 | 218 | TypeTableEntry *op1_type, TypeTableEntry *op2_type); |
| 219 | 219 | static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref); |
| 220 | static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, | |
| 221 | TypeTableEntry *type_entry, bool exact); | |
| 220 | 222 | |
| 221 | 223 | static TypeTableEntry *get_type_for_type_node(AstNode *node) { |
| 222 | 224 | Expr *expr = get_resolved_expr(node); |
| ... | ... | @@ -459,6 +461,18 @@ static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) { |
| 459 | 461 | return nullptr; |
| 460 | 462 | } |
| 461 | 463 | |
| 464 | static 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 | ||
| 462 | 476 | static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) { |
| 463 | 477 | assert(node->type == NodeTypeFnCallExpr); |
| 464 | 478 | |
| ... | ... | @@ -645,6 +659,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 645 | 659 | return gen_cmp_exchange(g, node); |
| 646 | 660 | case BuiltinFnIdFence: |
| 647 | 661 | return gen_fence(g, node); |
| 662 | case BuiltinFnIdDivExact: | |
| 663 | return gen_div_exact(g, node); | |
| 648 | 664 | } |
| 649 | 665 | zig_unreachable(); |
| 650 | 666 | } |
| ... | ... | @@ -1566,7 +1582,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1566 | 1582 | } |
| 1567 | 1583 | |
| 1568 | 1584 | static 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 | 1587 | set_debug_source_node(g, source_node); |
| 1572 | 1588 | |
| ... | ... | @@ -1591,9 +1607,38 @@ static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, |
| 1591 | 1607 | } |
| 1592 | 1608 | |
| 1593 | 1609 | if (type_entry->id == TypeTableEntryIdFloat) { |
| 1610 | assert(!exact); | |
| 1594 | 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 | 1641 | } else { |
| 1596 | assert(type_entry->id == TypeTableEntryIdInt); | |
| 1597 | 1642 | if (type_entry->data.integral.is_signed) { |
| 1598 | 1643 | return LLVMBuildSDiv(g->builder, val1, val2, ""); |
| 1599 | 1644 | } else { |
| ... | ... | @@ -1702,7 +1747,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 1702 | 1747 | } |
| 1703 | 1748 | case BinOpTypeDiv: |
| 1704 | 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 | 1751 | case BinOpTypeMod: |
| 1707 | 1752 | case BinOpTypeAssignMod: |
| 1708 | 1753 | set_debug_source_node(g, source_node); |
| ... | ... | @@ -4492,6 +4537,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 4492 | 4537 | create_builtin_fn_with_arg_count(g, BuiltinFnIdEmbedFile, "embed_file", 1); |
| 4493 | 4538 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); |
| 4494 | 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 | } |
| 4496 | 4542 | |
| 4497 | 4543 | static 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 | 709 | return false; |
| 710 | 710 | } |
| 711 | 711 | |
| 712 | static 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 | ||
| 712 | 752 | static bool eval_fn_with_overflow(EvalFn *ef, AstNode *node, ConstExprValue *out_val, |
| 713 | 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 | 807 | return eval_fn_with_overflow(ef, node, out_val, bignum_shl); |
| 768 | 808 | case BuiltinFnIdFence: |
| 769 | 809 | return false; |
| 810 | case BuiltinFnIdDivExact: | |
| 811 | return eval_div_exact(ef, node, out_val); | |
| 770 | 812 | case BuiltinFnIdMemcpy: |
| 771 | 813 | case BuiltinFnIdMemset: |
| 772 | 814 | case BuiltinFnIdSizeof: |
src/zig_llvm.cpp+7| ... | ... | @@ -680,6 +680,13 @@ LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa |
| 680 | 680 | return wrap(unwrap(builder)->CreateShl(unwrap(LHS), unwrap(RHS), name, false, true)); |
| 681 | 681 | } |
| 682 | 682 | |
| 683 | LLVMValueRef 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 | ||
| 683 | 690 | |
| 684 | 691 | //------------------------------------ |
| 685 | 692 |
src/zig_llvm.hpp+2| ... | ... | @@ -47,6 +47,8 @@ LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa |
| 47 | 47 | const char *name); |
| 48 | 48 | LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, |
| 49 | 49 | const char *name); |
| 50 | LLVMValueRef ZigLLVMBuildExactUDiv(LLVMBuilderRef B, LLVMValueRef LHS, | |
| 51 | LLVMValueRef RHS, const char *Name); | |
| 50 | 52 | |
| 51 | 53 | // 0 is return value, 1 is first arg |
| 52 | 54 | void LLVMZigAddNonNullAttr(LLVMValueRef fn, unsigned i); |
test/run_tests.cpp+10| ... | ... | @@ -1456,6 +1456,16 @@ fn div0(a: i32, b: i32) -> i32 { |
| 1456 | 1456 | } |
| 1457 | 1457 | )SOURCE"); |
| 1458 | 1458 | |
| 1459 | add_debug_safety_case("exact division failure", R"SOURCE( | |
| 1460 | pub fn main(args: [][]u8) -> %void { | |
| 1461 | div_exact(10, 3); | |
| 1462 | } | |
| 1463 | #static_eval_enable(false) | |
| 1464 | fn div_exact(a: i32, b: i32) -> i32 { | |
| 1465 | @div_exact(a, b) | |
| 1466 | } | |
| 1467 | )SOURCE"); | |
| 1468 | ||
| 1459 | 1469 | } |
| 1460 | 1470 | |
| 1461 | 1471 | ////////////////////////////////////////////////////////////////////////////// |
test/self_hosted.zig+9| ... | ... | @@ -1603,3 +1603,12 @@ fn float_division() { |
| 1603 | 1603 | fn fdiv32(a: f32, b: f32) -> f32 { |
| 1604 | 1604 | a / b |
| 1605 | 1605 | } |
| 1606 | ||
| 1607 | #attribute("test") | |
| 1608 | fn exact_division() { | |
| 1609 | assert(div_exact(55, 11) == 5); | |
| 1610 | } | |
| 1611 | #static_eval_enable(false) | |
| 1612 | fn div_exact(a: u32, b: u32) -> u32 { | |
| 1613 | @div_exact(a, b) | |
| 1614 | } |