| ... | @@ -8726,12 +8726,78 @@ pub const FuncGen = struct { | ... | @@ -8726,12 +8726,78 @@ pub const FuncGen = struct { |
| 8726 | return self.builder.buildShuffleVector(a, b, llvm_mask_value, ""); | 8726 | return self.builder.buildShuffleVector(a, b, llvm_mask_value, ""); |
| 8727 | } | 8727 | } |
| 8728 | | 8728 | |
| | 8729 | /// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result. |
| | 8730 | /// |
| | 8731 | /// Equivalent to: |
| | 8732 | /// reduce: { |
| | 8733 | /// var i: usize = 0; |
| | 8734 | /// var accum: T = init; |
| | 8735 | /// while (i < vec.len) : (i += 1) { |
| | 8736 | /// accum = llvm_fn(accum, vec[i]); |
| | 8737 | /// } |
| | 8738 | /// break :reduce accum; |
| | 8739 | /// } |
| | 8740 | /// |
| | 8741 | fn buildReducedCall( |
| | 8742 | self: *FuncGen, |
| | 8743 | llvm_fn: *llvm.Value, |
| | 8744 | operand_vector: *llvm.Value, |
| | 8745 | vector_len: usize, |
| | 8746 | accum_init: *llvm.Value, |
| | 8747 | ) !*llvm.Value { |
| | 8748 | const llvm_usize_ty = try self.dg.lowerType(Type.usize); |
| | 8749 | const llvm_vector_len = llvm_usize_ty.constInt(vector_len, .False); |
| | 8750 | const llvm_result_ty = accum_init.typeOf(); |
| | 8751 | |
| | 8752 | // Allocate and initialize our mutable variables |
| | 8753 | const i_ptr = self.buildAlloca(llvm_usize_ty); |
| | 8754 | _ = self.builder.buildStore(llvm_usize_ty.constInt(0, .False), i_ptr); |
| | 8755 | const accum_ptr = self.buildAlloca(llvm_result_ty); |
| | 8756 | _ = self.builder.buildStore(accum_init, accum_ptr); |
| | 8757 | |
| | 8758 | // Setup the loop |
| | 8759 | const loop = self.context.appendBasicBlock(self.llvm_func, "ReduceLoop"); |
| | 8760 | const loop_exit = self.context.appendBasicBlock(self.llvm_func, "AfterReduce"); |
| | 8761 | _ = self.builder.buildBr(loop); |
| | 8762 | { |
| | 8763 | self.builder.positionBuilderAtEnd(loop); |
| | 8764 | |
| | 8765 | // while (i < vec.len) |
| | 8766 | const i = self.builder.buildLoad(llvm_usize_ty, i_ptr, ""); |
| | 8767 | const cond = self.builder.buildICmp(.ULT, i, llvm_vector_len, ""); |
| | 8768 | const loop_then = self.context.appendBasicBlock(self.llvm_func, "ReduceLoopThen"); |
| | 8769 | |
| | 8770 | _ = self.builder.buildCondBr(cond, loop_then, loop_exit); |
| | 8771 | |
| | 8772 | { |
| | 8773 | self.builder.positionBuilderAtEnd(loop_then); |
| | 8774 | |
| | 8775 | // accum = f(accum, vec[i]); |
| | 8776 | const accum = self.builder.buildLoad(llvm_result_ty, accum_ptr, ""); |
| | 8777 | const element = self.builder.buildExtractElement(operand_vector, i, ""); |
| | 8778 | const params = [2]*llvm.Value{ accum, element }; |
| | 8779 | const new_accum = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &params, params.len, .C, .Auto, ""); |
| | 8780 | _ = self.builder.buildStore(new_accum, accum_ptr); |
| | 8781 | |
| | 8782 | // i += 1 |
| | 8783 | const new_i = self.builder.buildAdd(i, llvm_usize_ty.constInt(1, .False), ""); |
| | 8784 | _ = self.builder.buildStore(new_i, i_ptr); |
| | 8785 | _ = self.builder.buildBr(loop); |
| | 8786 | } |
| | 8787 | } |
| | 8788 | |
| | 8789 | self.builder.positionBuilderAtEnd(loop_exit); |
| | 8790 | return self.builder.buildLoad(llvm_result_ty, accum_ptr, ""); |
| | 8791 | } |
| | 8792 | |
| 8729 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { | 8793 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 8730 | if (self.liveness.isUnused(inst)) return null; | 8794 | if (self.liveness.isUnused(inst)) return null; |
| 8731 | self.builder.setFastMath(want_fast_math); | 8795 | self.builder.setFastMath(want_fast_math); |
| | 8796 | const target = self.dg.module.getTarget(); |
| 8732 | | 8797 | |
| 8733 | const reduce = self.air.instructions.items(.data)[inst].reduce; | 8798 | const reduce = self.air.instructions.items(.data)[inst].reduce; |
| 8734 | const operand = try self.resolveInst(reduce.operand); | 8799 | var operand = try self.resolveInst(reduce.operand); |
| | 8800 | const operand_ty = self.air.typeOf(reduce.operand); |
| 8735 | const scalar_ty = self.air.typeOfIndex(inst); | 8801 | const scalar_ty = self.air.typeOfIndex(inst); |
| 8736 | | 8802 | |
| 8737 | // TODO handle the fast math setting | 8803 | // TODO handle the fast math setting |
| ... | @@ -8742,17 +8808,21 @@ pub const FuncGen = struct { | ... | @@ -8742,17 +8808,21 @@ pub const FuncGen = struct { |
| 8742 | .Xor => return self.builder.buildXorReduce(operand), | 8808 | .Xor => return self.builder.buildXorReduce(operand), |
| 8743 | .Min => switch (scalar_ty.zigTypeTag()) { | 8809 | .Min => switch (scalar_ty.zigTypeTag()) { |
| 8744 | .Int => return self.builder.buildIntMinReduce(operand, scalar_ty.isSignedInt()), | 8810 | .Int => return self.builder.buildIntMinReduce(operand, scalar_ty.isSignedInt()), |
| 8745 | .Float => return self.builder.buildFPMinReduce(operand), | 8811 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| | 8812 | return self.builder.buildFPMinReduce(operand); |
| | 8813 | }, |
| 8746 | else => unreachable, | 8814 | else => unreachable, |
| 8747 | }, | 8815 | }, |
| 8748 | .Max => switch (scalar_ty.zigTypeTag()) { | 8816 | .Max => switch (scalar_ty.zigTypeTag()) { |
| 8749 | .Int => return self.builder.buildIntMaxReduce(operand, scalar_ty.isSignedInt()), | 8817 | .Int => return self.builder.buildIntMaxReduce(operand, scalar_ty.isSignedInt()), |
| 8750 | .Float => return self.builder.buildFPMaxReduce(operand), | 8818 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| | 8819 | return self.builder.buildFPMaxReduce(operand); |
| | 8820 | }, |
| 8751 | else => unreachable, | 8821 | else => unreachable, |
| 8752 | }, | 8822 | }, |
| 8753 | .Add => switch (scalar_ty.zigTypeTag()) { | 8823 | .Add => switch (scalar_ty.zigTypeTag()) { |
| 8754 | .Int => return self.builder.buildAddReduce(operand), | 8824 | .Int => return self.builder.buildAddReduce(operand), |
| 8755 | .Float => { | 8825 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 8756 | const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); | 8826 | const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); |
| 8757 | const neutral_value = scalar_llvm_ty.constReal(-0.0); | 8827 | const neutral_value = scalar_llvm_ty.constReal(-0.0); |
| 8758 | return self.builder.buildFPAddReduce(neutral_value, operand); | 8828 | return self.builder.buildFPAddReduce(neutral_value, operand); |
| ... | @@ -8761,7 +8831,7 @@ pub const FuncGen = struct { | ... | @@ -8761,7 +8831,7 @@ pub const FuncGen = struct { |
| 8761 | }, | 8831 | }, |
| 8762 | .Mul => switch (scalar_ty.zigTypeTag()) { | 8832 | .Mul => switch (scalar_ty.zigTypeTag()) { |
| 8763 | .Int => return self.builder.buildMulReduce(operand), | 8833 | .Int => return self.builder.buildMulReduce(operand), |
| 8764 | .Float => { | 8834 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 8765 | const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); | 8835 | const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); |
| 8766 | const neutral_value = scalar_llvm_ty.constReal(1.0); | 8836 | const neutral_value = scalar_llvm_ty.constReal(1.0); |
| 8767 | return self.builder.buildFPMulReduce(neutral_value, operand); | 8837 | return self.builder.buildFPMulReduce(neutral_value, operand); |
| ... | @@ -8769,6 +8839,44 @@ pub const FuncGen = struct { | ... | @@ -8769,6 +8839,44 @@ pub const FuncGen = struct { |
| 8769 | else => unreachable, | 8839 | else => unreachable, |
| 8770 | }, | 8840 | }, |
| 8771 | } | 8841 | } |
| | 8842 | |
| | 8843 | // Reduction could not be performed with intrinsics. |
| | 8844 | // Use a manual loop over a softfloat call instead. |
| | 8845 | var fn_name_buf: [64]u8 = undefined; |
| | 8846 | const float_bits = scalar_ty.floatBits(target); |
| | 8847 | const fn_name = switch (reduce.operation) { |
| | 8848 | .Min => std.fmt.bufPrintZ(&fn_name_buf, "{s}fmin{s}", .{ |
| | 8849 | libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), |
| | 8850 | }) catch unreachable, |
| | 8851 | .Max => std.fmt.bufPrintZ(&fn_name_buf, "{s}fmax{s}", .{ |
| | 8852 | libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), |
| | 8853 | }) catch unreachable, |
| | 8854 | .Add => std.fmt.bufPrintZ(&fn_name_buf, "__add{s}f3", .{ |
| | 8855 | compilerRtFloatAbbrev(float_bits), |
| | 8856 | }) catch unreachable, |
| | 8857 | .Mul => std.fmt.bufPrintZ(&fn_name_buf, "__mul{s}f3", .{ |
| | 8858 | compilerRtFloatAbbrev(float_bits), |
| | 8859 | }) catch unreachable, |
| | 8860 | else => unreachable, |
| | 8861 | }; |
| | 8862 | var init_value_payload = Value.Payload.Float_32{ |
| | 8863 | .data = switch (reduce.operation) { |
| | 8864 | .Min => std.math.nan(f32), |
| | 8865 | .Max => std.math.nan(f32), |
| | 8866 | .Add => -0.0, |
| | 8867 | .Mul => 1.0, |
| | 8868 | else => unreachable, |
| | 8869 | }, |
| | 8870 | }; |
| | 8871 | |
| | 8872 | const param_llvm_ty = try self.dg.lowerType(scalar_ty); |
| | 8873 | const param_types = [2]*llvm.Type{ param_llvm_ty, param_llvm_ty }; |
| | 8874 | const libc_fn = self.getLibcFunction(fn_name, &param_types, param_llvm_ty); |
| | 8875 | const init_value = try self.dg.lowerValue(.{ |
| | 8876 | .ty = scalar_ty, |
| | 8877 | .val = Value.initPayload(&init_value_payload.base), |
| | 8878 | }); |
| | 8879 | return self.buildReducedCall(libc_fn, operand, operand_ty.vectorLen(), init_value); |
| 8772 | } | 8880 | } |
| 8773 | | 8881 | |
| 8774 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | 8882 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |