| author | |
| committer | |
| log | 8dcb1eba60ebdb35b92860e586b0113630dcf6ac |
| tree | 41576e1fff20cd48118148d72c4cb42f43512316 |
| parent | f132f426b9a11774323eafc669da0c9731deb85b |
| parent | 5a7d43df23664385d6841ef98b17ff9447be1ec6 |
| signature |
Add compiler-rt functions for f809 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 | ... | @@ -43,11 +43,13 @@ pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF |
| 43 | pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); | 43 | pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); |
| 44 | pub const f128_toint = 1.0 / f128_epsilon; | 44 | pub const f128_toint = 1.0 / f128_epsilon; |
| 45 | 45 | ||
| 46 | const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { | 46 | pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { |
| 47 | fraction: u64, | 47 | fraction: u64, |
| 48 | exp: u16, | 48 | exp: u16, |
| 49 | _pad: u32 = undefined, | ||
| 49 | } else extern struct { | 50 | } else extern struct { |
| 50 | exp: u16, | 51 | exp: u16, |
| 52 | _pad: u32 = undefined, // TODO verify compatibility with hardware | ||
| 51 | fraction: u64, | 53 | fraction: u64, |
| 52 | }; | 54 | }; |
| 53 | 55 |
lib/std/special/compiler_rt.zig+33-8| ... | @@ -39,12 +39,23 @@ comptime { | ... | @@ -39,12 +39,23 @@ comptime { |
| 39 | const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2; | 39 | const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2; |
| 40 | @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); | 40 | @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); |
| 41 | 41 | ||
| 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 | |||
| 42 | const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2; | 51 | const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2; |
| 43 | @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); | 52 | @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); |
| 44 | const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2; | 53 | const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2; |
| 45 | @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); | 54 | @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); |
| 46 | const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2; | 55 | const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2; |
| 47 | @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); | 56 | @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); |
| 57 | const __lexf2 = @import("compiler_rt/compareXf2.zig").__lexf2; | ||
| 58 | @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage }); | ||
| 48 | 59 | ||
| 49 | const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2; | 60 | const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2; |
| 50 | @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); | 61 | @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); |
| ... | @@ -52,26 +63,36 @@ comptime { | ... | @@ -52,26 +63,36 @@ comptime { |
| 52 | @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); | 63 | @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); |
| 53 | const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2; | 64 | const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2; |
| 54 | @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); | 65 | @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); |
| 66 | const __gexf2 = @import("compiler_rt/compareXf2.zig").__gexf2; | ||
| 67 | @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage }); | ||
| 55 | 68 | ||
| 56 | const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2; | 69 | const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2; |
| 57 | @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); | 70 | @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); |
| 58 | const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2; | 71 | const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2; |
| 59 | @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); | 72 | @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); |
| 73 | const __eqxf2 = @import("compiler_rt/compareXf2.zig").__eqxf2; | ||
| 74 | @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage }); | ||
| 60 | 75 | ||
| 61 | const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2; | 76 | const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2; |
| 62 | @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); | 77 | @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); |
| 63 | const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2; | 78 | const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2; |
| 64 | @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); | 79 | @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); |
| 80 | const __ltxf2 = @import("compiler_rt/compareXf2.zig").__ltxf2; | ||
| 81 | @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage }); | ||
| 65 | 82 | ||
| 66 | const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2; | 83 | const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2; |
| 67 | @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); | 84 | @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); |
| 68 | const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2; | 85 | const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2; |
| 69 | @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); | 86 | @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); |
| 87 | const __nexf2 = @import("compiler_rt/compareXf2.zig").__nexf2; | ||
| 88 | @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage }); | ||
| 70 | 89 | ||
| 71 | const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2; | 90 | const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2; |
| 72 | @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); | 91 | @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); |
| 73 | const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2; | 92 | const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2; |
| 74 | @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); | 93 | @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); |
| 94 | const __gtxf2 = @import("compiler_rt/compareXf2.zig").__gtxf2; | ||
| 95 | @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage }); | ||
| 75 | 96 | ||
| 76 | if (!is_test) { | 97 | if (!is_test) { |
| 77 | @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); | 98 | @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); |
| ... | @@ -179,14 +200,14 @@ comptime { | ... | @@ -179,14 +200,14 @@ comptime { |
| 179 | const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; | 200 | const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; |
| 180 | @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); | 201 | @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); |
| 181 | 202 | ||
| 182 | if (!long_double_is_f128) { | 203 | const __truncxfhf2 = @import("compiler_rt/trunc_f80.zig").__truncxfhf2; |
| 183 | // TODO implement these | 204 | @export(__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = linkage }); |
| 184 | //const __extendxftf2 = @import("compiler_rt/extendXfYf2.zig").__extendxftf2; | 205 | const __truncxfff2 = @import("compiler_rt/trunc_f80.zig").__truncxfff2; |
| 185 | //@export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); | 206 | @export(__truncxfff2, .{ .name = "__truncxfff2", .linkage = linkage }); |
| 186 | 207 | const __truncxfdf2 = @import("compiler_rt/trunc_f80.zig").__truncxfdf2; | |
| 187 | //const __trunctfxf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfxf2; | 208 | @export(__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = linkage }); |
| 188 | //@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); | 209 | const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2; |
| 189 | } | 210 | @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); |
| 190 | 211 | ||
| 191 | if (builtin.zig_backend == .stage1) { | 212 | if (builtin.zig_backend == .stage1) { |
| 192 | switch (arch) { | 213 | switch (arch) { |
| ... | @@ -216,12 +237,16 @@ comptime { | ... | @@ -216,12 +237,16 @@ comptime { |
| 216 | @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); | 237 | @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); |
| 217 | const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; | 238 | const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; |
| 218 | @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); | 239 | @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); |
| 240 | const __addxf3 = @import("compiler_rt/addXf3.zig").__addxf3; | ||
| 241 | @export(__addxf3, .{ .name = "__addxf3", .linkage = linkage }); | ||
| 219 | const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; | 242 | const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; |
| 220 | @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); | 243 | @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); |
| 221 | const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; | 244 | const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; |
| 222 | @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); | 245 | @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); |
| 223 | const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; | 246 | const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; |
| 224 | @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); | 247 | @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); |
| 248 | const __subxf3 = @import("compiler_rt/addXf3.zig").__subxf3; | ||
| 249 | @export(__subxf3, .{ .name = "__subxf3", .linkage = linkage }); | ||
| 225 | 250 | ||
| 226 | const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; | 251 | const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; |
| 227 | @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); | 252 | @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 { | ... | @@ -225,6 +225,177 @@ fn addXf3(comptime T: type, a: T, b: T) T { |
| 225 | return @bitCast(T, result); | 225 | return @bitCast(T, result); |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | fn 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 | |||
| 234 | pub 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 | |||
| 393 | pub 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 | |||
| 228 | test { | 399 | test { |
| 229 | _ = @import("addXf3_test.zig"); | 400 | _ = @import("addXf3_test.zig"); |
| 230 | } | 401 | } |
lib/std/special/compiler_rt/compareXf2.zig+67| ... | @@ -144,6 +144,73 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { | ... | @@ -144,6 +144,73 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { |
| 144 | return __gedf2(a, b); | 144 | return __gedf2(a, b); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | // Comparison between f80 | ||
| 148 | |||
| 149 | pub 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 | |||
| 186 | pub 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 | |||
| 192 | pub 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 | |||
| 198 | pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 199 | return __lexf2(a, b); | ||
| 200 | } | ||
| 201 | |||
| 202 | pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 203 | return __lexf2(a, b); | ||
| 204 | } | ||
| 205 | |||
| 206 | pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 207 | return __lexf2(a, b); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 211 | return __gexf2(a, b); | ||
| 212 | } | ||
| 213 | |||
| 147 | // Comparison between f128 | 214 | // Comparison between f128 |
| 148 | 215 | ||
| 149 | pub fn __letf2(a: f128, b: f128) callconv(.C) i32 { | 216 | pub 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 { | ... | @@ -27,11 +27,6 @@ pub fn __extendhftf2(a: F16T) callconv(.C) f128 { |
| 27 | return extendXfYf2(f128, f16, @bitCast(u16, a)); | 27 | return extendXfYf2(f128, f16, @bitCast(u16, a)); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | pub fn __extendxftf2(a: c_longdouble) callconv(.C) f128 { | ||
| 31 | _ = a; | ||
| 32 | @panic("TODO implement"); | ||
| 33 | } | ||
| 34 | |||
| 35 | pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { | 30 | pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { |
| 36 | @setRuntimeSafety(false); | 31 | @setRuntimeSafety(false); |
| 37 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, arg }); | 32 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, arg }); |
lib/std/special/compiler_rt/extend_f80.zig created+131| ... | @@ -0,0 +1,131 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const is_test = builtin.is_test; | ||
| 4 | const 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. | ||
| 8 | pub const F16T = if (native_arch.isAARCH64()) f16 else u16; | ||
| 9 | |||
| 10 | pub fn __extendhfxf2(a: F16T) callconv(.C) f80 { | ||
| 11 | return extendF80(f16, @bitCast(u16, a)); | ||
| 12 | } | ||
| 13 | |||
| 14 | pub fn __extendffxf2(a: f32) callconv(.C) f80 { | ||
| 15 | return extendF80(f32, @bitCast(u32, a)); | ||
| 16 | } | ||
| 17 | |||
| 18 | pub fn __extenddfxf2(a: f64) callconv(.C) f80 { | ||
| 19 | return extendF80(f64, @bitCast(u64, a)); | ||
| 20 | } | ||
| 21 | |||
| 22 | inline 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 | |||
| 89 | pub 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 { | ... | @@ -26,11 +26,6 @@ pub fn __trunctfdf2(a: f128) callconv(.C) f64 { |
| 26 | return truncXfYf2(f64, f128, a); | 26 | return truncXfYf2(f64, f128, a); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | pub fn __trunctfxf2(a: f128) callconv(.C) c_longdouble { | ||
| 30 | _ = a; | ||
| 31 | @panic("TODO implement"); | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn __truncdfsf2(a: f64) callconv(.C) f32 { | 29 | pub fn __truncdfsf2(a: f64) callconv(.C) f32 { |
| 35 | return truncXfYf2(f32, f64, a); | 30 | return truncXfYf2(f32, f64, a); |
| 36 | } | 31 | } |
lib/std/special/compiler_rt/trunc_f80.zig created+159| ... | @@ -0,0 +1,159 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const 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. | ||
| 7 | pub const F16T = if (native_arch.isAARCH64()) f16 else u16; | ||
| 8 | |||
| 9 | pub fn __truncxfhf2(a: f80) callconv(.C) F16T { | ||
| 10 | return @bitCast(F16T, trunc(f16, a)); | ||
| 11 | } | ||
| 12 | |||
| 13 | pub fn __truncxfff2(a: f80) callconv(.C) f32 { | ||
| 14 | return trunc(f32, a); | ||
| 15 | } | ||
| 16 | |||
| 17 | pub fn __truncxfdf2(a: f80) callconv(.C) f64 { | ||
| 18 | return trunc(f64, a); | ||
| 19 | } | ||
| 20 | |||
| 21 | inline 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 | |||
| 105 | pub 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 | ... | @@ -3240,6 +3240,49 @@ static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int p |
| 3240 | return LLVMAddFunction(g->module, name, fn_type); | 3240 | return LLVMAddFunction(g->module, name, fn_type); |
| 3241 | } | 3241 | } |
| 3242 | 3242 | ||
| 3243 | enum SoftF80Icmp { | ||
| 3244 | NONE, | ||
| 3245 | EQ_ZERO, | ||
| 3246 | NE_ZERO, | ||
| 3247 | LE_ZERO, | ||
| 3248 | EQ_NEG, | ||
| 3249 | GE_ZERO, | ||
| 3250 | EQ_ONE, | ||
| 3251 | }; | ||
| 3252 | |||
| 3253 | static 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 | |||
| 3243 | static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, | 3286 | static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, |
| 3244 | Stage1AirInstBinOp *bin_op_instruction) | 3287 | Stage1AirInstBinOp *bin_op_instruction) |
| 3245 | { | 3288 | { |
| ... | @@ -3255,6 +3298,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, | ... | @@ -3255,6 +3298,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, |
| 3255 | LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type; | 3298 | LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type; |
| 3256 | int param_count = 2; | 3299 | int param_count = 2; |
| 3257 | const char *func_name; | 3300 | const char *func_name; |
| 3301 | SoftF80Icmp res_icmp = NONE; | ||
| 3258 | switch (op_id) { | 3302 | switch (op_id) { |
| 3259 | case IrBinOpInvalid: | 3303 | case IrBinOpInvalid: |
| 3260 | case IrBinOpArrayCat: | 3304 | case IrBinOpArrayCat: |
| ... | @@ -3280,20 +3324,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, | ... | @@ -3280,20 +3324,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, |
| 3280 | case IrBinOpCmpEq: | 3324 | case IrBinOpCmpEq: |
| 3281 | return_type = g->builtin_types.entry_i32->llvm_type; | 3325 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3282 | func_name = "__eqxf2"; | 3326 | func_name = "__eqxf2"; |
| 3327 | res_icmp = EQ_ZERO; | ||
| 3283 | break; | 3328 | break; |
| 3284 | case IrBinOpCmpNotEq: | 3329 | case IrBinOpCmpNotEq: |
| 3285 | return_type = g->builtin_types.entry_i32->llvm_type; | 3330 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3286 | func_name = "__nexf2"; | 3331 | func_name = "__nexf2"; |
| 3332 | res_icmp = NE_ZERO; | ||
| 3287 | break; | 3333 | break; |
| 3288 | case IrBinOpCmpLessOrEq: | 3334 | case IrBinOpCmpLessOrEq: |
| 3335 | return_type = g->builtin_types.entry_i32->llvm_type; | ||
| 3336 | func_name = "__lexf2"; | ||
| 3337 | res_icmp = LE_ZERO; | ||
| 3338 | break; | ||
| 3289 | case IrBinOpCmpLessThan: | 3339 | case IrBinOpCmpLessThan: |
| 3290 | return_type = g->builtin_types.entry_i32->llvm_type; | 3340 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3291 | func_name = "__lexf2"; | 3341 | func_name = "__lexf2"; |
| 3342 | res_icmp = EQ_NEG; | ||
| 3292 | break; | 3343 | break; |
| 3293 | case IrBinOpCmpGreaterOrEq: | 3344 | case IrBinOpCmpGreaterOrEq: |
| 3345 | return_type = g->builtin_types.entry_i32->llvm_type; | ||
| 3346 | func_name = "__gexf2"; | ||
| 3347 | res_icmp = GE_ZERO; | ||
| 3348 | break; | ||
| 3294 | case IrBinOpCmpGreaterThan: | 3349 | case IrBinOpCmpGreaterThan: |
| 3295 | return_type = g->builtin_types.entry_i32->llvm_type; | 3350 | return_type = g->builtin_types.entry_i32->llvm_type; |
| 3296 | func_name = "__gexf2"; | 3351 | func_name = "__gexf2"; |
| 3352 | res_icmp = EQ_ONE; | ||
| 3297 | break; | 3353 | break; |
| 3298 | case IrBinOpMaximum: | 3354 | case IrBinOpMaximum: |
| 3299 | func_name = "__fmaxx"; | 3355 | func_name = "__fmaxx"; |
| ... | @@ -3344,8 +3400,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, | ... | @@ -3344,8 +3400,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, |
| 3344 | if (vector_len == 0) { | 3400 | if (vector_len == 0) { |
| 3345 | LLVMValueRef params[2] = {op1_value, op2_value}; | 3401 | LLVMValueRef params[2] = {op1_value, op2_value}; |
| 3346 | result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); | 3402 | result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3403 | result = add_f80_icmp(g, result, res_icmp); | ||
| 3347 | } else { | 3404 | } 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); | ||
| 3349 | } | 3408 | } |
| 3350 | 3409 | ||
| 3351 | LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; | 3410 | 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, | ... | @@ -3356,6 +3415,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, |
| 3356 | LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), | 3415 | LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), |
| 3357 | }; | 3416 | }; |
| 3358 | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); | 3417 | LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); |
| 3418 | call_result = add_f80_icmp(g, call_result, res_icmp); | ||
| 3359 | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), | 3419 | LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), |
| 3360 | call_result, index_value, ""); | 3420 | call_result, index_value, ""); |
| 3361 | } | 3421 | } |
| ... | @@ -4052,12 +4112,47 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable, | ... | @@ -4052,12 +4112,47 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable, |
| 4052 | return LLVMBuildNot(g->builder, operand, ""); | 4112 | return LLVMBuildNot(g->builder, operand, ""); |
| 4053 | } | 4113 | } |
| 4054 | 4114 | ||
| 4115 | static 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 | |||
| 4055 | static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) { | 4147 | static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) { |
| 4056 | LLVMValueRef llvm_operand = ir_llvm_value(g, operand); | 4148 | LLVMValueRef llvm_operand = ir_llvm_value(g, operand); |
| 4057 | ZigType *operand_type = operand->value->type; | 4149 | ZigType *operand_type = operand->value->type; |
| 4058 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? | 4150 | ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? |
| 4059 | operand_type->data.vector.elem_type : operand_type; | 4151 | operand_type->data.vector.elem_type : operand_type; |
| 4060 | 4152 | ||
| 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 | |||
| 4061 | if (scalar_type->id == ZigTypeIdFloat) { | 4156 | if (scalar_type->id == ZigTypeIdFloat) { |
| 4062 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst)); | 4157 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst)); |
| 4063 | return LLVMBuildFNeg(g->builder, llvm_operand, ""); | 4158 | return LLVMBuildFNeg(g->builder, llvm_operand, ""); |
| ... | @@ -8108,6 +8203,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n | ... | @@ -8108,6 +8203,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 8108 | buf[1] = tmp; | 8203 | buf[1] = tmp; |
| 8109 | #endif | 8204 | #endif |
| 8110 | LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); | 8205 | LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); |
| 8206 | if (!target_has_f80(g->zig_target)) return as_i128; | ||
| 8111 | LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80)); | 8207 | LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80)); |
| 8112 | return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); | 8208 | return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); |
| 8113 | } | 8209 | } |
| ... | @@ -9331,13 +9427,15 @@ static void define_builtin_types(CodeGen *g) { | ... | @@ -9331,13 +9427,15 @@ static void define_builtin_types(CodeGen *g) { |
| 9331 | add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); | 9427 | add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); |
| 9332 | add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); | 9428 | add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); |
| 9333 | 9429 | ||
| 9334 | if (target_has_f80(g->zig_target)) { | 9430 | { |
| 9335 | add_fp_entry(g, "f80", 80, LLVMX86FP80Type(), &g->builtin_types.entry_f80); | ||
| 9336 | } else { | ||
| 9337 | ZigType *entry = new_type_table_entry(ZigTypeIdFloat); | 9431 | ZigType *entry = new_type_table_entry(ZigTypeIdFloat); |
| 9338 | entry->llvm_type = get_int_type(g, false, 128)->llvm_type; | 9432 | if (target_has_f80(g->zig_target)) { |
| 9339 | entry->size_in_bits = 8 * LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type); | 9433 | entry->llvm_type = LLVMX86FP80Type(); |
| 9340 | entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); | 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; | ||
| 9341 | entry->abi_align = 16; | 9439 | entry->abi_align = 16; |
| 9342 | buf_init_from_str(&entry->name, "f80"); | 9440 | buf_init_from_str(&entry->name, "f80"); |
| 9343 | entry->data.floating.bit_count = 80; | 9441 | entry->data.floating.bit_count = 80; |