| author | |
| committer | |
| log | 22b5e47839cf34c1e4a7c5e6dc256e041b4bf8fc |
| tree | a940511e3d881231d60276f824a6a9055877dc83 |
| parent | 7c5a24e08cd0bffd2a5cce6d1fd592a7d2bee678 |
The builtin folds a Vector(N,T) into a scalar T using a specified
operator.
Closes #26988 files changed, 430 insertions(+), 39 deletions(-)
lib/std/builtin.zig+10| ... | @@ -98,6 +98,16 @@ pub const AtomicOrder = enum { | ... | @@ -98,6 +98,16 @@ pub const AtomicOrder = enum { |
| 98 | SeqCst, | 98 | SeqCst, |
| 99 | }; | 99 | }; |
| 100 | 100 | ||
| 101 | /// This data structure is used by the Zig language code generation and | ||
| 102 | /// therefore must be kept in sync with the compiler implementation. | ||
| 103 | pub const ReduceOp = enum { | ||
| 104 | And, | ||
| 105 | Or, | ||
| 106 | Xor, | ||
| 107 | Min, | ||
| 108 | Max, | ||
| 109 | }; | ||
| 110 | |||
| 101 | /// This data structure is used by the Zig language code generation and | 111 | /// This data structure is used by the Zig language code generation and |
| 102 | /// therefore must be kept in sync with the compiler implementation. | 112 | /// therefore must be kept in sync with the compiler implementation. |
| 103 | pub const AtomicRmwOp = enum { | 113 | pub const AtomicRmwOp = enum { |
src/stage1/all_types.hpp+26| ... | @@ -1821,6 +1821,7 @@ enum BuiltinFnId { | ... | @@ -1821,6 +1821,7 @@ enum BuiltinFnId { |
| 1821 | BuiltinFnIdWasmMemorySize, | 1821 | BuiltinFnIdWasmMemorySize, |
| 1822 | BuiltinFnIdWasmMemoryGrow, | 1822 | BuiltinFnIdWasmMemoryGrow, |
| 1823 | BuiltinFnIdSrc, | 1823 | BuiltinFnIdSrc, |
| 1824 | BuiltinFnIdReduce, | ||
| 1824 | }; | 1825 | }; |
| 1825 | 1826 | ||
| 1826 | struct BuiltinFnEntry { | 1827 | struct BuiltinFnEntry { |
| ... | @@ -2436,6 +2437,15 @@ enum AtomicOrder { | ... | @@ -2436,6 +2437,15 @@ enum AtomicOrder { |
| 2436 | AtomicOrderSeqCst, | 2437 | AtomicOrderSeqCst, |
| 2437 | }; | 2438 | }; |
| 2438 | 2439 | ||
| 2440 | // synchronized with code in define_builtin_compile_vars | ||
| 2441 | enum ReduceOp { | ||
| 2442 | ReduceOp_and, | ||
| 2443 | ReduceOp_or, | ||
| 2444 | ReduceOp_xor, | ||
| 2445 | ReduceOp_min, | ||
| 2446 | ReduceOp_max, | ||
| 2447 | }; | ||
| 2448 | |||
| 2439 | // synchronized with the code in define_builtin_compile_vars | 2449 | // synchronized with the code in define_builtin_compile_vars |
| 2440 | enum AtomicRmwOp { | 2450 | enum AtomicRmwOp { |
| 2441 | AtomicRmwOp_xchg, | 2451 | AtomicRmwOp_xchg, |
| ... | @@ -2545,6 +2555,7 @@ enum IrInstSrcId { | ... | @@ -2545,6 +2555,7 @@ enum IrInstSrcId { |
| 2545 | IrInstSrcIdEmbedFile, | 2555 | IrInstSrcIdEmbedFile, |
| 2546 | IrInstSrcIdCmpxchg, | 2556 | IrInstSrcIdCmpxchg, |
| 2547 | IrInstSrcIdFence, | 2557 | IrInstSrcIdFence, |
| 2558 | IrInstSrcIdReduce, | ||
| 2548 | IrInstSrcIdTruncate, | 2559 | IrInstSrcIdTruncate, |
| 2549 | IrInstSrcIdIntCast, | 2560 | IrInstSrcIdIntCast, |
| 2550 | IrInstSrcIdFloatCast, | 2561 | IrInstSrcIdFloatCast, |
| ... | @@ -2667,6 +2678,7 @@ enum IrInstGenId { | ... | @@ -2667,6 +2678,7 @@ enum IrInstGenId { |
| 2667 | IrInstGenIdErrName, | 2678 | IrInstGenIdErrName, |
| 2668 | IrInstGenIdCmpxchg, | 2679 | IrInstGenIdCmpxchg, |
| 2669 | IrInstGenIdFence, | 2680 | IrInstGenIdFence, |
| 2681 | IrInstGenIdReduce, | ||
| 2670 | IrInstGenIdTruncate, | 2682 | IrInstGenIdTruncate, |
| 2671 | IrInstGenIdShuffleVector, | 2683 | IrInstGenIdShuffleVector, |
| 2672 | IrInstGenIdSplat, | 2684 | IrInstGenIdSplat, |
| ... | @@ -3516,6 +3528,20 @@ struct IrInstGenFence { | ... | @@ -3516,6 +3528,20 @@ struct IrInstGenFence { |
| 3516 | AtomicOrder order; | 3528 | AtomicOrder order; |
| 3517 | }; | 3529 | }; |
| 3518 | 3530 | ||
| 3531 | struct IrInstSrcReduce { | ||
| 3532 | IrInstSrc base; | ||
| 3533 | |||
| 3534 | IrInstSrc *op; | ||
| 3535 | IrInstSrc *value; | ||
| 3536 | }; | ||
| 3537 | |||
| 3538 | struct IrInstGenReduce { | ||
| 3539 | IrInstGen base; | ||
| 3540 | |||
| 3541 | ReduceOp op; | ||
| 3542 | IrInstGen *value; | ||
| 3543 | }; | ||
| 3544 | |||
| 3519 | struct IrInstSrcTruncate { | 3545 | struct IrInstSrcTruncate { |
| 3520 | IrInstSrc base; | 3546 | IrInstSrc base; |
| 3521 | 3547 |
src/stage1/codegen.cpp+56-39| ... | @@ -2583,36 +2583,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir | ... | @@ -2583,36 +2583,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir |
| 2583 | return nullptr; | 2583 | return nullptr; |
| 2584 | } | 2584 | } |
| 2585 | 2585 | ||
| 2586 | enum class ScalarizePredicate { | ||
| 2587 | // Returns true iff all the elements in the vector are 1. | ||
| 2588 | // Equivalent to folding all the bits with `and`. | ||
| 2589 | All, | ||
| 2590 | // Returns true iff there's at least one element in the vector that is 1. | ||
| 2591 | // Equivalent to folding all the bits with `or`. | ||
| 2592 | Any, | ||
| 2593 | }; | ||
| 2594 | |||
| 2595 | // Collapses a <N x i1> vector into a single i1 according to the given predicate | ||
| 2596 | static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) { | ||
| 2597 | assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); | ||
| 2598 | LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); | ||
| 2599 | LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); | ||
| 2600 | |||
| 2601 | switch (predicate) { | ||
| 2602 | case ScalarizePredicate::Any: { | ||
| 2603 | LLVMValueRef all_zeros = LLVMConstNull(scalar_type); | ||
| 2604 | return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, ""); | ||
| 2605 | } | ||
| 2606 | case ScalarizePredicate::All: { | ||
| 2607 | LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); | ||
| 2608 | return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); | ||
| 2609 | } | ||
| 2610 | } | ||
| 2611 | |||
| 2612 | zig_unreachable(); | ||
| 2613 | } | ||
| 2614 | |||
| 2615 | |||
| 2616 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, | 2586 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, |
| 2617 | LLVMValueRef val1, LLVMValueRef val2) | 2587 | LLVMValueRef val1, LLVMValueRef val2) |
| 2618 | { | 2588 | { |
| ... | @@ -2637,7 +2607,7 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, | ... | @@ -2637,7 +2607,7 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, |
| 2637 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); | 2607 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2638 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); | 2608 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 2639 | if (operand_type->id == ZigTypeIdVector) { | 2609 | if (operand_type->id == ZigTypeIdVector) { |
| 2640 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2610 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2641 | } | 2611 | } |
| 2642 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2612 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2643 | 2613 | ||
| ... | @@ -2668,7 +2638,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, | ... | @@ -2668,7 +2638,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, |
| 2668 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); | 2638 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2669 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); | 2639 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 2670 | if (operand_type->id == ZigTypeIdVector) { | 2640 | if (operand_type->id == ZigTypeIdVector) { |
| 2671 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2641 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2672 | } | 2642 | } |
| 2673 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2643 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2674 | 2644 | ||
| ... | @@ -2745,7 +2715,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2745,7 +2715,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2745 | } | 2715 | } |
| 2746 | 2716 | ||
| 2747 | if (operand_type->id == ZigTypeIdVector) { | 2717 | if (operand_type->id == ZigTypeIdVector) { |
| 2748 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); | 2718 | is_zero_bit = ZigLLVMBuildOrReduce(g->builder, is_zero_bit); |
| 2749 | } | 2719 | } |
| 2750 | 2720 | ||
| 2751 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); | 2721 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); |
| ... | @@ -2770,7 +2740,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2770,7 +2740,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2770 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); | 2740 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); |
| 2771 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); | 2741 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); |
| 2772 | if (operand_type->id == ZigTypeIdVector) { | 2742 | if (operand_type->id == ZigTypeIdVector) { |
| 2773 | overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any); | 2743 | overflow_fail_bit = ZigLLVMBuildOrReduce(g->builder, overflow_fail_bit); |
| 2774 | } | 2744 | } |
| 2775 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); | 2745 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); |
| 2776 | 2746 | ||
| ... | @@ -2795,7 +2765,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2795,7 +2765,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2795 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | 2765 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2796 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); | 2766 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); |
| 2797 | if (operand_type->id == ZigTypeIdVector) { | 2767 | if (operand_type->id == ZigTypeIdVector) { |
| 2798 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2768 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2799 | } | 2769 | } |
| 2800 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2770 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2801 | 2771 | ||
| ... | @@ -2812,7 +2782,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2812,7 +2782,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2812 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); | 2782 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); |
| 2813 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); | 2783 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); |
| 2814 | if (operand_type->id == ZigTypeIdVector) { | 2784 | if (operand_type->id == ZigTypeIdVector) { |
| 2815 | ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any); | 2785 | ltz = ZigLLVMBuildOrReduce(g->builder, ltz); |
| 2816 | } | 2786 | } |
| 2817 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); | 2787 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2818 | 2788 | ||
| ... | @@ -2864,7 +2834,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2864,7 +2834,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2864 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | 2834 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2865 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | 2835 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); |
| 2866 | if (operand_type->id == ZigTypeIdVector) { | 2836 | if (operand_type->id == ZigTypeIdVector) { |
| 2867 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2837 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2868 | } | 2838 | } |
| 2869 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2839 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2870 | 2840 | ||
| ... | @@ -2928,7 +2898,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2928,7 +2898,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2928 | } | 2898 | } |
| 2929 | 2899 | ||
| 2930 | if (operand_type->id == ZigTypeIdVector) { | 2900 | if (operand_type->id == ZigTypeIdVector) { |
| 2931 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); | 2901 | is_zero_bit = ZigLLVMBuildOrReduce(g->builder, is_zero_bit); |
| 2932 | } | 2902 | } |
| 2933 | 2903 | ||
| 2934 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); | 2904 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); |
| ... | @@ -2985,7 +2955,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type | ... | @@ -2985,7 +2955,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type |
| 2985 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); | 2955 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); |
| 2986 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); | 2956 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); |
| 2987 | if (rhs_type->id == ZigTypeIdVector) { | 2957 | if (rhs_type->id == ZigTypeIdVector) { |
| 2988 | less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any); | 2958 | less_than_bit = ZigLLVMBuildOrReduce(g->builder, less_than_bit); |
| 2989 | } | 2959 | } |
| 2990 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); | 2960 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); |
| 2991 | 2961 | ||
| ... | @@ -5470,6 +5440,50 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutableGen *executable, I | ... | @@ -5470,6 +5440,50 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutableGen *executable, I |
| 5470 | return result_loc; | 5440 | return result_loc; |
| 5471 | } | 5441 | } |
| 5472 | 5442 | ||
| 5443 | static LLVMValueRef ir_render_reduce(CodeGen *g, IrExecutableGen *executable, IrInstGenReduce *instruction) { | ||
| 5444 | LLVMValueRef value = ir_llvm_value(g, instruction->value); | ||
| 5445 | |||
| 5446 | ZigType *value_type = instruction->value->value->type; | ||
| 5447 | assert(value_type->id == ZigTypeIdVector); | ||
| 5448 | ZigType *scalar_type = value_type->data.vector.elem_type; | ||
| 5449 | |||
| 5450 | LLVMValueRef result_val; | ||
| 5451 | switch (instruction->op) { | ||
| 5452 | case ReduceOp_and: | ||
| 5453 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); | ||
| 5454 | result_val = ZigLLVMBuildAndReduce(g->builder, value); | ||
| 5455 | break; | ||
| 5456 | case ReduceOp_or: | ||
| 5457 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); | ||
| 5458 | result_val = ZigLLVMBuildOrReduce(g->builder, value); | ||
| 5459 | break; | ||
| 5460 | case ReduceOp_xor: | ||
| 5461 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); | ||
| 5462 | result_val = ZigLLVMBuildXorReduce(g->builder, value); | ||
| 5463 | break; | ||
| 5464 | case ReduceOp_min: { | ||
| 5465 | if (scalar_type->id == ZigTypeIdInt) { | ||
| 5466 | const bool is_signed = scalar_type->data.integral.is_signed; | ||
| 5467 | result_val = ZigLLVMBuildIntMinReduce(g->builder, value, is_signed); | ||
| 5468 | } else if (scalar_type->id == ZigTypeIdFloat) { | ||
| 5469 | result_val = ZigLLVMBuildFPMinReduce(g->builder, value); | ||
| 5470 | } else zig_unreachable(); | ||
| 5471 | } break; | ||
| 5472 | case ReduceOp_max: { | ||
| 5473 | if (scalar_type->id == ZigTypeIdInt) { | ||
| 5474 | const bool is_signed = scalar_type->data.integral.is_signed; | ||
| 5475 | result_val = ZigLLVMBuildIntMaxReduce(g->builder, value, is_signed); | ||
| 5476 | } else if (scalar_type->id == ZigTypeIdFloat) { | ||
| 5477 | result_val = ZigLLVMBuildFPMaxReduce(g->builder, value); | ||
| 5478 | } else zig_unreachable(); | ||
| 5479 | } break; | ||
| 5480 | default: | ||
| 5481 | zig_unreachable(); | ||
| 5482 | } | ||
| 5483 | |||
| 5484 | return result_val; | ||
| 5485 | } | ||
| 5486 | |||
| 5473 | static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) { | 5487 | static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) { |
| 5474 | LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order); | 5488 | LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order); |
| 5475 | LLVMBuildFence(g->builder, atomic_order, false, ""); | 5489 | LLVMBuildFence(g->builder, atomic_order, false, ""); |
| ... | @@ -6674,6 +6688,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl | ... | @@ -6674,6 +6688,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl |
| 6674 | return ir_render_cmpxchg(g, executable, (IrInstGenCmpxchg *)instruction); | 6688 | return ir_render_cmpxchg(g, executable, (IrInstGenCmpxchg *)instruction); |
| 6675 | case IrInstGenIdFence: | 6689 | case IrInstGenIdFence: |
| 6676 | return ir_render_fence(g, executable, (IrInstGenFence *)instruction); | 6690 | return ir_render_fence(g, executable, (IrInstGenFence *)instruction); |
| 6691 | case IrInstGenIdReduce: | ||
| 6692 | return ir_render_reduce(g, executable, (IrInstGenReduce *)instruction); | ||
| 6677 | case IrInstGenIdTruncate: | 6693 | case IrInstGenIdTruncate: |
| 6678 | return ir_render_truncate(g, executable, (IrInstGenTruncate *)instruction); | 6694 | return ir_render_truncate(g, executable, (IrInstGenTruncate *)instruction); |
| 6679 | case IrInstGenIdBoolNot: | 6695 | case IrInstGenIdBoolNot: |
| ... | @@ -8630,6 +8646,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -8630,6 +8646,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 8630 | create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); | 8646 | create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); |
| 8631 | create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); | 8647 | create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); |
| 8632 | create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); | 8648 | create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); |
| 8649 | create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2); | ||
| 8633 | } | 8650 | } |
| 8634 | 8651 | ||
| 8635 | static const char *bool_to_str(bool b) { | 8652 | static const char *bool_to_str(bool b) { |
src/stage1/ir.cpp+227| ... | @@ -402,6 +402,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { | ... | @@ -402,6 +402,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { |
| 402 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst)); | 402 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst)); |
| 403 | case IrInstSrcIdFence: | 403 | case IrInstSrcIdFence: |
| 404 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst)); | 404 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst)); |
| 405 | case IrInstSrcIdReduce: | ||
| 406 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReduce *>(inst)); | ||
| 405 | case IrInstSrcIdTruncate: | 407 | case IrInstSrcIdTruncate: |
| 406 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst)); | 408 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst)); |
| 407 | case IrInstSrcIdIntCast: | 409 | case IrInstSrcIdIntCast: |
| ... | @@ -636,6 +638,8 @@ void destroy_instruction_gen(IrInstGen *inst) { | ... | @@ -636,6 +638,8 @@ void destroy_instruction_gen(IrInstGen *inst) { |
| 636 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCmpxchg *>(inst)); | 638 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCmpxchg *>(inst)); |
| 637 | case IrInstGenIdFence: | 639 | case IrInstGenIdFence: |
| 638 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFence *>(inst)); | 640 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFence *>(inst)); |
| 641 | case IrInstGenIdReduce: | ||
| 642 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenReduce *>(inst)); | ||
| 639 | case IrInstGenIdTruncate: | 643 | case IrInstGenIdTruncate: |
| 640 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTruncate *>(inst)); | 644 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTruncate *>(inst)); |
| 641 | case IrInstGenIdShuffleVector: | 645 | case IrInstGenIdShuffleVector: |
| ... | @@ -1311,6 +1315,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) { | ... | @@ -1311,6 +1315,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) { |
| 1311 | return IrInstSrcIdFence; | 1315 | return IrInstSrcIdFence; |
| 1312 | } | 1316 | } |
| 1313 | 1317 | ||
| 1318 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcReduce *) { | ||
| 1319 | return IrInstSrcIdReduce; | ||
| 1320 | } | ||
| 1321 | |||
| 1314 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) { | 1322 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) { |
| 1315 | return IrInstSrcIdTruncate; | 1323 | return IrInstSrcIdTruncate; |
| 1316 | } | 1324 | } |
| ... | @@ -1775,6 +1783,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) { | ... | @@ -1775,6 +1783,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) { |
| 1775 | return IrInstGenIdFence; | 1783 | return IrInstGenIdFence; |
| 1776 | } | 1784 | } |
| 1777 | 1785 | ||
| 1786 | static constexpr IrInstGenId ir_inst_id(IrInstGenReduce *) { | ||
| 1787 | return IrInstGenIdReduce; | ||
| 1788 | } | ||
| 1789 | |||
| 1778 | static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) { | 1790 | static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) { |
| 1779 | return IrInstGenIdTruncate; | 1791 | return IrInstGenIdTruncate; |
| 1780 | } | 1792 | } |
| ... | @@ -3502,6 +3514,29 @@ static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, Atomi | ... | @@ -3502,6 +3514,29 @@ static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, Atomi |
| 3502 | return &instruction->base; | 3514 | return &instruction->base; |
| 3503 | } | 3515 | } |
| 3504 | 3516 | ||
| 3517 | static IrInstSrc *ir_build_reduce(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *op, IrInstSrc *value) { | ||
| 3518 | IrInstSrcReduce *instruction = ir_build_instruction<IrInstSrcReduce>(irb, scope, source_node); | ||
| 3519 | instruction->op = op; | ||
| 3520 | instruction->value = value; | ||
| 3521 | |||
| 3522 | ir_ref_instruction(op, irb->current_basic_block); | ||
| 3523 | ir_ref_instruction(value, irb->current_basic_block); | ||
| 3524 | |||
| 3525 | return &instruction->base; | ||
| 3526 | } | ||
| 3527 | |||
| 3528 | static IrInstGen *ir_build_reduce_gen(IrAnalyze *ira, IrInst *source_instruction, ReduceOp op, IrInstGen *value, ZigType *result_type) { | ||
| 3529 | IrInstGenReduce *instruction = ir_build_inst_gen<IrInstGenReduce>(&ira->new_irb, | ||
| 3530 | source_instruction->scope, source_instruction->source_node); | ||
| 3531 | instruction->base.value->type = result_type; | ||
| 3532 | instruction->op = op; | ||
| 3533 | instruction->value = value; | ||
| 3534 | |||
| 3535 | ir_ref_inst_gen(value); | ||
| 3536 | |||
| 3537 | return &instruction->base; | ||
| 3538 | } | ||
| 3539 | |||
| 3505 | static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, | 3540 | static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, |
| 3506 | IrInstSrc *dest_type, IrInstSrc *target) | 3541 | IrInstSrc *dest_type, IrInstSrc *target) |
| 3507 | { | 3542 | { |
| ... | @@ -6580,6 +6615,21 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod | ... | @@ -6580,6 +6615,21 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod |
| 6580 | IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value); | 6615 | IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value); |
| 6581 | return ir_lval_wrap(irb, scope, fence, lval, result_loc); | 6616 | return ir_lval_wrap(irb, scope, fence, lval, result_loc); |
| 6582 | } | 6617 | } |
| 6618 | case BuiltinFnIdReduce: | ||
| 6619 | { | ||
| 6620 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | ||
| 6621 | IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); | ||
| 6622 | if (arg0_value == irb->codegen->invalid_inst_src) | ||
| 6623 | return arg0_value; | ||
| 6624 | |||
| 6625 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | ||
| 6626 | IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); | ||
| 6627 | if (arg1_value == irb->codegen->invalid_inst_src) | ||
| 6628 | return arg1_value; | ||
| 6629 | |||
| 6630 | IrInstSrc *reduce = ir_build_reduce(irb, scope, node, arg0_value, arg1_value); | ||
| 6631 | return ir_lval_wrap(irb, scope, reduce, lval, result_loc); | ||
| 6632 | } | ||
| 6583 | case BuiltinFnIdDivExact: | 6633 | case BuiltinFnIdDivExact: |
| 6584 | { | 6634 | { |
| 6585 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | 6635 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | @@ -15932,6 +15982,24 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstGen *value, bool *out) { | ... | @@ -15932,6 +15982,24 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstGen *value, bool *out) { |
| 15932 | return ir_resolve_bool(ira, value, out); | 15982 | return ir_resolve_bool(ira, value, out); |
| 15933 | } | 15983 | } |
| 15934 | 15984 | ||
| 15985 | static bool ir_resolve_reduce_op(IrAnalyze *ira, IrInstGen *value, ReduceOp *out) { | ||
| 15986 | if (type_is_invalid(value->value->type)) | ||
| 15987 | return false; | ||
| 15988 | |||
| 15989 | ZigType *reduce_op_type = get_builtin_type(ira->codegen, "ReduceOp"); | ||
| 15990 | |||
| 15991 | IrInstGen *casted_value = ir_implicit_cast(ira, value, reduce_op_type); | ||
| 15992 | if (type_is_invalid(casted_value->value->type)) | ||
| 15993 | return false; | ||
| 15994 | |||
| 15995 | ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); | ||
| 15996 | if (!const_val) | ||
| 15997 | return false; | ||
| 15998 | |||
| 15999 | *out = (ReduceOp)bigint_as_u32(&const_val->data.x_enum_tag); | ||
| 16000 | return true; | ||
| 16001 | } | ||
| 16002 | |||
| 15935 | static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) { | 16003 | static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) { |
| 15936 | if (type_is_invalid(value->value->type)) | 16004 | if (type_is_invalid(value->value->type)) |
| 15937 | return false; | 16005 | return false; |
| ... | @@ -26802,6 +26870,161 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch | ... | @@ -26802,6 +26870,161 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch |
| 26802 | success_order, failure_order, instruction->is_weak, result_loc); | 26870 | success_order, failure_order, instruction->is_weak, result_loc); |
| 26803 | } | 26871 | } |
| 26804 | 26872 | ||
| 26873 | static ErrorMsg *ir_eval_reduce(IrAnalyze *ira, IrInst *source_instr, ReduceOp op, ZigValue *value, ZigValue *out_value) { | ||
| 26874 | assert(value->type->id == ZigTypeIdVector); | ||
| 26875 | ZigType *scalar_type = value->type->data.vector.elem_type; | ||
| 26876 | const size_t len = value->type->data.vector.len; | ||
| 26877 | assert(len > 0); | ||
| 26878 | |||
| 26879 | out_value->type = scalar_type; | ||
| 26880 | out_value->special = ConstValSpecialStatic; | ||
| 26881 | |||
| 26882 | if (scalar_type->id == ZigTypeIdBool) { | ||
| 26883 | ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0]; | ||
| 26884 | |||
| 26885 | bool result = first_elem_val->data.x_bool; | ||
| 26886 | for (size_t i = 1; i < len; i++) { | ||
| 26887 | ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i]; | ||
| 26888 | |||
| 26889 | switch (op) { | ||
| 26890 | case ReduceOp_and: | ||
| 26891 | result = result && elem_val->data.x_bool; | ||
| 26892 | if (!result) break; // Short circuit | ||
| 26893 | break; | ||
| 26894 | case ReduceOp_or: | ||
| 26895 | result = result || elem_val->data.x_bool; | ||
| 26896 | if (result) break; // Short circuit | ||
| 26897 | break; | ||
| 26898 | case ReduceOp_xor: | ||
| 26899 | result = result != elem_val->data.x_bool; | ||
| 26900 | break; | ||
| 26901 | default: | ||
| 26902 | zig_unreachable(); | ||
| 26903 | } | ||
| 26904 | } | ||
| 26905 | |||
| 26906 | out_value->data.x_bool = result; | ||
| 26907 | return nullptr; | ||
| 26908 | } | ||
| 26909 | |||
| 26910 | if (op != ReduceOp_min && op != ReduceOp_max) { | ||
| 26911 | ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0]; | ||
| 26912 | |||
| 26913 | copy_const_val(ira->codegen, out_value, first_elem_val); | ||
| 26914 | |||
| 26915 | for (size_t i = 1; i < len; i++) { | ||
| 26916 | ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i]; | ||
| 26917 | |||
| 26918 | IrBinOp bin_op; | ||
| 26919 | switch (op) { | ||
| 26920 | case ReduceOp_and: bin_op = IrBinOpBinAnd; break; | ||
| 26921 | case ReduceOp_or: bin_op = IrBinOpBinOr; break; | ||
| 26922 | case ReduceOp_xor: bin_op = IrBinOpBinXor; break; | ||
| 26923 | default: zig_unreachable(); | ||
| 26924 | } | ||
| 26925 | |||
| 26926 | ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, | ||
| 26927 | out_value, bin_op, elem_val, out_value); | ||
| 26928 | if (msg != nullptr) | ||
| 26929 | return msg; | ||
| 26930 | } | ||
| 26931 | |||
| 26932 | return nullptr; | ||
| 26933 | } | ||
| 26934 | |||
| 26935 | ZigValue *candidate_elem_val = &value->data.x_array.data.s_none.elements[0]; | ||
| 26936 | |||
| 26937 | ZigValue *dummy_cmp_value = ira->codegen->pass1_arena->create<ZigValue>(); | ||
| 26938 | for (size_t i = 1; i < len; i++) { | ||
| 26939 | ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i]; | ||
| 26940 | |||
| 26941 | IrBinOp bin_op; | ||
| 26942 | switch (op) { | ||
| 26943 | case ReduceOp_min: bin_op = IrBinOpCmpLessThan; break; | ||
| 26944 | case ReduceOp_max: bin_op = IrBinOpCmpGreaterThan; break; | ||
| 26945 | default: zig_unreachable(); | ||
| 26946 | } | ||
| 26947 | |||
| 26948 | ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr, | ||
| 26949 | elem_val, bin_op, candidate_elem_val, dummy_cmp_value); | ||
| 26950 | if (msg != nullptr) | ||
| 26951 | return msg; | ||
| 26952 | |||
| 26953 | if (dummy_cmp_value->data.x_bool) | ||
| 26954 | candidate_elem_val = elem_val; | ||
| 26955 | } | ||
| 26956 | |||
| 26957 | ira->codegen->pass1_arena->destroy(dummy_cmp_value); | ||
| 26958 | copy_const_val(ira->codegen, out_value, candidate_elem_val); | ||
| 26959 | |||
| 26960 | return nullptr; | ||
| 26961 | } | ||
| 26962 | |||
| 26963 | static IrInstGen *ir_analyze_instruction_reduce(IrAnalyze *ira, IrInstSrcReduce *instruction) { | ||
| 26964 | IrInstGen *op_inst = instruction->op->child; | ||
| 26965 | if (type_is_invalid(op_inst->value->type)) | ||
| 26966 | return ira->codegen->invalid_inst_gen; | ||
| 26967 | |||
| 26968 | IrInstGen *value_inst = instruction->value->child; | ||
| 26969 | if (type_is_invalid(value_inst->value->type)) | ||
| 26970 | return ira->codegen->invalid_inst_gen; | ||
| 26971 | |||
| 26972 | ZigType *value_type = value_inst->value->type; | ||
| 26973 | if (value_type->id != ZigTypeIdVector) { | ||
| 26974 | ir_add_error(ira, &value_inst->base, | ||
| 26975 | buf_sprintf("expected vector type, found '%s'", | ||
| 26976 | buf_ptr(&value_type->name))); | ||
| 26977 | return ira->codegen->invalid_inst_gen; | ||
| 26978 | } | ||
| 26979 | |||
| 26980 | ReduceOp op; | ||
| 26981 | if (!ir_resolve_reduce_op(ira, op_inst, &op)) | ||
| 26982 | return ira->codegen->invalid_inst_gen; | ||
| 26983 | |||
| 26984 | ZigType *elem_type = value_type->data.vector.elem_type; | ||
| 26985 | switch (elem_type->id) { | ||
| 26986 | case ZigTypeIdInt: | ||
| 26987 | break; | ||
| 26988 | case ZigTypeIdBool: | ||
| 26989 | if (op > ReduceOp_xor) { | ||
| 26990 | ir_add_error(ira, &op_inst->base, | ||
| 26991 | buf_sprintf("invalid operation for '%s' type", | ||
| 26992 | buf_ptr(&elem_type->name))); | ||
| 26993 | return ira->codegen->invalid_inst_gen; | ||
| 26994 | } break; | ||
| 26995 | case ZigTypeIdFloat: | ||
| 26996 | if (op < ReduceOp_min) { | ||
| 26997 | ir_add_error(ira, &op_inst->base, | ||
| 26998 | buf_sprintf("invalid operation for '%s' type", | ||
| 26999 | buf_ptr(&elem_type->name))); | ||
| 27000 | return ira->codegen->invalid_inst_gen; | ||
| 27001 | } break; | ||
| 27002 | default: | ||
| 27003 | // Vectors cannot have child types other than those listed above | ||
| 27004 | zig_unreachable(); | ||
| 27005 | } | ||
| 27006 | |||
| 27007 | // special case zero bit types | ||
| 27008 | switch (type_has_one_possible_value(ira->codegen, elem_type)) { | ||
| 27009 | case OnePossibleValueInvalid: | ||
| 27010 | return ira->codegen->invalid_inst_gen; | ||
| 27011 | case OnePossibleValueYes: | ||
| 27012 | return ir_const_move(ira, &instruction->base.base, | ||
| 27013 | get_the_one_possible_value(ira->codegen, elem_type)); | ||
| 27014 | case OnePossibleValueNo: | ||
| 27015 | break; | ||
| 27016 | } | ||
| 27017 | |||
| 27018 | if (instr_is_comptime(value_inst)) { | ||
| 27019 | IrInstGen *result = ir_const(ira, &instruction->base.base, elem_type); | ||
| 27020 | if (ir_eval_reduce(ira, &instruction->base.base, op, value_inst->value, result->value)) | ||
| 27021 | return ira->codegen->invalid_inst_gen; | ||
| 27022 | return result; | ||
| 27023 | } | ||
| 27024 | |||
| 27025 | return ir_build_reduce_gen(ira, &instruction->base.base, op, value_inst, elem_type); | ||
| 27026 | } | ||
| 27027 | |||
| 26805 | static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) { | 27028 | static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) { |
| 26806 | IrInstGen *order_inst = instruction->order->child; | 27029 | IrInstGen *order_inst = instruction->order->child; |
| 26807 | if (type_is_invalid(order_inst->value->type)) | 27030 | if (type_is_invalid(order_inst->value->type)) |
| ... | @@ -31550,6 +31773,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc | ... | @@ -31550,6 +31773,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc |
| 31550 | return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction); | 31773 | return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction); |
| 31551 | case IrInstSrcIdFence: | 31774 | case IrInstSrcIdFence: |
| 31552 | return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction); | 31775 | return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction); |
| 31776 | case IrInstSrcIdReduce: | ||
| 31777 | return ir_analyze_instruction_reduce(ira, (IrInstSrcReduce *)instruction); | ||
| 31553 | case IrInstSrcIdTruncate: | 31778 | case IrInstSrcIdTruncate: |
| 31554 | return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction); | 31779 | return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction); |
| 31555 | case IrInstSrcIdIntCast: | 31780 | case IrInstSrcIdIntCast: |
| ... | @@ -31937,6 +32162,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { | ... | @@ -31937,6 +32162,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { |
| 31937 | case IrInstGenIdNegation: | 32162 | case IrInstGenIdNegation: |
| 31938 | case IrInstGenIdNegationWrapping: | 32163 | case IrInstGenIdNegationWrapping: |
| 31939 | case IrInstGenIdWasmMemorySize: | 32164 | case IrInstGenIdWasmMemorySize: |
| 32165 | case IrInstGenIdReduce: | ||
| 31940 | return false; | 32166 | return false; |
| 31941 | 32167 | ||
| 31942 | case IrInstGenIdAsm: | 32168 | case IrInstGenIdAsm: |
| ... | @@ -32106,6 +32332,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { | ... | @@ -32106,6 +32332,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { |
| 32106 | case IrInstSrcIdSpillEnd: | 32332 | case IrInstSrcIdSpillEnd: |
| 32107 | case IrInstSrcIdWasmMemorySize: | 32333 | case IrInstSrcIdWasmMemorySize: |
| 32108 | case IrInstSrcIdSrc: | 32334 | case IrInstSrcIdSrc: |
| 32335 | case IrInstSrcIdReduce: | ||
| 32109 | return false; | 32336 | return false; |
| 32110 | 32337 | ||
| 32111 | case IrInstSrcIdAsm: | 32338 | case IrInstSrcIdAsm: |
src/stage1/ir_print.cpp+35| ... | @@ -200,6 +200,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { | ... | @@ -200,6 +200,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { |
| 200 | return "SrcCmpxchg"; | 200 | return "SrcCmpxchg"; |
| 201 | case IrInstSrcIdFence: | 201 | case IrInstSrcIdFence: |
| 202 | return "SrcFence"; | 202 | return "SrcFence"; |
| 203 | case IrInstSrcIdReduce: | ||
| 204 | return "SrcReduce"; | ||
| 203 | case IrInstSrcIdTruncate: | 205 | case IrInstSrcIdTruncate: |
| 204 | return "SrcTruncate"; | 206 | return "SrcTruncate"; |
| 205 | case IrInstSrcIdIntCast: | 207 | case IrInstSrcIdIntCast: |
| ... | @@ -436,6 +438,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { | ... | @@ -436,6 +438,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { |
| 436 | return "GenCmpxchg"; | 438 | return "GenCmpxchg"; |
| 437 | case IrInstGenIdFence: | 439 | case IrInstGenIdFence: |
| 438 | return "GenFence"; | 440 | return "GenFence"; |
| 441 | case IrInstGenIdReduce: | ||
| 442 | return "GenReduce"; | ||
| 439 | case IrInstGenIdTruncate: | 443 | case IrInstGenIdTruncate: |
| 440 | return "GenTruncate"; | 444 | return "GenTruncate"; |
| 441 | case IrInstGenIdBoolNot: | 445 | case IrInstGenIdBoolNot: |
| ... | @@ -1584,6 +1588,14 @@ static void ir_print_fence(IrPrintSrc *irp, IrInstSrcFence *instruction) { | ... | @@ -1584,6 +1588,14 @@ static void ir_print_fence(IrPrintSrc *irp, IrInstSrcFence *instruction) { |
| 1584 | fprintf(irp->f, ")"); | 1588 | fprintf(irp->f, ")"); |
| 1585 | } | 1589 | } |
| 1586 | 1590 | ||
| 1591 | static void ir_print_reduce(IrPrintSrc *irp, IrInstSrcReduce *instruction) { | ||
| 1592 | fprintf(irp->f, "@reduce("); | ||
| 1593 | ir_print_other_inst_src(irp, instruction->op); | ||
| 1594 | fprintf(irp->f, ", "); | ||
| 1595 | ir_print_other_inst_src(irp, instruction->value); | ||
| 1596 | fprintf(irp->f, ")"); | ||
| 1597 | } | ||
| 1598 | |||
| 1587 | static const char *atomic_order_str(AtomicOrder order) { | 1599 | static const char *atomic_order_str(AtomicOrder order) { |
| 1588 | switch (order) { | 1600 | switch (order) { |
| 1589 | case AtomicOrderUnordered: return "Unordered"; | 1601 | case AtomicOrderUnordered: return "Unordered"; |
| ... | @@ -1600,6 +1612,23 @@ static void ir_print_fence(IrPrintGen *irp, IrInstGenFence *instruction) { | ... | @@ -1600,6 +1612,23 @@ static void ir_print_fence(IrPrintGen *irp, IrInstGenFence *instruction) { |
| 1600 | fprintf(irp->f, "fence %s", atomic_order_str(instruction->order)); | 1612 | fprintf(irp->f, "fence %s", atomic_order_str(instruction->order)); |
| 1601 | } | 1613 | } |
| 1602 | 1614 | ||
| 1615 | static const char *reduce_op_str(ReduceOp op) { | ||
| 1616 | switch (op) { | ||
| 1617 | case ReduceOp_and: return "And"; | ||
| 1618 | case ReduceOp_or: return "Or"; | ||
| 1619 | case ReduceOp_xor: return "Xor"; | ||
| 1620 | case ReduceOp_min: return "Min"; | ||
| 1621 | case ReduceOp_max: return "Max"; | ||
| 1622 | } | ||
| 1623 | zig_unreachable(); | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | static void ir_print_reduce(IrPrintGen *irp, IrInstGenReduce *instruction) { | ||
| 1627 | fprintf(irp->f, "@reduce(.%s, ", reduce_op_str(instruction->op)); | ||
| 1628 | ir_print_other_inst_gen(irp, instruction->value); | ||
| 1629 | fprintf(irp->f, ")"); | ||
| 1630 | } | ||
| 1631 | |||
| 1603 | static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) { | 1632 | static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) { |
| 1604 | fprintf(irp->f, "@truncate("); | 1633 | fprintf(irp->f, "@truncate("); |
| 1605 | ir_print_other_inst_src(irp, instruction->dest_type); | 1634 | ir_print_other_inst_src(irp, instruction->dest_type); |
| ... | @@ -2749,6 +2778,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai | ... | @@ -2749,6 +2778,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai |
| 2749 | case IrInstSrcIdFence: | 2778 | case IrInstSrcIdFence: |
| 2750 | ir_print_fence(irp, (IrInstSrcFence *)instruction); | 2779 | ir_print_fence(irp, (IrInstSrcFence *)instruction); |
| 2751 | break; | 2780 | break; |
| 2781 | case IrInstSrcIdReduce: | ||
| 2782 | ir_print_reduce(irp, (IrInstSrcReduce *)instruction); | ||
| 2783 | break; | ||
| 2752 | case IrInstSrcIdTruncate: | 2784 | case IrInstSrcIdTruncate: |
| 2753 | ir_print_truncate(irp, (IrInstSrcTruncate *)instruction); | 2785 | ir_print_truncate(irp, (IrInstSrcTruncate *)instruction); |
| 2754 | break; | 2786 | break; |
| ... | @@ -3097,6 +3129,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai | ... | @@ -3097,6 +3129,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai |
| 3097 | case IrInstGenIdFence: | 3129 | case IrInstGenIdFence: |
| 3098 | ir_print_fence(irp, (IrInstGenFence *)instruction); | 3130 | ir_print_fence(irp, (IrInstGenFence *)instruction); |
| 3099 | break; | 3131 | break; |
| 3132 | case IrInstGenIdReduce: | ||
| 3133 | ir_print_reduce(irp, (IrInstGenReduce *)instruction); | ||
| 3134 | break; | ||
| 3100 | case IrInstGenIdTruncate: | 3135 | case IrInstGenIdTruncate: |
| 3101 | ir_print_truncate(irp, (IrInstGenTruncate *)instruction); | 3136 | ir_print_truncate(irp, (IrInstGenTruncate *)instruction); |
| 3102 | break; | 3137 | break; |
src/zig_llvm.cpp+28| ... | @@ -1123,6 +1123,34 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp | ... | @@ -1123,6 +1123,34 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp |
| 1123 | singleThread ? SyncScope::SingleThread : SyncScope::System)); | 1123 | singleThread ? SyncScope::SingleThread : SyncScope::System)); |
| 1124 | } | 1124 | } |
| 1125 | 1125 | ||
| 1126 | LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1127 | return wrap(unwrap(B)->CreateAndReduce(unwrap(Val))); | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1131 | return wrap(unwrap(B)->CreateOrReduce(unwrap(Val))); | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1135 | return wrap(unwrap(B)->CreateXorReduce(unwrap(Val))); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) { | ||
| 1139 | return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Val), is_signed)); | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) { | ||
| 1143 | return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Val), is_signed)); | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1147 | return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Val))); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1151 | return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Val))); | ||
| 1152 | } | ||
| 1153 | |||
| 1126 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); | 1154 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); |
| 1127 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); | 1155 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); |
| 1128 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); | 1156 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); |
src/zig_llvm.h+8| ... | @@ -455,6 +455,14 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp | ... | @@ -455,6 +455,14 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp |
| 455 | LLVMValueRef PTR, LLVMValueRef Val, | 455 | LLVMValueRef PTR, LLVMValueRef Val, |
| 456 | LLVMAtomicOrdering ordering, LLVMBool singleThread); | 456 | LLVMAtomicOrdering ordering, LLVMBool singleThread); |
| 457 | 457 | ||
| 458 | LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 459 | LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 460 | LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 461 | LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); | ||
| 462 | LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); | ||
| 463 | LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 464 | LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 465 | |||
| 458 | #define ZigLLVM_DIFlags_Zero 0U | 466 | #define ZigLLVM_DIFlags_Zero 0U |
| 459 | #define ZigLLVM_DIFlags_Private 1U | 467 | #define ZigLLVM_DIFlags_Private 1U |
| 460 | #define ZigLLVM_DIFlags_Protected 2U | 468 | #define ZigLLVM_DIFlags_Protected 2U |
test/stage1/behavior/vector.zig+40| ... | @@ -484,3 +484,43 @@ test "vector shift operators" { | ... | @@ -484,3 +484,43 @@ test "vector shift operators" { |
| 484 | S.doTheTest(); | 484 | S.doTheTest(); |
| 485 | comptime S.doTheTest(); | 485 | comptime S.doTheTest(); |
| 486 | } | 486 | } |
| 487 | |||
| 488 | test "vector reduce operation" { | ||
| 489 | const S = struct { | ||
| 490 | fn doTheTestReduce(comptime op: builtin.ReduceOp, x: anytype, expected: anytype) void { | ||
| 491 | const N = @typeInfo(@TypeOf(x)).Array.len; | ||
| 492 | const TX = @typeInfo(@TypeOf(x)).Array.child; | ||
| 493 | |||
| 494 | var r = @reduce(op, @as(Vector(N, TX), x)); | ||
| 495 | expectEqual(expected, r); | ||
| 496 | } | ||
| 497 | fn doTheTest() void { | ||
| 498 | doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); | ||
| 499 | doTheTestReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); | ||
| 500 | doTheTestReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); | ||
| 501 | |||
| 502 | doTheTestReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); | ||
| 503 | doTheTestReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); | ||
| 504 | doTheTestReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); | ||
| 505 | |||
| 506 | doTheTestReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010)); | ||
| 507 | doTheTestReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); | ||
| 508 | doTheTestReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0)); | ||
| 509 | |||
| 510 | doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); | ||
| 511 | doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); | ||
| 512 | |||
| 513 | doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); | ||
| 514 | doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); | ||
| 515 | |||
| 516 | doTheTestReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); | ||
| 517 | doTheTestReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); | ||
| 518 | |||
| 519 | doTheTestReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); | ||
| 520 | doTheTestReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); | ||
| 521 | } | ||
| 522 | }; | ||
| 523 | |||
| 524 | S.doTheTest(); | ||
| 525 | comptime S.doTheTest(); | ||
| 526 | } |