| ... | @@ -2040,7 +2040,7 @@ fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { | ... | @@ -2040,7 +2040,7 @@ fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { |
| 2040 | const dest_scalar_ty = dest_ty.scalarType(zcu); | 2040 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 2041 | const target = zcu.getTarget(); | 2041 | const target = zcu.getTarget(); |
| 2042 | | 2042 | |
| 2043 | if (intrinsicsAllowed(dest_scalar_ty, target)) | 2043 | if (intrinsicsAllowed(.compiler_rt, dest_scalar_ty, target)) |
| 2044 | return fg.wip.conv(.fromStdLang(operand_scalar_info.signedness), operand, try o.lowerType(dest_ty, .as_value), ""); | 2044 | return fg.wip.conv(.fromStdLang(operand_scalar_info.signedness), operand, try o.lowerType(dest_ty, .as_value), ""); |
| 2045 | | 2045 | |
| 2046 | const rt_int_ty = compilerRtPromoteInt(operand_scalar_info) orelse { | 2046 | const rt_int_ty = compilerRtPromoteInt(operand_scalar_info) orelse { |
| ... | @@ -2093,7 +2093,7 @@ fn airIntFromFloat( | ... | @@ -2093,7 +2093,7 @@ fn airIntFromFloat( |
| 2093 | const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); | 2093 | const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); |
| 2094 | const dest_scalar_info = dest_scalar_ty.intInfo(zcu); | 2094 | const dest_scalar_info = dest_scalar_ty.intInfo(zcu); |
| 2095 | | 2095 | |
| 2096 | if (intrinsicsAllowed(operand_scalar_ty, target)) { | 2096 | if (intrinsicsAllowed(.compiler_rt, operand_scalar_ty, target)) { |
| 2097 | // TODO set fast math flag | 2097 | // TODO set fast math flag |
| 2098 | return fg.wip.conv(.fromStdLang(dest_scalar_info.signedness), operand, dest_llvm_ty, ""); | 2098 | return fg.wip.conv(.fromStdLang(dest_scalar_info.signedness), operand, dest_llvm_ty, ""); |
| 2099 | } | 2099 | } |
| ... | @@ -3888,7 +3888,7 @@ fn buildFloatCmp( | ... | @@ -3888,7 +3888,7 @@ fn buildFloatCmp( |
| 3888 | const target = zcu.getTarget(); | 3888 | const target = zcu.getTarget(); |
| 3889 | const scalar_ty = ty.scalarType(zcu); | 3889 | const scalar_ty = ty.scalarType(zcu); |
| 3890 | | 3890 | |
| 3891 | if (intrinsicsAllowed(scalar_ty, target)) { | 3891 | if (intrinsicsAllowed(.compiler_rt, scalar_ty, target)) { |
| 3892 | const cond: Builder.FloatCondition = switch (pred) { | 3892 | const cond: Builder.FloatCondition = switch (pred) { |
| 3893 | .eq => .oeq, | 3893 | .eq => .oeq, |
| 3894 | .neq => .une, | 3894 | .neq => .une, |
| ... | @@ -3968,10 +3968,14 @@ fn buildFloatOp( | ... | @@ -3968,10 +3968,14 @@ fn buildFloatOp( |
| 3968 | const target = zcu.getTarget(); | 3968 | const target = zcu.getTarget(); |
| 3969 | const scalar_ty = ty.scalarType(zcu); | 3969 | const scalar_ty = ty.scalarType(zcu); |
| 3970 | | 3970 | |
| 3971 | if (op != .tan and intrinsicsAllowed(scalar_ty, target)) switch (op) { | 3971 | switch (op) { |
| 3972 | // Some operations are dedicated LLVM instructions, not available as intrinsics | 3972 | // Some operations are dedicated LLVM instructions, not available as intrinsics |
| 3973 | .neg => return fg.wip.un(.fneg, params[0], ""), | 3973 | .neg => if (intrinsicsAllowed(.compiler_rt, scalar_ty, target)) return fg.wip.un(.fneg, params[0], ""), |
| 3974 | .add, .sub, .mul, .div, .fmod => return fg.wip.bin(switch (fast) { | 3974 | .add, .sub, .mul, .div, .fmod => if (intrinsicsAllowed(switch (op) { |
| | 3975 | else => unreachable, |
| | 3976 | .add, .sub, .mul, .div => .compiler_rt, |
| | 3977 | .fmod => .libc, |
| | 3978 | }, scalar_ty, target)) return fg.wip.bin(switch (fast) { |
| 3975 | .normal => switch (op) { | 3979 | .normal => switch (op) { |
| 3976 | .add => .fadd, | 3980 | .add => .fadd, |
| 3977 | .sub => .fsub, | 3981 | .sub => .fsub, |
| ... | @@ -3989,6 +3993,7 @@ fn buildFloatOp( | ... | @@ -3989,6 +3993,7 @@ fn buildFloatOp( |
| 3989 | else => unreachable, | 3993 | else => unreachable, |
| 3990 | }, | 3994 | }, |
| 3991 | }, params[0], params[1], ""), | 3995 | }, params[0], params[1], ""), |
| | 3996 | .fma, |
| 3992 | .fmax, | 3997 | .fmax, |
| 3993 | .fmin, | 3998 | .fmin, |
| 3994 | .ceil, | 3999 | .ceil, |
| ... | @@ -4003,9 +4008,10 @@ fn buildFloatOp( | ... | @@ -4003,9 +4008,10 @@ fn buildFloatOp( |
| 4003 | .round, | 4008 | .round, |
| 4004 | .sin, | 4009 | .sin, |
| 4005 | .sqrt, | 4010 | .sqrt, |
| | 4011 | .tan, |
| 4006 | .trunc, | 4012 | .trunc, |
| 4007 | .fma, | 4013 | => if (intrinsicsAllowed(.libc, scalar_ty, target)) return fg.wip.callIntrinsic(fast, .none, switch (op) { |
| 4008 | => return fg.wip.callIntrinsic(fast, .none, switch (op) { | 4014 | .fma => .fma, |
| 4009 | .fmax => .maxnum, | 4015 | .fmax => .maxnum, |
| 4010 | .fmin => .minnum, | 4016 | .fmin => .minnum, |
| 4011 | .ceil => .ceil, | 4017 | .ceil => .ceil, |
| ... | @@ -4020,12 +4026,11 @@ fn buildFloatOp( | ... | @@ -4020,12 +4026,11 @@ fn buildFloatOp( |
| 4020 | .round => .round, | 4026 | .round => .round, |
| 4021 | .sin => .sin, | 4027 | .sin => .sin, |
| 4022 | .sqrt => .sqrt, | 4028 | .sqrt => .sqrt, |
| | 4029 | .tan => .tan, |
| 4023 | .trunc => .trunc, | 4030 | .trunc => .trunc, |
| 4024 | .fma => .fma, | | |
| 4025 | else => unreachable, | 4031 | else => unreachable, |
| 4026 | }, &.{try o.lowerType(ty, .as_value)}, &params, ""), | 4032 | }, &.{try o.lowerType(ty, .as_value)}, &params, ""), |
| 4027 | .tan => unreachable, | 4033 | } |
| 4028 | }; | | |
| 4029 | | 4034 | |
| 4030 | const float_bits = scalar_ty.floatBits(target); | 4035 | const float_bits = scalar_ty.floatBits(target); |
| 4031 | const fn_name = switch (op) { | 4036 | const fn_name = switch (op) { |
| ... | @@ -4589,7 +4594,8 @@ fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Valu | ... | @@ -4589,7 +4594,8 @@ fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Valu |
| 4589 | const dest_scalar_ty = dest_ty.scalarType(zcu); | 4594 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 4590 | const target = zcu.getTarget(); | 4595 | const target = zcu.getTarget(); |
| 4591 | | 4596 | |
| 4592 | if (intrinsicsAllowed(dest_scalar_ty, target) and intrinsicsAllowed(operand_scalar_ty, target)) | 4597 | if (intrinsicsAllowed(.compiler_rt, dest_scalar_ty, target) and |
| | 4598 | intrinsicsAllowed(.compiler_rt, operand_scalar_ty, target)) |
| 4593 | return self.wip.cast(.fptrunc, operand, try o.lowerType(dest_ty, .as_value), ""); | 4599 | return self.wip.cast(.fptrunc, operand, try o.lowerType(dest_ty, .as_value), ""); |
| 4594 | const dest_bits = dest_scalar_ty.floatBits(target); | 4600 | const dest_bits = dest_scalar_ty.floatBits(target); |
| 4595 | const src_bits = operand_scalar_ty.floatBits(target); | 4601 | const src_bits = operand_scalar_ty.floatBits(target); |
| ... | @@ -4610,7 +4616,8 @@ fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value | ... | @@ -4610,7 +4616,8 @@ fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value |
| 4610 | const dest_scalar_ty = dest_ty.scalarType(zcu); | 4616 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 4611 | const target = zcu.getTarget(); | 4617 | const target = zcu.getTarget(); |
| 4612 | | 4618 | |
| 4613 | if (intrinsicsAllowed(dest_scalar_ty, target) and intrinsicsAllowed(operand_scalar_ty, target)) | 4619 | if (intrinsicsAllowed(.compiler_rt, dest_scalar_ty, target) and |
| | 4620 | intrinsicsAllowed(.compiler_rt, operand_scalar_ty, target)) |
| 4614 | return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty, .as_value), ""); | 4621 | return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty, .as_value), ""); |
| 4615 | const dest_bits = dest_scalar_ty.floatBits(target); | 4622 | const dest_bits = dest_scalar_ty.floatBits(target); |
| 4616 | const src_bits = operand_scalar_ty.floatBits(target); | 4623 | const src_bits = operand_scalar_ty.floatBits(target); |
| ... | @@ -6161,7 +6168,7 @@ fn airReduce(fg: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) All | ... | @@ -6161,7 +6168,7 @@ fn airReduce(fg: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) All |
| 6161 | .@"vector.reduce.umax", | 6168 | .@"vector.reduce.umax", |
| 6162 | else => unreachable, | 6169 | else => unreachable, |
| 6163 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), | 6170 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), |
| 6164 | .float => if (intrinsicsAllowed(scalar_ty, target)) | 6171 | .float => if (intrinsicsAllowed(.libc, scalar_ty, target)) |
| 6165 | return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | 6172 | return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { |
| 6166 | .Min => .@"vector.reduce.fmin", | 6173 | .Min => .@"vector.reduce.fmin", |
| 6167 | .Max => .@"vector.reduce.fmax", | 6174 | .Max => .@"vector.reduce.fmax", |
| ... | @@ -6175,7 +6182,7 @@ fn airReduce(fg: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) All | ... | @@ -6175,7 +6182,7 @@ fn airReduce(fg: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) All |
| 6175 | .Mul => .@"vector.reduce.mul", | 6182 | .Mul => .@"vector.reduce.mul", |
| 6176 | else => unreachable, | 6183 | else => unreachable, |
| 6177 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), | 6184 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), |
| 6178 | .float => if (intrinsicsAllowed(scalar_ty, target)) | 6185 | .float => if (intrinsicsAllowed(.compiler_rt, scalar_ty, target)) |
| 6179 | return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | 6186 | return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { |
| 6180 | .Add => .@"vector.reduce.fadd", | 6187 | .Add => .@"vector.reduce.fadd", |
| 6181 | .Mul => .@"vector.reduce.fmul", | 6188 | .Mul => .@"vector.reduce.fmul", |
| ... | @@ -7936,9 +7943,18 @@ fn appendConstraints( | ... | @@ -7936,9 +7943,18 @@ fn appendConstraints( |
| 7936 | | 7943 | |
| 7937 | /// LLVM does not support all relevant intrinsics for all targets, so we | 7944 | /// LLVM does not support all relevant intrinsics for all targets, so we |
| 7938 | /// may need to manually generate a compiler-rt call using a soft type. | 7945 | /// may need to manually generate a compiler-rt call using a soft type. |
| 7939 | fn intrinsicsAllowed(scalar_ty: Type, target: *const std.Target) bool { | 7946 | fn intrinsicsAllowed(kind: enum { compiler_rt, libc }, scalar_ty: Type, target: *const std.Target) bool { |
| 7940 | if (!scalar_ty.isRuntimeFloat()) return true; | 7947 | if (!scalar_ty.isRuntimeFloat()) return true; |
| 7941 | return switch (std.zig.target.compilerRtFloatAbi(target, scalar_ty.floatBits(target))) { | 7948 | const bits = scalar_ty.floatBits(target); |
| | 7949 | // Since upstream musl/msvc do not actually define the *f128 functions, llvm decides |
| | 7950 | // that it is a much better idea to just emit a call to the entirely wrong function as |
| | 7951 | // a fallback. We wouldn't want any linker errors when trying to perform an operation |
| | 7952 | // that isn't actually implemented anywhere, now would we! |
| | 7953 | if (bits == 128 and target.cpu.arch.isX86() and !target.abi.isGnu()) return switch (kind) { |
| | 7954 | .compiler_rt => true, |
| | 7955 | .libc => false, |
| | 7956 | }; |
| | 7957 | return switch (std.zig.target.compilerRtFloatAbi(target, bits)) { |
| 7942 | .hard => true, | 7958 | .hard => true, |
| 7943 | .soft => false, | 7959 | .soft => false, |
| 7944 | }; | 7960 | }; |