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 {...@@ -112,7 +112,16 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {
112 return result;112 return result;
113 },113 },
114 .Float => switch (ty.floatBits(target)) {114 .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 => {
116 result[0] = .sse;125 result[0] = .sse;
117 return result;126 return result;
118 },127 },
...@@ -120,11 +129,15 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {...@@ -120,11 +129,15 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {
120 // "Arguments of types__float128, _Decimal128 and__m128 are129 // "Arguments of types__float128, _Decimal128 and__m128 are
121 // split into two halves. The least significant ones belong130 // split into two halves. The least significant ones belong
122 // to class SSE, the most significant one to class SSEUP."131 // to class SSE, the most significant one to class SSEUP."
132 if (ctx == .other) {
133 result[0] = .memory;
134 return result;
135 }
123 result[0] = .sse;136 result[0] = .sse;
124 result[1] = .sseup;137 result[1] = .sseup;
125 return result;138 return result;
126 },139 },
127 else => {140 80 => {
128 // "The 64-bit mantissa of arguments of type long double141 // "The 64-bit mantissa of arguments of type long double
129 // belongs to classX87, the 16-bit exponent plus 6 bytes142 // belongs to classX87, the 16-bit exponent plus 6 bytes
130 // of padding belongs to class X87UP."143 // of padding belongs to class X87UP."
...@@ -132,6 +145,7 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {...@@ -132,6 +145,7 @@ pub fn classifySystemV(ty: Type, target: Target, ctx: Context) [8]Class {
132 result[1] = .x87up;145 result[1] = .x87up;
133 return result;146 return result;
134 },147 },
148 else => unreachable,
135 },149 },
136 .Vector => {150 .Vector => {
137 const elem_ty = ty.childType();151 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...@@ -10395,7 +10395,12 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
10395 .mips, .mipsel => return false,10395 .mips, .mipsel => return false,
10396 .x86_64 => switch (target.os.tag) {10396 .x86_64 => switch (target.os.tag) {
10397 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,10397 .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 },
10399 },10404 },
10400 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,10405 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,
10401 .aarch64, .aarch64_be => return aarch64_c_abi.classifyType(fn_info.return_type, target) == .memory,10406 .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 {...@@ -10469,22 +10474,18 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10469 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);10474 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10470 llvm_types_index += 1;10475 llvm_types_index += 1;
10471 },10476 },
10472 .sse => {10477 .sse, .sseup => {
10473 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10474 llvm_types_index += 1;
10475 },
10476 .sseup => {
10477 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();10478 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10478 llvm_types_index += 1;10479 llvm_types_index += 1;
10479 },10480 },
10480 .x87 => {10481 .x87 => {
10482 if (llvm_types_index != 0 or classes[2] != .none) {
10483 return dg.context.voidType();
10484 }
10481 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();10485 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10482 llvm_types_index += 1;10486 llvm_types_index += 1;
10483 },10487 },
10484 .x87up => {10488 .x87up => continue,
10485 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10486 llvm_types_index += 1;
10487 },
10488 .complex_x87 => {10489 .complex_x87 => {
10489 @panic("TODO");10490 @panic("TODO");
10490 },10491 },
...@@ -10689,22 +10690,17 @@ const ParamTypeIterator = struct {...@@ -10689,22 +10690,17 @@ const ParamTypeIterator = struct {
10689 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);10690 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10690 llvm_types_index += 1;10691 llvm_types_index += 1;
10691 },10692 },
10692 .sse => {10693 .sse, .sseup => {
10693 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10694 llvm_types_index += 1;
10695 },
10696 .sseup => {
10697 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();10694 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10698 llvm_types_index += 1;10695 llvm_types_index += 1;
10699 },10696 },
10700 .x87 => {10697 .x87 => {
10701 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();10698 it.zig_index += 1;
10702 llvm_types_index += 1;10699 it.llvm_index += 1;
10703 },10700 it.byval_attr = true;
10704 .x87up => {10701 return .byref;
10705 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10706 llvm_types_index += 1;
10707 },10702 },
10703 .x87up => unreachable,
10708 .complex_x87 => {10704 .complex_x87 => {
10709 @panic("TODO");10705 @panic("TODO");
10710 },10706 },
test/c_abi/cfuncs.c+102-1
...@@ -4,7 +4,7 @@...@@ -4,7 +4,7 @@
4#include <stdlib.h>4#include <stdlib.h>
5#include <string.h>5#include <string.h>
66
7void zig_panic();7void zig_panic(void);
88
9static void assert_or_panic(bool ok) {9static void assert_or_panic(bool ok) {
10 if (!ok) {10 if (!ok) {
...@@ -60,6 +60,54 @@ static void assert_or_panic(bool ok) {...@@ -60,6 +60,54 @@ static void assert_or_panic(bool ok) {
60# define ZIG_NO_COMPLEX60# define ZIG_NO_COMPLEX
61#endif61#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
63#ifndef ZIG_NO_I128111#ifndef ZIG_NO_I128
64struct i128 {112struct i128 {
65 __int128 value;113 __int128 value;
...@@ -884,3 +932,56 @@ void c_func_ptr_byval(void *a, void *b, struct ByVal in, unsigned long c, void *...@@ -884,3 +932,56 @@ void c_func_ptr_byval(void *a, void *b, struct ByVal in, unsigned long c, void *
884 assert_or_panic((intptr_t)d == 4);932 assert_or_panic((intptr_t)d == 4);
885 assert_or_panic(e == 5);933 assert_or_panic(e == 5);
886}934}
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;...@@ -13,6 +13,9 @@ const expectEqual = std.testing.expectEqual;
13const has_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isARM() and13const has_i128 = builtin.cpu.arch != .x86 and !builtin.cpu.arch.isARM() and
14 !builtin.cpu.arch.isMIPS() and !builtin.cpu.arch.isPPC();14 !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
16extern fn run_c_tests() void;19extern fn run_c_tests() void;
1720
18export fn zig_panic() noreturn {21export fn zig_panic() noreturn {
...@@ -1069,3 +1072,80 @@ test "C function that takes byval struct called via function pointer" {...@@ -1069,3 +1072,80 @@ test "C function that takes byval struct called via function pointer" {
1069 @as(c_ulong, 5),1072 @as(c_ulong, 5),
1070 );1073 );
1071}1074}
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}