authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-13 19:15:19-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-13 19:15:19-04:00
log1653a9b2597c66cbcc88ea75d8a4b88c163584a5
tree9cbe5d66e5088006ac4b5d5b4861a3b8b25a7a54
parentfad95741db7529bbad873fb330c25d64ac765340
parent92bc3cbe27792be0300fb5f104c011a11f3cf40f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12098 from ziglang/llvm-riscv64

LLVM: implement signext/zeroext attributes

8 files changed, 148 insertions(+), 62 deletions(-)

lib/compiler_rt/common.zig+5-1
......@@ -68,7 +68,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) nore
6868/// need for extending them to wider fp types.
6969/// TODO remove this; do this type selection in the language rather than
7070/// here in compiler-rt.
71pub const F16T = if (builtin.cpu.arch.isAARCH64()) f16 else u16;
71pub const F16T = switch (builtin.cpu.arch) {
72 .aarch64, .aarch64_be, .aarch64_32 => f16,
73 .riscv64 => if (builtin.zig_backend == .stage1) u16 else f16,
74 else => u16,
75};
7276
7377pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
7478 switch (Z) {
lib/std/math/float.zig+17-17
......@@ -3,20 +3,20 @@ const assert = std.debug.assert;
33const expect = std.testing.expect;
44
55/// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic.
6fn mantissaOne(comptime T: type) comptime_int {
6inline fn mantissaOne(comptime T: type) comptime_int {
77 return if (@typeInfo(T).Float.bits == 80) 1 << floatFractionalBits(T) else 0;
88}
99
1010/// Creates floating point type T from an unbiased exponent and raw mantissa.
11fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T {
12 const TBits = std.meta.Int(.unsigned, @bitSizeOf(T));
11inline fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T {
12 const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } });
1313 const biased_exponent = @as(TBits, exponent + floatExponentMax(T));
1414 return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa));
1515}
1616
1717/// Returns the number of bits in the exponent of floating point type T.
18pub fn floatExponentBits(comptime T: type) comptime_int {
19 assert(@typeInfo(T) == .Float);
18pub inline fn floatExponentBits(comptime T: type) comptime_int {
19 comptime assert(@typeInfo(T) == .Float);
2020
2121 return switch (@typeInfo(T).Float.bits) {
2222 16 => 5,
......@@ -29,8 +29,8 @@ pub fn floatExponentBits(comptime T: type) comptime_int {
2929}
3030
3131/// Returns the number of bits in the mantissa of floating point type T.
32pub fn floatMantissaBits(comptime T: type) comptime_int {
33 assert(@typeInfo(T) == .Float);
32pub inline fn floatMantissaBits(comptime T: type) comptime_int {
33 comptime assert(@typeInfo(T) == .Float);
3434
3535 return switch (@typeInfo(T).Float.bits) {
3636 16 => 10,
......@@ -43,8 +43,8 @@ pub fn floatMantissaBits(comptime T: type) comptime_int {
4343}
4444
4545/// Returns the number of fractional bits in the mantissa of floating point type T.
46pub fn floatFractionalBits(comptime T: type) comptime_int {
47 assert(@typeInfo(T) == .Float);
46pub inline fn floatFractionalBits(comptime T: type) comptime_int {
47 comptime assert(@typeInfo(T) == .Float);
4848
4949 // standard IEEE floats have an implicit 0.m or 1.m integer part
5050 // f80 is special and has an explicitly stored bit in the MSB
......@@ -61,43 +61,43 @@ pub fn floatFractionalBits(comptime T: type) comptime_int {
6161
6262/// Returns the minimum exponent that can represent
6363/// a normalised value in floating point type T.
64pub fn floatExponentMin(comptime T: type) comptime_int {
64pub inline fn floatExponentMin(comptime T: type) comptime_int {
6565 return -floatExponentMax(T) + 1;
6666}
6767
6868/// Returns the maximum exponent that can represent
6969/// a normalised value in floating point type T.
70pub fn floatExponentMax(comptime T: type) comptime_int {
70pub inline fn floatExponentMax(comptime T: type) comptime_int {
7171 return (1 << (floatExponentBits(T) - 1)) - 1;
7272}
7373
7474/// Returns the smallest subnormal number representable in floating point type T.
75pub fn floatTrueMin(comptime T: type) T {
75pub inline fn floatTrueMin(comptime T: type) T {
7676 return reconstructFloat(T, floatExponentMin(T) - 1, 1);
7777}
7878
7979/// Returns the smallest normal number representable in floating point type T.
80pub fn floatMin(comptime T: type) T {
80pub inline fn floatMin(comptime T: type) T {
8181 return reconstructFloat(T, floatExponentMin(T), mantissaOne(T));
8282}
8383
8484/// Returns the largest normal number representable in floating point type T.
85pub fn floatMax(comptime T: type) T {
85pub inline fn floatMax(comptime T: type) T {
8686 const all1s_mantissa = (1 << floatMantissaBits(T)) - 1;
8787 return reconstructFloat(T, floatExponentMax(T), all1s_mantissa);
8888}
8989
9090/// Returns the machine epsilon of floating point type T.
91pub fn floatEps(comptime T: type) T {
91pub inline fn floatEps(comptime T: type) T {
9292 return reconstructFloat(T, -floatFractionalBits(T), mantissaOne(T));
9393}
9494
9595/// Returns the value inf for floating point type T.
96pub fn inf(comptime T: type) T {
96pub inline fn inf(comptime T: type) T {
9797 return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T));
9898}
9999
100test "std.math.float" {
100test "float bits" {
101101 inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
102102 // (1 +) for the sign bit, since it is separate from the other bits
103103 const size = 1 + floatExponentBits(T) + floatMantissaBits(T);
lib/std/math/isinf.zig+3-3
......@@ -3,7 +3,7 @@ const math = std.math;
33const expect = std.testing.expect;
44
55/// Returns whether x is an infinity, ignoring sign.
6pub fn isInf(x: anytype) bool {
6pub inline fn isInf(x: anytype) bool {
77 const T = @TypeOf(x);
88 const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
99 const remove_sign = ~@as(TBits, 0) >> 1;
......@@ -11,12 +11,12 @@ pub fn isInf(x: anytype) bool {
1111}
1212
1313/// Returns whether x is an infinity with a positive sign.
14pub fn isPositiveInf(x: anytype) bool {
14pub inline fn isPositiveInf(x: anytype) bool {
1515 return x == math.inf(@TypeOf(x));
1616}
1717
1818/// Returns whether x is an infinity with a negative sign.
19pub fn isNegativeInf(x: anytype) bool {
19pub inline fn isNegativeInf(x: anytype) bool {
2020 return x == -math.inf(@TypeOf(x));
2121}
2222
src/Sema.zig+42
......@@ -22571,6 +22571,48 @@ fn bitCastVal(
2257122571 const target = sema.mod.getTarget();
2257222572 if (old_ty.eql(new_ty, sema.mod)) return val;
2257322573
22574 // Some conversions have a bitwise definition that ignores in-memory layout,
22575 // such as converting between f80 and u80.
22576
22577 if (old_ty.eql(Type.f80, sema.mod) and new_ty.isAbiInt()) {
22578 const float = val.toFloat(f80);
22579 switch (new_ty.intInfo(target).signedness) {
22580 .signed => {
22581 const int = @bitCast(i80, float);
22582 const limbs = try sema.arena.alloc(std.math.big.Limb, 2);
22583 const big_int = std.math.big.int.Mutable.init(limbs, int);
22584 return Value.fromBigInt(sema.arena, big_int.toConst());
22585 },
22586 .unsigned => {
22587 const int = @bitCast(u80, float);
22588 const limbs = try sema.arena.alloc(std.math.big.Limb, 2);
22589 const big_int = std.math.big.int.Mutable.init(limbs, int);
22590 return Value.fromBigInt(sema.arena, big_int.toConst());
22591 },
22592 }
22593 }
22594
22595 if (new_ty.eql(Type.f80, sema.mod) and old_ty.isAbiInt()) {
22596 var bigint_space: Value.BigIntSpace = undefined;
22597 var bigint = try val.toBigIntAdvanced(&bigint_space, target, sema.kit(block, src));
22598 switch (old_ty.intInfo(target).signedness) {
22599 .signed => {
22600 // This conversion cannot fail because we already checked bit size before
22601 // calling bitCastVal.
22602 const int = bigint.to(i80) catch unreachable;
22603 const float = @bitCast(f80, int);
22604 return Value.Tag.float_80.create(sema.arena, float);
22605 },
22606 .unsigned => {
22607 // This conversion cannot fail because we already checked bit size before
22608 // calling bitCastVal.
22609 const int = bigint.to(u80) catch unreachable;
22610 const float = @bitCast(f80, int);
22611 return Value.Tag.float_80.create(sema.arena, float);
22612 },
22613 }
22614 }
22615
2257422616 // For types with well-defined memory layouts, we serialize them a byte buffer,
2257522617 // then deserialize to the new type.
2257622618 const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target));
src/codegen/llvm.zig+64-1
......@@ -717,6 +717,11 @@ pub const Object = struct {
717717 const ret_ptr = if (sret) llvm_func.getParam(0) else null;
718718 const gpa = dg.gpa;
719719
720 if (ccAbiPromoteInt(fn_info.cc, target, fn_info.return_type)) |s| switch (s) {
721 .signed => dg.addAttr(llvm_func, 0, "signext"),
722 .unsigned => dg.addAttr(llvm_func, 0, "zeroext"),
723 };
724
720725 const err_return_tracing = fn_info.return_type.isError() and
721726 dg.module.comp.bin_file.options.error_return_tracing;
722727
......@@ -774,7 +779,10 @@ pub const Object = struct {
774779 );
775780 dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align);
776781 }
777 }
782 } else if (ccAbiPromoteInt(fn_info.cc, target, param_ty)) |s| switch (s) {
783 .signed => dg.addArgAttr(llvm_func, llvm_arg_i, "signext"),
784 .unsigned => dg.addArgAttr(llvm_func, llvm_arg_i, "zeroext"),
785 };
778786 }
779787 llvm_arg_i += 1;
780788 },
......@@ -887,6 +895,13 @@ pub const Object = struct {
887895 };
888896 try args.append(loaded);
889897 },
898 .as_u16 => {
899 const param = llvm_func.getParam(llvm_arg_i);
900 llvm_arg_i += 1;
901 const casted = builder.buildBitCast(param, dg.context.halfType(), "");
902 try args.ensureUnusedCapacity(1);
903 args.appendAssumeCapacity(casted);
904 },
890905 };
891906 }
892907
......@@ -2794,6 +2809,9 @@ pub const DeclGen = struct {
27942809 llvm_params.appendAssumeCapacity(big_int_ty);
27952810 }
27962811 },
2812 .as_u16 => {
2813 try llvm_params.append(dg.context.intType(16));
2814 },
27972815 };
27982816
27992817 return llvm.functionType(
......@@ -4234,6 +4252,12 @@ pub const FuncGen = struct {
42344252 llvm_args.appendAssumeCapacity(load_inst);
42354253 }
42364254 },
4255 .as_u16 => {
4256 const arg = args[it.zig_index - 1];
4257 const llvm_arg = try self.resolveInst(arg);
4258 const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), "");
4259 try llvm_args.append(casted);
4260 },
42374261 };
42384262
42394263 const call = self.builder.buildCall(
......@@ -8965,6 +8989,7 @@ const ParamTypeIterator = struct {
89658989 abi_sized_int,
89668990 multiple_llvm_ints,
89678991 slice,
8992 as_u16,
89688993 };
89698994
89708995 pub fn next(it: *ParamTypeIterator) ?Lowering {
......@@ -9025,6 +9050,15 @@ const ParamTypeIterator = struct {
90259050 else => false,
90269051 };
90279052 switch (it.target.cpu.arch) {
9053 .riscv32, .riscv64 => {
9054 it.zig_index += 1;
9055 it.llvm_index += 1;
9056 if (ty.tag() == .f16) {
9057 return .as_u16;
9058 } else {
9059 return .byval;
9060 }
9061 },
90289062 .mips, .mipsel => {
90299063 it.zig_index += 1;
90309064 it.llvm_index += 1;
......@@ -9135,6 +9169,35 @@ fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTyp
91359169 };
91369170}
91379171
9172fn ccAbiPromoteInt(
9173 cc: std.builtin.CallingConvention,
9174 target: std.Target,
9175 ty: Type,
9176) ?std.builtin.Signedness {
9177 switch (cc) {
9178 .Unspecified, .Inline, .Async => return null,
9179 else => {},
9180 }
9181 const int_info = switch (ty.zigTypeTag()) {
9182 .Int, .Enum, .ErrorSet => ty.intInfo(target),
9183 else => return null,
9184 };
9185 if (int_info.bits <= 16) return int_info.signedness;
9186 switch (target.cpu.arch) {
9187 .sparc64,
9188 .riscv64,
9189 .powerpc64,
9190 .powerpc64le,
9191 => {
9192 if (int_info.bits < 64) {
9193 return int_info.signedness;
9194 }
9195 },
9196 else => {},
9197 }
9198 return null;
9199}
9200
91389201fn isByRef(ty: Type) bool {
91399202 // For tuples and structs, if there are more than this many non-void
91409203 // fields, then we make it byref, otherwise byval.
src/type.zig+10
......@@ -4439,6 +4439,16 @@ pub const Type = extern union {
44394439 };
44404440 }
44414441
4442 /// Returns true for integers, enums, error sets, and packed structs.
4443 /// If this function returns true, then intInfo() can be called on the type.
4444 pub fn isAbiInt(ty: Type) bool {
4445 return switch (ty.zigTypeTag()) {
4446 .Int, .Enum, .ErrorSet => true,
4447 .Struct => ty.containerLayout() == .Packed,
4448 else => false,
4449 };
4450 }
4451
44424452 /// Asserts the type is an integer, enum, error set, or vector of one of them.
44434453 pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {
44444454 var ty = self;
src/value.zig+7-10
......@@ -1468,8 +1468,7 @@ pub const Value = extern union {
14681468 const repr = std.math.break_f80(f);
14691469 std.mem.writeInt(u64, buffer[0..8], repr.fraction, endian);
14701470 std.mem.writeInt(u16, buffer[8..10], repr.exp, endian);
1471 // TODO set the rest of the bytes to undefined. should we use 0xaa
1472 // or is there a different way?
1471 std.mem.set(u8, buffer[10..], 0);
14731472 return;
14741473 }
14751474 const Int = @Type(.{ .Int = .{
......@@ -1481,20 +1480,18 @@ pub const Value = extern union {
14811480 }
14821481
14831482 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
1483 const endian = target.cpu.arch.endian();
14841484 if (F == f80) {
1485 switch (target.cpu.arch) {
1486 .i386, .x86_64 => return std.math.make_f80(.{
1487 .fraction = std.mem.readIntLittle(u64, buffer[0..8]),
1488 .exp = std.mem.readIntLittle(u16, buffer[8..10]),
1489 }),
1490 else => {},
1491 }
1485 return std.math.make_f80(.{
1486 .fraction = readInt(u64, buffer[0..8], endian),
1487 .exp = readInt(u16, buffer[8..10], endian),
1488 });
14921489 }
14931490 const Int = @Type(.{ .Int = .{
14941491 .signedness = .unsigned,
14951492 .bits = @typeInfo(F).Float.bits,
14961493 } });
1497 const int = readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());
1494 const int = readInt(Int, buffer[0..@sizeOf(Int)], endian);
14981495 return @bitCast(F, int);
14991496 }
15001497
test/behavior/math.zig-30
......@@ -1168,11 +1168,6 @@ test "remainder division" {
11681168 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11691169 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11701170
1171 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
1172 // https://github.com/ziglang/zig/issues/12054
1173 return error.SkipZigTest;
1174 }
1175
11761171 comptime try remdiv(f16);
11771172 comptime try remdiv(f32);
11781173 comptime try remdiv(f64);
......@@ -1204,11 +1199,6 @@ test "float remainder division using @rem" {
12041199 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12051200 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12061201
1207 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
1208 // https://github.com/ziglang/zig/issues/12054
1209 return error.SkipZigTest;
1210 }
1211
12121202 comptime try frem(f16);
12131203 comptime try frem(f32);
12141204 comptime try frem(f64);
......@@ -1251,11 +1241,6 @@ test "float modulo division using @mod" {
12511241 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12521242 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12531243
1254 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
1255 // https://github.com/ziglang/zig/issues/12054
1256 return error.SkipZigTest;
1257 }
1258
12591244 comptime try fmod(f16);
12601245 comptime try fmod(f32);
12611246 comptime try fmod(f64);
......@@ -1431,11 +1416,6 @@ test "@ceil f80" {
14311416 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14321417 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
14331418
1434 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
1435 // https://github.com/ziglang/zig/issues/12054
1436 return error.SkipZigTest;
1437 }
1438
14391419 try testCeil(f80, 12.0);
14401420 comptime try testCeil(f80, 12.0);
14411421}
......@@ -1447,11 +1427,6 @@ test "@ceil f128" {
14471427 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14481428 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
14491429
1450 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
1451 // https://github.com/ziglang/zig/issues/12054
1452 return error.SkipZigTest;
1453 }
1454
14551430 try testCeil(f128, 12.0);
14561431 comptime try testCeil(f128, 12.0);
14571432}
......@@ -1600,11 +1575,6 @@ test "NaN comparison" {
16001575 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
16011576 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
16021577
1603 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
1604 // https://github.com/ziglang/zig/issues/12054
1605 return error.SkipZigTest;
1606 }
1607
16081578 try testNanEqNan(f16);
16091579 try testNanEqNan(f32);
16101580 try testNanEqNan(f64);