Commit 5572c67e73

Veikka Tuominen <git@vexu.eu>
2023-01-11 21:39:25
add C ABI tests for exotic float types
1 parent 474848a
Changed files (4)
src
arch
x86_64
codegen
test
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();
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");
                                     },
test/c_abi/cfuncs.c
@@ -4,7 +4,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-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
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);
+}