mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
Merge pull request #12098 from ziglang/llvm-riscv64
LLVM: implement signext/zeroext attributes
This commit is contained in:
commit
1653a9b259
@ -68,7 +68,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) nore
|
||||
/// need for extending them to wider fp types.
|
||||
/// TODO remove this; do this type selection in the language rather than
|
||||
/// here in compiler-rt.
|
||||
pub const F16T = if (builtin.cpu.arch.isAARCH64()) f16 else u16;
|
||||
pub const F16T = switch (builtin.cpu.arch) {
|
||||
.aarch64, .aarch64_be, .aarch64_32 => f16,
|
||||
.riscv64 => if (builtin.zig_backend == .stage1) u16 else f16,
|
||||
else => u16,
|
||||
};
|
||||
|
||||
pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
|
||||
switch (Z) {
|
||||
|
||||
@ -3,20 +3,20 @@ const assert = std.debug.assert;
|
||||
const expect = std.testing.expect;
|
||||
|
||||
/// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic.
|
||||
fn mantissaOne(comptime T: type) comptime_int {
|
||||
inline fn mantissaOne(comptime T: type) comptime_int {
|
||||
return if (@typeInfo(T).Float.bits == 80) 1 << floatFractionalBits(T) else 0;
|
||||
}
|
||||
|
||||
/// Creates floating point type T from an unbiased exponent and raw mantissa.
|
||||
fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T {
|
||||
const TBits = std.meta.Int(.unsigned, @bitSizeOf(T));
|
||||
inline fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T {
|
||||
const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } });
|
||||
const biased_exponent = @as(TBits, exponent + floatExponentMax(T));
|
||||
return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa));
|
||||
}
|
||||
|
||||
/// Returns the number of bits in the exponent of floating point type T.
|
||||
pub fn floatExponentBits(comptime T: type) comptime_int {
|
||||
assert(@typeInfo(T) == .Float);
|
||||
pub inline fn floatExponentBits(comptime T: type) comptime_int {
|
||||
comptime assert(@typeInfo(T) == .Float);
|
||||
|
||||
return switch (@typeInfo(T).Float.bits) {
|
||||
16 => 5,
|
||||
@ -29,8 +29,8 @@ pub fn floatExponentBits(comptime T: type) comptime_int {
|
||||
}
|
||||
|
||||
/// Returns the number of bits in the mantissa of floating point type T.
|
||||
pub fn floatMantissaBits(comptime T: type) comptime_int {
|
||||
assert(@typeInfo(T) == .Float);
|
||||
pub inline fn floatMantissaBits(comptime T: type) comptime_int {
|
||||
comptime assert(@typeInfo(T) == .Float);
|
||||
|
||||
return switch (@typeInfo(T).Float.bits) {
|
||||
16 => 10,
|
||||
@ -43,8 +43,8 @@ pub fn floatMantissaBits(comptime T: type) comptime_int {
|
||||
}
|
||||
|
||||
/// Returns the number of fractional bits in the mantissa of floating point type T.
|
||||
pub fn floatFractionalBits(comptime T: type) comptime_int {
|
||||
assert(@typeInfo(T) == .Float);
|
||||
pub inline fn floatFractionalBits(comptime T: type) comptime_int {
|
||||
comptime assert(@typeInfo(T) == .Float);
|
||||
|
||||
// standard IEEE floats have an implicit 0.m or 1.m integer part
|
||||
// f80 is special and has an explicitly stored bit in the MSB
|
||||
@ -61,43 +61,43 @@ pub fn floatFractionalBits(comptime T: type) comptime_int {
|
||||
|
||||
/// Returns the minimum exponent that can represent
|
||||
/// a normalised value in floating point type T.
|
||||
pub fn floatExponentMin(comptime T: type) comptime_int {
|
||||
pub inline fn floatExponentMin(comptime T: type) comptime_int {
|
||||
return -floatExponentMax(T) + 1;
|
||||
}
|
||||
|
||||
/// Returns the maximum exponent that can represent
|
||||
/// a normalised value in floating point type T.
|
||||
pub fn floatExponentMax(comptime T: type) comptime_int {
|
||||
pub inline fn floatExponentMax(comptime T: type) comptime_int {
|
||||
return (1 << (floatExponentBits(T) - 1)) - 1;
|
||||
}
|
||||
|
||||
/// Returns the smallest subnormal number representable in floating point type T.
|
||||
pub fn floatTrueMin(comptime T: type) T {
|
||||
pub inline fn floatTrueMin(comptime T: type) T {
|
||||
return reconstructFloat(T, floatExponentMin(T) - 1, 1);
|
||||
}
|
||||
|
||||
/// Returns the smallest normal number representable in floating point type T.
|
||||
pub fn floatMin(comptime T: type) T {
|
||||
pub inline fn floatMin(comptime T: type) T {
|
||||
return reconstructFloat(T, floatExponentMin(T), mantissaOne(T));
|
||||
}
|
||||
|
||||
/// Returns the largest normal number representable in floating point type T.
|
||||
pub fn floatMax(comptime T: type) T {
|
||||
pub inline fn floatMax(comptime T: type) T {
|
||||
const all1s_mantissa = (1 << floatMantissaBits(T)) - 1;
|
||||
return reconstructFloat(T, floatExponentMax(T), all1s_mantissa);
|
||||
}
|
||||
|
||||
/// Returns the machine epsilon of floating point type T.
|
||||
pub fn floatEps(comptime T: type) T {
|
||||
pub inline fn floatEps(comptime T: type) T {
|
||||
return reconstructFloat(T, -floatFractionalBits(T), mantissaOne(T));
|
||||
}
|
||||
|
||||
/// Returns the value inf for floating point type T.
|
||||
pub fn inf(comptime T: type) T {
|
||||
pub inline fn inf(comptime T: type) T {
|
||||
return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T));
|
||||
}
|
||||
|
||||
test "std.math.float" {
|
||||
test "float bits" {
|
||||
inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
|
||||
// (1 +) for the sign bit, since it is separate from the other bits
|
||||
const size = 1 + floatExponentBits(T) + floatMantissaBits(T);
|
||||
|
||||
@ -3,7 +3,7 @@ const math = std.math;
|
||||
const expect = std.testing.expect;
|
||||
|
||||
/// Returns whether x is an infinity, ignoring sign.
|
||||
pub fn isInf(x: anytype) bool {
|
||||
pub inline fn isInf(x: anytype) bool {
|
||||
const T = @TypeOf(x);
|
||||
const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
|
||||
const remove_sign = ~@as(TBits, 0) >> 1;
|
||||
@ -11,12 +11,12 @@ pub fn isInf(x: anytype) bool {
|
||||
}
|
||||
|
||||
/// Returns whether x is an infinity with a positive sign.
|
||||
pub fn isPositiveInf(x: anytype) bool {
|
||||
pub inline fn isPositiveInf(x: anytype) bool {
|
||||
return x == math.inf(@TypeOf(x));
|
||||
}
|
||||
|
||||
/// Returns whether x is an infinity with a negative sign.
|
||||
pub fn isNegativeInf(x: anytype) bool {
|
||||
pub inline fn isNegativeInf(x: anytype) bool {
|
||||
return x == -math.inf(@TypeOf(x));
|
||||
}
|
||||
|
||||
|
||||
42
src/Sema.zig
42
src/Sema.zig
@ -22571,6 +22571,48 @@ fn bitCastVal(
|
||||
const target = sema.mod.getTarget();
|
||||
if (old_ty.eql(new_ty, sema.mod)) return val;
|
||||
|
||||
// Some conversions have a bitwise definition that ignores in-memory layout,
|
||||
// such as converting between f80 and u80.
|
||||
|
||||
if (old_ty.eql(Type.f80, sema.mod) and new_ty.isAbiInt()) {
|
||||
const float = val.toFloat(f80);
|
||||
switch (new_ty.intInfo(target).signedness) {
|
||||
.signed => {
|
||||
const int = @bitCast(i80, float);
|
||||
const limbs = try sema.arena.alloc(std.math.big.Limb, 2);
|
||||
const big_int = std.math.big.int.Mutable.init(limbs, int);
|
||||
return Value.fromBigInt(sema.arena, big_int.toConst());
|
||||
},
|
||||
.unsigned => {
|
||||
const int = @bitCast(u80, float);
|
||||
const limbs = try sema.arena.alloc(std.math.big.Limb, 2);
|
||||
const big_int = std.math.big.int.Mutable.init(limbs, int);
|
||||
return Value.fromBigInt(sema.arena, big_int.toConst());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (new_ty.eql(Type.f80, sema.mod) and old_ty.isAbiInt()) {
|
||||
var bigint_space: Value.BigIntSpace = undefined;
|
||||
var bigint = try val.toBigIntAdvanced(&bigint_space, target, sema.kit(block, src));
|
||||
switch (old_ty.intInfo(target).signedness) {
|
||||
.signed => {
|
||||
// This conversion cannot fail because we already checked bit size before
|
||||
// calling bitCastVal.
|
||||
const int = bigint.to(i80) catch unreachable;
|
||||
const float = @bitCast(f80, int);
|
||||
return Value.Tag.float_80.create(sema.arena, float);
|
||||
},
|
||||
.unsigned => {
|
||||
// This conversion cannot fail because we already checked bit size before
|
||||
// calling bitCastVal.
|
||||
const int = bigint.to(u80) catch unreachable;
|
||||
const float = @bitCast(f80, int);
|
||||
return Value.Tag.float_80.create(sema.arena, float);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// For types with well-defined memory layouts, we serialize them a byte buffer,
|
||||
// then deserialize to the new type.
|
||||
const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target));
|
||||
|
||||
@ -717,6 +717,11 @@ pub const Object = struct {
|
||||
const ret_ptr = if (sret) llvm_func.getParam(0) else null;
|
||||
const gpa = dg.gpa;
|
||||
|
||||
if (ccAbiPromoteInt(fn_info.cc, target, fn_info.return_type)) |s| switch (s) {
|
||||
.signed => dg.addAttr(llvm_func, 0, "signext"),
|
||||
.unsigned => dg.addAttr(llvm_func, 0, "zeroext"),
|
||||
};
|
||||
|
||||
const err_return_tracing = fn_info.return_type.isError() and
|
||||
dg.module.comp.bin_file.options.error_return_tracing;
|
||||
|
||||
@ -774,7 +779,10 @@ pub const Object = struct {
|
||||
);
|
||||
dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align);
|
||||
}
|
||||
}
|
||||
} else if (ccAbiPromoteInt(fn_info.cc, target, param_ty)) |s| switch (s) {
|
||||
.signed => dg.addArgAttr(llvm_func, llvm_arg_i, "signext"),
|
||||
.unsigned => dg.addArgAttr(llvm_func, llvm_arg_i, "zeroext"),
|
||||
};
|
||||
}
|
||||
llvm_arg_i += 1;
|
||||
},
|
||||
@ -887,6 +895,13 @@ pub const Object = struct {
|
||||
};
|
||||
try args.append(loaded);
|
||||
},
|
||||
.as_u16 => {
|
||||
const param = llvm_func.getParam(llvm_arg_i);
|
||||
llvm_arg_i += 1;
|
||||
const casted = builder.buildBitCast(param, dg.context.halfType(), "");
|
||||
try args.ensureUnusedCapacity(1);
|
||||
args.appendAssumeCapacity(casted);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -2794,6 +2809,9 @@ pub const DeclGen = struct {
|
||||
llvm_params.appendAssumeCapacity(big_int_ty);
|
||||
}
|
||||
},
|
||||
.as_u16 => {
|
||||
try llvm_params.append(dg.context.intType(16));
|
||||
},
|
||||
};
|
||||
|
||||
return llvm.functionType(
|
||||
@ -4234,6 +4252,12 @@ pub const FuncGen = struct {
|
||||
llvm_args.appendAssumeCapacity(load_inst);
|
||||
}
|
||||
},
|
||||
.as_u16 => {
|
||||
const arg = args[it.zig_index - 1];
|
||||
const llvm_arg = try self.resolveInst(arg);
|
||||
const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), "");
|
||||
try llvm_args.append(casted);
|
||||
},
|
||||
};
|
||||
|
||||
const call = self.builder.buildCall(
|
||||
@ -8965,6 +8989,7 @@ const ParamTypeIterator = struct {
|
||||
abi_sized_int,
|
||||
multiple_llvm_ints,
|
||||
slice,
|
||||
as_u16,
|
||||
};
|
||||
|
||||
pub fn next(it: *ParamTypeIterator) ?Lowering {
|
||||
@ -9025,6 +9050,15 @@ const ParamTypeIterator = struct {
|
||||
else => false,
|
||||
};
|
||||
switch (it.target.cpu.arch) {
|
||||
.riscv32, .riscv64 => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
if (ty.tag() == .f16) {
|
||||
return .as_u16;
|
||||
} else {
|
||||
return .byval;
|
||||
}
|
||||
},
|
||||
.mips, .mipsel => {
|
||||
it.zig_index += 1;
|
||||
it.llvm_index += 1;
|
||||
@ -9135,6 +9169,35 @@ fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTyp
|
||||
};
|
||||
}
|
||||
|
||||
fn ccAbiPromoteInt(
|
||||
cc: std.builtin.CallingConvention,
|
||||
target: std.Target,
|
||||
ty: Type,
|
||||
) ?std.builtin.Signedness {
|
||||
switch (cc) {
|
||||
.Unspecified, .Inline, .Async => return null,
|
||||
else => {},
|
||||
}
|
||||
const int_info = switch (ty.zigTypeTag()) {
|
||||
.Int, .Enum, .ErrorSet => ty.intInfo(target),
|
||||
else => return null,
|
||||
};
|
||||
if (int_info.bits <= 16) return int_info.signedness;
|
||||
switch (target.cpu.arch) {
|
||||
.sparc64,
|
||||
.riscv64,
|
||||
.powerpc64,
|
||||
.powerpc64le,
|
||||
=> {
|
||||
if (int_info.bits < 64) {
|
||||
return int_info.signedness;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn isByRef(ty: Type) bool {
|
||||
// For tuples and structs, if there are more than this many non-void
|
||||
// fields, then we make it byref, otherwise byval.
|
||||
|
||||
10
src/type.zig
10
src/type.zig
@ -4439,6 +4439,16 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true for integers, enums, error sets, and packed structs.
|
||||
/// If this function returns true, then intInfo() can be called on the type.
|
||||
pub fn isAbiInt(ty: Type) bool {
|
||||
return switch (ty.zigTypeTag()) {
|
||||
.Int, .Enum, .ErrorSet => true,
|
||||
.Struct => ty.containerLayout() == .Packed,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is an integer, enum, error set, or vector of one of them.
|
||||
pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {
|
||||
var ty = self;
|
||||
|
||||
@ -1468,8 +1468,7 @@ pub const Value = extern union {
|
||||
const repr = std.math.break_f80(f);
|
||||
std.mem.writeInt(u64, buffer[0..8], repr.fraction, endian);
|
||||
std.mem.writeInt(u16, buffer[8..10], repr.exp, endian);
|
||||
// TODO set the rest of the bytes to undefined. should we use 0xaa
|
||||
// or is there a different way?
|
||||
std.mem.set(u8, buffer[10..], 0);
|
||||
return;
|
||||
}
|
||||
const Int = @Type(.{ .Int = .{
|
||||
@ -1481,20 +1480,18 @@ pub const Value = extern union {
|
||||
}
|
||||
|
||||
fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
|
||||
const endian = target.cpu.arch.endian();
|
||||
if (F == f80) {
|
||||
switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => return std.math.make_f80(.{
|
||||
.fraction = std.mem.readIntLittle(u64, buffer[0..8]),
|
||||
.exp = std.mem.readIntLittle(u16, buffer[8..10]),
|
||||
}),
|
||||
else => {},
|
||||
}
|
||||
return std.math.make_f80(.{
|
||||
.fraction = readInt(u64, buffer[0..8], endian),
|
||||
.exp = readInt(u16, buffer[8..10], endian),
|
||||
});
|
||||
}
|
||||
const Int = @Type(.{ .Int = .{
|
||||
.signedness = .unsigned,
|
||||
.bits = @typeInfo(F).Float.bits,
|
||||
} });
|
||||
const int = readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());
|
||||
const int = readInt(Int, buffer[0..@sizeOf(Int)], endian);
|
||||
return @bitCast(F, int);
|
||||
}
|
||||
|
||||
|
||||
@ -1168,11 +1168,6 @@ test "remainder division" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
|
||||
// https://github.com/ziglang/zig/issues/12054
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
comptime try remdiv(f16);
|
||||
comptime try remdiv(f32);
|
||||
comptime try remdiv(f64);
|
||||
@ -1204,11 +1199,6 @@ test "float remainder division using @rem" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
|
||||
// https://github.com/ziglang/zig/issues/12054
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
comptime try frem(f16);
|
||||
comptime try frem(f32);
|
||||
comptime try frem(f64);
|
||||
@ -1251,11 +1241,6 @@ test "float modulo division using @mod" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
|
||||
// https://github.com/ziglang/zig/issues/12054
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
comptime try fmod(f16);
|
||||
comptime try fmod(f32);
|
||||
comptime try fmod(f64);
|
||||
@ -1431,11 +1416,6 @@ test "@ceil f80" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
|
||||
// https://github.com/ziglang/zig/issues/12054
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
try testCeil(f80, 12.0);
|
||||
comptime try testCeil(f80, 12.0);
|
||||
}
|
||||
@ -1447,11 +1427,6 @@ test "@ceil f128" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
|
||||
// https://github.com/ziglang/zig/issues/12054
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
try testCeil(f128, 12.0);
|
||||
comptime try testCeil(f128, 12.0);
|
||||
}
|
||||
@ -1600,11 +1575,6 @@ test "NaN comparison" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
|
||||
// https://github.com/ziglang/zig/issues/12054
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
try testNanEqNan(f16);
|
||||
try testNanEqNan(f32);
|
||||
try testNanEqNan(f64);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user