| ... | ... | @@ -8726,12 +8726,78 @@ pub const FuncGen = struct { |
| 8726 | 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 | 8793 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !?*llvm.Value { |
| 8730 | 8794 | if (self.liveness.isUnused(inst)) return null; |
| 8731 | 8795 | self.builder.setFastMath(want_fast_math); |
| 8796 | const target = self.dg.module.getTarget(); |
| 8732 | 8797 | |
| 8733 | 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 | 8801 | const scalar_ty = self.air.typeOfIndex(inst); |
| 8736 | 8802 | |
| 8737 | 8803 | // TODO handle the fast math setting |
| ... | ... | @@ -8742,17 +8808,21 @@ pub const FuncGen = struct { |
| 8742 | 8808 | .Xor => return self.builder.buildXorReduce(operand), |
| 8743 | 8809 | .Min => switch (scalar_ty.zigTypeTag()) { |
| 8744 | 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 | 8814 | else => unreachable, |
| 8747 | 8815 | }, |
| 8748 | 8816 | .Max => switch (scalar_ty.zigTypeTag()) { |
| 8749 | 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 | 8821 | else => unreachable, |
| 8752 | 8822 | }, |
| 8753 | 8823 | .Add => switch (scalar_ty.zigTypeTag()) { |
| 8754 | 8824 | .Int => return self.builder.buildAddReduce(operand), |
| 8755 | | .Float => { |
| 8825 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 8756 | 8826 | const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); |
| 8757 | 8827 | const neutral_value = scalar_llvm_ty.constReal(-0.0); |
| 8758 | 8828 | return self.builder.buildFPAddReduce(neutral_value, operand); |
| ... | ... | @@ -8761,7 +8831,7 @@ pub const FuncGen = struct { |
| 8761 | 8831 | }, |
| 8762 | 8832 | .Mul => switch (scalar_ty.zigTypeTag()) { |
| 8763 | 8833 | .Int => return self.builder.buildMulReduce(operand), |
| 8764 | | .Float => { |
| 8834 | .Float => if (intrinsicsAllowed(scalar_ty, target)) { |
| 8765 | 8835 | const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); |
| 8766 | 8836 | const neutral_value = scalar_llvm_ty.constReal(1.0); |
| 8767 | 8837 | return self.builder.buildFPMulReduce(neutral_value, operand); |
| ... | ... | @@ -8769,6 +8839,44 @@ pub const FuncGen = struct { |
| 8769 | 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 | 8882 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { |