authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-08 11:31:59-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-13 12:53:20-07:00
logf0d12dd82bfecf28ffe82ff706e98633d4c6b048
treebe9d15dd22813e41399e852de34daf30edfa1020
parentb992ea1b079c5967348655f8719cdadaf2df8261

stage2: Add softfloat support for `@reduce`


1 files changed, 113 insertions(+), 5 deletions(-)

src/codegen/llvm.zig+113-5
...@@ -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 }
87288728
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();
87328797
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);
87368802
8737 // TODO handle the fast math setting8803 // 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 }
87738881
8774 fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {8882 fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {