From 5572c67e73222716372762d30453cc44ca4339c0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 11 Jan 2023 22:39:25 +0200 Subject: [PATCH] add C ABI tests for exotic float types --- src/arch/x86_64/abi.zig | 18 ++++++- src/codegen/llvm.zig | 38 +++++++-------- test/c_abi/cfuncs.c | 103 +++++++++++++++++++++++++++++++++++++++- test/c_abi/main.zig | 80 +++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+), 24 deletions(-) diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 54c08e4aa9..9fb0f795e3 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -112,7 +112,16 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class { return result; }, .Float => switch (ty.floatBits(target)) { - 16, 32, 64 => { + 16 => { + if (ctx == .other) { + result[0] = .memory; + } else { + // TODO clang doesn't allow __fp16 as .ret or .arg + result[0] = .sse; + } + return result; + }, + 32, 64 => { result[0] = .sse; return result; }, @@ -120,11 +129,15 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class { // "Arguments of types__float128, _Decimal128 and__m128 are // split into two halves. The least significant ones belong // to class SSE, the most significant one to class SSEUP." + if (ctx == .other) { + result[0] = .memory; + return result; + } result[0] = .sse; result[1] = .sseup; return result; }, - else => { + 80 => { // "The 64-bit mantissa of arguments of type long double // belongs to classX87, the 16-bit exponent plus 6 bytes // of padding belongs to class X87UP." @@ -132,6 +145,7 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class { result[1] = .x87up; return result; }, + else => unreachable, }, .Vector => { const elem_ty = ty.childType(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bca116f18f..15ffe25ecd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -10395,7 +10395,12 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool .mips, .mipsel => return false, .x86_64 => switch (target.os.tag) { .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, - else => return x86_64_abi.classifySystemV(fn_info.return_type, target, .ret)[0] == .memory, + else => { + const class = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret); + if (class[0] == .memory) return true; + if (class[0] == .x87 and class[2] != .none) return true; + return false; + }, }, .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect, .aarch64, .aarch64_be => return aarch64_c_abi.classifyType(fn_info.return_type, target) == .memory, @@ -10469,22 +10474,18 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type { llvm_types_buffer[llvm_types_index] = dg.context.intType(64); llvm_types_index += 1; }, - .sse => { - llvm_types_buffer[llvm_types_index] = dg.context.doubleType(); - llvm_types_index += 1; - }, - .sseup => { + .sse, .sseup => { llvm_types_buffer[llvm_types_index] = dg.context.doubleType(); llvm_types_index += 1; }, .x87 => { + if (llvm_types_index != 0 or classes[2] != .none) { + return dg.context.voidType(); + } llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type(); llvm_types_index += 1; }, - .x87up => { - llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type(); - llvm_types_index += 1; - }, + .x87up => continue, .complex_x87 => { @panic("TODO"); }, @@ -10689,22 +10690,17 @@ const ParamTypeIterator = struct { llvm_types_buffer[llvm_types_index] = dg.context.intType(64); llvm_types_index += 1; }, - .sse => { - llvm_types_buffer[llvm_types_index] = dg.context.doubleType(); - llvm_types_index += 1; - }, - .sseup => { + .sse, .sseup => { llvm_types_buffer[llvm_types_index] = dg.context.doubleType(); llvm_types_index += 1; }, .x87 => { - llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type(); - llvm_types_index += 1; - }, - .x87up => { - llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type(); - llvm_types_index += 1; + it.zig_index += 1; + it.llvm_index += 1; + it.byval_attr = true; + return .byref; }, + .x87up => unreachable, .complex_x87 => { @panic("TODO"); }, diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 16851f1c7e..1cd2bbdcbc 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -4,7 +4,7 @@ #include #include -void zig_panic(); +void zig_panic(void); static void assert_or_panic(bool ok) { if (!ok) { @@ -60,6 +60,54 @@ static void assert_or_panic(bool ok) { # define ZIG_NO_COMPLEX #endif +#ifdef __x86_64__ +#define ZIG_NO_RAW_F16 +#endif + +#ifdef __i386__ +#define ZIG_NO_RAW_F16 +#endif + +#ifdef __mips__ +#define ZIG_NO_RAW_F16 +#endif + +#ifdef __riscv +#define ZIG_NO_RAW_F16 +#endif + +#ifdef __wasm__ +#define ZIG_NO_RAW_F16 +#endif + +#ifdef __powerpc__ +#define ZIG_NO_RAW_F16 +#endif + +#ifdef __aarch64__ +#define ZIG_NO_F128 +#endif + +#ifdef __arm__ +#define ZIG_NO_F128 +#endif + +#ifdef __mips__ +#define ZIG_NO_F128 +#endif + +#ifdef __riscv +#define ZIG_NO_F128 +#endif + +#ifdef __powerpc__ +#define ZIG_NO_F128 +#endif + +#ifdef __APPLE__ +#define ZIG_NO_F128 +#endif + #ifndef ZIG_NO_I128 struct i128 { __int128 value; @@ -884,3 +932,56 @@ void c_func_ptr_byval(void *a, void *b, struct ByVal in, unsigned long c, void * assert_or_panic((intptr_t)d == 4); assert_or_panic(e == 5); } + +#ifndef ZIG_NO_RAW_F16 +__fp16 c_f16(__fp16 a) { + assert_or_panic(a == 12); + return 34; +} +#endif + +typedef struct { + __fp16 a; +} f16_struct; +f16_struct c_f16_struct(f16_struct a) { + assert_or_panic(a.a == 12); + return (f16_struct){34}; +} + +#if defined __x86_64__ || defined __i386__ +typedef long double f80; +f80 c_f80(f80 a) { + assert_or_panic((double)a == 12.34); + return 56.78; +} +typedef struct { + f80 a; +} f80_struct; +f80_struct c_f80_struct(f80_struct a) { + assert_or_panic((double)a.a == 12.34); + return (f80_struct){56.78}; +} +typedef struct { + f80 a; + int b; +} f80_extra_struct; +f80_extra_struct c_f80_extra_struct(f80_extra_struct a) { + assert_or_panic((double)a.a == 12.34); + assert_or_panic(a.b == 42); + return (f80_extra_struct){56.78, 24}; +} +#endif + +#ifndef ZIG_NO_F128 +__float128 c_f128(__float128 a) { + assert_or_panic((double)a == 12.34); + return 56.78; +} +typedef struct { + __float128 a; +} f128_struct; +f128_struct c_f128_struct(f128_struct a) { + assert_or_panic((double)a.a == 12.34); + return (f128_struct){56.78}; +} +#endif diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 4b5ecb94ab..3bdb8c78a4 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -13,6 +13,9 @@ const expectEqual = std.testing.expectEqual; const has_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isARM() and !builtin.cpu.arch.isMIPS() and !builtin.cpu.arch.isPPC(); +const has_f128 = builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin(); +const has_f80 = builtin.cpu.arch.isX86(); + extern fn run_c_tests() void; export fn zig_panic() noreturn { @@ -1069,3 +1072,80 @@ test "C function that takes byval struct called via function pointer" { @as(c_ulong, 5), ); } + +extern fn c_f16(f16) f16; +test "f16 bare" { + if (!comptime builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + + const a = c_f16(12); + try expect(a == 34); +} + +const f16_struct = extern struct { + a: f16, +}; +extern fn c_f16_struct(f16_struct) f16_struct; +test "f16 struct" { + if (builtin.target.cpu.arch == .x86) return error.SkipZigTest; + if (comptime builtin.target.cpu.arch.isMIPS()) return error.SkipZigTest; + if (comptime builtin.target.cpu.arch.isPPC()) return error.SkipZigTest; + if (comptime builtin.target.cpu.arch.isPPC()) return error.SkipZigTest; + if (comptime builtin.cpu.arch.isARM() and builtin.mode != .Debug) return error.SkipZigTest; + + const a = c_f16_struct(.{ .a = 12 }); + try expect(a.a == 34); +} + +extern fn c_f80(f80) f80; +test "f80 bare" { + if (!has_f80) return error.SkipZigTest; + + const a = c_f80(12.34); + try expect(@floatCast(f64, a) == 56.78); +} + +const f80_struct = extern struct { + a: f80, +}; +extern fn c_f80_struct(f80_struct) f80_struct; +test "f80 struct" { + if (!has_f80) return error.SkipZigTest; + if (builtin.target.cpu.arch == .x86) return error.SkipZigTest; + if (builtin.mode != .Debug) return error.SkipZigTest; + + const a = c_f80_struct(.{ .a = 12.34 }); + try expect(@floatCast(f64, a.a) == 56.78); +} + +const f80_extra_struct = extern struct { + a: f80, + b: c_int, +}; +extern fn c_f80_extra_struct(f80_extra_struct) f80_extra_struct; +test "f80 extra struct" { + if (!has_f80) return error.SkipZigTest; + if (builtin.target.cpu.arch == .x86) return error.SkipZigTest; + + const a = c_f80_extra_struct(.{ .a = 12.34, .b = 42 }); + try expect(@floatCast(f64, a.a) == 56.78); + try expect(a.b == 24); +} + +extern fn c_f128(f128) f128; +test "f128 bare" { + if (!has_f128) return error.SkipZigTest; + + const a = c_f128(12.34); + try expect(@floatCast(f64, a) == 56.78); +} + +const f128_struct = extern struct { + a: f128, +}; +extern fn c_f128_struct(f128_struct) f128_struct; +test "f128 struct" { + if (!has_f128) return error.SkipZigTest; + + const a = c_f128_struct(.{ .a = 12.34 }); + try expect(@floatCast(f64, a.a) == 56.78); +}