authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-05 20:57:32-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-05 20:57:32-05:00
log8dcb1eba60ebdb35b92860e586b0113630dcf6ac
tree41576e1fff20cd48118148d72c4cb42f43512316
parentf132f426b9a11774323eafc669da0c9731deb85b
parent5a7d43df23664385d6841ef98b17ff9447be1ec6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10738 from Vexu/f80

Add compiler-rt functions for f80

9 files changed, 669 insertions(+), 26 deletions(-)

lib/std/math.zig+3-1
......@@ -43,11 +43,13 @@ pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF
4343pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000));
4444pub const f128_toint = 1.0 / f128_epsilon;
4545
46const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct {
46pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct {
4747 fraction: u64,
4848 exp: u16,
49 _pad: u32 = undefined,
4950} else extern struct {
5051 exp: u16,
52 _pad: u32 = undefined, // TODO verify compatibility with hardware
5153 fraction: u64,
5254};
5355
lib/std/special/compiler_rt.zig+33-8
......@@ -39,12 +39,23 @@ comptime {
3939 const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2;
4040 @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage });
4141
42 const __extendhfxf2 = @import("compiler_rt/extend_f80.zig").__extendhfxf2;
43 @export(__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = linkage });
44 const __extendffxf2 = @import("compiler_rt/extend_f80.zig").__extendffxf2;
45 @export(__extendffxf2, .{ .name = "__extendffxf2", .linkage = linkage });
46 const __extenddfxf2 = @import("compiler_rt/extend_f80.zig").__extenddfxf2;
47 @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = linkage });
48 const __extendxftf2 = @import("compiler_rt/extend_f80.zig").__extendxftf2;
49 @export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage });
50
4251 const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2;
4352 @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage });
4453 const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2;
4554 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
4655 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
4756 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
57 const __lexf2 = @import("compiler_rt/compareXf2.zig").__lexf2;
58 @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage });
4859
4960 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
5061 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
......@@ -52,26 +63,36 @@ comptime {
5263 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
5364 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
5465 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
66 const __gexf2 = @import("compiler_rt/compareXf2.zig").__gexf2;
67 @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage });
5568
5669 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
5770 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
5871 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
5972 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
73 const __eqxf2 = @import("compiler_rt/compareXf2.zig").__eqxf2;
74 @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage });
6075
6176 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
6277 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
6378 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
6479 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
80 const __ltxf2 = @import("compiler_rt/compareXf2.zig").__ltxf2;
81 @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage });
6582
6683 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
6784 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
6885 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
6986 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
87 const __nexf2 = @import("compiler_rt/compareXf2.zig").__nexf2;
88 @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage });
7089
7190 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
7291 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
7392 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
7493 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
94 const __gtxf2 = @import("compiler_rt/compareXf2.zig").__gtxf2;
95 @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage });
7596
7697 if (!is_test) {
7798 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });
......@@ -179,14 +200,14 @@ comptime {
179200 const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2;
180201 @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage });
181202
182 if (!long_double_is_f128) {
183 // TODO implement these
184 //const __extendxftf2 = @import("compiler_rt/extendXfYf2.zig").__extendxftf2;
185 //@export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage });
186
187 //const __trunctfxf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfxf2;
188 //@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });
189 }
203 const __truncxfhf2 = @import("compiler_rt/trunc_f80.zig").__truncxfhf2;
204 @export(__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = linkage });
205 const __truncxfff2 = @import("compiler_rt/trunc_f80.zig").__truncxfff2;
206 @export(__truncxfff2, .{ .name = "__truncxfff2", .linkage = linkage });
207 const __truncxfdf2 = @import("compiler_rt/trunc_f80.zig").__truncxfdf2;
208 @export(__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = linkage });
209 const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;
210 @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });
190211
191212 if (builtin.zig_backend == .stage1) {
192213 switch (arch) {
......@@ -216,12 +237,16 @@ comptime {
216237 @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage });
217238 const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3;
218239 @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage });
240 const __addxf3 = @import("compiler_rt/addXf3.zig").__addxf3;
241 @export(__addxf3, .{ .name = "__addxf3", .linkage = linkage });
219242 const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3;
220243 @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage });
221244 const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3;
222245 @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage });
223246 const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3;
224247 @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage });
248 const __subxf3 = @import("compiler_rt/addXf3.zig").__subxf3;
249 @export(__subxf3, .{ .name = "__subxf3", .linkage = linkage });
225250
226251 const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3;
227252 @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage });
lib/std/special/compiler_rt/addXf3.zig+171
......@@ -225,6 +225,177 @@ fn addXf3(comptime T: type, a: T, b: T) T {
225225 return @bitCast(T, result);
226226}
227227
228fn normalize_f80(exp: *i32, significand: *u80) void {
229 const shift = @clz(u64, @truncate(u64, significand.*));
230 significand.* = (significand.* << shift);
231 exp.* += -@as(i8, shift);
232}
233
234pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
235 var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*;
236 var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*;
237 var a_exp: i32 = a_rep.exp & 0x7FFF;
238 var b_exp: i32 = b_rep.exp & 0x7FFF;
239
240 const significand_bits = std.math.floatMantissaBits(f80);
241 const int_bit = 0x8000000000000000;
242 const significand_mask = 0x7FFFFFFFFFFFFFFF;
243 const qnan_bit = 0xC000000000000000;
244 const max_exp = 0x7FFF;
245 const sign_bit = 0x8000;
246
247 // Detect if a or b is infinity, or NaN.
248 if (a_exp == max_exp) {
249 if (a_rep.fraction ^ int_bit == 0) {
250 if (b_exp == max_exp and (b_rep.fraction ^ int_bit == 0)) {
251 // +/-infinity + -/+infinity = qNaN
252 return std.math.qnan_f80;
253 }
254 // +/-infinity + anything = +/- infinity
255 return a;
256 } else {
257 std.debug.assert(a_rep.fraction & significand_mask != 0);
258 // NaN + anything = qNaN
259 a_rep.fraction |= qnan_bit;
260 return @ptrCast(*const f80, &a_rep).*;
261 }
262 }
263 if (b_exp == max_exp) {
264 if (b_rep.fraction ^ int_bit == 0) {
265 // anything + +/-infinity = +/-infinity
266 return b;
267 } else {
268 std.debug.assert(b_rep.fraction & significand_mask != 0);
269 // anything + NaN = qNaN
270 b_rep.fraction |= qnan_bit;
271 return @ptrCast(*const f80, &b_rep).*;
272 }
273 }
274
275 const a_zero = (a_rep.fraction | @bitCast(u32, a_exp)) == 0;
276 const b_zero = (b_rep.fraction | @bitCast(u32, b_exp)) == 0;
277 if (a_zero) {
278 // zero + anything = anything
279 if (b_zero) {
280 // but we need to get the sign right for zero + zero
281 a_rep.exp &= b_rep.exp;
282 return @ptrCast(*const f80, &a_rep).*;
283 } else {
284 return b;
285 }
286 } else if (b_zero) {
287 // anything + zero = anything
288 return a;
289 }
290
291 var a_int: u80 = a_rep.fraction | (@as(u80, a_rep.exp & max_exp) << significand_bits);
292 var b_int: u80 = b_rep.fraction | (@as(u80, b_rep.exp & max_exp) << significand_bits);
293
294 // Swap a and b if necessary so that a has the larger absolute value.
295 if (b_int > a_int) {
296 const temp = a_rep;
297 a_rep = b_rep;
298 b_rep = temp;
299 }
300
301 // Extract the exponent and significand from the (possibly swapped) a and b.
302 a_exp = a_rep.exp & max_exp;
303 b_exp = b_rep.exp & max_exp;
304 a_int = a_rep.fraction;
305 b_int = b_rep.fraction;
306
307 // Normalize any denormals, and adjust the exponent accordingly.
308 normalize_f80(&a_exp, &a_int);
309 normalize_f80(&b_exp, &b_int);
310
311 // The sign of the result is the sign of the larger operand, a. If they
312 // have opposite signs, we are performing a subtraction; otherwise addition.
313 const result_sign = a_rep.exp & sign_bit;
314 const subtraction = (a_rep.exp ^ b_rep.exp) & sign_bit != 0;
315
316 // Shift the significands to give us round, guard and sticky, and or in the
317 // implicit significand bit. (If we fell through from the denormal path it
318 // was already set by normalize( ), but setting it twice won't hurt
319 // anything.)
320 a_int = a_int << 3;
321 b_int = b_int << 3;
322
323 // Shift the significand of b by the difference in exponents, with a sticky
324 // bottom bit to get rounding correct.
325 const @"align" = @intCast(u80, a_exp - b_exp);
326 if (@"align" != 0) {
327 if (@"align" < 80) {
328 const sticky = if (b_int << @intCast(u7, 80 - @"align") != 0) @as(u80, 1) else 0;
329 b_int = (b_int >> @truncate(u7, @"align")) | sticky;
330 } else {
331 b_int = 1; // sticky; b is known to be non-zero.
332 }
333 }
334 if (subtraction) {
335 a_int -= b_int;
336 // If a == -b, return +zero.
337 if (a_int == 0) return 0.0;
338
339 // If partial cancellation occurred, we need to left-shift the result
340 // and adjust the exponent:
341 if (a_int < int_bit << 3) {
342 const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, int_bit << 3));
343 a_int <<= @intCast(u7, shift);
344 a_exp -= shift;
345 }
346 } else { // addition
347 a_int += b_int;
348
349 // If the addition carried up, we need to right-shift the result and
350 // adjust the exponent:
351 if (a_int & (int_bit << 4) != 0) {
352 const sticky = a_int & 1;
353 a_int = a_int >> 1 | sticky;
354 a_exp += 1;
355 }
356 }
357
358 // If we have overflowed the type, return +/- infinity:
359 if (a_exp >= max_exp) {
360 a_rep.exp = max_exp | result_sign;
361 a_rep.fraction = int_bit; // integer bit is set for +/-inf
362 return @ptrCast(*const f80, &a_rep).*;
363 }
364
365 if (a_exp <= 0) {
366 // Result is denormal before rounding; the exponent is zero and we
367 // need to shift the significand.
368 const shift = @intCast(u80, 1 - a_exp);
369 const sticky = if (a_int << @intCast(u7, 80 - shift) != 0) @as(u1, 1) else 0;
370 a_int = a_int >> @intCast(u7, shift | sticky);
371 a_exp = 0;
372 }
373
374 // Low three bits are round, guard, and sticky.
375 const round_guard_sticky = @truncate(u3, a_int);
376
377 // Shift the significand into place.
378 a_int = @truncate(u64, a_int >> 3);
379
380 // // Insert the exponent and sign.
381 a_int |= (@intCast(u80, a_exp) | result_sign) << significand_bits;
382
383 // Final rounding. The result may overflow to infinity, but that is the
384 // correct result in that case.
385 if (round_guard_sticky > 0x4) a_int += 1;
386 if (round_guard_sticky == 0x4) a_int += a_int & 1;
387
388 a_rep.fraction = @truncate(u64, a_int);
389 a_rep.exp = @truncate(u16, a_int >> significand_bits);
390 return @ptrCast(*const f80, &a_rep).*;
391}
392
393pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 {
394 var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*;
395 b_rep.exp ^= 0x8000;
396 return __addxf3(a, @ptrCast(*const f80, &b_rep).*);
397}
398
228399test {
229400 _ = @import("addXf3_test.zig");
230401}
lib/std/special/compiler_rt/compareXf2.zig+67
......@@ -144,6 +144,73 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {
144144 return __gedf2(a, b);
145145}
146146
147// Comparison between f80
148
149pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT {
150 const a_rep = @ptrCast(*const std.math.F80Repr, &a).*;
151 const b_rep = @ptrCast(*const std.math.F80Repr, &b).*;
152 const sig_bits = std.math.floatMantissaBits(f80);
153 const int_bit = 0x8000000000000000;
154 const sign_bit = 0x8000;
155 const special_exp = 0x7FFF;
156
157 // If either a or b is NaN, they are unordered.
158 if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or
159 (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0))
160 return RT.Unordered;
161
162 // If a and b are both zeros, they are equal.
163 if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0)
164 return .Equal;
165
166 if (@boolToInt(a_rep.exp == b_rep.exp) & @boolToInt(a_rep.fraction == b_rep.fraction) != 0) {
167 return .Equal;
168 } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) {
169 // signs are different
170 if (@bitCast(i16, a_rep.exp) < @bitCast(i16, b_rep.exp)) {
171 return .Less;
172 } else {
173 return .Greater;
174 }
175 } else {
176 const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits);
177 const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits);
178 if (a_fraction < b_fraction) {
179 return .Less;
180 } else {
181 return .Greater;
182 }
183 }
184}
185
186pub fn __lexf2(a: f80, b: f80) callconv(.C) i32 {
187 @setRuntimeSafety(builtin.is_test);
188 const float = cmp_f80(LE, a, b);
189 return @bitCast(i32, float);
190}
191
192pub fn __gexf2(a: f80, b: f80) callconv(.C) i32 {
193 @setRuntimeSafety(builtin.is_test);
194 const float = cmp_f80(GE, a, b);
195 return @bitCast(i32, float);
196}
197
198pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 {
199 return __lexf2(a, b);
200}
201
202pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 {
203 return __lexf2(a, b);
204}
205
206pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 {
207 return __lexf2(a, b);
208}
209
210pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 {
211 return __gexf2(a, b);
212}
213
147214// Comparison between f128
148215
149216pub fn __letf2(a: f128, b: f128) callconv(.C) i32 {
lib/std/special/compiler_rt/extendXfYf2.zig-5
......@@ -27,11 +27,6 @@ pub fn __extendhftf2(a: F16T) callconv(.C) f128 {
2727 return extendXfYf2(f128, f16, @bitCast(u16, a));
2828}
2929
30pub fn __extendxftf2(a: c_longdouble) callconv(.C) f128 {
31 _ = a;
32 @panic("TODO implement");
33}
34
3530pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 {
3631 @setRuntimeSafety(false);
3732 return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, arg });
lib/std/special/compiler_rt/extend_f80.zig created+131
......@@ -0,0 +1,131 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const is_test = builtin.is_test;
4const native_arch = builtin.cpu.arch;
5
6// AArch64 is the only ABI (at the moment) to support f16 arguments without the
7// need for extending them to wider fp types.
8pub const F16T = if (native_arch.isAARCH64()) f16 else u16;
9
10pub fn __extendhfxf2(a: F16T) callconv(.C) f80 {
11 return extendF80(f16, @bitCast(u16, a));
12}
13
14pub fn __extendffxf2(a: f32) callconv(.C) f80 {
15 return extendF80(f32, @bitCast(u32, a));
16}
17
18pub fn __extenddfxf2(a: f64) callconv(.C) f80 {
19 return extendF80(f64, @bitCast(u64, a));
20}
21
22inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) f80 {
23 @setRuntimeSafety(builtin.is_test);
24
25 const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits);
26 const src_sig_bits = std.math.floatMantissaBits(src_t);
27 const dst_int_bit = 0x8000000000000000;
28 const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit
29
30 const dst_exp_bias = 16383;
31
32 const src_bits = @bitSizeOf(src_t);
33 const src_exp_bits = src_bits - src_sig_bits - 1;
34 const src_inf_exp = (1 << src_exp_bits) - 1;
35 const src_exp_bias = src_inf_exp >> 1;
36
37 const src_min_normal = 1 << src_sig_bits;
38 const src_inf = src_inf_exp << src_sig_bits;
39 const src_sign_mask = 1 << (src_sig_bits + src_exp_bits);
40 const src_abs_mask = src_sign_mask - 1;
41 const src_qnan = 1 << (src_sig_bits - 1);
42 const src_nan_code = src_qnan - 1;
43
44 var dst: std.math.F80Repr align(16) = undefined;
45
46 // Break a into a sign and representation of the absolute value
47 const a_abs = a & src_abs_mask;
48 const sign: u16 = if (a & src_sign_mask != 0) 0x8000 else 0;
49
50 if (a_abs -% src_min_normal < src_inf - src_min_normal) {
51 // a is a normal number.
52 // Extend to the destination type by shifting the significand and
53 // exponent into the proper position and rebiasing the exponent.
54 dst.exp = @intCast(u16, a_abs >> src_sig_bits);
55 dst.exp += dst_exp_bias - src_exp_bias;
56 dst.fraction = @as(u64, a_abs) << (dst_sig_bits - src_sig_bits);
57 dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers
58 } else if (a_abs >= src_inf) {
59 // a is NaN or infinity.
60 // Conjure the result by beginning with infinity, then setting the qNaN
61 // bit (if needed) and right-aligning the rest of the trailing NaN
62 // payload field.
63 dst.exp = 0x7fff;
64 dst.fraction = dst_int_bit;
65 dst.fraction |= @as(u64, a_abs & src_qnan) << (dst_sig_bits - src_sig_bits);
66 dst.fraction |= @as(u64, a_abs & src_nan_code) << (dst_sig_bits - src_sig_bits);
67 } else if (a_abs != 0) {
68 // a is denormal.
69 // renormalize the significand and clear the leading bit, then insert
70 // the correct adjusted exponent in the destination type.
71 const scale: u16 = @clz(src_rep_t, a_abs) -
72 @clz(src_rep_t, @as(src_rep_t, src_min_normal));
73
74 dst.fraction = @as(u64, a_abs) << @intCast(u6, dst_sig_bits - src_sig_bits + scale);
75 dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers
76 dst.exp = @truncate(u16, a_abs >> @intCast(u4, src_sig_bits - scale));
77 dst.exp ^= 1;
78 dst.exp |= dst_exp_bias - src_exp_bias - scale + 1;
79 } else {
80 // a is zero.
81 dst.exp = 0;
82 dst.fraction = 0;
83 }
84
85 dst.exp |= sign;
86 return @ptrCast(*const f80, &dst).*;
87}
88
89pub fn __extendxftf2(a: f80) callconv(.C) f128 {
90 @setRuntimeSafety(builtin.is_test);
91
92 const src_int_bit: u64 = 0x8000000000000000;
93 const src_sig_mask = ~src_int_bit;
94 const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit
95 const dst_sig_bits = std.math.floatMantissaBits(f128);
96
97 const dst_bits = @bitSizeOf(f128);
98
99 const dst_min_normal = @as(u128, 1) << dst_sig_bits;
100
101 // Break a into a sign and representation of the absolute value
102 var a_rep = @ptrCast(*const std.math.F80Repr, &a).*;
103 const sign = a_rep.exp & 0x8000;
104 a_rep.exp &= 0x7FFF;
105 var abs_result: u128 = undefined;
106
107 if (a_rep.exp == 0 and a_rep.fraction == 0) {
108 // zero
109 abs_result = 0;
110 } else if (a_rep.exp == 0x7FFF) {
111 // a is nan or infinite
112 abs_result = @as(u128, a_rep.fraction) << (dst_sig_bits - src_sig_bits);
113 abs_result |= @as(u128, a_rep.exp) << dst_sig_bits;
114 } else if (a_rep.fraction & src_int_bit != 0) {
115 // a is a normal value
116 abs_result = @as(u128, a_rep.fraction & src_sig_mask) << (dst_sig_bits - src_sig_bits);
117 abs_result |= @as(u128, a_rep.exp) << dst_sig_bits;
118 } else {
119 // a is denormal
120 // renormalize the significand and clear the leading bit and integer part,
121 // then insert the correct adjusted exponent in the destination type.
122 const scale: u32 = @clz(u64, a_rep.fraction);
123 abs_result = @as(u128, a_rep.fraction) << @intCast(u7, dst_sig_bits - src_sig_bits + scale + 1);
124 abs_result ^= dst_min_normal;
125 abs_result |= @as(u128, scale + 1) << dst_sig_bits;
126 }
127
128 // Apply the signbit to (dst_t)abs(a).
129 const result: u128 align(@alignOf(f128)) = abs_result | @as(u128, sign) << (dst_bits - 16);
130 return @bitCast(f128, result);
131}
lib/std/special/compiler_rt/truncXfYf2.zig-5
......@@ -26,11 +26,6 @@ pub fn __trunctfdf2(a: f128) callconv(.C) f64 {
2626 return truncXfYf2(f64, f128, a);
2727}
2828
29pub fn __trunctfxf2(a: f128) callconv(.C) c_longdouble {
30 _ = a;
31 @panic("TODO implement");
32}
33
3429pub fn __truncdfsf2(a: f64) callconv(.C) f32 {
3530 return truncXfYf2(f32, f64, a);
3631}
lib/std/special/compiler_rt/trunc_f80.zig created+159
......@@ -0,0 +1,159 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const native_arch = builtin.cpu.arch;
4
5// AArch64 is the only ABI (at the moment) to support f16 arguments without the
6// need for extending them to wider fp types.
7pub const F16T = if (native_arch.isAARCH64()) f16 else u16;
8
9pub fn __truncxfhf2(a: f80) callconv(.C) F16T {
10 return @bitCast(F16T, trunc(f16, a));
11}
12
13pub fn __truncxfff2(a: f80) callconv(.C) f32 {
14 return trunc(f32, a);
15}
16
17pub fn __truncxfdf2(a: f80) callconv(.C) f64 {
18 return trunc(f64, a);
19}
20
21inline fn trunc(comptime dst_t: type, a: f80) dst_t {
22 @setRuntimeSafety(builtin.is_test);
23
24 const dst_rep_t = std.meta.Int(.unsigned, @typeInfo(dst_t).Float.bits);
25 const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit
26 const dst_sig_bits = std.math.floatMantissaBits(dst_t);
27
28 const src_exp_bias = 16383;
29
30 const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1;
31 const halfway = 1 << (src_sig_bits - dst_sig_bits - 1);
32
33 const dst_bits = @typeInfo(dst_t).Float.bits;
34 const dst_exp_bits = dst_bits - dst_sig_bits - 1;
35 const dst_inf_exp = (1 << dst_exp_bits) - 1;
36 const dst_exp_bias = dst_inf_exp >> 1;
37
38 const underflow = src_exp_bias + 1 - dst_exp_bias;
39 const overflow = src_exp_bias + dst_inf_exp - dst_exp_bias;
40
41 const dst_qnan = 1 << (dst_sig_bits - 1);
42 const dst_nan_mask = dst_qnan - 1;
43
44 // Break a into a sign and representation of the absolute value
45 var a_rep = @ptrCast(*const std.math.F80Repr, &a).*;
46 const sign = a_rep.exp & 0x8000;
47 a_rep.exp &= 0x7FFF;
48 a_rep.fraction &= 0x7FFFFFFFFFFFFFFF;
49 var abs_result: dst_rep_t = undefined;
50
51 if (a_rep.exp -% underflow < a_rep.exp -% overflow) {
52 // The exponent of a is within the range of normal numbers in the
53 // destination format. We can convert by simply right-shifting with
54 // rounding and adjusting the exponent.
55 abs_result = @as(dst_rep_t, a_rep.exp) << dst_sig_bits;
56 abs_result |= @truncate(dst_rep_t, a_rep.fraction >> (src_sig_bits - dst_sig_bits));
57 abs_result -%= @as(dst_rep_t, src_exp_bias - dst_exp_bias) << dst_sig_bits;
58
59 const round_bits = a_rep.fraction & round_mask;
60 if (round_bits > halfway) {
61 // Round to nearest
62 abs_result += 1;
63 } else if (round_bits == halfway) {
64 // Ties to even
65 abs_result += abs_result & 1;
66 }
67 } else if (a_rep.exp == 0x7FFF and a_rep.fraction != 0) {
68 // a is NaN.
69 // Conjure the result by beginning with infinity, setting the qNaN
70 // bit and inserting the (truncated) trailing NaN field.
71 abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits;
72 abs_result |= dst_qnan;
73 abs_result |= @intCast(dst_rep_t, (a_rep.fraction >> (src_sig_bits - dst_sig_bits)) & dst_nan_mask);
74 } else if (a_rep.exp >= overflow) {
75 // a overflows to infinity.
76 abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits;
77 } else {
78 // a underflows on conversion to the destination type or is an exact
79 // zero. The result may be a denormal or zero. Extract the exponent
80 // to get the shift amount for the denormalization.
81 const shift = src_exp_bias - dst_exp_bias - a_rep.exp;
82
83 // Right shift by the denormalization amount with sticky.
84 if (shift > src_sig_bits) {
85 abs_result = 0;
86 } else {
87 const sticky = @boolToInt(a_rep.fraction << @intCast(u6, shift) != 0);
88 const denormalized_significand = a_rep.fraction >> @intCast(u6, shift) | sticky;
89 abs_result = @intCast(dst_rep_t, denormalized_significand >> (src_sig_bits - dst_sig_bits));
90 const round_bits = denormalized_significand & round_mask;
91 if (round_bits > halfway) {
92 // Round to nearest
93 abs_result += 1;
94 } else if (round_bits == halfway) {
95 // Ties to even
96 abs_result += abs_result & 1;
97 }
98 }
99 }
100
101 const result align(@alignOf(dst_t)) = abs_result | @as(dst_rep_t, sign) << dst_bits - 16;
102 return @bitCast(dst_t, result);
103}
104
105pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
106 const src_sig_bits = std.math.floatMantissaBits(f128);
107 const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit
108
109 // Various constants whose values follow from the type parameters.
110 // Any reasonable optimizer will fold and propagate all of these.
111 const src_bits = @typeInfo(f128).Float.bits;
112 const src_exp_bits = src_bits - src_sig_bits - 1;
113 const src_inf_exp = 0x7FFF;
114
115 const src_inf = src_inf_exp << src_sig_bits;
116 const src_sign_mask = 1 << (src_sig_bits + src_exp_bits);
117 const src_abs_mask = src_sign_mask - 1;
118 const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1;
119 const halfway = 1 << (src_sig_bits - dst_sig_bits - 1);
120 const src_qnan = 1 << (src_sig_bits - 1);
121 const src_nan_mask = src_qnan - 1;
122
123 // Break a into a sign and representation of the absolute value
124 const a_rep = @bitCast(u128, a);
125 const a_abs = a_rep & src_abs_mask;
126 const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0;
127
128 var res: std.math.F80Repr align(16) = undefined;
129
130 if (a_abs > src_inf) {
131 // a is NaN.
132 // Conjure the result by beginning with infinity, setting the qNaN
133 // bit and inserting the (truncated) trailing NaN field.
134 res.exp = 0x7fff;
135 res.fraction = 0x8000000000000000;
136 res.fraction |= @truncate(u64, (a_abs & src_qnan) << (src_sig_bits - dst_sig_bits));
137 res.fraction |= @truncate(u64, (a_abs & src_nan_mask) << (src_sig_bits - dst_sig_bits));
138 } else {
139 // The exponent of a is within the range of normal numbers in the
140 // destination format. We can convert by simply right-shifting with
141 // rounding and adjusting the exponent.
142 res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits));
143 res.exp = @truncate(u16, a_abs >> src_sig_bits);
144
145 const round_bits = a_abs & round_mask;
146 if (round_bits > halfway) {
147 // Round to nearest
148 const exp = @addWithOverflow(u64, res.fraction, 1, &res.fraction);
149 res.exp += @boolToInt(exp);
150 } else if (round_bits == halfway) {
151 // Ties to even
152 const exp = @addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction);
153 res.exp += @boolToInt(exp);
154 }
155 }
156
157 res.exp |= sign;
158 return @ptrCast(*const f80, &res).*;
159}
src/stage1/codegen.cpp+105-7
......@@ -3240,6 +3240,49 @@ static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int p
32403240 return LLVMAddFunction(g->module, name, fn_type);
32413241}
32423242
3243enum SoftF80Icmp {
3244 NONE,
3245 EQ_ZERO,
3246 NE_ZERO,
3247 LE_ZERO,
3248 EQ_NEG,
3249 GE_ZERO,
3250 EQ_ONE,
3251};
3252
3253static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) {
3254 switch (kind) {
3255 case NONE:
3256 return val;
3257 case EQ_ZERO: {
3258 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3259 return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, "");
3260 }
3261 case NE_ZERO: {
3262 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3263 return LLVMBuildICmp(g->builder, LLVMIntNE, val, zero, "");
3264 }
3265 case LE_ZERO: {
3266 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3267 return LLVMBuildICmp(g->builder, LLVMIntSLE, val, zero, "");
3268 }
3269 case EQ_NEG: {
3270 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, -1, true);
3271 return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, "");
3272 }
3273 case GE_ZERO: {
3274 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3275 return LLVMBuildICmp(g->builder, LLVMIntSGE, val, zero, "");
3276 }
3277 case EQ_ONE: {
3278 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 1, true);
3279 return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, "");
3280 }
3281 default:
3282 zig_unreachable();
3283 }
3284}
3285
32433286static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
32443287 Stage1AirInstBinOp *bin_op_instruction)
32453288{
......@@ -3255,6 +3298,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
32553298 LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type;
32563299 int param_count = 2;
32573300 const char *func_name;
3301 SoftF80Icmp res_icmp = NONE;
32583302 switch (op_id) {
32593303 case IrBinOpInvalid:
32603304 case IrBinOpArrayCat:
......@@ -3280,20 +3324,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
32803324 case IrBinOpCmpEq:
32813325 return_type = g->builtin_types.entry_i32->llvm_type;
32823326 func_name = "__eqxf2";
3327 res_icmp = EQ_ZERO;
32833328 break;
32843329 case IrBinOpCmpNotEq:
32853330 return_type = g->builtin_types.entry_i32->llvm_type;
32863331 func_name = "__nexf2";
3332 res_icmp = NE_ZERO;
32873333 break;
32883334 case IrBinOpCmpLessOrEq:
3335 return_type = g->builtin_types.entry_i32->llvm_type;
3336 func_name = "__lexf2";
3337 res_icmp = LE_ZERO;
3338 break;
32893339 case IrBinOpCmpLessThan:
32903340 return_type = g->builtin_types.entry_i32->llvm_type;
32913341 func_name = "__lexf2";
3342 res_icmp = EQ_NEG;
32923343 break;
32933344 case IrBinOpCmpGreaterOrEq:
3345 return_type = g->builtin_types.entry_i32->llvm_type;
3346 func_name = "__gexf2";
3347 res_icmp = GE_ZERO;
3348 break;
32943349 case IrBinOpCmpGreaterThan:
32953350 return_type = g->builtin_types.entry_i32->llvm_type;
32963351 func_name = "__gexf2";
3352 res_icmp = EQ_ONE;
32973353 break;
32983354 case IrBinOpMaximum:
32993355 func_name = "__fmaxx";
......@@ -3344,8 +3400,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
33443400 if (vector_len == 0) {
33453401 LLVMValueRef params[2] = {op1_value, op2_value};
33463402 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3403 result = add_f80_icmp(g, result, res_icmp);
33473404 } else {
3348 result = build_alloca(g, op1->value->type, "", 0);
3405 ZigType *alloca_ty = op1->value->type;
3406 if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool);
3407 result = build_alloca(g, alloca_ty, "", 0);
33493408 }
33503409
33513410 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
......@@ -3356,6 +3415,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
33563415 LLVMBuildExtractElement(g->builder, op2_value, index_value, ""),
33573416 };
33583417 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3418 call_result = add_f80_icmp(g, call_result, res_icmp);
33593419 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
33603420 call_result, index_value, "");
33613421 }
......@@ -4052,12 +4112,47 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable,
40524112 return LLVMBuildNot(g->builder, operand, "");
40534113}
40544114
4115static LLVMValueRef ir_gen_soft_f80_neg(CodeGen *g, ZigType *op_type, LLVMValueRef operand) {
4116 uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0;
4117
4118 uint64_t buf[2] = {0, 0};
4119 if (g->is_big_endian != native_is_big_endian) {
4120 buf[1] = 0x8000000000000000;
4121 } else {
4122 buf[1] = 0x8000;
4123 }
4124 LLVMValueRef sign_mask = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf);
4125
4126 LLVMValueRef result;
4127 if (vector_len == 0) {
4128 result = LLVMBuildXor(g->builder, operand, sign_mask, "");
4129 } else {
4130 result = build_alloca(g, op_type, "", 0);
4131 }
4132
4133 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
4134 for (uint32_t i = 0; i < vector_len; i++) {
4135 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
4136 LLVMValueRef xor_operand = LLVMBuildExtractElement(g->builder, operand, index_value, "");
4137 LLVMValueRef xor_result = LLVMBuildXor(g->builder, xor_operand, sign_mask, "");
4138 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
4139 xor_result, index_value, "");
4140 }
4141 if (vector_len != 0) {
4142 result = LLVMBuildLoad(g->builder, result, "");
4143 }
4144 return result;
4145}
4146
40554147static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) {
40564148 LLVMValueRef llvm_operand = ir_llvm_value(g, operand);
40574149 ZigType *operand_type = operand->value->type;
40584150 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
40594151 operand_type->data.vector.elem_type : operand_type;
40604152
4153 if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target))
4154 return ir_gen_soft_f80_neg(g, operand_type, llvm_operand);
4155
40614156 if (scalar_type->id == ZigTypeIdFloat) {
40624157 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst));
40634158 return LLVMBuildFNeg(g->builder, llvm_operand, "");
......@@ -8108,6 +8203,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
81088203 buf[1] = tmp;
81098204#endif
81108205 LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf);
8206 if (!target_has_f80(g->zig_target)) return as_i128;
81118207 LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80));
81128208 return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry));
81138209 }
......@@ -9331,13 +9427,15 @@ static void define_builtin_types(CodeGen *g) {
93319427 add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64);
93329428 add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128);
93339429
9334 if (target_has_f80(g->zig_target)) {
9335 add_fp_entry(g, "f80", 80, LLVMX86FP80Type(), &g->builtin_types.entry_f80);
9336 } else {
9430 {
93379431 ZigType *entry = new_type_table_entry(ZigTypeIdFloat);
9338 entry->llvm_type = get_int_type(g, false, 128)->llvm_type;
9339 entry->size_in_bits = 8 * LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type);
9340 entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type);
9432 if (target_has_f80(g->zig_target)) {
9433 entry->llvm_type = LLVMX86FP80Type();
9434 } else {
9435 entry->llvm_type = get_int_type(g, false, 128)->llvm_type;
9436 }
9437 entry->size_in_bits = 8 * 16;
9438 entry->abi_size = 16;
93419439 entry->abi_align = 16;
93429440 buf_init_from_str(&entry->name, "f80");
93439441 entry->data.floating.bit_count = 80;