authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-05 18:34:47-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-05 18:34:47-04:00
log05b587fcdee91a7c9f170da4a186a512b51b39a8
tree638c7444a2f73fc304b662bf50329ccc18d2c86c
parente2dc63644ab3d8e5cdaec2d58dc57c587295081f
parente84b9b70ff2814d6e50a851dc9f094b15399d2fe
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'LemonBoy-vec-div'

closes #4737

5 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 {
501501
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/903505 ~@as(@Vector(usize_count, usize), other_set.ints);
506 for (set.ints) |*int, i| {
507 int.* &= ~other_set.ints[i];
508 }
509 }506 }
510507
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}
25372537
2538static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry,2538enum 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
2548static 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
2568static 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 shift2571 // for unsigned left shifting, we do the lossy shift, then logically shift
2542 // right the same number of bits2572 // right the same number of bits
2543 // if the values don't match, we have an overflow2573 // if the values don't match, we have an overflow
2544 // for signed left shifting we do the same except arithmetic shift right2574 // 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;
25452577
2546 assert(type_entry->id == ZigTypeIdInt);2578 assert(scalar_type->id == ZigTypeIdInt);
25472579
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,
25562588
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);
25602595
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}
25672602
2568static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,2603static 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);
25722610
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,
25812619
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);
25852626
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}
25922633
2593static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {2634static 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}
26272678
2628static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math,2679static 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);
26332686
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
26502709
2651 LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block);2710 LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block);
26522711
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);
26642728
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 }
26712735
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, "");
26832749 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);
26852753
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);
26992770
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);
27042775
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);
27092780
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 }
27222793
2723 assert(type_entry->id == ZigTypeIdInt);2794 assert(scalar_type->id == ZigTypeIdInt);
27242795
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, "");
27432814
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);
27472822
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
27502825
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};
27852860
2786static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math,2861static 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);
27912868
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 }
28122895
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 the2928 // otherwise the check is useful as the allowed values are limited by the
2846 // operand type itself2929 // 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);
28542943
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), "");
29703060
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), "");
29913082
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);
284static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr,284static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr,
285 IrInstGen *struct_operand, TypeStructField *field);285 IrInstGen *struct_operand, TypeStructField *field);
286static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);
287static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);
286288
287static void destroy_instruction_src(IrInstSrc *inst) {289static 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;
1683016831
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 }
1683716845
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 }
1684116864
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 }
1684816871
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 width16875 // comptime_int has no finite bit width
16853 casted_op2 = op2;16876 casted_op2 = op2;
1685416877
...@@ -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);
1688016903
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;
1689016918
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);
1689316921
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;
1691216940
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 }
1691616944
...@@ -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;
1692516953
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 }
1692816956
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;
1694716976
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}
1699317021
17022static 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
17061static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right) {
17062 return value_cmp_numeric_val(left, predicate, right, true);
17063}
17064
17065static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right) {
17066 return value_cmp_numeric_val(left, predicate, right, false);
17067}
17068
16994static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) {17069static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) {
16995 Error err;17070 Error err;
1699617071
...@@ -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;
1709817173
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;
1715617176
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;
1716117179
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 }
1722717188
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 }
1723717207
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;
1725317216
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 }
1725617305
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;
2033920415
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;
2034520418
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 }
2035120450
20352 return ir_build_binary_not(ira, &instruction->base.base, value, expr_type);20451 return result;
20353 }20452 }
2035420453
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}
2035920456
20360static IrInstGen *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstSrcUnOp *instruction) {20457static 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 );
507507
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 );
571586
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 );
584613
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 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const math = std.math;
3const expect = std.testing.expect;4const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;5const expectEqual = std.testing.expectEqual;
56
...@@ -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
281test "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
363test "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
388test "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}