authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-01-11 22:39:25+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-01-14 16:26:50+02:00
log5572c67e73222716372762d30453cc44ca4339c0
treeb3da065281add01a271a7142297e0423ae1c4610
parent474848ac0b6cd026502d85f0f77f0474516e887b

add C ABI tests for exotic float types


4 files changed, 215 insertions(+), 24 deletions(-)

src/arch/x86_64/abi.zig+16-2
......@@ -112,7 +112,16 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {
112112 return result;
113113 },
114114 .Float => switch (ty.floatBits(target)) {
115 16, 32, 64 => {
115 16 => {
116 if (ctx == .other) {
117 result[0] = .memory;
118 } else {
119 // TODO clang doesn't allow __fp16 as .ret or .arg
120 result[0] = .sse;
121 }
122 return result;
123 },
124 32, 64 => {
116125 result[0] = .sse;
117126 return result;
118127 },
......@@ -120,11 +129,15 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {
120129 // "Arguments of types__float128, _Decimal128 and__m128 are
121130 // split into two halves. The least significant ones belong
122131 // to class SSE, the most significant one to class SSEUP."
132 if (ctx == .other) {
133 result[0] = .memory;
134 return result;
135 }
123136 result[0] = .sse;
124137 result[1] = .sseup;
125138 return result;
126139 },
127 else => {
140 80 => {
128141 // "The 64-bit mantissa of arguments of type long double
129142 // belongs to classX87, the 16-bit exponent plus 6 bytes
130143 // of padding belongs to class X87UP."
......@@ -132,6 +145,7 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {
132145 result[1] = .x87up;
133146 return result;
134147 },
148 else => unreachable,
135149 },
136150 .Vector => {
137151 const elem_ty = ty.childType();
src/codegen/llvm.zig+17-21
......@@ -10395,7 +10395,12 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
1039510395 .mips, .mipsel => return false,
1039610396 .x86_64 => switch (target.os.tag) {
1039710397 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
10398 else => return x86_64_abi.classifySystemV(fn_info.return_type, target, .ret)[0] == .memory,
10398 else => {
10399 const class = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret);
10400 if (class[0] == .memory) return true;
10401 if (class[0] == .x87 and class[2] != .none) return true;
10402 return false;
10403 },
1039910404 },
1040010405 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,
1040110406 .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 {
1046910474 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
1047010475 llvm_types_index += 1;
1047110476 },
10472 .sse => {
10473 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10474 llvm_types_index += 1;
10475 },
10476 .sseup => {
10477 .sse, .sseup => {
1047710478 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
1047810479 llvm_types_index += 1;
1047910480 },
1048010481 .x87 => {
10482 if (llvm_types_index != 0 or classes[2] != .none) {
10483 return dg.context.voidType();
10484 }
1048110485 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
1048210486 llvm_types_index += 1;
1048310487 },
10484 .x87up => {
10485 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10486 llvm_types_index += 1;
10487 },
10488 .x87up => continue,
1048810489 .complex_x87 => {
1048910490 @panic("TODO");
1049010491 },
......@@ -10689,22 +10690,17 @@ const ParamTypeIterator = struct {
1068910690 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
1069010691 llvm_types_index += 1;
1069110692 },
10692 .sse => {
10693 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10694 llvm_types_index += 1;
10695 },
10696 .sseup => {
10693 .sse, .sseup => {
1069710694 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
1069810695 llvm_types_index += 1;
1069910696 },
1070010697 .x87 => {
10701 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10702 llvm_types_index += 1;
10703 },
10704 .x87up => {
10705 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10706 llvm_types_index += 1;
10698 it.zig_index += 1;
10699 it.llvm_index += 1;
10700 it.byval_attr = true;
10701 return .byref;
1070710702 },
10703 .x87up => unreachable,
1070810704 .complex_x87 => {
1070910705 @panic("TODO");
1071010706 },
test/c_abi/cfuncs.c+102-1
......@@ -4,7 +4,7 @@
44#include <stdlib.h>
55#include <string.h>
66
7void zig_panic();
7void zig_panic(void);
88
99static void assert_or_panic(bool ok) {
1010 if (!ok) {
......@@ -60,6 +60,54 @@ static void assert_or_panic(bool ok) {
6060# define ZIG_NO_COMPLEX
6161#endif
6262
63#ifdef __x86_64__
64#define ZIG_NO_RAW_F16
65#endif
66
67#ifdef __i386__
68#define ZIG_NO_RAW_F16
69#endif
70
71#ifdef __mips__
72#define ZIG_NO_RAW_F16
73#endif
74
75#ifdef __riscv
76#define ZIG_NO_RAW_F16
77#endif
78
79#ifdef __wasm__
80#define ZIG_NO_RAW_F16
81#endif
82
83#ifdef __powerpc__
84#define ZIG_NO_RAW_F16
85#endif
86
87#ifdef __aarch64__
88#define ZIG_NO_F128
89#endif
90
91#ifdef __arm__
92#define ZIG_NO_F128
93#endif
94
95#ifdef __mips__
96#define ZIG_NO_F128
97#endif
98
99#ifdef __riscv
100#define ZIG_NO_F128
101#endif
102
103#ifdef __powerpc__
104#define ZIG_NO_F128
105#endif
106
107#ifdef __APPLE__
108#define ZIG_NO_F128
109#endif
110
63111#ifndef ZIG_NO_I128
64112struct i128 {
65113 __int128 value;
......@@ -884,3 +932,56 @@ void c_func_ptr_byval(void *a, void *b, struct ByVal in, unsigned long c, void *
884932 assert_or_panic((intptr_t)d == 4);
885933 assert_or_panic(e == 5);
886934}
935
936#ifndef ZIG_NO_RAW_F16
937__fp16 c_f16(__fp16 a) {
938 assert_or_panic(a == 12);
939 return 34;
940}
941#endif
942
943typedef struct {
944 __fp16 a;
945} f16_struct;
946f16_struct c_f16_struct(f16_struct a) {
947 assert_or_panic(a.a == 12);
948 return (f16_struct){34};
949}
950
951#if defined __x86_64__ || defined __i386__
952typedef long double f80;
953f80 c_f80(f80 a) {
954 assert_or_panic((double)a == 12.34);
955 return 56.78;
956}
957typedef struct {
958 f80 a;
959} f80_struct;
960f80_struct c_f80_struct(f80_struct a) {
961 assert_or_panic((double)a.a == 12.34);
962 return (f80_struct){56.78};
963}
964typedef struct {
965 f80 a;
966 int b;
967} f80_extra_struct;
968f80_extra_struct c_f80_extra_struct(f80_extra_struct a) {
969 assert_or_panic((double)a.a == 12.34);
970 assert_or_panic(a.b == 42);
971 return (f80_extra_struct){56.78, 24};
972}
973#endif
974
975#ifndef ZIG_NO_F128
976__float128 c_f128(__float128 a) {
977 assert_or_panic((double)a == 12.34);
978 return 56.78;
979}
980typedef struct {
981 __float128 a;
982} f128_struct;
983f128_struct c_f128_struct(f128_struct a) {
984 assert_or_panic((double)a.a == 12.34);
985 return (f128_struct){56.78};
986}
987#endif
test/c_abi/main.zig+80
......@@ -13,6 +13,9 @@ const expectEqual = std.testing.expectEqual;
1313const has_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isARM() and
1414 !builtin.cpu.arch.isMIPS() and !builtin.cpu.arch.isPPC();
1515
16const has_f128 = builtin.cpu.arch.isX86() and !builtin.os.tag.isDarwin();
17const has_f80 = builtin.cpu.arch.isX86();
18
1619extern fn run_c_tests() void;
1720
1821export fn zig_panic() noreturn {
......@@ -1069,3 +1072,80 @@ test "C function that takes byval struct called via function pointer" {
10691072 @as(c_ulong, 5),
10701073 );
10711074}
1075
1076extern fn c_f16(f16) f16;
1077test "f16 bare" {
1078 if (!comptime builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
1079
1080 const a = c_f16(12);
1081 try expect(a == 34);
1082}
1083
1084const f16_struct = extern struct {
1085 a: f16,
1086};
1087extern fn c_f16_struct(f16_struct) f16_struct;
1088test "f16 struct" {
1089 if (builtin.target.cpu.arch == .x86) return error.SkipZigTest;
1090 if (comptime builtin.target.cpu.arch.isMIPS()) return error.SkipZigTest;
1091 if (comptime builtin.target.cpu.arch.isPPC()) return error.SkipZigTest;
1092 if (comptime builtin.target.cpu.arch.isPPC()) return error.SkipZigTest;
1093 if (comptime builtin.cpu.arch.isARM() and builtin.mode != .Debug) return error.SkipZigTest;
1094
1095 const a = c_f16_struct(.{ .a = 12 });
1096 try expect(a.a == 34);
1097}
1098
1099extern fn c_f80(f80) f80;
1100test "f80 bare" {
1101 if (!has_f80) return error.SkipZigTest;
1102
1103 const a = c_f80(12.34);
1104 try expect(@floatCast(f64, a) == 56.78);
1105}
1106
1107const f80_struct = extern struct {
1108 a: f80,
1109};
1110extern fn c_f80_struct(f80_struct) f80_struct;
1111test "f80 struct" {
1112 if (!has_f80) return error.SkipZigTest;
1113 if (builtin.target.cpu.arch == .x86) return error.SkipZigTest;
1114 if (builtin.mode != .Debug) return error.SkipZigTest;
1115
1116 const a = c_f80_struct(.{ .a = 12.34 });
1117 try expect(@floatCast(f64, a.a) == 56.78);
1118}
1119
1120const f80_extra_struct = extern struct {
1121 a: f80,
1122 b: c_int,
1123};
1124extern fn c_f80_extra_struct(f80_extra_struct) f80_extra_struct;
1125test "f80 extra struct" {
1126 if (!has_f80) return error.SkipZigTest;
1127 if (builtin.target.cpu.arch == .x86) return error.SkipZigTest;
1128
1129 const a = c_f80_extra_struct(.{ .a = 12.34, .b = 42 });
1130 try expect(@floatCast(f64, a.a) == 56.78);
1131 try expect(a.b == 24);
1132}
1133
1134extern fn c_f128(f128) f128;
1135test "f128 bare" {
1136 if (!has_f128) return error.SkipZigTest;
1137
1138 const a = c_f128(12.34);
1139 try expect(@floatCast(f64, a) == 56.78);
1140}
1141
1142const f128_struct = extern struct {
1143 a: f128,
1144};
1145extern fn c_f128_struct(f128_struct) f128_struct;
1146test "f128 struct" {
1147 if (!has_f128) return error.SkipZigTest;
1148
1149 const a = c_f128_struct(.{ .a = 12.34 });
1150 try expect(@floatCast(f64, a.a) == 56.78);
1151}