| ... | ... | @@ -7576,6 +7576,8 @@ pub const FuncGen = struct { |
| 7576 | 7576 | const src_bits = operand_ty.floatBits(target); |
| 7577 | 7577 | if (!backendSupportsF80(target) and (src_bits == 80 or dest_bits == 80)) { |
| 7578 | 7578 | return softF80TruncOrExt(self, operand, src_bits, dest_bits); |
| 7579 | } else if (!backendSupportsF128(target) and (src_bits == 128 or dest_bits == 128)) { |
| 7580 | return softF128TruncOrExt(self, operand, src_bits, dest_bits); |
| 7579 | 7581 | } |
| 7580 | 7582 | const dest_llvm_ty = try self.dg.lowerType(dest_ty); |
| 7581 | 7583 | return self.builder.buildFPTrunc(operand, dest_llvm_ty, ""); |
| ... | ... | @@ -7594,6 +7596,8 @@ pub const FuncGen = struct { |
| 7594 | 7596 | const src_bits = operand_ty.floatBits(target); |
| 7595 | 7597 | if (!backendSupportsF80(target) and (src_bits == 80 or dest_bits == 80)) { |
| 7596 | 7598 | return softF80TruncOrExt(self, operand, src_bits, dest_bits); |
| 7599 | } else if (!backendSupportsF128(target) and (src_bits == 128 or dest_bits == 128)) { |
| 7600 | return softF128TruncOrExt(self, operand, src_bits, dest_bits); |
| 7597 | 7601 | } |
| 7598 | 7602 | const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); |
| 7599 | 7603 | return self.builder.buildFPExt(operand, dest_llvm_ty, ""); |
| ... | ... | @@ -9138,6 +9142,88 @@ pub const FuncGen = struct { |
| 9138 | 9142 | return self.builder.buildBitCast(result, final_cast_llvm_ty, ""); |
| 9139 | 9143 | } |
| 9140 | 9144 | |
| 9145 | fn softF128TruncOrExt( |
| 9146 | self: *FuncGen, |
| 9147 | operand: *llvm.Value, |
| 9148 | src_bits: u16, |
| 9149 | dest_bits: u16, |
| 9150 | ) !?*llvm.Value { |
| 9151 | const target = self.dg.module.getTarget(); |
| 9152 | |
| 9153 | var param_llvm_ty: *llvm.Type = self.context.fp128Type(); |
| 9154 | var ret_llvm_ty: *llvm.Type = param_llvm_ty; |
| 9155 | var fn_name: [*:0]const u8 = undefined; |
| 9156 | var arg = operand; |
| 9157 | var final_cast: ?*llvm.Type = null; |
| 9158 | |
| 9159 | assert(src_bits == 128 or dest_bits == 128); |
| 9160 | |
| 9161 | // TODO: Implement proper names and compiler-rt functions for this!! |
| 9162 | if (src_bits == 128) switch (dest_bits) { |
| 9163 | 16 => { |
| 9164 | // See corresponding condition at definition of |
| 9165 | // __truncxfhf2 in compiler-rt. |
| 9166 | if (target.cpu.arch.isAARCH64()) { |
| 9167 | ret_llvm_ty = self.context.halfType(); |
| 9168 | } else { |
| 9169 | ret_llvm_ty = self.context.intType(16); |
| 9170 | final_cast = self.context.halfType(); |
| 9171 | } |
| 9172 | fn_name = "__trunctfhf2"; |
| 9173 | }, |
| 9174 | 32 => { |
| 9175 | ret_llvm_ty = self.context.floatType(); |
| 9176 | fn_name = "__trunctfsf2"; |
| 9177 | }, |
| 9178 | 64 => { |
| 9179 | ret_llvm_ty = self.context.doubleType(); |
| 9180 | fn_name = "__trunctfdf2"; |
| 9181 | }, |
| 9182 | 80 => { |
| 9183 | ret_llvm_ty = self.context.intType(80); |
| 9184 | fn_name = "__trunctfxf2"; |
| 9185 | }, |
| 9186 | 128 => return operand, |
| 9187 | else => unreachable, |
| 9188 | } else switch (src_bits) { |
| 9189 | 16 => { |
| 9190 | // See corresponding condition at definition of |
| 9191 | // __extendhftf2 in compiler-rt. |
| 9192 | param_llvm_ty = if (target.cpu.arch.isAARCH64()) |
| 9193 | self.context.halfType() |
| 9194 | else |
| 9195 | self.context.intType(16); |
| 9196 | arg = self.builder.buildBitCast(arg, param_llvm_ty, ""); |
| 9197 | fn_name = "__extendhftf2"; |
| 9198 | }, |
| 9199 | 32 => { |
| 9200 | param_llvm_ty = self.context.floatType(); |
| 9201 | fn_name = "__extendsftf2"; |
| 9202 | }, |
| 9203 | 64 => { |
| 9204 | param_llvm_ty = self.context.doubleType(); |
| 9205 | fn_name = "__extenddftf2"; |
| 9206 | }, |
| 9207 | 80 => { |
| 9208 | param_llvm_ty = self.context.intType(80); |
| 9209 | fn_name = "__extendxftf2"; |
| 9210 | }, |
| 9211 | 128 => return operand, |
| 9212 | else => unreachable, |
| 9213 | } |
| 9214 | |
| 9215 | const llvm_fn = self.dg.object.llvm_module.getNamedFunction(fn_name) orelse f: { |
| 9216 | const param_types = [_]*llvm.Type{param_llvm_ty}; |
| 9217 | const fn_type = llvm.functionType(ret_llvm_ty, &param_types, param_types.len, .False); |
| 9218 | break :f self.dg.object.llvm_module.addFunction(fn_name, fn_type); |
| 9219 | }; |
| 9220 | |
| 9221 | var args: [1]*llvm.Value = .{arg}; |
| 9222 | const result = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .C, .Auto, ""); |
| 9223 | const final_cast_llvm_ty = final_cast orelse return result; |
| 9224 | return self.builder.buildBitCast(result, final_cast_llvm_ty, ""); |
| 9225 | } |
| 9226 | |
| 9141 | 9227 | fn getErrorNameTable(self: *FuncGen) !*llvm.Value { |
| 9142 | 9228 | if (self.dg.object.error_name_table) |table| { |
| 9143 | 9229 | return table; |
| ... | ... | @@ -10489,13 +10575,23 @@ fn backendSupportsF16(target: std.Target) bool { |
| 10489 | 10575 | }; |
| 10490 | 10576 | } |
| 10491 | 10577 | |
| 10578 | /// This function returns true if we expect LLVM to lower f128 correctly, |
| 10579 | /// and false if we expect LLVm to crash if it encounters and f128 type |
| 10580 | /// or if it produces miscompilations. |
| 10581 | fn backendSupportsF128(target: std.Target) bool { |
| 10582 | return switch (target.cpu.arch) { |
| 10583 | .amdgcn => false, |
| 10584 | else => true, |
| 10585 | }; |
| 10586 | } |
| 10587 | |
| 10492 | 10588 | /// LLVM does not support all relevant intrinsics for all targets, so we |
| 10493 | 10589 | /// may need to manually generate a libc call |
| 10494 | 10590 | fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool { |
| 10495 | 10591 | return switch (scalar_ty.tag()) { |
| 10496 | 10592 | .f16 => backendSupportsF16(target), |
| 10497 | 10593 | .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), |
| 10498 | | .f128 => target.longDoubleIs(f128), |
| 10594 | .f128 => target.longDoubleIs(f128) and backendSupportsF128(target), |
| 10499 | 10595 | else => true, |
| 10500 | 10596 | }; |
| 10501 | 10597 | } |