Skip to content

Commit 1653a9b

Browse files
authored
Merge pull request #12098 from ziglang/llvm-riscv64
LLVM: implement signext/zeroext attributes
2 parents fad9574 + 92bc3cb commit 1653a9b

File tree

8 files changed

+148
-62
lines changed

8 files changed

+148
-62
lines changed

lib/compiler_rt/common.zig

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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.
71-
pub const F16T = if (builtin.cpu.arch.isAARCH64()) f16 else u16;
71+
pub 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

7377
pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
7478
switch (Z) {

lib/std/math/float.zig

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@ const assert = std.debug.assert;
33
const expect = std.testing.expect;
44

55
/// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic.
6-
fn mantissaOne(comptime T: type) comptime_int {
6+
inline 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.
11-
fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T {
12-
const TBits = std.meta.Int(.unsigned, @bitSizeOf(T));
11+
inline 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.
18-
pub fn floatExponentBits(comptime T: type) comptime_int {
19-
assert(@typeInfo(T) == .Float);
18+
pub 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.
32-
pub fn floatMantissaBits(comptime T: type) comptime_int {
33-
assert(@typeInfo(T) == .Float);
32+
pub 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.
46-
pub fn floatFractionalBits(comptime T: type) comptime_int {
47-
assert(@typeInfo(T) == .Float);
46+
pub 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.
64-
pub fn floatExponentMin(comptime T: type) comptime_int {
64+
pub 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.
70-
pub fn floatExponentMax(comptime T: type) comptime_int {
70+
pub 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.
75-
pub fn floatTrueMin(comptime T: type) T {
75+
pub 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.
80-
pub fn floatMin(comptime T: type) T {
80+
pub 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.
85-
pub fn floatMax(comptime T: type) T {
85+
pub 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.
91-
pub fn floatEps(comptime T: type) T {
91+
pub 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.
96-
pub fn inf(comptime T: type) T {
96+
pub inline fn inf(comptime T: type) T {
9797
return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T));
9898
}
9999

100-
test "std.math.float" {
100+
test "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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@ const math = std.math;
33
const expect = std.testing.expect;
44

55
/// Returns whether x is an infinity, ignoring sign.
6-
pub fn isInf(x: anytype) bool {
6+
pub 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;
1010
return @bitCast(TBits, x) & remove_sign == @bitCast(TBits, math.inf(T));
1111
}
1212

1313
/// Returns whether x is an infinity with a positive sign.
14-
pub fn isPositiveInf(x: anytype) bool {
14+
pub 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.
19-
pub fn isNegativeInf(x: anytype) bool {
19+
pub inline fn isNegativeInf(x: anytype) bool {
2020
return x == -math.inf(@TypeOf(x));
2121
}
2222

src/Sema.zig

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

9172+
fn 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+
91389201
fn 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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

0 commit comments

Comments
 (0)