| 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,11 +501,8 @@ pub const Target = struct { |
| 501 | 501 | ||
| 502 | /// Removes the specified feature but not its dependents. | 502 | /// Removes the specified feature but not its dependents. |
| 503 | pub fn removeFeatureSet(set: *Set, other_set: Set) void { | 503 | pub fn removeFeatureSet(set: *Set, other_set: Set) void { |
| 504 | // TODO should be able to use binary not on @Vector type. | 504 | set.ints = @as(@Vector(usize_count, usize), set.ints) & |
| 505 | // https://github.com/ziglang/zig/issues/903 | 505 | ~@as(@Vector(usize_count, usize), other_set.ints); |
| 506 | for (set.ints) |*int, i| { | ||
| 507 | int.* &= ~other_set.ints[i]; | ||
| 508 | } | ||
| 509 | } | 506 | } |
| 510 | 507 | ||
| 511 | pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { | 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,19 +2535,51 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir |
| 2535 | return nullptr; | 2535 | return nullptr; |
| 2536 | } | 2536 | } |
| 2537 | 2537 | ||
| 2538 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, | 2538 | enum class ScalarizePredicate { |
| 2539 | LLVMValueRef val1, LLVMValueRef val2) | 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 | // for unsigned left shifting, we do the lossy shift, then logically shift | 2571 | // for unsigned left shifting, we do the lossy shift, then logically shift |
| 2542 | // right the same number of bits | 2572 | // right the same number of bits |
| 2543 | // if the values don't match, we have an overflow | 2573 | // if the values don't match, we have an overflow |
| 2544 | // for signed left shifting we do the same except arithmetic shift right | 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 | LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); | 2580 | LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); |
| 2549 | LLVMValueRef orig_val; | 2581 | LLVMValueRef orig_val; |
| 2550 | if (type_entry->data.integral.is_signed) { | 2582 | if (scalar_type->data.integral.is_signed) { |
| 2551 | orig_val = LLVMBuildAShr(g->builder, result, val2, ""); | 2583 | orig_val = LLVMBuildAShr(g->builder, result, val2, ""); |
| 2552 | } else { | 2584 | } else { |
| 2553 | orig_val = LLVMBuildLShr(g->builder, result, val2, ""); | 2585 | orig_val = LLVMBuildLShr(g->builder, result, val2, ""); |
| ... | @@ -2556,6 +2588,9 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, | ... | @@ -2556,6 +2588,9 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, |
| 2556 | 2588 | ||
| 2557 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); | 2589 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2558 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); | 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 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2594 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2560 | 2595 | ||
| 2561 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | 2596 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | @@ -2565,13 +2600,16 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, | ... | @@ -2565,13 +2600,16 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, |
| 2565 | return result; | 2600 | return result; |
| 2566 | } | 2601 | } |
| 2567 | 2602 | ||
| 2568 | static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, | 2603 | static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, |
| 2569 | LLVMValueRef val1, LLVMValueRef val2) | 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 | LLVMValueRef result; | 2611 | LLVMValueRef result; |
| 2574 | if (type_entry->data.integral.is_signed) { | 2612 | if (scalar_type->data.integral.is_signed) { |
| 2575 | result = LLVMBuildAShr(g->builder, val1, val2, ""); | 2613 | result = LLVMBuildAShr(g->builder, val1, val2, ""); |
| 2576 | } else { | 2614 | } else { |
| 2577 | result = LLVMBuildLShr(g->builder, val1, val2, ""); | 2615 | result = LLVMBuildLShr(g->builder, val1, val2, ""); |
| ... | @@ -2581,6 +2619,9 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, | ... | @@ -2581,6 +2619,9 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2581 | 2619 | ||
| 2582 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); | 2620 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2583 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); | 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 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2625 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2585 | 2626 | ||
| 2586 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | 2627 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | @@ -2591,12 +2632,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, | ... | @@ -2591,12 +2632,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2591 | } | 2632 | } |
| 2592 | 2633 | ||
| 2593 | static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { | 2634 | static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { |
| 2594 | if ((op == BuiltinFnIdCeil || | 2635 | assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); |
| 2595 | op == BuiltinFnIdFloor) && | ||
| 2596 | type_entry->id == ZigTypeIdInt) | ||
| 2597 | return val; | ||
| 2598 | assert(type_entry->id == ZigTypeIdFloat); | ||
| 2599 | |||
| 2600 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); | 2636 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); |
| 2601 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); | 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,6 +2648,21 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { |
| 2612 | if (bigint->digit_count == 0) { | 2648 | if (bigint->digit_count == 0) { |
| 2613 | return LLVMConstNull(type_ref); | 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 | LLVMValueRef unsigned_val; | 2666 | LLVMValueRef unsigned_val; |
| 2616 | if (bigint->digit_count == 1) { | 2667 | if (bigint->digit_count == 1) { |
| 2617 | unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); | 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,21 +2677,29 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { |
| 2626 | } | 2677 | } |
| 2627 | 2678 | ||
| 2628 | static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math, | 2679 | static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math, |
| 2629 | LLVMValueRef val1, LLVMValueRef val2, | 2680 | LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind) |
| 2630 | ZigType *type_entry, DivKind div_kind) | ||
| 2631 | { | 2681 | { |
| 2682 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | ||
| 2683 | operand_type->data.vector.elem_type : operand_type; | ||
| 2684 | |||
| 2632 | ZigLLVMSetFastMath(g->builder, want_fast_math); | 2685 | ZigLLVMSetFastMath(g->builder, want_fast_math); |
| 2633 | 2686 | ||
| 2634 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry)); | 2687 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); |
| 2635 | if (want_runtime_safety && (want_fast_math || type_entry->id != ZigTypeIdFloat)) { | 2688 | if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) { |
| 2689 | // Safety check: divisor != 0 | ||
| 2636 | LLVMValueRef is_zero_bit; | 2690 | LLVMValueRef is_zero_bit; |
| 2637 | if (type_entry->id == ZigTypeIdInt) { | 2691 | if (scalar_type->id == ZigTypeIdInt) { |
| 2638 | is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); | 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 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); | 2694 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 2641 | } else { | 2695 | } else { |
| 2642 | zig_unreachable(); | 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 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); | 2703 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); |
| 2645 | LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); | 2704 | LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); |
| 2646 | LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); | 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,16 +2709,21 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2650 | 2709 | ||
| 2651 | LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block); | 2710 | LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block); |
| 2652 | 2711 | ||
| 2653 | if (type_entry->id == ZigTypeIdInt && type_entry->data.integral.is_signed) { | 2712 | // Safety check: check for overflow (dividend = minInt and divisor = -1) |
| 2654 | LLVMValueRef neg_1_value = LLVMConstInt(get_llvm_type(g, type_entry), -1, true); | 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 | BigInt int_min_bi = {0}; | 2715 | BigInt int_min_bi = {0}; |
| 2656 | eval_min_max_value_int(g, type_entry, &int_min_bi, false); | 2716 | eval_min_max_value_int(g, scalar_type, &int_min_bi, false); |
| 2657 | LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, type_entry), &int_min_bi); | 2717 | LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi); |
| 2718 | |||
| 2658 | LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); | 2719 | LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); |
| 2659 | LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); | 2720 | LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); |
| 2660 | LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); | 2721 | LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); |
| 2661 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); | 2722 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); |
| 2662 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); | 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 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); | 2727 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); |
| 2664 | 2728 | ||
| 2665 | LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block); | 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,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 | LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); | 2737 | LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); |
| 2674 | switch (div_kind) { | 2738 | switch (div_kind) { |
| 2675 | case DivKindFloat: | 2739 | case DivKindFloat: |
| 2676 | return result; | 2740 | return result; |
| 2677 | case DivKindExact: | 2741 | case DivKindExact: |
| 2678 | if (want_runtime_safety) { | 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 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); | 2746 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2681 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | 2747 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2682 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); | 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 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2752 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2685 | 2753 | ||
| 2686 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | 2754 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | @@ -2695,54 +2763,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2695,54 +2763,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2695 | LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero"); | 2763 | LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero"); |
| 2696 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); | 2764 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); |
| 2697 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); | 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 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); | 2769 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2699 | 2770 | ||
| 2700 | LLVMPositionBuilderAtEnd(g->builder, ltz_block); | 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 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); | 2773 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); |
| 2703 | LLVMBuildBr(g->builder, end_block); | 2774 | LLVMBuildBr(g->builder, end_block); |
| 2704 | 2775 | ||
| 2705 | LLVMPositionBuilderAtEnd(g->builder, gez_block); | 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 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); | 2778 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); |
| 2708 | LLVMBuildBr(g->builder, end_block); | 2779 | LLVMBuildBr(g->builder, end_block); |
| 2709 | 2780 | ||
| 2710 | LLVMPositionBuilderAtEnd(g->builder, end_block); | 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 | LLVMValueRef incoming_values[] = { ceiled, floored }; | 2783 | LLVMValueRef incoming_values[] = { ceiled, floored }; |
| 2713 | LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block }; | 2784 | LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block }; |
| 2714 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); | 2785 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); |
| 2715 | return phi; | 2786 | return phi; |
| 2716 | } | 2787 | } |
| 2717 | case DivKindFloor: | 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 | zig_unreachable(); | 2791 | zig_unreachable(); |
| 2721 | } | 2792 | } |
| 2722 | 2793 | ||
| 2723 | assert(type_entry->id == ZigTypeIdInt); | 2794 | assert(scalar_type->id == ZigTypeIdInt); |
| 2724 | 2795 | ||
| 2725 | switch (div_kind) { | 2796 | switch (div_kind) { |
| 2726 | case DivKindFloat: | 2797 | case DivKindFloat: |
| 2727 | zig_unreachable(); | 2798 | zig_unreachable(); |
| 2728 | case DivKindTrunc: | 2799 | case DivKindTrunc: |
| 2729 | if (type_entry->data.integral.is_signed) { | 2800 | if (scalar_type->data.integral.is_signed) { |
| 2730 | return LLVMBuildSDiv(g->builder, val1, val2, ""); | 2801 | return LLVMBuildSDiv(g->builder, val1, val2, ""); |
| 2731 | } else { | 2802 | } else { |
| 2732 | return LLVMBuildUDiv(g->builder, val1, val2, ""); | 2803 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 2733 | } | 2804 | } |
| 2734 | case DivKindExact: | 2805 | case DivKindExact: |
| 2735 | if (want_runtime_safety) { | 2806 | if (want_runtime_safety) { |
| 2807 | // Safety check: a % b == 0 | ||
| 2736 | LLVMValueRef remainder_val; | 2808 | LLVMValueRef remainder_val; |
| 2737 | if (type_entry->data.integral.is_signed) { | 2809 | if (scalar_type->data.integral.is_signed) { |
| 2738 | remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); | 2810 | remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); |
| 2739 | } else { | 2811 | } else { |
| 2740 | remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); | 2812 | remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); |
| 2741 | } | 2813 | } |
| 2742 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | ||
| 2743 | 2814 | ||
| 2744 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); | 2815 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2745 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | 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 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2821 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2747 | 2822 | ||
| 2748 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | 2823 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | @@ -2750,14 +2825,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2750,14 +2825,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2750 | 2825 | ||
| 2751 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | 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 | return LLVMBuildExactSDiv(g->builder, val1, val2, ""); | 2829 | return LLVMBuildExactSDiv(g->builder, val1, val2, ""); |
| 2755 | } else { | 2830 | } else { |
| 2756 | return LLVMBuildExactUDiv(g->builder, val1, val2, ""); | 2831 | return LLVMBuildExactUDiv(g->builder, val1, val2, ""); |
| 2757 | } | 2832 | } |
| 2758 | case DivKindFloor: | 2833 | case DivKindFloor: |
| 2759 | { | 2834 | { |
| 2760 | if (!type_entry->data.integral.is_signed) { | 2835 | if (!scalar_type->data.integral.is_signed) { |
| 2761 | return LLVMBuildUDiv(g->builder, val1, val2, ""); | 2836 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 2762 | } | 2837 | } |
| 2763 | // const d = @divTrunc(a, b); | 2838 | // const d = @divTrunc(a, b); |
| ... | @@ -2784,22 +2859,30 @@ enum RemKind { | ... | @@ -2784,22 +2859,30 @@ enum RemKind { |
| 2784 | }; | 2859 | }; |
| 2785 | 2860 | ||
| 2786 | static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math, | 2861 | static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math, |
| 2787 | LLVMValueRef val1, LLVMValueRef val2, | 2862 | LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind) |
| 2788 | ZigType *type_entry, RemKind rem_kind) | ||
| 2789 | { | 2863 | { |
| 2864 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | ||
| 2865 | operand_type->data.vector.elem_type : operand_type; | ||
| 2866 | |||
| 2790 | ZigLLVMSetFastMath(g->builder, want_fast_math); | 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 | if (want_runtime_safety) { | 2870 | if (want_runtime_safety) { |
| 2871 | // Safety check: divisor != 0 | ||
| 2794 | LLVMValueRef is_zero_bit; | 2872 | LLVMValueRef is_zero_bit; |
| 2795 | if (type_entry->id == ZigTypeIdInt) { | 2873 | if (scalar_type->id == ZigTypeIdInt) { |
| 2796 | LLVMIntPredicate pred = type_entry->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; | 2874 | LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; |
| 2797 | is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, ""); | 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 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); | 2877 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 2800 | } else { | 2878 | } else { |
| 2801 | zig_unreachable(); | 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 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); | 2886 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); |
| 2804 | LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); | 2887 | LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); |
| 2805 | LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block); | 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,7 +2893,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2810 | LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block); | 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 | if (rem_kind == RemKindRem) { | 2897 | if (rem_kind == RemKindRem) { |
| 2815 | return LLVMBuildFRem(g->builder, val1, val2, ""); | 2898 | return LLVMBuildFRem(g->builder, val1, val2, ""); |
| 2816 | } else { | 2899 | } else { |
| ... | @@ -2821,8 +2904,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2821,8 +2904,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2821 | return LLVMBuildSelect(g->builder, ltz, c, a, ""); | 2904 | return LLVMBuildSelect(g->builder, ltz, c, a, ""); |
| 2822 | } | 2905 | } |
| 2823 | } else { | 2906 | } else { |
| 2824 | assert(type_entry->id == ZigTypeIdInt); | 2907 | assert(scalar_type->id == ZigTypeIdInt); |
| 2825 | if (type_entry->data.integral.is_signed) { | 2908 | if (scalar_type->data.integral.is_signed) { |
| 2826 | if (rem_kind == RemKindRem) { | 2909 | if (rem_kind == RemKindRem) { |
| 2827 | return LLVMBuildSRem(g->builder, val1, val2, ""); | 2910 | return LLVMBuildSRem(g->builder, val1, val2, ""); |
| 2828 | } else { | 2911 | } else { |
| ... | @@ -2845,11 +2928,17 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type | ... | @@ -2845,11 +2928,17 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type |
| 2845 | // otherwise the check is useful as the allowed values are limited by the | 2928 | // otherwise the check is useful as the allowed values are limited by the |
| 2846 | // operand type itself | 2929 | // operand type itself |
| 2847 | if (!is_power_of_2(lhs_type->data.integral.bit_count)) { | 2930 | if (!is_power_of_2(lhs_type->data.integral.bit_count)) { |
| 2848 | LLVMValueRef bit_count_value = LLVMConstInt(get_llvm_type(g, rhs_type), | 2931 | BigInt bit_count_bi = {0}; |
| 2849 | lhs_type->data.integral.bit_count, false); | 2932 | bigint_init_unsigned(&bit_count_bi, lhs_type->data.integral.bit_count); |
| 2850 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); | 2933 | LLVMValueRef bit_count_value = bigint_to_llvm_const(get_llvm_type(g, rhs_type), |
| 2934 | &bit_count_bi); | ||
| 2935 | |||
| 2851 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail"); | 2936 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail"); |
| 2852 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); | 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 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); | 2942 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); |
| 2854 | 2943 | ||
| 2855 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | 2944 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | @@ -2966,7 +3055,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, | ... | @@ -2966,7 +3055,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2966 | case IrBinOpBitShiftLeftExact: | 3055 | case IrBinOpBitShiftLeftExact: |
| 2967 | { | 3056 | { |
| 2968 | assert(scalar_type->id == ZigTypeIdInt); | 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 | if (want_runtime_safety) { | 3061 | if (want_runtime_safety) { |
| 2972 | gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); | 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,7 +3066,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2976 | if (is_sloppy) { | 3066 | if (is_sloppy) { |
| 2977 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); | 3067 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); |
| 2978 | } else if (want_runtime_safety) { | 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 | } else if (scalar_type->data.integral.is_signed) { | 3070 | } else if (scalar_type->data.integral.is_signed) { |
| 2981 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); | 3071 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); |
| 2982 | } else { | 3072 | } else { |
| ... | @@ -2987,7 +3077,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, | ... | @@ -2987,7 +3077,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2987 | case IrBinOpBitShiftRightExact: | 3077 | case IrBinOpBitShiftRightExact: |
| 2988 | { | 3078 | { |
| 2989 | assert(scalar_type->id == ZigTypeIdInt); | 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 | if (want_runtime_safety) { | 3083 | if (want_runtime_safety) { |
| 2993 | gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); | 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,7 +3092,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 3001 | return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); | 3092 | return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); |
| 3002 | } | 3093 | } |
| 3003 | } else if (want_runtime_safety) { | 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 | } else if (scalar_type->data.integral.is_signed) { | 3096 | } else if (scalar_type->data.integral.is_signed) { |
| 3006 | return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); | 3097 | return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); |
| 3007 | } else { | 3098 | } else { |
| ... | @@ -3010,22 +3101,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, | ... | @@ -3010,22 +3101,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 3010 | } | 3101 | } |
| 3011 | case IrBinOpDivUnspecified: | 3102 | case IrBinOpDivUnspecified: |
| 3012 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpDivExact: | 3105 | case IrBinOpDivExact: |
| 3015 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpDivTrunc: | 3108 | case IrBinOpDivTrunc: |
| 3018 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpDivFloor: | 3111 | case IrBinOpDivFloor: |
| 3021 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpRemRem: | 3114 | case IrBinOpRemRem: |
| 3024 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | case IrBinOpRemMod: | 3117 | case IrBinOpRemMod: |
| 3027 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 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 | zig_unreachable(); | 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,6 +283,8 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi |
| 283 | IrInstGen *result_loc); | 283 | IrInstGen *result_loc); |
| 284 | static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, | 284 | static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, |
| 285 | IrInstGen *struct_operand, TypeStructField *field); | 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 | static void destroy_instruction_src(IrInstSrc *inst) { | 289 | static void destroy_instruction_src(IrInstSrc *inst) { |
| 288 | switch (inst->id) { | 290 | switch (inst->id) { |
| ... | @@ -16803,7 +16805,6 @@ static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr, | ... | @@ -16803,7 +16805,6 @@ static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr, |
| 16803 | ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; | 16805 | ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; |
| 16804 | ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; | 16806 | ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; |
| 16805 | assert(scalar_op1_val->type == scalar_type); | 16807 | assert(scalar_op1_val->type == scalar_type); |
| 16806 | assert(scalar_op2_val->type == scalar_type); | ||
| 16807 | assert(scalar_out_val->type == scalar_type); | 16808 | assert(scalar_out_val->type == scalar_type); |
| 16808 | ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, | 16809 | ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, |
| 16809 | scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); | 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,27 +16829,49 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16828 | if (type_is_invalid(op1->value->type)) | 16829 | if (type_is_invalid(op1->value->type)) |
| 16829 | return ira->codegen->invalid_inst_gen; | 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 | ir_add_error(ira, &bin_op_instruction->op1->base, | 16840 | ir_add_error(ira, &bin_op_instruction->op1->base, |
| 16833 | buf_sprintf("bit shifting operation expected integer type, found '%s'", | 16841 | buf_sprintf("bit shifting operation expected vector type, found '%s'", |
| 16834 | buf_ptr(&op1->value->type->name))); | 16842 | buf_ptr(&op2_type->name))); |
| 16835 | return ira->codegen->invalid_inst_gen; | 16843 | return ira->codegen->invalid_inst_gen; |
| 16836 | } | 16844 | } |
| 16837 | 16845 | ||
| 16838 | IrInstGen *op2 = bin_op_instruction->op2->child; | 16846 | if (op1_type->id != ZigTypeIdVector && op2_type->id == ZigTypeIdVector) { |
| 16839 | if (type_is_invalid(op2->value->type)) | 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 | return ira->codegen->invalid_inst_gen; | 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 | ir_add_error(ira, &bin_op_instruction->op2->base, | 16866 | ir_add_error(ira, &bin_op_instruction->op2->base, |
| 16844 | buf_sprintf("shift amount has to be an integer type, but found '%s'", | 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 | return ira->codegen->invalid_inst_gen; | 16869 | return ira->codegen->invalid_inst_gen; |
| 16847 | } | 16870 | } |
| 16848 | 16871 | ||
| 16849 | IrInstGen *casted_op2; | 16872 | IrInstGen *casted_op2; |
| 16850 | IrBinOp op_id = bin_op_instruction->op_id; | 16873 | IrBinOp op_id = bin_op_instruction->op_id; |
| 16851 | if (op1->value->type->id == ZigTypeIdComptimeInt) { | 16874 | if (op1_scalar_type->id == ZigTypeIdComptimeInt) { |
| 16852 | // comptime_int has no finite bit width | 16875 | // comptime_int has no finite bit width |
| 16853 | casted_op2 = op2; | 16876 | casted_op2 = op2; |
| 16854 | 16877 | ||
| ... | @@ -16874,10 +16897,15 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in | ... | @@ -16874,10 +16897,15 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16874 | return ira->codegen->invalid_inst_gen; | 16897 | return ira->codegen->invalid_inst_gen; |
| 16875 | } | 16898 | } |
| 16876 | } else { | 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 | ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, | 16901 | ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, |
| 16879 | bit_count > 0 ? bit_count - 1 : 0); | 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 | casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); | 16909 | casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); |
| 16882 | if (type_is_invalid(casted_op2->value->type)) | 16910 | if (type_is_invalid(casted_op2->value->type)) |
| 16883 | return ira->codegen->invalid_inst_gen; | 16911 | return ira->codegen->invalid_inst_gen; |
| ... | @@ -16888,10 +16916,10 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in | ... | @@ -16888,10 +16916,10 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16888 | if (op2_val == nullptr) | 16916 | if (op2_val == nullptr) |
| 16889 | return ira->codegen->invalid_inst_gen; | 16917 | return ira->codegen->invalid_inst_gen; |
| 16890 | 16918 | ||
| 16891 | BigInt bit_count_value = {0}; | 16919 | ZigValue bit_count_value; |
| 16892 | bigint_init_unsigned(&bit_count_value, bit_count); | 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 | ErrorMsg* msg = ir_add_error(ira, | 16923 | ErrorMsg* msg = ir_add_error(ira, |
| 16896 | &bin_op_instruction->base.base, | 16924 | &bin_op_instruction->base.base, |
| 16897 | buf_sprintf("RHS of shift is too large for LHS type")); | 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,7 +16938,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16910 | if (op2_val == nullptr) | 16938 | if (op2_val == nullptr) |
| 16911 | return ira->codegen->invalid_inst_gen; | 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 | return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1); | 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,7 +16951,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16923 | if (op2_val == nullptr) | 16951 | if (op2_val == nullptr) |
| 16924 | return ira->codegen->invalid_inst_gen; | 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 | return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, | 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,6 +16971,7 @@ static bool ok_float_op(IrBinOp op) { |
| 16943 | case IrBinOpDivExact: | 16971 | case IrBinOpDivExact: |
| 16944 | case IrBinOpRemRem: | 16972 | case IrBinOpRemRem: |
| 16945 | case IrBinOpRemMod: | 16973 | case IrBinOpRemMod: |
| 16974 | case IrBinOpRemUnspecified: | ||
| 16946 | return true; | 16975 | return true; |
| 16947 | 16976 | ||
| 16948 | case IrBinOpBoolOr: | 16977 | case IrBinOpBoolOr: |
| ... | @@ -16963,7 +16992,6 @@ static bool ok_float_op(IrBinOp op) { | ... | @@ -16963,7 +16992,6 @@ static bool ok_float_op(IrBinOp op) { |
| 16963 | case IrBinOpAddWrap: | 16992 | case IrBinOpAddWrap: |
| 16964 | case IrBinOpSubWrap: | 16993 | case IrBinOpSubWrap: |
| 16965 | case IrBinOpMultWrap: | 16994 | case IrBinOpMultWrap: |
| 16966 | case IrBinOpRemUnspecified: | ||
| 16967 | case IrBinOpArrayCat: | 16995 | case IrBinOpArrayCat: |
| 16968 | case IrBinOpArrayMult: | 16996 | case IrBinOpArrayMult: |
| 16969 | return false; | 16997 | return false; |
| ... | @@ -16991,6 +17019,53 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { | ... | @@ -16991,6 +17019,53 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { |
| 16991 | zig_unreachable(); | 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 | static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { | 17069 | static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { |
| 16995 | Error err; | 17070 | Error err; |
| 16996 | 17071 | ||
| ... | @@ -17096,127 +17171,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc | ... | @@ -17096,127 +17171,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17096 | if (type_is_invalid(resolved_type)) | 17171 | if (type_is_invalid(resolved_type)) |
| 17097 | return ira->codegen->invalid_inst_gen; | 17172 | return ira->codegen->invalid_inst_gen; |
| 17098 | 17173 | ||
| 17099 | bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt; | 17174 | ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ? |
| 17100 | bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat; | 17175 | resolved_type->data.vector.elem_type : resolved_type; |
| 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; | ||
| 17156 | 17176 | ||
| 17157 | if (is_int) { | 17177 | bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt; |
| 17158 | ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); | 17178 | bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat; |
| 17159 | if (op2_val == nullptr) | ||
| 17160 | return ira->codegen->invalid_inst_gen; | ||
| 17161 | 17179 | ||
| 17162 | if (bigint_cmp_zero(&op2->value->data.x_bigint) == CmpEQ) { | 17180 | if (!is_int && !(is_float && ok_float_op(op_id))) { |
| 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) { | ||
| 17220 | AstNode *source_node = instruction->base.base.source_node; | 17181 | AstNode *source_node = instruction->base.base.source_node; |
| 17221 | ir_add_error_node(ira, source_node, | 17182 | ir_add_error_node(ira, source_node, |
| 17222 | buf_sprintf("invalid operands to binary expression: '%s' and '%s'", | 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,7 +17186,16 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc |
| 17225 | return ira->codegen->invalid_inst_gen; | 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 | if (op_id == IrBinOpAddWrap) { | 17199 | if (op_id == IrBinOpAddWrap) { |
| 17230 | op_id = IrBinOpAdd; | 17200 | op_id = IrBinOpAdd; |
| 17231 | } else if (op_id == IrBinOpSubWrap) { | 17201 | } else if (op_id == IrBinOpSubWrap) { |
| ... | @@ -17235,25 +17205,131 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc | ... | @@ -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 | if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { | 17208 | if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { |
| 17247 | ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); | 17209 | ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); |
| 17248 | if (op1_val == nullptr) | 17210 | if (op1_val == nullptr) |
| 17249 | return ira->codegen->invalid_inst_gen; | 17211 | return ira->codegen->invalid_inst_gen; |
| 17212 | |||
| 17250 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); | 17213 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); |
| 17251 | if (op2_val == nullptr) | 17214 | if (op2_val == nullptr) |
| 17252 | return ira->codegen->invalid_inst_gen; | 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 | return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val); | 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 | return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, | 17333 | return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, |
| 17258 | op_id, casted_op1, casted_op2, instruction->safety_check_on); | 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,24 +20413,45 @@ static IrInstGen *ir_analyze_bin_not(IrAnalyze *ira, IrInstSrcUnOp *instruction) |
| 20337 | if (type_is_invalid(expr_type)) | 20413 | if (type_is_invalid(expr_type)) |
| 20338 | return ira->codegen->invalid_inst_gen; | 20414 | return ira->codegen->invalid_inst_gen; |
| 20339 | 20415 | ||
| 20340 | if (expr_type->id == ZigTypeIdInt) { | 20416 | ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? |
| 20341 | if (instr_is_comptime(value)) { | 20417 | expr_type->data.vector.elem_type : expr_type; |
| 20342 | ZigValue *target_const_val = ir_resolve_const(ira, value, UndefBad); | ||
| 20343 | if (target_const_val == nullptr) | ||
| 20344 | return ira->codegen->invalid_inst_gen; | ||
| 20345 | 20418 | ||
| 20346 | IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); | 20419 | if (scalar_type->id != ZigTypeIdInt) { |
| 20347 | bigint_not(&result->value->data.x_bigint, &target_const_val->data.x_bigint, | 20420 | ir_add_error(ira, &instruction->base.base, |
| 20348 | expr_type->data.integral.bit_count, expr_type->data.integral.is_signed); | 20421 | buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); |
| 20349 | return result; | 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, | 20454 | return ir_build_binary_not(ira, &instruction->base.base, value, expr_type); |
| 20356 | buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); | ||
| 20357 | return ira->codegen->invalid_inst_gen; | ||
| 20358 | } | 20455 | } |
| 20359 | 20456 | ||
| 20360 | static IrInstGen *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstSrcUnOp *instruction) { | 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,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 | cases.addRuntimeSafety("signed shift left overflow", | 523 | cases.addRuntimeSafety("signed shift left overflow", |
| 509 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | 524 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 510 | \\ @import("std").os.exit(126); | 525 | \\ @import("std").os.exit(126); |
| ... | @@ -569,6 +584,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -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 | cases.addRuntimeSafety("exact division failure", | 601 | cases.addRuntimeSafety("exact division failure", |
| 573 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | 602 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 574 | \\ @import("std").os.exit(126); | 603 | \\ @import("std").os.exit(126); |
| ... | @@ -582,6 +611,20 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -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 | cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size", | 628 | cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size", |
| 586 | \\const std = @import("std"); | 629 | \\const std = @import("std"); |
| 587 | \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 630 | \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
test/stage1/behavior/vector.zig+195| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | const math = std.math; | ||
| 3 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
| 4 | const expectEqual = std.testing.expectEqual; | 5 | const expectEqual = std.testing.expectEqual; |
| 5 | 6 | ||
| ... | @@ -276,3 +277,197 @@ test "vector comparison operators" { | ... | @@ -276,3 +277,197 @@ test "vector comparison operators" { |
| 276 | S.doTheTest(); | 277 | S.doTheTest(); |
| 277 | comptime S.doTheTest(); | 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 | } |