authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-12 13:25:44+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-12 13:25:44+01:00
log9643f32c998f92c882f0793f3956d51a923acfcb
tree32c463e9268bd6dbb21d387f40880178a872dbd3
parentbeb275b371cd50ee1d57528d2792f2f267e6013d
parent16076964d6e7c8f17117614d5a7d83070d5b8902
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10861 from ziglang/f80

make f80 less hacky; lower as u80 on non-x86

16 files changed, 334 insertions(+), 131 deletions(-)

CMakeLists.txt+3
...@@ -791,6 +791,9 @@ add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES})...@@ -791,6 +791,9 @@ add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES})
791set_target_properties(opt_c_util PROPERTIES791set_target_properties(opt_c_util PROPERTIES
792 COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}"792 COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}"
793)793)
794target_include_directories(opt_c_util PRIVATE
795 "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e-prebuilt"
796)
794797
795add_library(zigstage1 STATIC ${STAGE1_SOURCES})798add_library(zigstage1 STATIC ${STAGE1_SOURCES})
796set_target_properties(zigstage1 PROPERTIES799set_target_properties(zigstage1 PROPERTIES
ci/zinc/linux_test.sh+1
...@@ -5,6 +5,7 @@...@@ -5,6 +5,7 @@
5ZIG=$DEBUG_STAGING/bin/zig5ZIG=$DEBUG_STAGING/bin/zig
66
7$ZIG test test/behavior.zig -fno-stage1 -I test -fLLVM7$ZIG test test/behavior.zig -fno-stage1 -I test -fLLVM
8$ZIG test test/behavior.zig -fno-stage1 -I test -fLLVM -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin
8$ZIG test test/behavior.zig -fno-stage1 -I test -ofmt=c9$ZIG test test/behavior.zig -fno-stage1 -I test -ofmt=c
9$ZIG test test/behavior.zig -fno-stage1 -I test -target wasm32-wasi --test-cmd wasmtime --test-cmd-bin10$ZIG test test/behavior.zig -fno-stage1 -I test -target wasm32-wasi --test-cmd wasmtime --test-cmd-bin
10$ZIG test test/behavior.zig -fno-stage1 -I test -target arm-linux --test-cmd qemu-arm --test-cmd-bin11$ZIG test test/behavior.zig -fno-stage1 -I test -target arm-linux --test-cmd qemu-arm --test-cmd-bin
deps/SoftFloat-3e/source/include/primitiveTypes.h+1
...@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE....@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37#ifndef primitiveTypes_h 37#ifndef primitiveTypes_h
38#define primitiveTypes_h 1 38#define primitiveTypes_h 1
39 39
40#include "platform.h"
40#include <stdint.h> 41#include <stdint.h>
41 42
42#ifdef SOFTFLOAT_FAST_INT64 43#ifdef SOFTFLOAT_FAST_INT64
deps/SoftFloat-3e/source/include/softfloat_types.h+1
...@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE....@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37#ifndef softfloat_types_h 37#ifndef softfloat_types_h
38#define softfloat_types_h 1 38#define softfloat_types_h 1
39 39
40#include "platform.h"
40#include <stdint.h> 41#include <stdint.h>
41 42
42/*---------------------------------------------------------------------------- 43/*----------------------------------------------------------------------------
lib/std/math.zig+25-18
...@@ -36,28 +36,17 @@ pub const sqrt2 = 1.414213562373095048801688724209698079;...@@ -36,28 +36,17 @@ pub const sqrt2 = 1.414213562373095048801688724209698079;
36/// 1/sqrt(2)36/// 1/sqrt(2)
37pub const sqrt1_2 = 0.707106781186547524400844362104849039;37pub const sqrt1_2 = 0.707106781186547524400844362104849039;
3838
39// From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128)
40pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001));39pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001));
41pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000));40pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000));
42pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF));41pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF));
43pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000));42pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000));
44pub const f128_toint = 1.0 / f128_epsilon;43pub const f128_toint = 1.0 / f128_epsilon;
4544
46pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct {
47 fraction: u64,
48 exp: u16,
49 _pad: u32 = undefined,
50} else extern struct {
51 exp: u16,
52 _pad: u32 = undefined, // TODO verify compatibility with hardware
53 fraction: u64,
54};
55
56// float.h details45// float.h details
57pub const f80_true_min = @ptrCast(*const f80, &F80Repr{ .fraction = 1, .exp = 0 }).*;46pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 });
58pub const f80_min = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 1 }).*;47pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 });
59pub const f80_max = @ptrCast(*const f80, &F80Repr{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }).*;48pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE });
60pub const f80_epsilon = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }).*;49pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 });
61pub const f80_toint = 1.0 / f80_epsilon;50pub const f80_toint = 1.0 / f80_epsilon;
6251
63pub const f64_true_min = 4.94065645841246544177e-324;52pub const f64_true_min = 4.94065645841246544177e-324;
...@@ -107,9 +96,9 @@ pub const qnan_f64 = @bitCast(f64, qnan_u64);...@@ -107,9 +96,9 @@ pub const qnan_f64 = @bitCast(f64, qnan_u64);
107pub const inf_u64 = @as(u64, 0x7FF << 52);96pub const inf_u64 = @as(u64, 0x7FF << 52);
108pub const inf_f64 = @bitCast(f64, inf_u64);97pub const inf_f64 = @bitCast(f64, inf_u64);
10998
110pub const inf_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x7fff }).*;99pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff });
111pub const nan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xA000000000000000, .exp = 0x7fff }).*;100pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff });
112pub const qnan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xC000000000000000, .exp = 0x7fff }).*;101pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff });
113102
114pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);103pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);
115pub const nan_f128 = @bitCast(f128, nan_u128);104pub const nan_f128 = @bitCast(f128, nan_u128);
...@@ -1504,3 +1493,21 @@ test "boolMask" {...@@ -1504,3 +1493,21 @@ test "boolMask" {
1504pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) {1493pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) {
1505 return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom));1494 return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom));
1506}1495}
1496
1497pub const F80 = struct {
1498 fraction: u64,
1499 exp: u16,
1500};
1501
1502pub fn make_f80(repr: F80) f80 {
1503 const int = (@as(u80, repr.exp) << 64) | repr.fraction;
1504 return @bitCast(f80, int);
1505}
1506
1507pub fn break_f80(x: f80) F80 {
1508 const int = @bitCast(u80, x);
1509 return .{
1510 .fraction = @truncate(u64, int),
1511 .exp = @truncate(u16, int >> 64),
1512 };
1513}
lib/std/special/compiler_rt/addXf3.zig+9-9
...@@ -232,8 +232,8 @@ fn normalize_f80(exp: *i32, significand: *u80) void {...@@ -232,8 +232,8 @@ fn normalize_f80(exp: *i32, significand: *u80) void {
232}232}
233233
234pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {234pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
235 var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*;235 var a_rep = std.math.break_f80(a);
236 var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*;236 var b_rep = std.math.break_f80(b);
237 var a_exp: i32 = a_rep.exp & 0x7FFF;237 var a_exp: i32 = a_rep.exp & 0x7FFF;
238 var b_exp: i32 = b_rep.exp & 0x7FFF;238 var b_exp: i32 = b_rep.exp & 0x7FFF;
239239
...@@ -257,7 +257,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {...@@ -257,7 +257,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
257 std.debug.assert(a_rep.fraction & significand_mask != 0);257 std.debug.assert(a_rep.fraction & significand_mask != 0);
258 // NaN + anything = qNaN258 // NaN + anything = qNaN
259 a_rep.fraction |= qnan_bit;259 a_rep.fraction |= qnan_bit;
260 return @ptrCast(*const f80, &a_rep).*;260 return std.math.make_f80(a_rep);
261 }261 }
262 }262 }
263 if (b_exp == max_exp) {263 if (b_exp == max_exp) {
...@@ -268,7 +268,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {...@@ -268,7 +268,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
268 std.debug.assert(b_rep.fraction & significand_mask != 0);268 std.debug.assert(b_rep.fraction & significand_mask != 0);
269 // anything + NaN = qNaN269 // anything + NaN = qNaN
270 b_rep.fraction |= qnan_bit;270 b_rep.fraction |= qnan_bit;
271 return @ptrCast(*const f80, &b_rep).*;271 return std.math.make_f80(b_rep);
272 }272 }
273 }273 }
274274
...@@ -279,7 +279,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {...@@ -279,7 +279,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
279 if (b_zero) {279 if (b_zero) {
280 // but we need to get the sign right for zero + zero280 // but we need to get the sign right for zero + zero
281 a_rep.exp &= b_rep.exp;281 a_rep.exp &= b_rep.exp;
282 return @ptrCast(*const f80, &a_rep).*;282 return std.math.make_f80(a_rep);
283 } else {283 } else {
284 return b;284 return b;
285 }285 }
...@@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {...@@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
359 if (a_exp >= max_exp) {359 if (a_exp >= max_exp) {
360 a_rep.exp = max_exp | result_sign;360 a_rep.exp = max_exp | result_sign;
361 a_rep.fraction = int_bit; // integer bit is set for +/-inf361 a_rep.fraction = int_bit; // integer bit is set for +/-inf
362 return @ptrCast(*const f80, &a_rep).*;362 return std.math.make_f80(a_rep);
363 }363 }
364364
365 if (a_exp <= 0) {365 if (a_exp <= 0) {
...@@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {...@@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
387387
388 a_rep.fraction = @truncate(u64, a_int);388 a_rep.fraction = @truncate(u64, a_int);
389 a_rep.exp = @truncate(u16, a_int >> significand_bits);389 a_rep.exp = @truncate(u16, a_int >> significand_bits);
390 return @ptrCast(*const f80, &a_rep).*;390 return std.math.make_f80(a_rep);
391}391}
392392
393pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 {393pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 {
394 var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*;394 var b_rep = std.math.break_f80(b);
395 b_rep.exp ^= 0x8000;395 b_rep.exp ^= 0x8000;
396 return __addxf3(a, @ptrCast(*const f80, &b_rep).*);396 return __addxf3(a, std.math.make_f80(b_rep));
397}397}
398398
399test {399test {
lib/std/special/compiler_rt/compareXf2.zig+2-2
...@@ -147,8 +147,8 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {...@@ -147,8 +147,8 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {
147// Comparison between f80147// Comparison between f80
148148
149pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT {149pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT {
150 const a_rep = @ptrCast(*const std.math.F80Repr, &a).*;150 const a_rep = std.math.break_f80(a);
151 const b_rep = @ptrCast(*const std.math.F80Repr, &b).*;151 const b_rep = std.math.break_f80(b);
152 const sig_bits = std.math.floatMantissaBits(f80);152 const sig_bits = std.math.floatMantissaBits(f80);
153 const int_bit = 0x8000000000000000;153 const int_bit = 0x8000000000000000;
154 const sign_bit = 0x8000;154 const sign_bit = 0x8000;
lib/std/special/compiler_rt/extend_f80.zig+3-3
...@@ -41,7 +41,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s...@@ -41,7 +41,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s
41 const src_qnan = 1 << (src_sig_bits - 1);41 const src_qnan = 1 << (src_sig_bits - 1);
42 const src_nan_code = src_qnan - 1;42 const src_nan_code = src_qnan - 1;
4343
44 var dst: std.math.F80Repr align(16) = undefined;44 var dst: std.math.F80 = undefined;
4545
46 // Break a into a sign and representation of the absolute value46 // Break a into a sign and representation of the absolute value
47 const a_abs = a & src_abs_mask;47 const a_abs = a & src_abs_mask;
...@@ -83,7 +83,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s...@@ -83,7 +83,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s
83 }83 }
8484
85 dst.exp |= sign;85 dst.exp |= sign;
86 return @ptrCast(*const f80, &dst).*;86 return std.math.make_f80(dst);
87}87}
8888
89pub fn __extendxftf2(a: f80) callconv(.C) f128 {89pub fn __extendxftf2(a: f80) callconv(.C) f128 {
...@@ -99,7 +99,7 @@ pub fn __extendxftf2(a: f80) callconv(.C) f128 {...@@ -99,7 +99,7 @@ pub fn __extendxftf2(a: f80) callconv(.C) f128 {
99 const dst_min_normal = @as(u128, 1) << dst_sig_bits;99 const dst_min_normal = @as(u128, 1) << dst_sig_bits;
100100
101 // Break a into a sign and representation of the absolute value101 // Break a into a sign and representation of the absolute value
102 var a_rep = @ptrCast(*const std.math.F80Repr, &a).*;102 var a_rep = std.math.break_f80(a);
103 const sign = a_rep.exp & 0x8000;103 const sign = a_rep.exp & 0x8000;
104 a_rep.exp &= 0x7FFF;104 a_rep.exp &= 0x7FFF;
105 var abs_result: u128 = undefined;105 var abs_result: u128 = undefined;
lib/std/special/compiler_rt/trunc_f80.zig+3-3
...@@ -42,7 +42,7 @@ inline fn trunc(comptime dst_t: type, a: f80) dst_t {...@@ -42,7 +42,7 @@ inline fn trunc(comptime dst_t: type, a: f80) dst_t {
42 const dst_nan_mask = dst_qnan - 1;42 const dst_nan_mask = dst_qnan - 1;
4343
44 // Break a into a sign and representation of the absolute value44 // Break a into a sign and representation of the absolute value
45 var a_rep = @ptrCast(*const std.math.F80Repr, &a).*;45 var a_rep = std.math.break_f80(a);
46 const sign = a_rep.exp & 0x8000;46 const sign = a_rep.exp & 0x8000;
47 a_rep.exp &= 0x7FFF;47 a_rep.exp &= 0x7FFF;
48 a_rep.fraction &= 0x7FFFFFFFFFFFFFFF;48 a_rep.fraction &= 0x7FFFFFFFFFFFFFFF;
...@@ -125,7 +125,7 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {...@@ -125,7 +125,7 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
125 const a_abs = a_rep & src_abs_mask;125 const a_abs = a_rep & src_abs_mask;
126 const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0;126 const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0;
127127
128 var res: std.math.F80Repr align(16) = undefined;128 var res: std.math.F80 = undefined;
129129
130 if (a_abs > src_inf) {130 if (a_abs > src_inf) {
131 // a is NaN.131 // a is NaN.
...@@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {...@@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
155 }155 }
156156
157 res.exp |= sign;157 res.exp |= sign;
158 return @ptrCast(*const f80, &res).*;158 return std.math.make_f80(res);
159}159}
src/Sema.zig+16-12
...@@ -5977,8 +5977,13 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -5977,8 +5977,13 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
5977 return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{});5977 return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{});
5978 }5978 }
59795979
5980 try sema.requireRuntimeBlock(block, operand_src);
5981 // TODO insert safety check to make sure the value fits in the dest type5980 // TODO insert safety check to make sure the value fits in the dest type
5981
5982 if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {
5983 return sema.addConstant(dest_ty, opv);
5984 }
5985
5986 try sema.requireRuntimeBlock(block, operand_src);
5982 return block.addTyOp(.intcast, dest_ty, operand);5987 return block.addTyOp(.intcast, dest_ty, operand);
5983}5988}
59845989
...@@ -7537,17 +7542,21 @@ fn zirShl(...@@ -7537,17 +7542,21 @@ fn zirShl(
7537 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);7542 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
7538 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);7543 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
75397544
7545 if (maybe_rhs_val) |rhs_val| {
7546 if (rhs_val.isUndef()) {
7547 return sema.addConstUndef(sema.typeOf(lhs));
7548 }
7549 if (rhs_val.compareWithZero(.eq)) {
7550 return lhs;
7551 }
7552 }
7553
7540 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {7554 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
7541 const lhs_ty = sema.typeOf(lhs);7555 const lhs_ty = sema.typeOf(lhs);
75427556
7543 if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);7557 if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
7544 const rhs_val = maybe_rhs_val orelse break :rs rhs_src;7558 const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
7545 if (rhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
75467559
7547 // If rhs is 0, return lhs without doing any calculations.
7548 if (rhs_val.compareWithZero(.eq)) {
7549 return sema.addConstant(lhs_ty, lhs_val);
7550 }
7551 const target = sema.mod.getTarget();7560 const target = sema.mod.getTarget();
7552 const val = switch (air_tag) {7561 const val = switch (air_tag) {
7553 .shl_exact => val: {7562 .shl_exact => val: {
...@@ -7577,12 +7586,7 @@ fn zirShl(...@@ -7577,12 +7586,7 @@ fn zirShl(
7577 };7586 };
75787587
7579 return sema.addConstant(lhs_ty, val);7588 return sema.addConstant(lhs_ty, val);
7580 } else rs: {7589 } else lhs_src;
7581 if (maybe_rhs_val) |rhs_val| {
7582 if (rhs_val.isUndef()) return sema.addConstUndef(sema.typeOf(lhs));
7583 }
7584 break :rs lhs_src;
7585 };
75867590
7587 // TODO: insert runtime safety check for shl_exact7591 // TODO: insert runtime safety check for shl_exact
75887592
src/codegen/llvm.zig+120-53
...@@ -661,7 +661,11 @@ pub const DeclGen = struct {...@@ -661,7 +661,11 @@ pub const DeclGen = struct {
661 new_global.setUnnamedAddr(global.getUnnamedAddress());661 new_global.setUnnamedAddr(global.getUnnamedAddress());
662 new_global.setAlignment(global.getAlignment());662 new_global.setAlignment(global.getAlignment());
663 new_global.setInitializer(llvm_init);663 new_global.setInitializer(llvm_init);
664 global.replaceAllUsesWith(new_global);664 // replaceAllUsesWith requires the type to be unchanged. So we bitcast
665 // the new global to the old type and use that as the thing to replace
666 // old uses.
667 const new_global_ptr = new_global.constBitCast(global.typeOf());
668 global.replaceAllUsesWith(new_global_ptr);
665 dg.object.decl_map.putAssumeCapacity(decl, new_global);669 dg.object.decl_map.putAssumeCapacity(decl, new_global);
666 new_global.takeName(global);670 new_global.takeName(global);
667 global.deleteGlobal();671 global.deleteGlobal();
...@@ -683,9 +687,6 @@ pub const DeclGen = struct {...@@ -683,9 +687,6 @@ pub const DeclGen = struct {
683 const target = dg.module.getTarget();687 const target = dg.module.getTarget();
684 const sret = firstParamSRet(fn_info, target);688 const sret = firstParamSRet(fn_info, target);
685689
686 const return_type = fn_info.return_type;
687 const raw_llvm_ret_ty = try dg.llvmType(return_type);
688
689 const fn_type = try dg.llvmType(zig_fn_type);690 const fn_type = try dg.llvmType(zig_fn_type);
690691
691 const fqn = try decl.getFullyQualifiedName(dg.gpa);692 const fqn = try decl.getFullyQualifiedName(dg.gpa);
...@@ -704,6 +705,8 @@ pub const DeclGen = struct {...@@ -704,6 +705,8 @@ pub const DeclGen = struct {
704 if (sret) {705 if (sret) {
705 dg.addArgAttr(llvm_fn, 0, "nonnull"); // Sret pointers must not be address 0706 dg.addArgAttr(llvm_fn, 0, "nonnull"); // Sret pointers must not be address 0
706 dg.addArgAttr(llvm_fn, 0, "noalias");707 dg.addArgAttr(llvm_fn, 0, "noalias");
708
709 const raw_llvm_ret_ty = try dg.llvmType(fn_info.return_type);
707 llvm_fn.addSretAttr(0, raw_llvm_ret_ty);710 llvm_fn.addSretAttr(0, raw_llvm_ret_ty);
708 }711 }
709712
...@@ -733,7 +736,7 @@ pub const DeclGen = struct {...@@ -733,7 +736,7 @@ pub const DeclGen = struct {
733 // Function attributes that are independent of analysis results of the function body.736 // Function attributes that are independent of analysis results of the function body.
734 dg.addCommonFnAttributes(llvm_fn);737 dg.addCommonFnAttributes(llvm_fn);
735738
736 if (return_type.isNoReturn()) {739 if (fn_info.return_type.isNoReturn()) {
737 dg.addFnAttr(llvm_fn, "noreturn");740 dg.addFnAttr(llvm_fn, "noreturn");
738 }741 }
739742
...@@ -820,23 +823,26 @@ pub const DeclGen = struct {...@@ -820,23 +823,26 @@ pub const DeclGen = struct {
820823
821 fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {824 fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
822 const gpa = dg.gpa;825 const gpa = dg.gpa;
826 const target = dg.module.getTarget();
823 switch (t.zigTypeTag()) {827 switch (t.zigTypeTag()) {
824 .Void, .NoReturn => return dg.context.voidType(),828 .Void, .NoReturn => return dg.context.voidType(),
825 .Int => {829 .Int => {
826 const info = t.intInfo(dg.module.getTarget());830 const info = t.intInfo(target);
831 assert(info.bits != 0);
827 return dg.context.intType(info.bits);832 return dg.context.intType(info.bits);
828 },833 },
829 .Enum => {834 .Enum => {
830 var buffer: Type.Payload.Bits = undefined;835 var buffer: Type.Payload.Bits = undefined;
831 const int_ty = t.intTagType(&buffer);836 const int_ty = t.intTagType(&buffer);
832 const bit_count = int_ty.intInfo(dg.module.getTarget()).bits;837 const bit_count = int_ty.intInfo(target).bits;
838 assert(bit_count != 0);
833 return dg.context.intType(bit_count);839 return dg.context.intType(bit_count);
834 },840 },
835 .Float => switch (t.floatBits(dg.module.getTarget())) {841 .Float => switch (t.floatBits(target)) {
836 16 => return dg.context.halfType(),842 16 => return dg.context.halfType(),
837 32 => return dg.context.floatType(),843 32 => return dg.context.floatType(),
838 64 => return dg.context.doubleType(),844 64 => return dg.context.doubleType(),
839 80 => return dg.context.x86FP80Type(),845 80 => return if (backendSupportsF80(target)) dg.context.x86FP80Type() else dg.context.intType(80),
840 128 => return dg.context.fp128Type(),846 128 => return dg.context.fp128Type(),
841 else => unreachable,847 else => unreachable,
842 },848 },
...@@ -855,7 +861,8 @@ pub const DeclGen = struct {...@@ -855,7 +861,8 @@ pub const DeclGen = struct {
855 const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());861 const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
856 const elem_ty = t.childType();862 const elem_ty = t.childType();
857 const lower_elem_ty = switch (elem_ty.zigTypeTag()) {863 const lower_elem_ty = switch (elem_ty.zigTypeTag()) {
858 .Opaque, .Array, .Fn => true,864 .Opaque, .Fn => true,
865 .Array => elem_ty.childType().hasRuntimeBits(),
859 else => elem_ty.hasRuntimeBits(),866 else => elem_ty.hasRuntimeBits(),
860 };867 };
861 const llvm_elem_ty = if (lower_elem_ty)868 const llvm_elem_ty = if (lower_elem_ty)
...@@ -885,9 +892,11 @@ pub const DeclGen = struct {...@@ -885,9 +892,11 @@ pub const DeclGen = struct {
885 else => unreachable,892 else => unreachable,
886 },893 },
887 .Array => {894 .Array => {
888 const elem_type = try dg.llvmType(t.childType());895 const elem_ty = t.childType();
896 assert(elem_ty.onePossibleValue() == null);
897 const elem_llvm_ty = try dg.llvmType(elem_ty);
889 const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);898 const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);
890 return elem_type.arrayType(@intCast(c_uint, total_len));899 return elem_llvm_ty.arrayType(@intCast(c_uint, total_len));
891 },900 },
892 .Vector => {901 .Vector => {
893 const elem_type = try dg.llvmType(t.childType());902 const elem_type = try dg.llvmType(t.childType());
...@@ -974,7 +983,6 @@ pub const DeclGen = struct {...@@ -974,7 +983,6 @@ pub const DeclGen = struct {
974983
975 if (struct_obj.layout == .Packed) {984 if (struct_obj.layout == .Packed) {
976 try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2);985 try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2);
977 const target = dg.module.getTarget();
978 comptime assert(Type.packed_struct_layout_version == 1);986 comptime assert(Type.packed_struct_layout_version == 1);
979 var offset: u64 = 0;987 var offset: u64 = 0;
980 var big_align: u32 = 0;988 var big_align: u32 = 0;
...@@ -1069,12 +1077,11 @@ pub const DeclGen = struct {...@@ -1069,12 +1077,11 @@ pub const DeclGen = struct {
1069 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());1077 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
10701078
1071 const union_obj = t.cast(Type.Payload.Union).?.data;1079 const union_obj = t.cast(Type.Payload.Union).?.data;
1072 const target = dg.module.getTarget();
1073 if (t.unionTagType()) |enum_tag_ty| {1080 if (t.unionTagType()) |enum_tag_ty| {
1074 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
1075 const layout = union_obj.getLayout(target, true);1081 const layout = union_obj.getLayout(target, true);
10761082
1077 if (layout.payload_size == 0) {1083 if (layout.payload_size == 0) {
1084 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
1078 gop.value_ptr.* = enum_tag_llvm_ty;1085 gop.value_ptr.* = enum_tag_llvm_ty;
1079 return enum_tag_llvm_ty;1086 return enum_tag_llvm_ty;
1080 }1087 }
...@@ -1105,6 +1112,7 @@ pub const DeclGen = struct {...@@ -1105,6 +1112,7 @@ pub const DeclGen = struct {
1105 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);1112 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
1106 return llvm_union_ty;1113 return llvm_union_ty;
1107 }1114 }
1115 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
11081116
1109 // Put the tag before or after the payload depending on which one's1117 // Put the tag before or after the payload depending on which one's
1110 // alignment is greater.1118 // alignment is greater.
...@@ -1137,20 +1145,19 @@ pub const DeclGen = struct {...@@ -1137,20 +1145,19 @@ pub const DeclGen = struct {
1137 },1145 },
1138 .Fn => {1146 .Fn => {
1139 const fn_info = t.fnInfo();1147 const fn_info = t.fnInfo();
1140 const target = dg.module.getTarget();
1141 const sret = firstParamSRet(fn_info, target);1148 const sret = firstParamSRet(fn_info, target);
1142 const return_type = fn_info.return_type;1149 const return_type = fn_info.return_type;
1143 const raw_llvm_ret_ty = try dg.llvmType(return_type);1150 const llvm_sret_ty = if (return_type.hasRuntimeBits())
1144 const llvm_ret_ty = if (!return_type.hasRuntimeBits() or sret)1151 try dg.llvmType(return_type)
1145 dg.context.voidType()
1146 else1152 else
1147 raw_llvm_ret_ty;1153 dg.context.voidType();
1154 const llvm_ret_ty = if (sret) dg.context.voidType() else llvm_sret_ty;
11481155
1149 var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);1156 var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
1150 defer llvm_params.deinit();1157 defer llvm_params.deinit();
11511158
1152 if (sret) {1159 if (sret) {
1153 try llvm_params.append(raw_llvm_ret_ty.pointerType(0));1160 try llvm_params.append(llvm_sret_ty.pointerType(0));
1154 }1161 }
11551162
1156 for (fn_info.param_types) |param_ty| {1163 for (fn_info.param_types) |param_ty| {
...@@ -1203,6 +1210,7 @@ pub const DeclGen = struct {...@@ -1203,6 +1210,7 @@ pub const DeclGen = struct {
1203 const bigint = tv.val.toBigInt(&bigint_space);1210 const bigint = tv.val.toBigInt(&bigint_space);
1204 const target = dg.module.getTarget();1211 const target = dg.module.getTarget();
1205 const int_info = tv.ty.intInfo(target);1212 const int_info = tv.ty.intInfo(target);
1213 assert(int_info.bits != 0);
1206 const llvm_type = dg.context.intType(int_info.bits);1214 const llvm_type = dg.context.intType(int_info.bits);
12071215
1208 const unsigned_val = v: {1216 const unsigned_val = v: {
...@@ -1253,19 +1261,34 @@ pub const DeclGen = struct {...@@ -1253,19 +1261,34 @@ pub const DeclGen = struct {
1253 },1261 },
1254 .Float => {1262 .Float => {
1255 const llvm_ty = try dg.llvmType(tv.ty);1263 const llvm_ty = try dg.llvmType(tv.ty);
1256 if (tv.ty.floatBits(dg.module.getTarget()) <= 64) {1264 const target = dg.module.getTarget();
1257 return llvm_ty.constReal(tv.val.toFloat(f64));1265 switch (tv.ty.floatBits(target)) {
1258 }1266 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)),
12591267 80 => {
1260 var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));1268 const float = tv.val.toFloat(f80);
1261 // LLVM seems to require that the lower half of the f128 be placed first1269 const repr = std.math.break_f80(float);
1262 // in the buffer.1270 const llvm_i80 = dg.context.intType(80);
1263 if (native_endian == .Big) {1271 var x = llvm_i80.constInt(repr.exp, .False);
1264 std.mem.swap(u64, &buf[0], &buf[1]);1272 x = x.constShl(llvm_i80.constInt(64, .False));
1273 x = x.constOr(llvm_i80.constInt(repr.fraction, .False));
1274 if (backendSupportsF80(target)) {
1275 return x.constBitCast(llvm_ty);
1276 } else {
1277 return x;
1278 }
1279 },
1280 128 => {
1281 var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));
1282 // LLVM seems to require that the lower half of the f128 be placed first
1283 // in the buffer.
1284 if (native_endian == .Big) {
1285 std.mem.swap(u64, &buf[0], &buf[1]);
1286 }
1287 const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf);
1288 return int.constBitCast(llvm_ty);
1289 },
1290 else => unreachable,
1265 }1291 }
1266
1267 const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf);
1268 return int.constBitCast(llvm_ty);
1269 },1292 },
1270 .Pointer => switch (tv.val.tag()) {1293 .Pointer => switch (tv.val.tag()) {
1271 .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),1294 .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),
...@@ -1348,14 +1371,24 @@ pub const DeclGen = struct {...@@ -1348,14 +1371,24 @@ pub const DeclGen = struct {
1348 const gpa = dg.gpa;1371 const gpa = dg.gpa;
1349 const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len);1372 const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len);
1350 defer gpa.free(llvm_elems);1373 defer gpa.free(llvm_elems);
1374 var need_unnamed = false;
1351 for (elem_vals) |elem_val, i| {1375 for (elem_vals) |elem_val, i| {
1352 llvm_elems[i] = try dg.genTypedValue(.{ .ty = elem_ty, .val = elem_val });1376 llvm_elems[i] = try dg.genTypedValue(.{ .ty = elem_ty, .val = elem_val });
1377 need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[i]);
1378 }
1379 if (need_unnamed) {
1380 return dg.context.constStruct(
1381 llvm_elems.ptr,
1382 @intCast(c_uint, llvm_elems.len),
1383 .True,
1384 );
1385 } else {
1386 const llvm_elem_ty = try dg.llvmType(elem_ty);
1387 return llvm_elem_ty.constArray(
1388 llvm_elems.ptr,
1389 @intCast(c_uint, llvm_elems.len),
1390 );
1353 }1391 }
1354 const llvm_elem_ty = try dg.llvmType(elem_ty);
1355 return llvm_elem_ty.constArray(
1356 llvm_elems.ptr,
1357 @intCast(c_uint, llvm_elems.len),
1358 );
1359 },1392 },
1360 .repeated => {1393 .repeated => {
1361 const val = tv.val.castTag(.repeated).?.data;1394 const val = tv.val.castTag(.repeated).?.data;
...@@ -1366,25 +1399,46 @@ pub const DeclGen = struct {...@@ -1366,25 +1399,46 @@ pub const DeclGen = struct {
1366 const gpa = dg.gpa;1399 const gpa = dg.gpa;
1367 const llvm_elems = try gpa.alloc(*const llvm.Value, len_including_sent);1400 const llvm_elems = try gpa.alloc(*const llvm.Value, len_including_sent);
1368 defer gpa.free(llvm_elems);1401 defer gpa.free(llvm_elems);
1369 for (llvm_elems[0..len]) |*elem| {1402
1370 elem.* = try dg.genTypedValue(.{ .ty = elem_ty, .val = val });1403 var need_unnamed = false;
1404 if (len != 0) {
1405 for (llvm_elems[0..len]) |*elem| {
1406 elem.* = try dg.genTypedValue(.{ .ty = elem_ty, .val = val });
1407 }
1408 need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[0]);
1371 }1409 }
1410
1372 if (sentinel) |sent| {1411 if (sentinel) |sent| {
1373 llvm_elems[len] = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent });1412 llvm_elems[len] = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent });
1413 need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[len]);
1414 }
1415
1416 if (need_unnamed) {
1417 return dg.context.constStruct(
1418 llvm_elems.ptr,
1419 @intCast(c_uint, llvm_elems.len),
1420 .True,
1421 );
1422 } else {
1423 const llvm_elem_ty = try dg.llvmType(elem_ty);
1424 return llvm_elem_ty.constArray(
1425 llvm_elems.ptr,
1426 @intCast(c_uint, llvm_elems.len),
1427 );
1374 }1428 }
1375 const llvm_elem_ty = try dg.llvmType(elem_ty);
1376 return llvm_elem_ty.constArray(
1377 llvm_elems.ptr,
1378 @intCast(c_uint, llvm_elems.len),
1379 );
1380 },1429 },
1381 .empty_array_sentinel => {1430 .empty_array_sentinel => {
1382 const elem_ty = tv.ty.elemType();1431 const elem_ty = tv.ty.elemType();
1383 const sent_val = tv.ty.sentinel().?;1432 const sent_val = tv.ty.sentinel().?;
1384 const sentinel = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent_val });1433 const sentinel = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent_val });
1385 const llvm_elems: [1]*const llvm.Value = .{sentinel};1434 const llvm_elems: [1]*const llvm.Value = .{sentinel};
1386 const llvm_elem_ty = try dg.llvmType(elem_ty);1435 const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]);
1387 return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);1436 if (need_unnamed) {
1437 return dg.context.constStruct(&llvm_elems, llvm_elems.len, .True);
1438 } else {
1439 const llvm_elem_ty = try dg.llvmType(elem_ty);
1440 return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);
1441 }
1388 },1442 },
1389 else => unreachable,1443 else => unreachable,
1390 },1444 },
...@@ -1472,7 +1526,7 @@ pub const DeclGen = struct {...@@ -1472,7 +1526,7 @@ pub const DeclGen = struct {
1472 var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);1526 var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);
1473 defer llvm_fields.deinit(gpa);1527 defer llvm_fields.deinit(gpa);
14741528
1475 var make_unnamed_struct = false;1529 var need_unnamed = false;
1476 const struct_obj = tv.ty.castTag(.@"struct").?.data;1530 const struct_obj = tv.ty.castTag(.@"struct").?.data;
1477 if (struct_obj.layout == .Packed) {1531 if (struct_obj.layout == .Packed) {
1478 const target = dg.module.getTarget();1532 const target = dg.module.getTarget();
...@@ -1573,14 +1627,13 @@ pub const DeclGen = struct {...@@ -1573,14 +1627,13 @@ pub const DeclGen = struct {
1573 .val = field_val,1627 .val = field_val,
1574 });1628 });
15751629
1576 make_unnamed_struct = make_unnamed_struct or1630 need_unnamed = need_unnamed or dg.isUnnamedType(field_ty, field_llvm_val);
1577 dg.isUnnamedType(field_ty, field_llvm_val);
15781631
1579 llvm_fields.appendAssumeCapacity(field_llvm_val);1632 llvm_fields.appendAssumeCapacity(field_llvm_val);
1580 }1633 }
1581 }1634 }
15821635
1583 if (make_unnamed_struct) {1636 if (need_unnamed) {
1584 return dg.context.constStruct(1637 return dg.context.constStruct(
1585 llvm_fields.items.ptr,1638 llvm_fields.items.ptr,
1586 @intCast(c_uint, llvm_fields.items.len),1639 @intCast(c_uint, llvm_fields.items.len),
...@@ -1836,7 +1889,11 @@ pub const DeclGen = struct {...@@ -1836,7 +1889,11 @@ pub const DeclGen = struct {
1836 try self.resolveGlobalDecl(decl);1889 try self.resolveGlobalDecl(decl);
18371890
1838 const llvm_type = try self.llvmType(tv.ty);1891 const llvm_type = try self.llvmType(tv.ty);
1839 return llvm_val.constBitCast(llvm_type);1892 if (tv.ty.zigTypeTag() == .Int) {
1893 return llvm_val.constPtrToInt(llvm_type);
1894 } else {
1895 return llvm_val.constBitCast(llvm_type);
1896 }
1840 }1897 }
18411898
1842 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {1899 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
...@@ -2215,7 +2272,6 @@ pub const FuncGen = struct {...@@ -2215,7 +2272,6 @@ pub const FuncGen = struct {
2215 };2272 };
2216 const fn_info = zig_fn_ty.fnInfo();2273 const fn_info = zig_fn_ty.fnInfo();
2217 const return_type = fn_info.return_type;2274 const return_type = fn_info.return_type;
2218 const llvm_ret_ty = try self.dg.llvmType(return_type);
2219 const llvm_fn = try self.resolveInst(pl_op.operand);2275 const llvm_fn = try self.resolveInst(pl_op.operand);
2220 const target = self.dg.module.getTarget();2276 const target = self.dg.module.getTarget();
2221 const sret = firstParamSRet(fn_info, target);2277 const sret = firstParamSRet(fn_info, target);
...@@ -2224,6 +2280,7 @@ pub const FuncGen = struct {...@@ -2224,6 +2280,7 @@ pub const FuncGen = struct {
2224 defer llvm_args.deinit();2280 defer llvm_args.deinit();
22252281
2226 const ret_ptr = if (!sret) null else blk: {2282 const ret_ptr = if (!sret) null else blk: {
2283 const llvm_ret_ty = try self.dg.llvmType(return_type);
2227 const ret_ptr = self.buildAlloca(llvm_ret_ty);2284 const ret_ptr = self.buildAlloca(llvm_ret_ty);
2228 ret_ptr.setAlignment(return_type.abiAlignment(target));2285 ret_ptr.setAlignment(return_type.abiAlignment(target));
2229 try llvm_args.append(ret_ptr);2286 try llvm_args.append(ret_ptr);
...@@ -2258,6 +2315,7 @@ pub const FuncGen = struct {...@@ -2258,6 +2315,7 @@ pub const FuncGen = struct {
2258 } else if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits()) {2315 } else if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits()) {
2259 return null;2316 return null;
2260 } else if (sret) {2317 } else if (sret) {
2318 const llvm_ret_ty = try self.dg.llvmType(return_type);
2261 call.setCallSret(llvm_ret_ty);2319 call.setCallSret(llvm_ret_ty);
2262 return ret_ptr;2320 return ret_ptr;
2263 } else {2321 } else {
...@@ -5339,3 +5397,12 @@ fn isByRef(ty: Type) bool {...@@ -5339,3 +5397,12 @@ fn isByRef(ty: Type) bool {
5339 },5397 },
5340 }5398 }
5341}5399}
5400
5401/// This function returns true if we expect LLVM to lower x86_fp80 correctly
5402/// and false if we expect LLVM to crash if it counters an x86_fp80 type.
5403fn backendSupportsF80(target: std.Target) bool {
5404 return switch (target.cpu.arch) {
5405 .x86_64, .i386 => true,
5406 else => false,
5407 };
5408}
src/stage1/codegen.cpp+43-18
...@@ -8195,17 +8195,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n...@@ -8195,17 +8195,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
8195 case 64:8195 case 64:
8196 return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64);8196 return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64);
8197 case 80: {8197 case 80: {
8198 uint64_t buf[2];8198 LLVMTypeRef llvm_i80 = LLVMIntType(80);
8199 memcpy(&buf, &const_val->data.x_f80, 16);8199 LLVMValueRef x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false);
8200#if ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN8200 x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false));
8201 uint64_t tmp = buf[0];8201 x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false));
8202 buf[0] = buf[1];8202 if (target_has_f80(g->zig_target)) {
8203 buf[1] = tmp;8203 return LLVMConstBitCast(x, LLVMX86FP80Type());
8204#endif8204 } else {
8205 LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf);8205 return x;
8206 if (!target_has_f80(g->zig_target)) return as_i128;8206 }
8207 LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80));
8208 return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry));
8209 }8207 }
8210 case 128:8208 case 128:
8211 {8209 {
...@@ -9429,19 +9427,46 @@ static void define_builtin_types(CodeGen *g) {...@@ -9429,19 +9427,46 @@ static void define_builtin_types(CodeGen *g) {
94299427
9430 {9428 {
9431 ZigType *entry = new_type_table_entry(ZigTypeIdFloat);9429 ZigType *entry = new_type_table_entry(ZigTypeIdFloat);
9430 entry->size_in_bits = 80;
9431
9432 buf_init_from_str(&entry->name, "f80");
9433 entry->data.floating.bit_count = 80;
9434
9432 if (target_has_f80(g->zig_target)) {9435 if (target_has_f80(g->zig_target)) {
9433 entry->llvm_type = LLVMX86FP80Type();9436 entry->llvm_type = LLVMX86FP80Type();
9437
9438 // Note the following u64 alignments:
9439 // x86-linux: 4
9440 // x86-windows: 8
9441 // LLVM makes x86_fp80 have the following alignment and sizes regardless
9442 // of operating system:
9443 // x86_64: size=16, align=16
9444 // x86: size=12, align=4
9445 // However in Zig we override x86-windows to have size=16, align=16
9446 // in order for the property to hold that u80 and f80 have the same ABI size.
9447 unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type());
9448
9449 if (u64_alignment >= 8) {
9450 entry->abi_size = 16;
9451 entry->abi_align = 16;
9452 } else if (u64_alignment >= 4) {
9453 entry->abi_size = 12;
9454 entry->abi_align = 4;
9455 } else {
9456 entry->abi_size = 10;
9457 entry->abi_align = u64_alignment;
9458 }
9434 } else {9459 } else {
9435 // We use i128 here instead of x86_fp80 because on targets such as arm,9460 // We use an int here instead of x86_fp80 because on targets such as arm,
9436 // LLVM will give "ERROR: Cannot select" for any instructions involving9461 // LLVM will give "ERROR: Cannot select" for any instructions involving
9437 // the x86_fp80 type.9462 // the x86_fp80 type.
9438 entry->llvm_type = get_int_type(g, false, 128)->llvm_type;9463 ZigType *u80_ty = get_int_type(g, false, 80);
9464 assert(!target_has_f80(g->zig_target));
9465 assert(u80_ty->size_in_bits == entry->size_in_bits);
9466 entry->llvm_type = get_llvm_type(g, u80_ty);
9467 entry->abi_size = u80_ty->abi_size;
9468 entry->abi_align = u80_ty->abi_align;
9439 }9469 }
9440 entry->size_in_bits = 8 * 16;
9441 entry->abi_size = 16; // matches LLVMABISizeOfType(LLVMX86FP80Type())
9442 entry->abi_align = 16; // matches LLVMABIAlignmentOfType(LLVMX86FP80Type())
9443 buf_init_from_str(&entry->name, "f80");
9444 entry->data.floating.bit_count = 80;
94459470
9446 entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),9471 entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
9447 entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned());9472 entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned());
src/type.zig+50-5
...@@ -1877,9 +1877,28 @@ pub const Type = extern union {...@@ -1877,9 +1877,28 @@ pub const Type = extern union {
1877 .f16 => return 2,1877 .f16 => return 2,
1878 .f32 => return 4,1878 .f32 => return 4,
1879 .f64 => return 8,1879 .f64 => return 8,
1880 .f80 => return 16,
1881 .f128 => return 16,1880 .f128 => return 16,
1882 .c_longdouble => return 16,1881
1882 .f80 => switch (target.cpu.arch) {
1883 .i386 => return 4,
1884 .x86_64 => return 16,
1885 else => {
1886 var payload: Payload.Bits = .{
1887 .base = .{ .tag = .int_unsigned },
1888 .data = 80,
1889 };
1890 const u80_ty = initPayload(&payload.base);
1891 return abiAlignment(u80_ty, target);
1892 },
1893 },
1894 .c_longdouble => switch (CType.longdouble.sizeInBits(target)) {
1895 16 => return abiAlignment(Type.f16, target),
1896 32 => return abiAlignment(Type.f32, target),
1897 64 => return abiAlignment(Type.f64, target),
1898 80 => return abiAlignment(Type.f80, target),
1899 128 => return abiAlignment(Type.f128, target),
1900 else => unreachable,
1901 },
18831902
1884 .error_set,1903 .error_set,
1885 .error_set_single,1904 .error_set_single,
...@@ -2158,9 +2177,28 @@ pub const Type = extern union {...@@ -2158,9 +2177,28 @@ pub const Type = extern union {
2158 .f16 => return 2,2177 .f16 => return 2,
2159 .f32 => return 4,2178 .f32 => return 4,
2160 .f64 => return 8,2179 .f64 => return 8,
2161 .f80 => return 16,
2162 .f128 => return 16,2180 .f128 => return 16,
2163 .c_longdouble => return 16,2181
2182 .f80 => switch (target.cpu.arch) {
2183 .i386 => return 12,
2184 .x86_64 => return 16,
2185 else => {
2186 var payload: Payload.Bits = .{
2187 .base = .{ .tag = .int_unsigned },
2188 .data = 80,
2189 };
2190 const u80_ty = initPayload(&payload.base);
2191 return abiSize(u80_ty, target);
2192 },
2193 },
2194 .c_longdouble => switch (CType.longdouble.sizeInBits(target)) {
2195 16 => return abiSize(Type.f16, target),
2196 32 => return abiSize(Type.f32, target),
2197 64 => return abiSize(Type.f64, target),
2198 80 => return abiSize(Type.f80, target),
2199 128 => return abiSize(Type.f128, target),
2200 else => unreachable,
2201 },
21642202
2165 .error_set,2203 .error_set,
2166 .error_set_single,2204 .error_set_single,
...@@ -2349,7 +2387,7 @@ pub const Type = extern union {...@@ -2349,7 +2387,7 @@ pub const Type = extern union {
2349 .c_ulong => return CType.ulong.sizeInBits(target),2387 .c_ulong => return CType.ulong.sizeInBits(target),
2350 .c_longlong => return CType.longlong.sizeInBits(target),2388 .c_longlong => return CType.longlong.sizeInBits(target),
2351 .c_ulonglong => return CType.ulonglong.sizeInBits(target),2389 .c_ulonglong => return CType.ulonglong.sizeInBits(target),
2352 .c_longdouble => 128,2390 .c_longdouble => return CType.longdouble.sizeInBits(target),
23532391
2354 .error_set,2392 .error_set,
2355 .error_set_single,2393 .error_set_single,
...@@ -4772,6 +4810,13 @@ pub const Type = extern union {...@@ -4772,6 +4810,13 @@ pub const Type = extern union {
4772 pub const @"u8" = initTag(.u8);4810 pub const @"u8" = initTag(.u8);
4773 pub const @"u32" = initTag(.u32);4811 pub const @"u32" = initTag(.u32);
4774 pub const @"u64" = initTag(.u64);4812 pub const @"u64" = initTag(.u64);
4813
4814 pub const @"f16" = initTag(.f16);
4815 pub const @"f32" = initTag(.f32);
4816 pub const @"f64" = initTag(.f64);
4817 pub const @"f80" = initTag(.f80);
4818 pub const @"f128" = initTag(.f128);
4819
4775 pub const @"bool" = initTag(.bool);4820 pub const @"bool" = initTag(.bool);
4776 pub const @"usize" = initTag(.usize);4821 pub const @"usize" = initTag(.usize);
4777 pub const @"isize" = initTag(.isize);4822 pub const @"isize" = initTag(.isize);
src/value.zig+42-5
...@@ -1112,6 +1112,19 @@ pub const Value = extern union {...@@ -1112,6 +1112,19 @@ pub const Value = extern union {
1112 }1112 }
11131113
1114 fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {1114 fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {
1115 if (F == f80) {
1116 switch (target.cpu.arch) {
1117 .i386, .x86_64 => {
1118 const repr = std.math.break_f80(f);
1119 std.mem.writeIntLittle(u64, buffer[0..8], repr.fraction);
1120 std.mem.writeIntLittle(u16, buffer[8..10], repr.exp);
1121 // TODO set the rest of the bytes to undefined. should we use 0xaa
1122 // or is there a different way?
1123 return;
1124 },
1125 else => {},
1126 }
1127 }
1115 const Int = @Type(.{ .Int = .{1128 const Int = @Type(.{ .Int = .{
1116 .signedness = .unsigned,1129 .signedness = .unsigned,
1117 .bits = @typeInfo(F).Float.bits,1130 .bits = @typeInfo(F).Float.bits,
...@@ -1122,19 +1135,43 @@ pub const Value = extern union {...@@ -1122,19 +1135,43 @@ pub const Value = extern union {
11221135
1123 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {1136 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
1124 if (F == f80) {1137 if (F == f80) {
1125 // TODO: use std.math.F80Repr?1138 switch (target.cpu.arch) {
1126 const int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian());1139 .i386, .x86_64 => return std.math.make_f80(.{
1127 // TODO shouldn't this be a bitcast from u80 to f80 instead of u128 to f80?1140 .fraction = std.mem.readIntLittle(u64, buffer[0..8]),
1128 return @bitCast(F, int);1141 .exp = std.mem.readIntLittle(u16, buffer[8..10]),
1142 }),
1143 else => {},
1144 }
1129 }1145 }
1130 const Int = @Type(.{ .Int = .{1146 const Int = @Type(.{ .Int = .{
1131 .signedness = .unsigned,1147 .signedness = .unsigned,
1132 .bits = @typeInfo(F).Float.bits,1148 .bits = @typeInfo(F).Float.bits,
1133 } });1149 } });
1134 const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());1150 const int = readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());
1135 return @bitCast(F, int);1151 return @bitCast(F, int);
1136 }1152 }
11371153
1154 fn readInt(comptime Int: type, buffer: *const [@sizeOf(Int)]u8, endian: std.builtin.Endian) Int {
1155 var result: Int = 0;
1156 switch (endian) {
1157 .Big => {
1158 for (buffer) |byte| {
1159 result <<= 8;
1160 result |= byte;
1161 }
1162 },
1163 .Little => {
1164 var i: usize = buffer.len;
1165 while (i != 0) {
1166 i -= 1;
1167 result <<= 8;
1168 result |= buffer[i];
1169 }
1170 },
1171 }
1172 return result;
1173 }
1174
1138 /// Asserts that the value is a float or an integer.1175 /// Asserts that the value is a float or an integer.
1139 pub fn toFloat(val: Value, comptime T: type) T {1176 pub fn toFloat(val: Value, comptime T: type) T {
1140 return switch (val.tag()) {1177 return switch (val.tag()) {
test/behavior/floatop.zig+4-1
...@@ -5,7 +5,10 @@ const math = std.math;...@@ -5,7 +5,10 @@ const math = std.math;
5const pi = std.math.pi;5const pi = std.math.pi;
6const e = std.math.e;6const e = std.math.e;
7const Vector = std.meta.Vector;7const Vector = std.meta.Vector;
8const has_f80_rt = @import("builtin").cpu.arch == .x86_64;8const has_f80_rt = switch (builtin.cpu.arch) {
9 .x86_64, .i386 => true,
10 else => false,
11};
912
10const epsilon_16 = 0.001;13const epsilon_16 = 0.001;
11const epsilon = 0.000001;14const epsilon = 0.000001;
test/behavior/math.zig+11-2
...@@ -126,6 +126,13 @@ fn testOneCtz(comptime T: type, x: T) u32 {...@@ -126,6 +126,13 @@ fn testOneCtz(comptime T: type, x: T) u32 {
126}126}
127127
128test "@ctz vectors" {128test "@ctz vectors" {
129 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
130 // TODO this is tripping an LLVM assert:
131 // zig: /home/andy/Downloads/llvm-project-13/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp:198: llvm::LegalizeActionStep llvm::LegalizeRuleSet::apply(const llvm::LegalityQuery&) const: Assertion `mutationIsSane(Rule, Query, Mutation) && "legality mutation invalid for match"' failed.
132 // I need to report a zig issue and an llvm issue
133 return error.SkipZigTest;
134 }
135
129 try testCtzVectors();136 try testCtzVectors();
130 comptime try testCtzVectors();137 comptime try testCtzVectors();
131}138}
...@@ -981,12 +988,14 @@ test "NaN comparison" {...@@ -981,12 +988,14 @@ test "NaN comparison" {
981 try testNanEqNan(f32);988 try testNanEqNan(f32);
982 try testNanEqNan(f64);989 try testNanEqNan(f64);
983 try testNanEqNan(f128);990 try testNanEqNan(f128);
984 if (has_f80_rt and (builtin.zig_backend == .stage1)) try testNanEqNan(f80); // TODO
985 comptime try testNanEqNan(f16);991 comptime try testNanEqNan(f16);
986 comptime try testNanEqNan(f32);992 comptime try testNanEqNan(f32);
987 comptime try testNanEqNan(f64);993 comptime try testNanEqNan(f64);
988 comptime try testNanEqNan(f128);994 comptime try testNanEqNan(f128);
989 // comptime try testNanEqNan(f80); // TODO995
996 // TODO make this pass on all targets
997 // try testNanEqNan(f80);
998 // comptime try testNanEqNan(f80);
990}999}
9911000
992fn testNanEqNan(comptime F: type) !void {1001fn testNanEqNan(comptime F: type) !void {