2018-09-06 20:29:35 +00:00
|
|
|
const std = @import("std");
|
2022-08-26 20:00:22 +00:00
|
|
|
const builtin = @import("builtin");
|
stage1: Expand SysV C ABI support for small structs
While the SysV ABI is not that complicated, LLVM does not allow us
direct access to enforce it. By mimicking the IR generated by clang,
we can trick LLVM into doing the right thing. This involves two main
additions:
1. `AGG` ABI class
This is not part of the spec, but since we have to track class per
eightbyte and not per struct, the current enum is not enough. I
considered adding multiple classes like: `INTEGER_INTEGER`,
`INTEGER_SSE`, `SSE_INTEGER`. However, all of those cases would trigger
the same code path so it's simpler to collapse into one. This class is
only used on SysV.
2. LLVM C ABI type
Clang uses different types in C ABI function signatures than the
original structs passed in, and does conversion. For example, this
struct: `{ i8, i8, float }` would use `{ i16, float }` at ABI boundaries.
When passed as an argument, it is instead split into two arguments `i16`
and `float`. Therefore, for every struct that passes ABI boundaries we
need to keep track of its corresponding ABI type. Here are some more
examples:
```
| Struct | ABI equivalent |
| { i8, i8, i8, i8 } | i32 |
| { float, float } | double |
| { float, i32, i8 } | { float, i64 } |
```
Then, we must update function calls, returns, parameter lists and inits
to properly convert back and forth as needed.
2021-07-23 16:43:38 +00:00
|
|
|
const print = std.debug.print;
|
2019-02-08 23:18:47 +00:00
|
|
|
const expect = std.testing.expect;
|
2018-09-06 20:29:35 +00:00
|
|
|
|
|
|
|
extern fn run_c_tests() void;
|
|
|
|
|
|
|
|
export fn zig_panic() noreturn {
|
|
|
|
@panic("zig_panic called from C");
|
|
|
|
}
|
|
|
|
|
|
|
|
test "C importing Zig ABI Tests" {
|
|
|
|
run_c_tests();
|
|
|
|
}
|
|
|
|
|
|
|
|
extern fn c_u8(u8) void;
|
|
|
|
extern fn c_u16(u16) void;
|
|
|
|
extern fn c_u32(u32) void;
|
|
|
|
extern fn c_u64(u64) void;
|
2022-02-07 21:32:24 +00:00
|
|
|
extern fn c_struct_u128(U128) void;
|
2018-09-06 20:29:35 +00:00
|
|
|
extern fn c_i8(i8) void;
|
|
|
|
extern fn c_i16(i16) void;
|
|
|
|
extern fn c_i32(i32) void;
|
|
|
|
extern fn c_i64(i64) void;
|
2022-02-07 21:32:24 +00:00
|
|
|
extern fn c_struct_i128(I128) void;
|
2018-09-06 20:29:35 +00:00
|
|
|
|
2019-03-19 21:09:12 +00:00
|
|
|
// On windows x64, the first 4 are passed via registers, others on the stack.
|
|
|
|
extern fn c_five_integers(i32, i32, i32, i32, i32) void;
|
|
|
|
|
|
|
|
export fn zig_five_integers(a: i32, b: i32, c: i32, d: i32, e: i32) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(a == 12) catch @panic("test failure: zig_five_integers 12");
|
|
|
|
expect(b == 34) catch @panic("test failure: zig_five_integers 34");
|
|
|
|
expect(c == 56) catch @panic("test failure: zig_five_integers 56");
|
|
|
|
expect(d == 78) catch @panic("test failure: zig_five_integers 78");
|
|
|
|
expect(e == 90) catch @panic("test failure: zig_five_integers 90");
|
2019-03-19 21:09:12 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 20:29:35 +00:00
|
|
|
test "C ABI integers" {
|
|
|
|
c_u8(0xff);
|
|
|
|
c_u16(0xfffe);
|
|
|
|
c_u32(0xfffffffd);
|
|
|
|
c_u64(0xfffffffffffffffc);
|
2022-02-07 21:32:24 +00:00
|
|
|
c_struct_u128(.{ .value = 0xfffffffffffffffc });
|
2018-09-06 20:29:35 +00:00
|
|
|
|
|
|
|
c_i8(-1);
|
|
|
|
c_i16(-2);
|
|
|
|
c_i32(-3);
|
|
|
|
c_i64(-4);
|
2022-02-07 21:32:24 +00:00
|
|
|
c_struct_i128(.{ .value = -6 });
|
2019-03-19 21:09:12 +00:00
|
|
|
c_five_integers(12, 34, 56, 78, 90);
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_u8(x: u8) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == 0xff) catch @panic("test failure: zig_u8");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
export fn zig_u16(x: u16) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == 0xfffe) catch @panic("test failure: zig_u16");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
export fn zig_u32(x: u32) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == 0xfffffffd) catch @panic("test failure: zig_u32");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
export fn zig_u64(x: u64) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == 0xfffffffffffffffc) catch @panic("test failure: zig_u64");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
export fn zig_i8(x: i8) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == -1) catch @panic("test failure: zig_i8");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
export fn zig_i16(x: i16) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == -2) catch @panic("test failure: zig_i16");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
export fn zig_i32(x: i32) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == -3) catch @panic("test failure: zig_i32");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
|
|
|
export fn zig_i64(x: i64) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == -4) catch @panic("test failure: zig_i64");
|
2018-09-06 20:29:35 +00:00
|
|
|
}
|
2022-02-07 21:32:24 +00:00
|
|
|
|
|
|
|
const I128 = extern struct {
|
|
|
|
value: i128,
|
|
|
|
};
|
|
|
|
const U128 = extern struct {
|
|
|
|
value: u128,
|
|
|
|
};
|
|
|
|
export fn zig_struct_i128(a: I128) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(a.value == -6) catch @panic("test failure: zig_struct_i128");
|
2022-02-07 21:32:24 +00:00
|
|
|
}
|
|
|
|
export fn zig_struct_u128(a: U128) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(a.value == 0xfffffffffffffffc) catch @panic("test failure: zig_struct_u128");
|
2022-02-07 21:32:24 +00:00
|
|
|
}
|
2018-09-07 15:52:57 +00:00
|
|
|
|
|
|
|
extern fn c_f32(f32) void;
|
|
|
|
extern fn c_f64(f64) void;
|
2022-08-26 19:01:05 +00:00
|
|
|
extern fn c_long_double(c_longdouble) void;
|
2018-09-07 15:52:57 +00:00
|
|
|
|
2019-03-19 21:09:12 +00:00
|
|
|
// On windows x64, the first 4 are passed via registers, others on the stack.
|
|
|
|
extern fn c_five_floats(f32, f32, f32, f32, f32) void;
|
|
|
|
|
|
|
|
export fn zig_five_floats(a: f32, b: f32, c: f32, d: f32, e: f32) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(a == 1.0) catch @panic("test failure: zig_five_floats 1.0");
|
|
|
|
expect(b == 2.0) catch @panic("test failure: zig_five_floats 2.0");
|
|
|
|
expect(c == 3.0) catch @panic("test failure: zig_five_floats 3.0");
|
|
|
|
expect(d == 4.0) catch @panic("test failure: zig_five_floats 4.0");
|
|
|
|
expect(e == 5.0) catch @panic("test failure: zig_five_floats 5.0");
|
2019-03-19 21:09:12 +00:00
|
|
|
}
|
|
|
|
|
2018-09-07 15:52:57 +00:00
|
|
|
test "C ABI floats" {
|
|
|
|
c_f32(12.34);
|
|
|
|
c_f64(56.78);
|
2019-03-19 21:09:12 +00:00
|
|
|
c_five_floats(1.0, 2.0, 3.0, 4.0, 5.0);
|
2022-08-26 20:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
test "C ABI long double" {
|
2022-09-03 12:47:58 +00:00
|
|
|
if (!builtin.cpu.arch.isWasm() and !builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
|
2022-08-26 19:01:05 +00:00
|
|
|
c_long_double(12.34);
|
2018-09-07 15:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_f32(x: f32) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == 12.34) catch @panic("test failure: zig_f32");
|
2018-09-07 15:52:57 +00:00
|
|
|
}
|
|
|
|
export fn zig_f64(x: f64) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x == 56.78) catch @panic("test failure: zig_f64");
|
2018-09-07 15:52:57 +00:00
|
|
|
}
|
2022-08-26 19:01:05 +00:00
|
|
|
export fn zig_longdouble(x: c_longdouble) void {
|
2022-08-26 20:00:22 +00:00
|
|
|
if (!builtin.cpu.arch.isWasm()) return; // waiting for #1481
|
2022-08-26 19:01:05 +00:00
|
|
|
expect(x == 12.34) catch @panic("test failure: zig_longdouble");
|
|
|
|
}
|
2018-09-07 15:52:57 +00:00
|
|
|
|
2021-12-19 05:24:45 +00:00
|
|
|
extern fn c_ptr(*anyopaque) void;
|
2018-09-07 15:52:57 +00:00
|
|
|
|
|
|
|
test "C ABI pointer" {
|
2021-12-19 05:24:45 +00:00
|
|
|
c_ptr(@intToPtr(*anyopaque, 0xdeadbeef));
|
2018-09-07 15:52:57 +00:00
|
|
|
}
|
|
|
|
|
2021-12-19 05:24:45 +00:00
|
|
|
export fn zig_ptr(x: *anyopaque) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(@ptrToInt(x) == 0xdeadbeef) catch @panic("test failure: zig_ptr");
|
2018-09-07 15:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern fn c_bool(bool) void;
|
|
|
|
|
|
|
|
test "C ABI bool" {
|
|
|
|
c_bool(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_bool(x: bool) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x) catch @panic("test failure: zig_bool");
|
2018-09-07 15:52:57 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 13:08:37 +00:00
|
|
|
const BigStruct = extern struct {
|
2018-09-07 15:52:57 +00:00
|
|
|
a: u64,
|
|
|
|
b: u64,
|
|
|
|
c: u64,
|
|
|
|
d: u64,
|
|
|
|
e: u8,
|
|
|
|
};
|
|
|
|
extern fn c_big_struct(BigStruct) void;
|
|
|
|
|
|
|
|
test "C ABI big struct" {
|
2018-11-13 13:08:37 +00:00
|
|
|
var s = BigStruct{
|
2018-09-07 15:52:57 +00:00
|
|
|
.a = 1,
|
|
|
|
.b = 2,
|
|
|
|
.c = 3,
|
|
|
|
.d = 4,
|
|
|
|
.e = 5,
|
|
|
|
};
|
|
|
|
c_big_struct(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_big_struct(x: BigStruct) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x.a == 1) catch @panic("test failure: zig_big_struct 1");
|
|
|
|
expect(x.b == 2) catch @panic("test failure: zig_big_struct 2");
|
|
|
|
expect(x.c == 3) catch @panic("test failure: zig_big_struct 3");
|
|
|
|
expect(x.d == 4) catch @panic("test failure: zig_big_struct 4");
|
|
|
|
expect(x.e == 5) catch @panic("test failure: zig_big_struct 5");
|
2018-09-07 15:52:57 +00:00
|
|
|
}
|
2018-09-07 17:51:11 +00:00
|
|
|
|
2018-11-13 13:08:37 +00:00
|
|
|
const BigUnion = extern union {
|
2018-09-07 17:51:11 +00:00
|
|
|
a: BigStruct,
|
|
|
|
};
|
|
|
|
extern fn c_big_union(BigUnion) void;
|
|
|
|
|
|
|
|
test "C ABI big union" {
|
2018-11-13 13:08:37 +00:00
|
|
|
var x = BigUnion{
|
|
|
|
.a = BigStruct{
|
2018-09-07 17:51:11 +00:00
|
|
|
.a = 1,
|
|
|
|
.b = 2,
|
|
|
|
.c = 3,
|
|
|
|
.d = 4,
|
|
|
|
.e = 5,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
c_big_union(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_big_union(x: BigUnion) void {
|
2022-02-09 04:12:50 +00:00
|
|
|
expect(x.a.a == 1) catch @panic("test failure: zig_big_union a");
|
|
|
|
expect(x.a.b == 2) catch @panic("test failure: zig_big_union b");
|
|
|
|
expect(x.a.c == 3) catch @panic("test failure: zig_big_union c");
|
|
|
|
expect(x.a.d == 4) catch @panic("test failure: zig_big_union d");
|
|
|
|
expect(x.a.e == 5) catch @panic("test failure: zig_big_union e");
|
2018-09-07 17:51:11 +00:00
|
|
|
}
|
|
|
|
|
stage1: Expand SysV C ABI support for small structs
While the SysV ABI is not that complicated, LLVM does not allow us
direct access to enforce it. By mimicking the IR generated by clang,
we can trick LLVM into doing the right thing. This involves two main
additions:
1. `AGG` ABI class
This is not part of the spec, but since we have to track class per
eightbyte and not per struct, the current enum is not enough. I
considered adding multiple classes like: `INTEGER_INTEGER`,
`INTEGER_SSE`, `SSE_INTEGER`. However, all of those cases would trigger
the same code path so it's simpler to collapse into one. This class is
only used on SysV.
2. LLVM C ABI type
Clang uses different types in C ABI function signatures than the
original structs passed in, and does conversion. For example, this
struct: `{ i8, i8, float }` would use `{ i16, float }` at ABI boundaries.
When passed as an argument, it is instead split into two arguments `i16`
and `float`. Therefore, for every struct that passes ABI boundaries we
need to keep track of its corresponding ABI type. Here are some more
examples:
```
| Struct | ABI equivalent |
| { i8, i8, i8, i8 } | i32 |
| { float, float } | double |
| { float, i32, i8 } | { float, i64 } |
```
Then, we must update function calls, returns, parameter lists and inits
to properly convert back and forth as needed.
2021-07-23 16:43:38 +00:00
|
|
|
const MedStructMixed = extern struct {
|
|
|
|
a: u32,
|
|
|
|
b: f32,
|
|
|
|
c: f32,
|
|
|
|
d: u32 = 0,
|
|
|
|
};
|
|
|
|
extern fn c_med_struct_mixed(MedStructMixed) void;
|
|
|
|
extern fn c_ret_med_struct_mixed() MedStructMixed;
|
|
|
|
|
|
|
|
test "C ABI medium struct of ints and floats" {
|
|
|
|
var s = MedStructMixed{
|
|
|
|
.a = 1234,
|
|
|
|
.b = 100.0,
|
|
|
|
.c = 1337.0,
|
|
|
|
};
|
|
|
|
c_med_struct_mixed(s);
|
|
|
|
var s2 = c_ret_med_struct_mixed();
|
|
|
|
expect(s2.a == 1234) catch @panic("test failure");
|
|
|
|
expect(s2.b == 100.0) catch @panic("test failure");
|
|
|
|
expect(s2.c == 1337.0) catch @panic("test failure");
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_med_struct_mixed(x: MedStructMixed) void {
|
|
|
|
expect(x.a == 1234) catch @panic("test failure");
|
|
|
|
expect(x.b == 100.0) catch @panic("test failure");
|
|
|
|
expect(x.c == 1337.0) catch @panic("test failure");
|
|
|
|
}
|
|
|
|
|
2018-11-13 13:08:37 +00:00
|
|
|
const SmallStructInts = extern struct {
|
2018-09-07 17:51:11 +00:00
|
|
|
a: u8,
|
|
|
|
b: u8,
|
|
|
|
c: u8,
|
|
|
|
d: u8,
|
|
|
|
};
|
|
|
|
extern fn c_small_struct_ints(SmallStructInts) void;
|
stage1: Expand SysV C ABI support for small structs
While the SysV ABI is not that complicated, LLVM does not allow us
direct access to enforce it. By mimicking the IR generated by clang,
we can trick LLVM into doing the right thing. This involves two main
additions:
1. `AGG` ABI class
This is not part of the spec, but since we have to track class per
eightbyte and not per struct, the current enum is not enough. I
considered adding multiple classes like: `INTEGER_INTEGER`,
`INTEGER_SSE`, `SSE_INTEGER`. However, all of those cases would trigger
the same code path so it's simpler to collapse into one. This class is
only used on SysV.
2. LLVM C ABI type
Clang uses different types in C ABI function signatures than the
original structs passed in, and does conversion. For example, this
struct: `{ i8, i8, float }` would use `{ i16, float }` at ABI boundaries.
When passed as an argument, it is instead split into two arguments `i16`
and `float`. Therefore, for every struct that passes ABI boundaries we
need to keep track of its corresponding ABI type. Here are some more
examples:
```
| Struct | ABI equivalent |
| { i8, i8, i8, i8 } | i32 |
| { float, float } | double |
| { float, i32, i8 } | { float, i64 } |
```
Then, we must update function calls, returns, parameter lists and inits
to properly convert back and forth as needed.
2021-07-23 16:43:38 +00:00
|
|
|
extern fn c_ret_small_struct_ints() SmallStructInts;
|
2018-09-07 17:51:11 +00:00
|
|
|
|
|
|
|
test "C ABI small struct of ints" {
|
2018-11-13 13:08:37 +00:00
|
|
|
var s = SmallStructInts{
|
2018-09-07 17:51:11 +00:00
|
|
|
.a = 1,
|
|
|
|
.b = 2,
|
|
|
|
.c = 3,
|
|
|
|
.d = 4,
|
|
|
|
};
|
|
|
|
c_small_struct_ints(s);
|
stage1: Expand SysV C ABI support for small structs
While the SysV ABI is not that complicated, LLVM does not allow us
direct access to enforce it. By mimicking the IR generated by clang,
we can trick LLVM into doing the right thing. This involves two main
additions:
1. `AGG` ABI class
This is not part of the spec, but since we have to track class per
eightbyte and not per struct, the current enum is not enough. I
considered adding multiple classes like: `INTEGER_INTEGER`,
`INTEGER_SSE`, `SSE_INTEGER`. However, all of those cases would trigger
the same code path so it's simpler to collapse into one. This class is
only used on SysV.
2. LLVM C ABI type
Clang uses different types in C ABI function signatures than the
original structs passed in, and does conversion. For example, this
struct: `{ i8, i8, float }` would use `{ i16, float }` at ABI boundaries.
When passed as an argument, it is instead split into two arguments `i16`
and `float`. Therefore, for every struct that passes ABI boundaries we
need to keep track of its corresponding ABI type. Here are some more
examples:
```
| Struct | ABI equivalent |
| { i8, i8, i8, i8 } | i32 |
| { float, float } | double |
| { float, i32, i8 } | { float, i64 } |
```
Then, we must update function calls, returns, parameter lists and inits
to properly convert back and forth as needed.
2021-07-23 16:43:38 +00:00
|
|
|
var s2 = c_ret_small_struct_ints();
|
|
|
|
expect(s2.a == 1) catch @panic("test failure");
|
|
|
|
expect(s2.b == 2) catch @panic("test failure");
|
|
|
|
expect(s2.c == 3) catch @panic("test failure");
|
|
|
|
expect(s2.d == 4) catch @panic("test failure");
|
2018-09-07 17:51:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_small_struct_ints(x: SmallStructInts) void {
|
2021-05-04 18:23:22 +00:00
|
|
|
expect(x.a == 1) catch @panic("test failure");
|
|
|
|
expect(x.b == 2) catch @panic("test failure");
|
|
|
|
expect(x.c == 3) catch @panic("test failure");
|
|
|
|
expect(x.d == 4) catch @panic("test failure");
|
2018-09-07 17:51:11 +00:00
|
|
|
}
|
2018-09-07 22:47:57 +00:00
|
|
|
|
2022-01-03 19:25:48 +00:00
|
|
|
const SmallPackedStruct = packed struct {
|
|
|
|
a: u2,
|
|
|
|
b: u2,
|
|
|
|
c: u2,
|
|
|
|
d: u2,
|
|
|
|
};
|
2022-09-02 17:26:33 +00:00
|
|
|
extern fn c_small_packed_struct(SmallPackedStruct) void;
|
2022-01-03 19:25:48 +00:00
|
|
|
extern fn c_ret_small_packed_struct() SmallPackedStruct;
|
|
|
|
|
2022-09-02 17:26:33 +00:00
|
|
|
export fn zig_small_packed_struct(x: SmallPackedStruct) void {
|
|
|
|
expect(x.a == 0) catch @panic("test failure");
|
|
|
|
expect(x.b == 1) catch @panic("test failure");
|
|
|
|
expect(x.c == 2) catch @panic("test failure");
|
|
|
|
expect(x.d == 3) catch @panic("test failure");
|
|
|
|
}
|
2022-01-03 19:25:48 +00:00
|
|
|
|
|
|
|
test "C ABI small packed struct" {
|
2022-09-02 17:26:33 +00:00
|
|
|
var s = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3 };
|
|
|
|
c_small_packed_struct(s);
|
2022-01-03 19:25:48 +00:00
|
|
|
var s2 = c_ret_small_packed_struct();
|
|
|
|
try expect(s2.a == 0);
|
|
|
|
try expect(s2.b == 1);
|
|
|
|
try expect(s2.c == 2);
|
|
|
|
try expect(s2.d == 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
const BigPackedStruct = packed struct {
|
|
|
|
a: u64,
|
|
|
|
b: u64,
|
|
|
|
};
|
|
|
|
extern fn c_big_packed_struct(BigPackedStruct) void;
|
|
|
|
extern fn c_ret_big_packed_struct() BigPackedStruct;
|
|
|
|
|
|
|
|
export fn zig_big_packed_struct(x: BigPackedStruct) void {
|
|
|
|
expect(x.a == 1) catch @panic("test failure");
|
|
|
|
expect(x.b == 2) catch @panic("test failure");
|
|
|
|
}
|
|
|
|
|
|
|
|
test "C ABI big packed struct" {
|
2022-09-02 17:26:33 +00:00
|
|
|
var s = BigPackedStruct{ .a = 1, .b = 2 };
|
2022-01-03 19:25:48 +00:00
|
|
|
c_big_packed_struct(s);
|
|
|
|
var s2 = c_ret_big_packed_struct();
|
|
|
|
try expect(s2.a == 1);
|
|
|
|
try expect(s2.b == 2);
|
|
|
|
}
|
|
|
|
|
2018-11-13 13:08:37 +00:00
|
|
|
const SplitStructInt = extern struct {
|
2018-09-07 22:47:57 +00:00
|
|
|
a: u64,
|
|
|
|
b: u8,
|
|
|
|
c: u32,
|
|
|
|
};
|
|
|
|
extern fn c_split_struct_ints(SplitStructInt) void;
|
|
|
|
|
|
|
|
test "C ABI split struct of ints" {
|
2018-11-13 13:08:37 +00:00
|
|
|
var s = SplitStructInt{
|
2018-09-07 22:47:57 +00:00
|
|
|
.a = 1234,
|
|
|
|
.b = 100,
|
|
|
|
.c = 1337,
|
|
|
|
};
|
|
|
|
c_split_struct_ints(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_split_struct_ints(x: SplitStructInt) void {
|
2021-05-04 18:23:22 +00:00
|
|
|
expect(x.a == 1234) catch @panic("test failure");
|
|
|
|
expect(x.b == 100) catch @panic("test failure");
|
|
|
|
expect(x.c == 1337) catch @panic("test failure");
|
2018-09-07 22:47:57 +00:00
|
|
|
}
|
2018-09-08 00:09:33 +00:00
|
|
|
|
stage1: Expand SysV C ABI support for small structs
While the SysV ABI is not that complicated, LLVM does not allow us
direct access to enforce it. By mimicking the IR generated by clang,
we can trick LLVM into doing the right thing. This involves two main
additions:
1. `AGG` ABI class
This is not part of the spec, but since we have to track class per
eightbyte and not per struct, the current enum is not enough. I
considered adding multiple classes like: `INTEGER_INTEGER`,
`INTEGER_SSE`, `SSE_INTEGER`. However, all of those cases would trigger
the same code path so it's simpler to collapse into one. This class is
only used on SysV.
2. LLVM C ABI type
Clang uses different types in C ABI function signatures than the
original structs passed in, and does conversion. For example, this
struct: `{ i8, i8, float }` would use `{ i16, float }` at ABI boundaries.
When passed as an argument, it is instead split into two arguments `i16`
and `float`. Therefore, for every struct that passes ABI boundaries we
need to keep track of its corresponding ABI type. Here are some more
examples:
```
| Struct | ABI equivalent |
| { i8, i8, i8, i8 } | i32 |
| { float, float } | double |
| { float, i32, i8 } | { float, i64 } |
```
Then, we must update function calls, returns, parameter lists and inits
to properly convert back and forth as needed.
2021-07-23 16:43:38 +00:00
|
|
|
const SplitStructMixed = extern struct {
|
|
|
|
a: u64,
|
|
|
|
b: u8,
|
|
|
|
c: f32,
|
|
|
|
};
|
|
|
|
extern fn c_split_struct_mixed(SplitStructMixed) void;
|
|
|
|
extern fn c_ret_split_struct_mixed() SplitStructMixed;
|
|
|
|
|
|
|
|
test "C ABI split struct of ints and floats" {
|
|
|
|
var s = SplitStructMixed{
|
|
|
|
.a = 1234,
|
|
|
|
.b = 100,
|
|
|
|
.c = 1337.0,
|
|
|
|
};
|
|
|
|
c_split_struct_mixed(s);
|
|
|
|
var s2 = c_ret_split_struct_mixed();
|
|
|
|
expect(s2.a == 1234) catch @panic("test failure");
|
|
|
|
expect(s2.b == 100) catch @panic("test failure");
|
|
|
|
expect(s2.c == 1337.0) catch @panic("test failure");
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_split_struct_mixed(x: SplitStructMixed) void {
|
|
|
|
expect(x.a == 1234) catch @panic("test failure");
|
|
|
|
expect(x.b == 100) catch @panic("test failure");
|
|
|
|
expect(x.c == 1337.0) catch @panic("test failure");
|
|
|
|
}
|
|
|
|
|
2018-09-08 00:09:33 +00:00
|
|
|
extern fn c_big_struct_both(BigStruct) BigStruct;
|
|
|
|
|
2022-09-28 02:57:43 +00:00
|
|
|
extern fn c_multiple_struct_ints(Rect, Rect) void;
|
|
|
|
extern fn c_multiple_struct_floats(FloatRect, FloatRect) void;
|
|
|
|
|
2018-09-08 00:09:33 +00:00
|
|
|
test "C ABI sret and byval together" {
|
2018-11-13 13:08:37 +00:00
|
|
|
var s = BigStruct{
|
2018-09-08 00:09:33 +00:00
|
|
|
.a = 1,
|
|
|
|
.b = 2,
|
|
|
|
.c = 3,
|
|
|
|
.d = 4,
|
|
|
|
.e = 5,
|
|
|
|
};
|
|
|
|
var y = c_big_struct_both(s);
|
2021-05-04 18:23:22 +00:00
|
|
|
try expect(y.a == 10);
|
|
|
|
try expect(y.b == 11);
|
|
|
|
try expect(y.c == 12);
|
|
|
|
try expect(y.d == 13);
|
|
|
|
try expect(y.e == 14);
|
2018-09-08 00:09:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_big_struct_both(x: BigStruct) BigStruct {
|
2021-05-04 18:23:22 +00:00
|
|
|
expect(x.a == 30) catch @panic("test failure");
|
|
|
|
expect(x.b == 31) catch @panic("test failure");
|
|
|
|
expect(x.c == 32) catch @panic("test failure");
|
|
|
|
expect(x.d == 33) catch @panic("test failure");
|
|
|
|
expect(x.e == 34) catch @panic("test failure");
|
2018-11-13 13:08:37 +00:00
|
|
|
var s = BigStruct{
|
2018-09-08 00:09:33 +00:00
|
|
|
.a = 20,
|
|
|
|
.b = 21,
|
|
|
|
.c = 22,
|
|
|
|
.d = 23,
|
|
|
|
.e = 24,
|
|
|
|
};
|
|
|
|
return s;
|
|
|
|
}
|
2020-05-28 17:45:11 +00:00
|
|
|
|
|
|
|
const Vector3 = extern struct {
|
|
|
|
x: f32,
|
|
|
|
y: f32,
|
|
|
|
z: f32,
|
|
|
|
};
|
|
|
|
extern fn c_small_struct_floats(Vector3) void;
|
2021-06-10 09:46:27 +00:00
|
|
|
extern fn c_small_struct_floats_extra(Vector3, ?[*]const u8) void;
|
2020-05-28 17:45:11 +00:00
|
|
|
|
|
|
|
const Vector5 = extern struct {
|
|
|
|
x: f32,
|
|
|
|
y: f32,
|
|
|
|
z: f32,
|
|
|
|
w: f32,
|
|
|
|
q: f32,
|
|
|
|
};
|
|
|
|
extern fn c_big_struct_floats(Vector5) void;
|
|
|
|
|
|
|
|
test "C ABI structs of floats as parameter" {
|
|
|
|
var v3 = Vector3{
|
|
|
|
.x = 3.0,
|
|
|
|
.y = 6.0,
|
|
|
|
.z = 12.0,
|
|
|
|
};
|
|
|
|
c_small_struct_floats(v3);
|
2021-06-10 09:46:27 +00:00
|
|
|
c_small_struct_floats_extra(v3, "hello");
|
2020-05-28 17:45:11 +00:00
|
|
|
|
|
|
|
var v5 = Vector5{
|
|
|
|
.x = 76.0,
|
|
|
|
.y = -1.0,
|
|
|
|
.z = -12.0,
|
|
|
|
.w = 69.0,
|
|
|
|
.q = 55,
|
|
|
|
};
|
|
|
|
c_big_struct_floats(v5);
|
|
|
|
}
|
2021-03-12 10:20:37 +00:00
|
|
|
|
2022-09-28 02:57:43 +00:00
|
|
|
const Rect = extern struct {
|
|
|
|
left: u32,
|
|
|
|
right: u32,
|
|
|
|
top: u32,
|
|
|
|
bottom: u32,
|
|
|
|
};
|
|
|
|
|
|
|
|
export fn zig_multiple_struct_ints(x: Rect, y: Rect) void {
|
|
|
|
expect(x.left == 1) catch @panic("test failure");
|
|
|
|
expect(x.right == 21) catch @panic("test failure");
|
|
|
|
expect(x.top == 16) catch @panic("test failure");
|
|
|
|
expect(x.bottom == 4) catch @panic("test failure");
|
|
|
|
expect(y.left == 178) catch @panic("test failure");
|
|
|
|
expect(y.right == 189) catch @panic("test failure");
|
|
|
|
expect(y.top == 21) catch @panic("test failure");
|
|
|
|
expect(y.bottom == 15) catch @panic("test failure");
|
|
|
|
}
|
|
|
|
|
|
|
|
test "C ABI structs of ints as multiple parameters" {
|
|
|
|
var r1 = Rect{
|
|
|
|
.left = 1,
|
|
|
|
.right = 21,
|
|
|
|
.top = 16,
|
|
|
|
.bottom = 4,
|
|
|
|
};
|
|
|
|
var r2 = Rect{
|
|
|
|
.left = 178,
|
|
|
|
.right = 189,
|
|
|
|
.top = 21,
|
|
|
|
.bottom = 15,
|
|
|
|
};
|
|
|
|
c_multiple_struct_ints(r1, r2);
|
|
|
|
}
|
|
|
|
|
|
|
|
const FloatRect = extern struct {
|
|
|
|
left: f32,
|
|
|
|
right: f32,
|
|
|
|
top: f32,
|
|
|
|
bottom: f32,
|
|
|
|
};
|
|
|
|
|
|
|
|
export fn zig_multiple_struct_floats(x: FloatRect, y: FloatRect) void {
|
|
|
|
expect(x.left == 1) catch @panic("test failure");
|
|
|
|
expect(x.right == 21) catch @panic("test failure");
|
|
|
|
expect(x.top == 16) catch @panic("test failure");
|
|
|
|
expect(x.bottom == 4) catch @panic("test failure");
|
|
|
|
expect(y.left == 178) catch @panic("test failure");
|
|
|
|
expect(y.right == 189) catch @panic("test failure");
|
|
|
|
expect(y.top == 21) catch @panic("test failure");
|
|
|
|
expect(y.bottom == 15) catch @panic("test failure");
|
|
|
|
}
|
|
|
|
|
|
|
|
test "C ABI structs of floats as multiple parameters" {
|
|
|
|
var r1 = FloatRect{
|
|
|
|
.left = 1,
|
|
|
|
.right = 21,
|
|
|
|
.top = 16,
|
|
|
|
.bottom = 4,
|
|
|
|
};
|
|
|
|
var r2 = FloatRect{
|
|
|
|
.left = 178,
|
|
|
|
.right = 189,
|
|
|
|
.top = 21,
|
|
|
|
.bottom = 15,
|
|
|
|
};
|
|
|
|
c_multiple_struct_floats(r1, r2);
|
|
|
|
}
|
|
|
|
|
2021-03-12 10:20:37 +00:00
|
|
|
export fn zig_ret_bool() bool {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
export fn zig_ret_u8() u8 {
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
export fn zig_ret_u16() u16 {
|
|
|
|
return 0xffff;
|
|
|
|
}
|
|
|
|
export fn zig_ret_u32() u32 {
|
|
|
|
return 0xffffffff;
|
|
|
|
}
|
|
|
|
export fn zig_ret_u64() u64 {
|
|
|
|
return 0xffffffffffffffff;
|
|
|
|
}
|
|
|
|
export fn zig_ret_i8() i8 {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
export fn zig_ret_i16() i16 {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
export fn zig_ret_i32() i32 {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
export fn zig_ret_i64() i64 {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
stage1: Expand SysV C ABI support for small structs
While the SysV ABI is not that complicated, LLVM does not allow us
direct access to enforce it. By mimicking the IR generated by clang,
we can trick LLVM into doing the right thing. This involves two main
additions:
1. `AGG` ABI class
This is not part of the spec, but since we have to track class per
eightbyte and not per struct, the current enum is not enough. I
considered adding multiple classes like: `INTEGER_INTEGER`,
`INTEGER_SSE`, `SSE_INTEGER`. However, all of those cases would trigger
the same code path so it's simpler to collapse into one. This class is
only used on SysV.
2. LLVM C ABI type
Clang uses different types in C ABI function signatures than the
original structs passed in, and does conversion. For example, this
struct: `{ i8, i8, float }` would use `{ i16, float }` at ABI boundaries.
When passed as an argument, it is instead split into two arguments `i16`
and `float`. Therefore, for every struct that passes ABI boundaries we
need to keep track of its corresponding ABI type. Here are some more
examples:
```
| Struct | ABI equivalent |
| { i8, i8, i8, i8 } | i32 |
| { float, float } | double |
| { float, i32, i8 } | { float, i64 } |
```
Then, we must update function calls, returns, parameter lists and inits
to properly convert back and forth as needed.
2021-07-23 16:43:38 +00:00
|
|
|
export fn zig_ret_small_struct_ints() SmallStructInts {
|
|
|
|
return .{
|
|
|
|
.a = 1,
|
|
|
|
.b = 2,
|
|
|
|
.c = 3,
|
|
|
|
.d = 4,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_ret_med_struct_mixed() MedStructMixed {
|
|
|
|
return .{
|
|
|
|
.a = 1234,
|
|
|
|
.b = 100.0,
|
|
|
|
.c = 1337.0,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export fn zig_ret_split_struct_mixed() SplitStructMixed {
|
|
|
|
return .{
|
|
|
|
.a = 1234,
|
|
|
|
.b = 100,
|
|
|
|
.c = 1337.0,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-03-12 10:20:37 +00:00
|
|
|
extern fn c_ret_bool() bool;
|
|
|
|
extern fn c_ret_u8() u8;
|
|
|
|
extern fn c_ret_u16() u16;
|
|
|
|
extern fn c_ret_u32() u32;
|
|
|
|
extern fn c_ret_u64() u64;
|
|
|
|
extern fn c_ret_i8() i8;
|
|
|
|
extern fn c_ret_i16() i16;
|
|
|
|
extern fn c_ret_i32() i32;
|
|
|
|
extern fn c_ret_i64() i64;
|
|
|
|
|
|
|
|
test "C ABI integer return types" {
|
2021-05-04 18:23:22 +00:00
|
|
|
try expect(c_ret_bool() == true);
|
2021-03-12 10:20:37 +00:00
|
|
|
|
2021-05-04 18:23:22 +00:00
|
|
|
try expect(c_ret_u8() == 0xff);
|
|
|
|
try expect(c_ret_u16() == 0xffff);
|
|
|
|
try expect(c_ret_u32() == 0xffffffff);
|
|
|
|
try expect(c_ret_u64() == 0xffffffffffffffff);
|
2021-03-12 10:20:37 +00:00
|
|
|
|
2021-05-04 18:23:22 +00:00
|
|
|
try expect(c_ret_i8() == -1);
|
|
|
|
try expect(c_ret_i16() == -1);
|
|
|
|
try expect(c_ret_i32() == -1);
|
|
|
|
try expect(c_ret_i64() == -1);
|
2021-03-12 10:20:37 +00:00
|
|
|
}
|