| author | |
| committer | |
| log | 05b587fcdee91a7c9f170da4a186a512b51b39a8 |
| tree | 638c7444a2f73fc304b662bf50329ccc18d2c86c |
| parent | e2dc63644ab3d8e5cdaec2d58dc57c587295081f |
| parent | e84b9b70ff2814d6e50a851dc9f094b15399d2fe |
| signature |
closes #47375 files changed, 644 insertions(+), 221 deletions(-)
lib/std/target.zig+2-5| ... | ... | @@ -501,11 +501,8 @@ pub const Target = struct { |
| 501 | 501 | |
| 502 | 502 | /// Removes the specified feature but not its dependents. |
| 503 | 503 | pub fn removeFeatureSet(set: *Set, other_set: Set) void { |
| 504 | // TODO should be able to use binary not on @Vector type. | |
| 505 | // https://github.com/ziglang/zig/issues/903 | |
| 506 | for (set.ints) |*int, i| { | |
| 507 | int.* &= ~other_set.ints[i]; | |
| 508 | } | |
| 504 | set.ints = @as(@Vector(usize_count, usize), set.ints) & | |
| 505 | ~@as(@Vector(usize_count, usize), other_set.ints); | |
| 509 | 506 | } |
| 510 | 507 | |
| 511 | 508 | pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { |
src/codegen.cpp+150-59| ... | ... | @@ -2535,19 +2535,51 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir |
| 2535 | 2535 | return nullptr; |
| 2536 | 2536 | } |
| 2537 | 2537 | |
| 2538 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, | |
| 2539 | LLVMValueRef val1, LLVMValueRef val2) | |
| 2538 | enum class ScalarizePredicate { | |
| 2539 | // Returns true iff all the elements in the vector are 1. | |
| 2540 | // Equivalent to folding all the bits with `and`. | |
| 2541 | All, | |
| 2542 | // Returns true iff there's at least one element in the vector that is 1. | |
| 2543 | // Equivalent to folding all the bits with `or`. | |
| 2544 | Any, | |
| 2545 | }; | |
| 2546 | ||
| 2547 | // Collapses a <N x i1> vector into a single i1 according to the given predicate | |
| 2548 | static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) { | |
| 2549 | assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); | |
| 2550 | LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); | |
| 2551 | LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); | |
| 2552 | ||
| 2553 | switch (predicate) { | |
| 2554 | case ScalarizePredicate::Any: { | |
| 2555 | LLVMValueRef all_zeros = LLVMConstNull(scalar_type); | |
| 2556 | return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, ""); | |
| 2557 | } | |
| 2558 | case ScalarizePredicate::All: { | |
| 2559 | LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); | |
| 2560 | return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); | |
| 2561 | } | |
| 2562 | } | |
| 2563 | ||
| 2564 | zig_unreachable(); | |
| 2565 | } | |
| 2566 | ||
| 2567 | ||
| 2568 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, | |
| 2569 | LLVMValueRef val1, LLVMValueRef val2) | |
| 2540 | 2570 | { |
| 2541 | 2571 | // for unsigned left shifting, we do the lossy shift, then logically shift |
| 2542 | 2572 | // right the same number of bits |
| 2543 | 2573 | // if the values don't match, we have an overflow |
| 2544 | 2574 | // for signed left shifting we do the same except arithmetic shift right |
| 2575 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | |
| 2576 | operand_type->data.vector.elem_type : operand_type; | |
| 2545 | 2577 | |
| 2546 | assert(type_entry->id == ZigTypeIdInt); | |
| 2578 | assert(scalar_type->id == ZigTypeIdInt); | |
| 2547 | 2579 | |
| 2548 | 2580 | LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); |
| 2549 | 2581 | LLVMValueRef orig_val; |
| 2550 | if (type_entry->data.integral.is_signed) { | |
| 2582 | if (scalar_type->data.integral.is_signed) { | |
| 2551 | 2583 | orig_val = LLVMBuildAShr(g->builder, result, val2, ""); |
| 2552 | 2584 | } else { |
| 2553 | 2585 | orig_val = LLVMBuildLShr(g->builder, result, val2, ""); |
| ... | ... | @@ -2556,6 +2588,9 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, |
| 2556 | 2588 | |
| 2557 | 2589 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2558 | 2590 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 2591 | if (operand_type->id == ZigTypeIdVector) { | |
| 2592 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | |
| 2593 | } | |
| 2559 | 2594 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2560 | 2595 | |
| 2561 | 2596 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -2565,13 +2600,16 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, |
| 2565 | 2600 | return result; |
| 2566 | 2601 | } |
| 2567 | 2602 | |
| 2568 | static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, | |
| 2569 | LLVMValueRef val1, LLVMValueRef val2) | |
| 2603 | static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, | |
| 2604 | LLVMValueRef val1, LLVMValueRef val2) | |
| 2570 | 2605 | { |
| 2571 | assert(type_entry->id == ZigTypeIdInt); | |
| 2606 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | |
| 2607 | operand_type->data.vector.elem_type : operand_type; | |
| 2608 | ||
| 2609 | assert(scalar_type->id == ZigTypeIdInt); | |
| 2572 | 2610 | |
| 2573 | 2611 | LLVMValueRef result; |
| 2574 | if (type_entry->data.integral.is_signed) { | |
| 2612 | if (scalar_type->data.integral.is_signed) { | |
| 2575 | 2613 | result = LLVMBuildAShr(g->builder, val1, val2, ""); |
| 2576 | 2614 | } else { |
| 2577 | 2615 | result = LLVMBuildLShr(g->builder, val1, val2, ""); |
| ... | ... | @@ -2581,6 +2619,9 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2581 | 2619 | |
| 2582 | 2620 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2583 | 2621 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 2622 | if (operand_type->id == ZigTypeIdVector) { | |
| 2623 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | |
| 2624 | } | |
| 2584 | 2625 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2585 | 2626 | |
| 2586 | 2627 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -2591,12 +2632,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2591 | 2632 | } |
| 2592 | 2633 | |
| 2593 | 2634 | static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { |
| 2594 | if ((op == BuiltinFnIdCeil || | |
| 2595 | op == BuiltinFnIdFloor) && | |
| 2596 | type_entry->id == ZigTypeIdInt) | |
| 2597 | return val; | |
| 2598 | assert(type_entry->id == ZigTypeIdFloat); | |
| 2599 | ||
| 2635 | assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); | |
| 2600 | 2636 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); |
| 2601 | 2637 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); |
| 2602 | 2638 | } |
| ... | ... | @@ -2612,6 +2648,21 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { |
| 2612 | 2648 | if (bigint->digit_count == 0) { |
| 2613 | 2649 | return LLVMConstNull(type_ref); |
| 2614 | 2650 | } |
| 2651 | ||
| 2652 | if (LLVMGetTypeKind(type_ref) == LLVMVectorTypeKind) { | |
| 2653 | const unsigned vector_len = LLVMGetVectorSize(type_ref); | |
| 2654 | LLVMTypeRef elem_type = LLVMGetElementType(type_ref); | |
| 2655 | ||
| 2656 | LLVMValueRef *values = heap::c_allocator.allocate_nonzero<LLVMValueRef>(vector_len); | |
| 2657 | // Create a vector with all the elements having the same value | |
| 2658 | for (unsigned i = 0; i < vector_len; i++) { | |
| 2659 | values[i] = bigint_to_llvm_const(elem_type, bigint); | |
| 2660 | } | |
| 2661 | LLVMValueRef result = LLVMConstVector(values, vector_len); | |
| 2662 | heap::c_allocator.deallocate(values, vector_len); | |
| 2663 | return result; | |
| 2664 | } | |
| 2665 | ||
| 2615 | 2666 | LLVMValueRef unsigned_val; |
| 2616 | 2667 | if (bigint->digit_count == 1) { |
| 2617 | 2668 | unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); |
| ... | ... | @@ -2626,21 +2677,29 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { |
| 2626 | 2677 | } |
| 2627 | 2678 | |
| 2628 | 2679 | static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math, |
| 2629 | LLVMValueRef val1, LLVMValueRef val2, | |
| 2630 | ZigType *type_entry, DivKind div_kind) | |
| 2680 | LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind) | |
| 2631 | 2681 | { |
| 2682 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | |
| 2683 | operand_type->data.vector.elem_type : operand_type; | |
| 2684 | ||
| 2632 | 2685 | ZigLLVMSetFastMath(g->builder, want_fast_math); |
| 2633 | 2686 | |
| 2634 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry)); | |
| 2635 | if (want_runtime_safety && (want_fast_math || type_entry->id != ZigTypeIdFloat)) { | |
| 2687 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); | |
| 2688 | if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) { | |
| 2689 | // Safety check: divisor != 0 | |
| 2636 | 2690 | LLVMValueRef is_zero_bit; |
| 2637 | if (type_entry->id == ZigTypeIdInt) { | |
| 2691 | if (scalar_type->id == ZigTypeIdInt) { | |
| 2638 | 2692 | is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); |
| 2639 | } else if (type_entry->id == ZigTypeIdFloat) { | |
| 2693 | } else if (scalar_type->id == ZigTypeIdFloat) { | |
| 2640 | 2694 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 2641 | 2695 | } else { |
| 2642 | 2696 | zig_unreachable(); |
| 2643 | 2697 | } |
| 2698 | ||
| 2699 | if (operand_type->id == ZigTypeIdVector) { | |
| 2700 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); | |
| 2701 | } | |
| 2702 | ||
| 2644 | 2703 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); |
| 2645 | 2704 | LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); |
| 2646 | 2705 | LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); |
| ... | ... | @@ -2650,16 +2709,21 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2650 | 2709 | |
| 2651 | 2710 | LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block); |
| 2652 | 2711 | |
| 2653 | if (type_entry->id == ZigTypeIdInt && type_entry->data.integral.is_signed) { | |
| 2654 | LLVMValueRef neg_1_value = LLVMConstInt(get_llvm_type(g, type_entry), -1, true); | |
| 2712 | // Safety check: check for overflow (dividend = minInt and divisor = -1) | |
| 2713 | if (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) { | |
| 2714 | LLVMValueRef neg_1_value = LLVMConstAllOnes(get_llvm_type(g, operand_type)); | |
| 2655 | 2715 | BigInt int_min_bi = {0}; |
| 2656 | eval_min_max_value_int(g, type_entry, &int_min_bi, false); | |
| 2657 | LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, type_entry), &int_min_bi); | |
| 2716 | eval_min_max_value_int(g, scalar_type, &int_min_bi, false); | |
| 2717 | LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi); | |
| 2718 | ||
| 2658 | 2719 | LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); |
| 2659 | 2720 | LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); |
| 2660 | 2721 | LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); |
| 2661 | 2722 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); |
| 2662 | 2723 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); |
| 2724 | if (operand_type->id == ZigTypeIdVector) { | |
| 2725 | overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any); | |
| 2726 | } | |
| 2663 | 2727 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); |
| 2664 | 2728 | |
| 2665 | 2729 | LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block); |
| ... | ... | @@ -2669,18 +2733,22 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2669 | 2733 | } |
| 2670 | 2734 | } |
| 2671 | 2735 | |
| 2672 | if (type_entry->id == ZigTypeIdFloat) { | |
| 2736 | if (scalar_type->id == ZigTypeIdFloat) { | |
| 2673 | 2737 | LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); |
| 2674 | 2738 | switch (div_kind) { |
| 2675 | 2739 | case DivKindFloat: |
| 2676 | 2740 | return result; |
| 2677 | 2741 | case DivKindExact: |
| 2678 | 2742 | if (want_runtime_safety) { |
| 2679 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2743 | // Safety check: a / b == floor(a / b) | |
| 2744 | LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); | |
| 2745 | ||
| 2680 | 2746 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2681 | 2747 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2682 | 2748 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); |
| 2683 | ||
| 2749 | if (operand_type->id == ZigTypeIdVector) { | |
| 2750 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | |
| 2751 | } | |
| 2684 | 2752 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2685 | 2753 | |
| 2686 | 2754 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -2695,54 +2763,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2695 | 2763 | LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero"); |
| 2696 | 2764 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); |
| 2697 | 2765 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); |
| 2766 | if (operand_type->id == ZigTypeIdVector) { | |
| 2767 | ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any); | |
| 2768 | } | |
| 2698 | 2769 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2699 | 2770 | |
| 2700 | 2771 | LLVMPositionBuilderAtEnd(g->builder, ltz_block); |
| 2701 | LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil); | |
| 2772 | LLVMValueRef ceiled = gen_float_op(g, result, operand_type, BuiltinFnIdCeil); | |
| 2702 | 2773 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); |
| 2703 | 2774 | LLVMBuildBr(g->builder, end_block); |
| 2704 | 2775 | |
| 2705 | 2776 | LLVMPositionBuilderAtEnd(g->builder, gez_block); |
| 2706 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2777 | LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); | |
| 2707 | 2778 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); |
| 2708 | 2779 | LLVMBuildBr(g->builder, end_block); |
| 2709 | 2780 | |
| 2710 | 2781 | LLVMPositionBuilderAtEnd(g->builder, end_block); |
| 2711 | LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, type_entry), ""); | |
| 2782 | LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, operand_type), ""); | |
| 2712 | 2783 | LLVMValueRef incoming_values[] = { ceiled, floored }; |
| 2713 | 2784 | LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block }; |
| 2714 | 2785 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); |
| 2715 | 2786 | return phi; |
| 2716 | 2787 | } |
| 2717 | 2788 | case DivKindFloor: |
| 2718 | return gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2789 | return gen_float_op(g, result, operand_type, BuiltinFnIdFloor); | |
| 2719 | 2790 | } |
| 2720 | 2791 | zig_unreachable(); |
| 2721 | 2792 | } |
| 2722 | 2793 | |
| 2723 | assert(type_entry->id == ZigTypeIdInt); | |
| 2794 | assert(scalar_type->id == ZigTypeIdInt); | |
| 2724 | 2795 | |
| 2725 | 2796 | switch (div_kind) { |
| 2726 | 2797 | case DivKindFloat: |
| 2727 | 2798 | zig_unreachable(); |
| 2728 | 2799 | case DivKindTrunc: |
| 2729 | if (type_entry->data.integral.is_signed) { | |
| 2800 | if (scalar_type->data.integral.is_signed) { | |
| 2730 | 2801 | return LLVMBuildSDiv(g->builder, val1, val2, ""); |
| 2731 | 2802 | } else { |
| 2732 | 2803 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 2733 | 2804 | } |
| 2734 | 2805 | case DivKindExact: |
| 2735 | 2806 | if (want_runtime_safety) { |
| 2807 | // Safety check: a % b == 0 | |
| 2736 | 2808 | LLVMValueRef remainder_val; |
| 2737 | if (type_entry->data.integral.is_signed) { | |
| 2809 | if (scalar_type->data.integral.is_signed) { | |
| 2738 | 2810 | remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); |
| 2739 | 2811 | } else { |
| 2740 | 2812 | remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); |
| 2741 | 2813 | } |
| 2742 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | |
| 2743 | 2814 | |
| 2744 | 2815 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2745 | 2816 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2817 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | |
| 2818 | if (operand_type->id == ZigTypeIdVector) { | |
| 2819 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | |
| 2820 | } | |
| 2746 | 2821 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2747 | 2822 | |
| 2748 | 2823 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -2750,14 +2825,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2750 | 2825 | |
| 2751 | 2826 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 2752 | 2827 | } |
| 2753 | if (type_entry->data.integral.is_signed) { | |
| 2828 | if (scalar_type->data.integral.is_signed) { | |
| 2754 | 2829 | return LLVMBuildExactSDiv(g->builder, val1, val2, ""); |
| 2755 | 2830 | } else { |
| 2756 | 2831 | return LLVMBuildExactUDiv(g->builder, val1, val2, ""); |
| 2757 | 2832 | } |
| 2758 | 2833 | case DivKindFloor: |
| 2759 | 2834 | { |
| 2760 | if (!type_entry->data.integral.is_signed) { | |
| 2835 | if (!scalar_type->data.integral.is_signed) { | |
| 2761 | 2836 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 2762 | 2837 | } |
| 2763 | 2838 | // const d = @divTrunc(a, b); |
| ... | ... | @@ -2784,22 +2859,30 @@ enum RemKind { |
| 2784 | 2859 | }; |
| 2785 | 2860 | |
| 2786 | 2861 | static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math, |
| 2787 | LLVMValueRef val1, LLVMValueRef val2, | |
| 2788 | ZigType *type_entry, RemKind rem_kind) | |
| 2862 | LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind) | |
| 2789 | 2863 | { |
| 2864 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | |
| 2865 | operand_type->data.vector.elem_type : operand_type; | |
| 2866 | ||
| 2790 | 2867 | ZigLLVMSetFastMath(g->builder, want_fast_math); |
| 2791 | 2868 | |
| 2792 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry)); | |
| 2869 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); | |
| 2793 | 2870 | if (want_runtime_safety) { |
| 2871 | // Safety check: divisor != 0 | |
| 2794 | 2872 | LLVMValueRef is_zero_bit; |
| 2795 | if (type_entry->id == ZigTypeIdInt) { | |
| 2796 | LLVMIntPredicate pred = type_entry->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; | |
| 2873 | if (scalar_type->id == ZigTypeIdInt) { | |
| 2874 | LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; | |
| 2797 | 2875 | is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, ""); |
| 2798 | } else if (type_entry->id == ZigTypeIdFloat) { | |
| 2876 | } else if (scalar_type->id == ZigTypeIdFloat) { | |
| 2799 | 2877 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 2800 | 2878 | } else { |
| 2801 | 2879 | zig_unreachable(); |
| 2802 | 2880 | } |
| 2881 | ||
| 2882 | if (operand_type->id == ZigTypeIdVector) { | |
| 2883 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); | |
| 2884 | } | |
| 2885 | ||
| 2803 | 2886 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); |
| 2804 | 2887 | LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); |
| 2805 | 2888 | LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block); |
| ... | ... | @@ -2810,7 +2893,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2810 | 2893 | LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block); |
| 2811 | 2894 | } |
| 2812 | 2895 | |
| 2813 | if (type_entry->id == ZigTypeIdFloat) { | |
| 2896 | if (scalar_type->id == ZigTypeIdFloat) { | |
| 2814 | 2897 | if (rem_kind == RemKindRem) { |
| 2815 | 2898 | return LLVMBuildFRem(g->builder, val1, val2, ""); |
| 2816 | 2899 | } else { |
| ... | ... | @@ -2821,8 +2904,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2821 | 2904 | return LLVMBuildSelect(g->builder, ltz, c, a, ""); |
| 2822 | 2905 | } |
| 2823 | 2906 | } else { |
| 2824 | assert(type_entry->id == ZigTypeIdInt); | |
| 2825 | if (type_entry->data.integral.is_signed) { | |
| 2907 | assert(scalar_type->id == ZigTypeIdInt); | |
| 2908 | if (scalar_type->data.integral.is_signed) { | |
| 2826 | 2909 | if (rem_kind == RemKindRem) { |
| 2827 | 2910 | return LLVMBuildSRem(g->builder, val1, val2, ""); |
| 2828 | 2911 | } else { |
| ... | ... | @@ -2845,11 +2928,17 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type |
| 2845 | 2928 | // otherwise the check is useful as the allowed values are limited by the |
| 2846 | 2929 | // operand type itself |
| 2847 | 2930 | if (!is_power_of_2(lhs_type->data.integral.bit_count)) { |
| 2848 | LLVMValueRef bit_count_value = LLVMConstInt(get_llvm_type(g, rhs_type), | |
| 2849 | lhs_type->data.integral.bit_count, false); | |
| 2850 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); | |
| 2931 | BigInt bit_count_bi = {0}; | |
| 2932 | bigint_init_unsigned(&bit_count_bi, lhs_type->data.integral.bit_count); | |
| 2933 | LLVMValueRef bit_count_value = bigint_to_llvm_const(get_llvm_type(g, rhs_type), | |
| 2934 | &bit_count_bi); | |
| 2935 | ||
| 2851 | 2936 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail"); |
| 2852 | 2937 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); |
| 2938 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); | |
| 2939 | if (rhs_type->id == ZigTypeIdVector) { | |
| 2940 | less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any); | |
| 2941 | } | |
| 2853 | 2942 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); |
| 2854 | 2943 | |
| 2855 | 2944 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | ... | @@ -2966,7 +3055,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2966 | 3055 | case IrBinOpBitShiftLeftExact: |
| 2967 | 3056 | { |
| 2968 | 3057 | assert(scalar_type->id == ZigTypeIdInt); |
| 2969 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); | |
| 3058 | LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, | |
| 3059 | LLVMTypeOf(op1_value), ""); | |
| 2970 | 3060 | |
| 2971 | 3061 | if (want_runtime_safety) { |
| 2972 | 3062 | gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); |
| ... | ... | @@ -2976,7 +3066,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2976 | 3066 | if (is_sloppy) { |
| 2977 | 3067 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); |
| 2978 | 3068 | } else if (want_runtime_safety) { |
| 2979 | return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted); | |
| 3069 | return gen_overflow_shl_op(g, operand_type, op1_value, op2_casted); | |
| 2980 | 3070 | } else if (scalar_type->data.integral.is_signed) { |
| 2981 | 3071 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); |
| 2982 | 3072 | } else { |
| ... | ... | @@ -2987,7 +3077,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2987 | 3077 | case IrBinOpBitShiftRightExact: |
| 2988 | 3078 | { |
| 2989 | 3079 | assert(scalar_type->id == ZigTypeIdInt); |
| 2990 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); | |
| 3080 | LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, | |
| 3081 | LLVMTypeOf(op1_value), ""); | |
| 2991 | 3082 | |
| 2992 | 3083 | if (want_runtime_safety) { |
| 2993 | 3084 | gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); |
| ... | ... | @@ -3001,7 +3092,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 3001 | 3092 | return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); |
| 3002 | 3093 | } |
| 3003 | 3094 | } else if (want_runtime_safety) { |
| 3004 | return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted); | |
| 3095 | return gen_overflow_shr_op(g, operand_type, op1_value, op2_casted); | |
| 3005 | 3096 | } else if (scalar_type->data.integral.is_signed) { |
| 3006 | 3097 | return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); |
| 3007 | 3098 | } else { |
| ... | ... | @@ -3010,22 +3101,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 3010 | 3101 | } |
| 3011 | 3102 | case IrBinOpDivUnspecified: |
| 3012 | 3103 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3013 | op1_value, op2_value, scalar_type, DivKindFloat); | |
| 3104 | op1_value, op2_value, operand_type, DivKindFloat); | |
| 3014 | 3105 | case IrBinOpDivExact: |
| 3015 | 3106 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3016 | op1_value, op2_value, scalar_type, DivKindExact); | |
| 3107 | op1_value, op2_value, operand_type, DivKindExact); | |
| 3017 | 3108 | case IrBinOpDivTrunc: |
| 3018 | 3109 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3019 | op1_value, op2_value, scalar_type, DivKindTrunc); | |
| 3110 | op1_value, op2_value, operand_type, DivKindTrunc); | |
| 3020 | 3111 | case IrBinOpDivFloor: |
| 3021 | 3112 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3022 | op1_value, op2_value, scalar_type, DivKindFloor); | |
| 3113 | op1_value, op2_value, operand_type, DivKindFloor); | |
| 3023 | 3114 | case IrBinOpRemRem: |
| 3024 | 3115 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3025 | op1_value, op2_value, scalar_type, RemKindRem); | |
| 3116 | op1_value, op2_value, operand_type, RemKindRem); | |
| 3026 | 3117 | case IrBinOpRemMod: |
| 3027 | 3118 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3028 | op1_value, op2_value, scalar_type, RemKindMod); | |
| 3119 | op1_value, op2_value, operand_type, RemKindMod); | |
| 3029 | 3120 | } |
| 3030 | 3121 | zig_unreachable(); |
| 3031 | 3122 | } |
src/ir.cpp+254-157| ... | ... | @@ -283,6 +283,8 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi |
| 283 | 283 | IrInstGen *result_loc); |
| 284 | 284 | static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, |
| 285 | 285 | IrInstGen *struct_operand, TypeStructField *field); |
| 286 | static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); | |
| 287 | static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); | |
| 286 | 288 | |
| 287 | 289 | static void destroy_instruction_src(IrInstSrc *inst) { |
| 288 | 290 | switch (inst->id) { |
| ... | ... | @@ -16803,7 +16805,6 @@ static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr, |
| 16803 | 16805 | ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; |
| 16804 | 16806 | ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; |
| 16805 | 16807 | assert(scalar_op1_val->type == scalar_type); |
| 16806 | assert(scalar_op2_val->type == scalar_type); | |
| 16807 | 16808 | assert(scalar_out_val->type == scalar_type); |
| 16808 | 16809 | ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, |
| 16809 | 16810 | scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); |
| ... | ... | @@ -16828,27 +16829,49 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16828 | 16829 | if (type_is_invalid(op1->value->type)) |
| 16829 | 16830 | return ira->codegen->invalid_inst_gen; |
| 16830 | 16831 | |
| 16831 | if (op1->value->type->id != ZigTypeIdInt && op1->value->type->id != ZigTypeIdComptimeInt) { | |
| 16832 | IrInstGen *op2 = bin_op_instruction->op2->child; | |
| 16833 | if (type_is_invalid(op2->value->type)) | |
| 16834 | return ira->codegen->invalid_inst_gen; | |
| 16835 | ||
| 16836 | ZigType *op1_type = op1->value->type; | |
| 16837 | ZigType *op2_type = op2->value->type; | |
| 16838 | ||
| 16839 | if (op1_type->id == ZigTypeIdVector && op2_type->id != ZigTypeIdVector) { | |
| 16832 | 16840 | ir_add_error(ira, &bin_op_instruction->op1->base, |
| 16833 | buf_sprintf("bit shifting operation expected integer type, found '%s'", | |
| 16834 | buf_ptr(&op1->value->type->name))); | |
| 16841 | buf_sprintf("bit shifting operation expected vector type, found '%s'", | |
| 16842 | buf_ptr(&op2_type->name))); | |
| 16835 | 16843 | return ira->codegen->invalid_inst_gen; |
| 16836 | 16844 | } |
| 16837 | 16845 | |
| 16838 | IrInstGen *op2 = bin_op_instruction->op2->child; | |
| 16839 | if (type_is_invalid(op2->value->type)) | |
| 16846 | if (op1_type->id != ZigTypeIdVector && op2_type->id == ZigTypeIdVector) { | |
| 16847 | ir_add_error(ira, &bin_op_instruction->op1->base, | |
| 16848 | buf_sprintf("bit shifting operation expected vector type, found '%s'", | |
| 16849 | buf_ptr(&op1_type->name))); | |
| 16850 | return ira->codegen->invalid_inst_gen; | |
| 16851 | } | |
| 16852 | ||
| 16853 | ZigType *op1_scalar_type = (op1_type->id == ZigTypeIdVector) ? | |
| 16854 | op1_type->data.vector.elem_type : op1_type; | |
| 16855 | ZigType *op2_scalar_type = (op2_type->id == ZigTypeIdVector) ? | |
| 16856 | op2_type->data.vector.elem_type : op2_type; | |
| 16857 | ||
| 16858 | if (op1_scalar_type->id != ZigTypeIdInt && op1_scalar_type->id != ZigTypeIdComptimeInt) { | |
| 16859 | ir_add_error(ira, &bin_op_instruction->op1->base, | |
| 16860 | buf_sprintf("bit shifting operation expected integer type, found '%s'", | |
| 16861 | buf_ptr(&op1_scalar_type->name))); | |
| 16840 | 16862 | return ira->codegen->invalid_inst_gen; |
| 16863 | } | |
| 16841 | 16864 | |
| 16842 | if (op2->value->type->id != ZigTypeIdInt && op2->value->type->id != ZigTypeIdComptimeInt) { | |
| 16865 | if (op2_scalar_type->id != ZigTypeIdInt && op2_scalar_type->id != ZigTypeIdComptimeInt) { | |
| 16843 | 16866 | ir_add_error(ira, &bin_op_instruction->op2->base, |
| 16844 | 16867 | buf_sprintf("shift amount has to be an integer type, but found '%s'", |
| 16845 | buf_ptr(&op2->value->type->name))); | |
| 16868 | buf_ptr(&op2_scalar_type->name))); | |
| 16846 | 16869 | return ira->codegen->invalid_inst_gen; |
| 16847 | 16870 | } |
| 16848 | 16871 | |
| 16849 | 16872 | IrInstGen *casted_op2; |
| 16850 | 16873 | IrBinOp op_id = bin_op_instruction->op_id; |
| 16851 | if (op1->value->type->id == ZigTypeIdComptimeInt) { | |
| 16874 | if (op1_scalar_type->id == ZigTypeIdComptimeInt) { | |
| 16852 | 16875 | // comptime_int has no finite bit width |
| 16853 | 16876 | casted_op2 = op2; |
| 16854 | 16877 | |
| ... | ... | @@ -16874,10 +16897,15 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16874 | 16897 | return ira->codegen->invalid_inst_gen; |
| 16875 | 16898 | } |
| 16876 | 16899 | } else { |
| 16877 | const unsigned bit_count = op1->value->type->data.integral.bit_count; | |
| 16900 | const unsigned bit_count = op1_scalar_type->data.integral.bit_count; | |
| 16878 | 16901 | ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, |
| 16879 | 16902 | bit_count > 0 ? bit_count - 1 : 0); |
| 16880 | 16903 | |
| 16904 | if (op1_type->id == ZigTypeIdVector) { | |
| 16905 | shift_amt_type = get_vector_type(ira->codegen, op1_type->data.vector.len, | |
| 16906 | shift_amt_type); | |
| 16907 | } | |
| 16908 | ||
| 16881 | 16909 | casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); |
| 16882 | 16910 | if (type_is_invalid(casted_op2->value->type)) |
| 16883 | 16911 | return ira->codegen->invalid_inst_gen; |
| ... | ... | @@ -16888,10 +16916,10 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16888 | 16916 | if (op2_val == nullptr) |
| 16889 | 16917 | return ira->codegen->invalid_inst_gen; |
| 16890 | 16918 | |
| 16891 | BigInt bit_count_value = {0}; | |
| 16892 | bigint_init_unsigned(&bit_count_value, bit_count); | |
| 16919 | ZigValue bit_count_value; | |
| 16920 | init_const_usize(ira->codegen, &bit_count_value, bit_count); | |
| 16893 | 16921 | |
| 16894 | if (bigint_cmp(&op2_val->data.x_bigint, &bit_count_value) != CmpLT) { | |
| 16922 | if (!value_cmp_numeric_val_all(op2_val, CmpLT, &bit_count_value)) { | |
| 16895 | 16923 | ErrorMsg* msg = ir_add_error(ira, |
| 16896 | 16924 | &bin_op_instruction->base.base, |
| 16897 | 16925 | buf_sprintf("RHS of shift is too large for LHS type")); |
| ... | ... | @@ -16910,7 +16938,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16910 | 16938 | if (op2_val == nullptr) |
| 16911 | 16939 | return ira->codegen->invalid_inst_gen; |
| 16912 | 16940 | |
| 16913 | if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) | |
| 16941 | if (value_cmp_numeric_val_all(op2_val, CmpEQ, nullptr)) | |
| 16914 | 16942 | return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1); |
| 16915 | 16943 | } |
| 16916 | 16944 | |
| ... | ... | @@ -16923,7 +16951,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16923 | 16951 | if (op2_val == nullptr) |
| 16924 | 16952 | return ira->codegen->invalid_inst_gen; |
| 16925 | 16953 | |
| 16926 | return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1->value->type, op1_val, op_id, op2_val); | |
| 16954 | return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1_type, op1_val, op_id, op2_val); | |
| 16927 | 16955 | } |
| 16928 | 16956 | |
| 16929 | 16957 | return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, |
| ... | ... | @@ -16943,6 +16971,7 @@ static bool ok_float_op(IrBinOp op) { |
| 16943 | 16971 | case IrBinOpDivExact: |
| 16944 | 16972 | case IrBinOpRemRem: |
| 16945 | 16973 | case IrBinOpRemMod: |
| 16974 | case IrBinOpRemUnspecified: | |
| 16946 | 16975 | return true; |
| 16947 | 16976 | |
| 16948 | 16977 | case IrBinOpBoolOr: |
| ... | ... | @@ -16963,7 +16992,6 @@ static bool ok_float_op(IrBinOp op) { |
| 16963 | 16992 | case IrBinOpAddWrap: |
| 16964 | 16993 | case IrBinOpSubWrap: |
| 16965 | 16994 | case IrBinOpMultWrap: |
| 16966 | case IrBinOpRemUnspecified: | |
| 16967 | 16995 | case IrBinOpArrayCat: |
| 16968 | 16996 | case IrBinOpArrayMult: |
| 16969 | 16997 | return false; |
| ... | ... | @@ -16991,6 +17019,53 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { |
| 16991 | 17019 | zig_unreachable(); |
| 16992 | 17020 | } |
| 16993 | 17021 | |
| 17022 | static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { | |
| 17023 | assert(left->special == ConstValSpecialStatic); | |
| 17024 | assert(right == nullptr || right->special == ConstValSpecialStatic); | |
| 17025 | ||
| 17026 | switch (left->type->id) { | |
| 17027 | case ZigTypeIdComptimeInt: | |
| 17028 | case ZigTypeIdInt: { | |
| 17029 | const Cmp result = right ? | |
| 17030 | bigint_cmp(&left->data.x_bigint, &right->data.x_bigint) : | |
| 17031 | bigint_cmp_zero(&left->data.x_bigint); | |
| 17032 | return result == predicate; | |
| 17033 | } | |
| 17034 | case ZigTypeIdComptimeFloat: | |
| 17035 | case ZigTypeIdFloat: { | |
| 17036 | if (float_is_nan(left)) | |
| 17037 | return false; | |
| 17038 | if (right != nullptr && float_is_nan(right)) | |
| 17039 | return false; | |
| 17040 | ||
| 17041 | const Cmp result = right ? float_cmp(left, right) : float_cmp_zero(left); | |
| 17042 | return result == predicate; | |
| 17043 | } | |
| 17044 | case ZigTypeIdVector: { | |
| 17045 | for (size_t i = 0; i < left->type->data.vector.len; i++) { | |
| 17046 | ZigValue *scalar_val = &left->data.x_array.data.s_none.elements[i]; | |
| 17047 | const bool result = value_cmp_numeric_val(scalar_val, predicate, right, any); | |
| 17048 | ||
| 17049 | if (any && result) | |
| 17050 | return true; // This element satisfies the predicate | |
| 17051 | else if (!any && !result) | |
| 17052 | return false; // This element doesn't satisfy the predicate | |
| 17053 | } | |
| 17054 | return any ? false : true; | |
| 17055 | } | |
| 17056 | default: | |
| 17057 | zig_unreachable(); | |
| 17058 | } | |
| 17059 | } | |
| 17060 | ||
| 17061 | static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right) { | |
| 17062 | return value_cmp_numeric_val(left, predicate, right, true); | |
| 17063 | } | |
| 17064 | ||
| 17065 | static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right) { | |
| 17066 | return value_cmp_numeric_val(left, predicate, right, false); | |
| 17067 | } | |
| 17068 | ||
| 16994 | 17069 | static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { |
| 16995 | 17070 | Error err; |
| 16996 | 17071 | |
| ... | ... | @@ -17096,127 +17171,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17096 | 17171 | if (type_is_invalid(resolved_type)) |
| 17097 | 17172 | return ira->codegen->invalid_inst_gen; |
| 17098 | 17173 | |
| 17099 | bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt; | |
| 17100 | bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat; | |
| 17101 | bool is_signed_div = ( | |
| 17102 | (resolved_type->id == ZigTypeIdInt && resolved_type->data.integral.is_signed) || | |
| 17103 | resolved_type->id == ZigTypeIdFloat || | |
| 17104 | (resolved_type->id == ZigTypeIdComptimeFloat && | |
| 17105 | ((bigfloat_cmp_zero(&op1->value->data.x_bigfloat) != CmpGT) != | |
| 17106 | (bigfloat_cmp_zero(&op2->value->data.x_bigfloat) != CmpGT))) || | |
| 17107 | (resolved_type->id == ZigTypeIdComptimeInt && | |
| 17108 | ((bigint_cmp_zero(&op1->value->data.x_bigint) != CmpGT) != | |
| 17109 | (bigint_cmp_zero(&op2->value->data.x_bigint) != CmpGT))) | |
| 17110 | ); | |
| 17111 | if (op_id == IrBinOpDivUnspecified && is_int) { | |
| 17112 | if (is_signed_div) { | |
| 17113 | bool ok = false; | |
| 17114 | if (instr_is_comptime(op1) && instr_is_comptime(op2)) { | |
| 17115 | ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); | |
| 17116 | if (op1_val == nullptr) | |
| 17117 | return ira->codegen->invalid_inst_gen; | |
| 17118 | ||
| 17119 | ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); | |
| 17120 | if (op2_val == nullptr) | |
| 17121 | return ira->codegen->invalid_inst_gen; | |
| 17122 | ||
| 17123 | if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) { | |
| 17124 | // the division by zero error will be caught later, but we don't have a | |
| 17125 | // division function ambiguity problem. | |
| 17126 | op_id = IrBinOpDivTrunc; | |
| 17127 | ok = true; | |
| 17128 | } else { | |
| 17129 | BigInt trunc_result; | |
| 17130 | BigInt floor_result; | |
| 17131 | bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); | |
| 17132 | bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); | |
| 17133 | if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) { | |
| 17134 | ok = true; | |
| 17135 | op_id = IrBinOpDivTrunc; | |
| 17136 | } | |
| 17137 | } | |
| 17138 | } | |
| 17139 | if (!ok) { | |
| 17140 | ir_add_error(ira, &instruction->base.base, | |
| 17141 | buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", | |
| 17142 | buf_ptr(&op1->value->type->name), | |
| 17143 | buf_ptr(&op2->value->type->name))); | |
| 17144 | return ira->codegen->invalid_inst_gen; | |
| 17145 | } | |
| 17146 | } else { | |
| 17147 | op_id = IrBinOpDivTrunc; | |
| 17148 | } | |
| 17149 | } else if (op_id == IrBinOpRemUnspecified) { | |
| 17150 | if (is_signed_div && (is_int || is_float)) { | |
| 17151 | bool ok = false; | |
| 17152 | if (instr_is_comptime(op1) && instr_is_comptime(op2)) { | |
| 17153 | ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); | |
| 17154 | if (op1_val == nullptr) | |
| 17155 | return ira->codegen->invalid_inst_gen; | |
| 17174 | ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ? | |
| 17175 | resolved_type->data.vector.elem_type : resolved_type; | |
| 17156 | 17176 | |
| 17157 | if (is_int) { | |
| 17158 | ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); | |
| 17159 | if (op2_val == nullptr) | |
| 17160 | return ira->codegen->invalid_inst_gen; | |
| 17177 | bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt; | |
| 17178 | bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat; | |
| 17161 | 17179 | |
| 17162 | if (bigint_cmp_zero(&op2->value->data.x_bigint) == CmpEQ) { | |
| 17163 | // the division by zero error will be caught later, but we don't | |
| 17164 | // have a remainder function ambiguity problem | |
| 17165 | ok = true; | |
| 17166 | } else { | |
| 17167 | BigInt rem_result; | |
| 17168 | BigInt mod_result; | |
| 17169 | bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); | |
| 17170 | bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); | |
| 17171 | ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ; | |
| 17172 | } | |
| 17173 | } else { | |
| 17174 | IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); | |
| 17175 | if (type_is_invalid(casted_op2->value->type)) | |
| 17176 | return ira->codegen->invalid_inst_gen; | |
| 17177 | ||
| 17178 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); | |
| 17179 | if (op2_val == nullptr) | |
| 17180 | return ira->codegen->invalid_inst_gen; | |
| 17181 | ||
| 17182 | if (float_cmp_zero(casted_op2->value) == CmpEQ) { | |
| 17183 | // the division by zero error will be caught later, but we don't | |
| 17184 | // have a remainder function ambiguity problem | |
| 17185 | ok = true; | |
| 17186 | } else { | |
| 17187 | ZigValue rem_result = {}; | |
| 17188 | ZigValue mod_result = {}; | |
| 17189 | float_rem(&rem_result, op1_val, op2_val); | |
| 17190 | float_mod(&mod_result, op1_val, op2_val); | |
| 17191 | ok = float_cmp(&rem_result, &mod_result) == CmpEQ; | |
| 17192 | } | |
| 17193 | } | |
| 17194 | } | |
| 17195 | if (!ok) { | |
| 17196 | ir_add_error(ira, &instruction->base.base, | |
| 17197 | buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", | |
| 17198 | buf_ptr(&op1->value->type->name), | |
| 17199 | buf_ptr(&op2->value->type->name))); | |
| 17200 | return ira->codegen->invalid_inst_gen; | |
| 17201 | } | |
| 17202 | } | |
| 17203 | op_id = IrBinOpRemRem; | |
| 17204 | } | |
| 17205 | ||
| 17206 | bool ok = false; | |
| 17207 | if (is_int) { | |
| 17208 | ok = true; | |
| 17209 | } else if (is_float && ok_float_op(op_id)) { | |
| 17210 | ok = true; | |
| 17211 | } else if (resolved_type->id == ZigTypeIdVector) { | |
| 17212 | ZigType *elem_type = resolved_type->data.vector.elem_type; | |
| 17213 | if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) { | |
| 17214 | ok = true; | |
| 17215 | } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) { | |
| 17216 | ok = true; | |
| 17217 | } | |
| 17218 | } | |
| 17219 | if (!ok) { | |
| 17180 | if (!is_int && !(is_float && ok_float_op(op_id))) { | |
| 17220 | 17181 | AstNode *source_node = instruction->base.base.source_node; |
| 17221 | 17182 | ir_add_error_node(ira, source_node, |
| 17222 | 17183 | buf_sprintf("invalid operands to binary expression: '%s' and '%s'", |
| ... | ... | @@ -17225,7 +17186,16 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17225 | 17186 | return ira->codegen->invalid_inst_gen; |
| 17226 | 17187 | } |
| 17227 | 17188 | |
| 17228 | if (resolved_type->id == ZigTypeIdComptimeInt) { | |
| 17189 | IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); | |
| 17190 | if (type_is_invalid(casted_op1->value->type)) | |
| 17191 | return ira->codegen->invalid_inst_gen; | |
| 17192 | ||
| 17193 | IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); | |
| 17194 | if (type_is_invalid(casted_op2->value->type)) | |
| 17195 | return ira->codegen->invalid_inst_gen; | |
| 17196 | ||
| 17197 | // Comptime integers have no fixed size | |
| 17198 | if (scalar_type->id == ZigTypeIdComptimeInt) { | |
| 17229 | 17199 | if (op_id == IrBinOpAddWrap) { |
| 17230 | 17200 | op_id = IrBinOpAdd; |
| 17231 | 17201 | } else if (op_id == IrBinOpSubWrap) { |
| ... | ... | @@ -17235,25 +17205,131 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17235 | 17205 | } |
| 17236 | 17206 | } |
| 17237 | 17207 | |
| 17238 | IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); | |
| 17239 | if (type_is_invalid(casted_op1->value->type)) | |
| 17240 | return ira->codegen->invalid_inst_gen; | |
| 17241 | ||
| 17242 | IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); | |
| 17243 | if (type_is_invalid(casted_op2->value->type)) | |
| 17244 | return ira->codegen->invalid_inst_gen; | |
| 17245 | ||
| 17246 | 17208 | if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { |
| 17247 | 17209 | ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); |
| 17248 | 17210 | if (op1_val == nullptr) |
| 17249 | 17211 | return ira->codegen->invalid_inst_gen; |
| 17212 | ||
| 17250 | 17213 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); |
| 17251 | 17214 | if (op2_val == nullptr) |
| 17252 | 17215 | return ira->codegen->invalid_inst_gen; |
| 17253 | 17216 | |
| 17217 | // Promote division with negative numbers to signed | |
| 17218 | bool is_signed_div = value_cmp_numeric_val_any(op1_val, CmpLT, nullptr) || | |
| 17219 | value_cmp_numeric_val_any(op2_val, CmpLT, nullptr); | |
| 17220 | ||
| 17221 | if (op_id == IrBinOpDivUnspecified && is_int) { | |
| 17222 | // Default to truncating division and check if it's valid for the | |
| 17223 | // given operands if signed | |
| 17224 | op_id = IrBinOpDivTrunc; | |
| 17225 | ||
| 17226 | if (is_signed_div) { | |
| 17227 | bool ok = false; | |
| 17228 | ||
| 17229 | if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { | |
| 17230 | // the division by zero error will be caught later, but we don't have a | |
| 17231 | // division function ambiguity problem. | |
| 17232 | ok = true; | |
| 17233 | } else { | |
| 17234 | IrInstGen *trunc_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17235 | op1_val, IrBinOpDivTrunc, op2_val); | |
| 17236 | if (type_is_invalid(trunc_val->value->type)) | |
| 17237 | return ira->codegen->invalid_inst_gen; | |
| 17238 | ||
| 17239 | IrInstGen *floor_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17240 | op1_val, IrBinOpDivFloor, op2_val); | |
| 17241 | if (type_is_invalid(floor_val->value->type)) | |
| 17242 | return ira->codegen->invalid_inst_gen; | |
| 17243 | ||
| 17244 | IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, | |
| 17245 | trunc_val, floor_val, IrBinOpCmpEq); | |
| 17246 | if (type_is_invalid(cmp_val->value->type)) | |
| 17247 | return ira->codegen->invalid_inst_gen; | |
| 17248 | ||
| 17249 | // We can "upgrade" the operator only if trunc(a/b) == floor(a/b) | |
| 17250 | if (!ir_resolve_bool(ira, cmp_val, &ok)) | |
| 17251 | return ira->codegen->invalid_inst_gen; | |
| 17252 | } | |
| 17253 | ||
| 17254 | if (!ok) { | |
| 17255 | ir_add_error(ira, &instruction->base.base, | |
| 17256 | buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", | |
| 17257 | buf_ptr(&op1->value->type->name), | |
| 17258 | buf_ptr(&op2->value->type->name))); | |
| 17259 | return ira->codegen->invalid_inst_gen; | |
| 17260 | } | |
| 17261 | } | |
| 17262 | } else if (op_id == IrBinOpRemUnspecified) { | |
| 17263 | op_id = IrBinOpRemRem; | |
| 17264 | ||
| 17265 | if (is_signed_div) { | |
| 17266 | bool ok = false; | |
| 17267 | ||
| 17268 | if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { | |
| 17269 | // the division by zero error will be caught later, but we don't have a | |
| 17270 | // division function ambiguity problem. | |
| 17271 | ok = true; | |
| 17272 | } else { | |
| 17273 | IrInstGen *rem_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17274 | op1_val, IrBinOpRemRem, op2_val); | |
| 17275 | if (type_is_invalid(rem_val->value->type)) | |
| 17276 | return ira->codegen->invalid_inst_gen; | |
| 17277 | ||
| 17278 | IrInstGen *mod_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, | |
| 17279 | op1_val, IrBinOpRemMod, op2_val); | |
| 17280 | if (type_is_invalid(mod_val->value->type)) | |
| 17281 | return ira->codegen->invalid_inst_gen; | |
| 17282 | ||
| 17283 | IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, | |
| 17284 | rem_val, mod_val, IrBinOpCmpEq); | |
| 17285 | if (type_is_invalid(cmp_val->value->type)) | |
| 17286 | return ira->codegen->invalid_inst_gen; | |
| 17287 | ||
| 17288 | // We can "upgrade" the operator only if mod(a,b) == rem(a,b) | |
| 17289 | if (!ir_resolve_bool(ira, cmp_val, &ok)) | |
| 17290 | return ira->codegen->invalid_inst_gen; | |
| 17291 | } | |
| 17292 | ||
| 17293 | if (!ok) { | |
| 17294 | ir_add_error(ira, &instruction->base.base, | |
| 17295 | buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", | |
| 17296 | buf_ptr(&op1->value->type->name), | |
| 17297 | buf_ptr(&op2->value->type->name))); | |
| 17298 | return ira->codegen->invalid_inst_gen; | |
| 17299 | } | |
| 17300 | } | |
| 17301 | } | |
| 17302 | ||
| 17254 | 17303 | return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val); |
| 17255 | 17304 | } |
| 17256 | 17305 | |
| 17306 | const bool is_signed_div = | |
| 17307 | (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || | |
| 17308 | scalar_type->id == ZigTypeIdFloat; | |
| 17309 | ||
| 17310 | // Warn the user to use the proper operators here | |
| 17311 | if (op_id == IrBinOpDivUnspecified && is_int) { | |
| 17312 | op_id = IrBinOpDivTrunc; | |
| 17313 | ||
| 17314 | if (is_signed_div) { | |
| 17315 | ir_add_error(ira, &instruction->base.base, | |
| 17316 | buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", | |
| 17317 | buf_ptr(&op1->value->type->name), | |
| 17318 | buf_ptr(&op2->value->type->name))); | |
| 17319 | return ira->codegen->invalid_inst_gen; | |
| 17320 | } | |
| 17321 | } else if (op_id == IrBinOpRemUnspecified) { | |
| 17322 | op_id = IrBinOpRemRem; | |
| 17323 | ||
| 17324 | if (is_signed_div) { | |
| 17325 | ir_add_error(ira, &instruction->base.base, | |
| 17326 | buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", | |
| 17327 | buf_ptr(&op1->value->type->name), | |
| 17328 | buf_ptr(&op2->value->type->name))); | |
| 17329 | return ira->codegen->invalid_inst_gen; | |
| 17330 | } | |
| 17331 | } | |
| 17332 | ||
| 17257 | 17333 | return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, |
| 17258 | 17334 | op_id, casted_op1, casted_op2, instruction->safety_check_on); |
| 17259 | 17335 | } |
| ... | ... | @@ -20337,24 +20413,45 @@ static IrInstGen *ir_analyze_bin_not(IrAnalyze *ira, IrInstSrcUnOp *instruction) |
| 20337 | 20413 | if (type_is_invalid(expr_type)) |
| 20338 | 20414 | return ira->codegen->invalid_inst_gen; |
| 20339 | 20415 | |
| 20340 | if (expr_type->id == ZigTypeIdInt) { | |
| 20341 | if (instr_is_comptime(value)) { | |
| 20342 | ZigValue *target_const_val = ir_resolve_const(ira, value, UndefBad); | |
| 20343 | if (target_const_val == nullptr) | |
| 20344 | return ira->codegen->invalid_inst_gen; | |
| 20416 | ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? | |
| 20417 | expr_type->data.vector.elem_type : expr_type; | |
| 20345 | 20418 | |
| 20346 | IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); | |
| 20347 | bigint_not(&result->value->data.x_bigint, &target_const_val->data.x_bigint, | |
| 20348 | expr_type->data.integral.bit_count, expr_type->data.integral.is_signed); | |
| 20349 | return result; | |
| 20419 | if (scalar_type->id != ZigTypeIdInt) { | |
| 20420 | ir_add_error(ira, &instruction->base.base, | |
| 20421 | buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); | |
| 20422 | return ira->codegen->invalid_inst_gen; | |
| 20423 | } | |
| 20424 | ||
| 20425 | if (instr_is_comptime(value)) { | |
| 20426 | ZigValue *expr_val = ir_resolve_const(ira, value, UndefBad); | |
| 20427 | if (expr_val == nullptr) | |
| 20428 | return ira->codegen->invalid_inst_gen; | |
| 20429 | ||
| 20430 | IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); | |
| 20431 | ||
| 20432 | if (expr_type->id == ZigTypeIdVector) { | |
| 20433 | expand_undef_array(ira->codegen, expr_val); | |
| 20434 | result->value->special = ConstValSpecialUndef; | |
| 20435 | expand_undef_array(ira->codegen, result->value); | |
| 20436 | ||
| 20437 | for (size_t i = 0; i < expr_type->data.vector.len; i++) { | |
| 20438 | ZigValue *src_val = &expr_val->data.x_array.data.s_none.elements[i]; | |
| 20439 | ZigValue *dst_val = &result->value->data.x_array.data.s_none.elements[i]; | |
| 20440 | ||
| 20441 | dst_val->type = scalar_type; | |
| 20442 | dst_val->special = ConstValSpecialStatic; | |
| 20443 | bigint_not(&dst_val->data.x_bigint, &src_val->data.x_bigint, | |
| 20444 | scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); | |
| 20445 | } | |
| 20446 | } else { | |
| 20447 | bigint_not(&result->value->data.x_bigint, &expr_val->data.x_bigint, | |
| 20448 | scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); | |
| 20350 | 20449 | } |
| 20351 | 20450 | |
| 20352 | return ir_build_binary_not(ira, &instruction->base.base, value, expr_type); | |
| 20451 | return result; | |
| 20353 | 20452 | } |
| 20354 | 20453 | |
| 20355 | ir_add_error(ira, &instruction->base.base, | |
| 20356 | buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); | |
| 20357 | return ira->codegen->invalid_inst_gen; | |
| 20454 | return ir_build_binary_not(ira, &instruction->base.base, value, expr_type); | |
| 20358 | 20455 | } |
| 20359 | 20456 | |
| 20360 | 20457 | static IrInstGen *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstSrcUnOp *instruction) { |
test/runtime_safety.zig+43| ... | ... | @@ -505,6 +505,21 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 505 | 505 | \\} |
| 506 | 506 | ); |
| 507 | 507 | |
| 508 | cases.addRuntimeSafety("signed integer division overflow - vectors", | |
| 509 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 510 | \\ @import("std").os.exit(126); | |
| 511 | \\} | |
| 512 | \\pub fn main() !void { | |
| 513 | \\ var a: @Vector(4, i16) = [_]i16{ 1, 2, -32768, 4 }; | |
| 514 | \\ var b: @Vector(4, i16) = [_]i16{ 1, 2, -1, 4 }; | |
| 515 | \\ const x = div(a, b); | |
| 516 | \\ if (x[2] == 32767) return error.Whatever; | |
| 517 | \\} | |
| 518 | \\fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) { | |
| 519 | \\ return @divTrunc(a, b); | |
| 520 | \\} | |
| 521 | ); | |
| 522 | ||
| 508 | 523 | cases.addRuntimeSafety("signed shift left overflow", |
| 509 | 524 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 510 | 525 | \\ @import("std").os.exit(126); |
| ... | ... | @@ -569,6 +584,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 569 | 584 | \\} |
| 570 | 585 | ); |
| 571 | 586 | |
| 587 | cases.addRuntimeSafety("integer division by zero - vectors", | |
| 588 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 589 | \\ @import("std").os.exit(126); | |
| 590 | \\} | |
| 591 | \\pub fn main() void { | |
| 592 | \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; | |
| 593 | \\ var b: @Vector(4, i32) = [4]i32{111, 0, 333, 444}; | |
| 594 | \\ const x = div0(a, b); | |
| 595 | \\} | |
| 596 | \\fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { | |
| 597 | \\ return @divTrunc(a, b); | |
| 598 | \\} | |
| 599 | ); | |
| 600 | ||
| 572 | 601 | cases.addRuntimeSafety("exact division failure", |
| 573 | 602 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 574 | 603 | \\ @import("std").os.exit(126); |
| ... | ... | @@ -582,6 +611,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 582 | 611 | \\} |
| 583 | 612 | ); |
| 584 | 613 | |
| 614 | cases.addRuntimeSafety("exact division failure - vectors", | |
| 615 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 616 | \\ @import("std").os.exit(126); | |
| 617 | \\} | |
| 618 | \\pub fn main() !void { | |
| 619 | \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; | |
| 620 | \\ var b: @Vector(4, i32) = [4]i32{111, 222, 333, 441}; | |
| 621 | \\ const x = divExact(a, b); | |
| 622 | \\} | |
| 623 | \\fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { | |
| 624 | \\ return @divExact(a, b); | |
| 625 | \\} | |
| 626 | ); | |
| 627 | ||
| 585 | 628 | cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size", |
| 586 | 629 | \\const std = @import("std"); |
| 587 | 630 | \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
test/stage1/behavior/vector.zig+195| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | const math = std.math; | |
| 3 | 4 | const expect = std.testing.expect; |
| 4 | 5 | const expectEqual = std.testing.expectEqual; |
| 5 | 6 | |
| ... | ... | @@ -276,3 +277,197 @@ test "vector comparison operators" { |
| 276 | 277 | S.doTheTest(); |
| 277 | 278 | comptime S.doTheTest(); |
| 278 | 279 | } |
| 280 | ||
| 281 | test "vector division operators" { | |
| 282 | const S = struct { | |
| 283 | fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void { | |
| 284 | if (!comptime std.meta.trait.isSignedInt(T)) { | |
| 285 | const d0 = x / y; | |
| 286 | for (@as([4]T, d0)) |v, i| { | |
| 287 | expectEqual(x[i] / y[i], v); | |
| 288 | } | |
| 289 | } | |
| 290 | const d1 = @divExact(x, y); | |
| 291 | for (@as([4]T, d1)) |v, i| { | |
| 292 | expectEqual(@divExact(x[i], y[i]), v); | |
| 293 | } | |
| 294 | const d2 = @divFloor(x, y); | |
| 295 | for (@as([4]T, d2)) |v, i| { | |
| 296 | expectEqual(@divFloor(x[i], y[i]), v); | |
| 297 | } | |
| 298 | const d3 = @divTrunc(x, y); | |
| 299 | for (@as([4]T, d3)) |v, i| { | |
| 300 | expectEqual(@divTrunc(x[i], y[i]), v); | |
| 301 | } | |
| 302 | } | |
| 303 | ||
| 304 | fn doTheTestMod(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void { | |
| 305 | if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) { | |
| 306 | const r0 = x % y; | |
| 307 | for (@as([4]T, r0)) |v, i| { | |
| 308 | expectEqual(x[i] % y[i], v); | |
| 309 | } | |
| 310 | } | |
| 311 | const r1 = @mod(x, y); | |
| 312 | for (@as([4]T, r1)) |v, i| { | |
| 313 | expectEqual(@mod(x[i], y[i]), v); | |
| 314 | } | |
| 315 | const r2 = @rem(x, y); | |
| 316 | for (@as([4]T, r2)) |v, i| { | |
| 317 | expectEqual(@rem(x[i], y[i]), v); | |
| 318 | } | |
| 319 | } | |
| 320 | ||
| 321 | fn doTheTest() void { | |
| 322 | // https://github.com/ziglang/zig/issues/4952 | |
| 323 | if (std.builtin.os.tag != .windows) { | |
| 324 | doTheTestDiv(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, -1.0, -2.0 }); | |
| 325 | } | |
| 326 | ||
| 327 | doTheTestDiv(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, -1.0, -2.0 }); | |
| 328 | doTheTestDiv(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, -1.0, -2.0 }); | |
| 329 | ||
| 330 | // https://github.com/ziglang/zig/issues/4952 | |
| 331 | if (std.builtin.os.tag != .windows) { | |
| 332 | doTheTestMod(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, 0.5, 3.0 }); | |
| 333 | } | |
| 334 | doTheTestMod(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, 0.5, 3.0 }); | |
| 335 | doTheTestMod(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, 0.5, 3.0 }); | |
| 336 | ||
| 337 | doTheTestDiv(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, -1, -2 }); | |
| 338 | doTheTestDiv(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, -1, -2 }); | |
| 339 | doTheTestDiv(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, -1, -2 }); | |
| 340 | doTheTestDiv(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, -1, -2 }); | |
| 341 | ||
| 342 | doTheTestMod(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, 4, 8 }); | |
| 343 | doTheTestMod(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, 4, 8 }); | |
| 344 | doTheTestMod(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, 4, 8 }); | |
| 345 | doTheTestMod(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, 4, 8 }); | |
| 346 | ||
| 347 | doTheTestDiv(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 }); | |
| 348 | doTheTestDiv(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); | |
| 349 | doTheTestDiv(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); | |
| 350 | doTheTestDiv(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); | |
| 351 | ||
| 352 | doTheTestMod(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 }); | |
| 353 | doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); | |
| 354 | doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); | |
| 355 | doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); | |
| 356 | } | |
| 357 | }; | |
| 358 | ||
| 359 | S.doTheTest(); | |
| 360 | comptime S.doTheTest(); | |
| 361 | } | |
| 362 | ||
| 363 | test "vector bitwise not operator" { | |
| 364 | const S = struct { | |
| 365 | fn doTheTestNot(comptime T: type, x: @Vector(4, T)) void { | |
| 366 | var y = ~x; | |
| 367 | for (@as([4]T, y)) |v, i| { | |
| 368 | expectEqual(~x[i], v); | |
| 369 | } | |
| 370 | } | |
| 371 | fn doTheTest() void { | |
| 372 | doTheTestNot(u8, [_]u8{ 0, 2, 4, 255 }); | |
| 373 | doTheTestNot(u16, [_]u16{ 0, 2, 4, 255 }); | |
| 374 | doTheTestNot(u32, [_]u32{ 0, 2, 4, 255 }); | |
| 375 | doTheTestNot(u64, [_]u64{ 0, 2, 4, 255 }); | |
| 376 | ||
| 377 | doTheTestNot(u8, [_]u8{ 0, 2, 4, 255 }); | |
| 378 | doTheTestNot(u16, [_]u16{ 0, 2, 4, 255 }); | |
| 379 | doTheTestNot(u32, [_]u32{ 0, 2, 4, 255 }); | |
| 380 | doTheTestNot(u64, [_]u64{ 0, 2, 4, 255 }); | |
| 381 | } | |
| 382 | }; | |
| 383 | ||
| 384 | S.doTheTest(); | |
| 385 | comptime S.doTheTest(); | |
| 386 | } | |
| 387 | ||
| 388 | test "vector shift operators" { | |
| 389 | const S = struct { | |
| 390 | fn doTheTestShift(x: var, y: var) void { | |
| 391 | const N = @typeInfo(@TypeOf(x)).Array.len; | |
| 392 | const TX = @typeInfo(@TypeOf(x)).Array.child; | |
| 393 | const TY = @typeInfo(@TypeOf(y)).Array.child; | |
| 394 | ||
| 395 | var xv = @as(@Vector(N, TX), x); | |
| 396 | var yv = @as(@Vector(N, TY), y); | |
| 397 | ||
| 398 | var z0 = xv >> yv; | |
| 399 | for (@as([N]TX, z0)) |v, i| { | |
| 400 | expectEqual(x[i] >> y[i], v); | |
| 401 | } | |
| 402 | var z1 = xv << yv; | |
| 403 | for (@as([N]TX, z1)) |v, i| { | |
| 404 | expectEqual(x[i] << y[i], v); | |
| 405 | } | |
| 406 | } | |
| 407 | fn doTheTestShiftExact(x: var, y: var, dir: enum { Left, Right }) void { | |
| 408 | const N = @typeInfo(@TypeOf(x)).Array.len; | |
| 409 | const TX = @typeInfo(@TypeOf(x)).Array.child; | |
| 410 | const TY = @typeInfo(@TypeOf(y)).Array.child; | |
| 411 | ||
| 412 | var xv = @as(@Vector(N, TX), x); | |
| 413 | var yv = @as(@Vector(N, TY), y); | |
| 414 | ||
| 415 | var z = if (dir == .Left) @shlExact(xv, yv) else @shrExact(xv, yv); | |
| 416 | for (@as([N]TX, z)) |v, i| { | |
| 417 | const check = if (dir == .Left) x[i] << y[i] else x[i] >> y[i]; | |
| 418 | expectEqual(check, v); | |
| 419 | } | |
| 420 | } | |
| 421 | fn doTheTest() void { | |
| 422 | doTheTestShift([_]u8{ 0, 2, 4, math.maxInt(u8) }, [_]u3{ 2, 0, 2, 7 }); | |
| 423 | doTheTestShift([_]u16{ 0, 2, 4, math.maxInt(u16) }, [_]u4{ 2, 0, 2, 15 }); | |
| 424 | doTheTestShift([_]u24{ 0, 2, 4, math.maxInt(u24) }, [_]u5{ 2, 0, 2, 23 }); | |
| 425 | doTheTestShift([_]u32{ 0, 2, 4, math.maxInt(u32) }, [_]u5{ 2, 0, 2, 31 }); | |
| 426 | doTheTestShift([_]u64{ 0xfe, math.maxInt(u64) }, [_]u6{ 0, 63 }); | |
| 427 | ||
| 428 | doTheTestShift([_]i8{ 0, 2, 4, math.maxInt(i8) }, [_]u3{ 2, 0, 2, 7 }); | |
| 429 | doTheTestShift([_]i16{ 0, 2, 4, math.maxInt(i16) }, [_]u4{ 2, 0, 2, 7 }); | |
| 430 | doTheTestShift([_]i24{ 0, 2, 4, math.maxInt(i24) }, [_]u5{ 2, 0, 2, 7 }); | |
| 431 | doTheTestShift([_]i32{ 0, 2, 4, math.maxInt(i32) }, [_]u5{ 2, 0, 2, 7 }); | |
| 432 | doTheTestShift([_]i64{ 0xfe, math.maxInt(i64) }, [_]u6{ 0, 63 }); | |
| 433 | ||
| 434 | doTheTestShiftExact([_]u8{ 0, 1, 1 << 7, math.maxInt(u8) ^ 1 }, [_]u3{ 4, 0, 7, 1 }, .Right); | |
| 435 | doTheTestShiftExact([_]u16{ 0, 1, 1 << 15, math.maxInt(u16) ^ 1 }, [_]u4{ 4, 0, 15, 1 }, .Right); | |
| 436 | doTheTestShiftExact([_]u24{ 0, 1, 1 << 23, math.maxInt(u24) ^ 1 }, [_]u5{ 4, 0, 23, 1 }, .Right); | |
| 437 | doTheTestShiftExact([_]u32{ 0, 1, 1 << 31, math.maxInt(u32) ^ 1 }, [_]u5{ 4, 0, 31, 1 }, .Right); | |
| 438 | doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 63, 0 }, .Right); | |
| 439 | ||
| 440 | doTheTestShiftExact([_]u8{ 0, 1, 1, math.maxInt(u8) ^ (1 << 7) }, [_]u3{ 4, 0, 7, 1 }, .Left); | |
| 441 | doTheTestShiftExact([_]u16{ 0, 1, 1, math.maxInt(u16) ^ (1 << 15) }, [_]u4{ 4, 0, 15, 1 }, .Left); | |
| 442 | doTheTestShiftExact([_]u24{ 0, 1, 1, math.maxInt(u24) ^ (1 << 23) }, [_]u5{ 4, 0, 23, 1 }, .Left); | |
| 443 | doTheTestShiftExact([_]u32{ 0, 1, 1, math.maxInt(u32) ^ (1 << 31) }, [_]u5{ 4, 0, 31, 1 }, .Left); | |
| 444 | doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 0, 63 }, .Left); | |
| 445 | } | |
| 446 | }; | |
| 447 | ||
| 448 | switch (std.builtin.arch) { | |
| 449 | .i386, | |
| 450 | .aarch64, | |
| 451 | .aarch64_be, | |
| 452 | .aarch64_32, | |
| 453 | .arm, | |
| 454 | .armeb, | |
| 455 | .thumb, | |
| 456 | .thumbeb, | |
| 457 | .mips, | |
| 458 | .mipsel, | |
| 459 | .mips64, | |
| 460 | .mips64el, | |
| 461 | .riscv64, | |
| 462 | .sparcv9, | |
| 463 | => { | |
| 464 | // LLVM miscompiles on this architecture | |
| 465 | // https://github.com/ziglang/zig/issues/4951 | |
| 466 | return error.SkipZigTest; | |
| 467 | }, | |
| 468 | else => {}, | |
| 469 | } | |
| 470 | ||
| 471 | S.doTheTest(); | |
| 472 | comptime S.doTheTest(); | |
| 473 | } |