add C ABI tests with double

const DC = extern struct { v1: f64, v2: u8 };
const CFF = extern struct { v1: u8, v2: f32, v3: f32 };
const PD = extern struct { v1: ?*anyopaque, v2: f64 };

Signed-off-by: Guillaume Wenzek <gwenzek@users.noreply.github.com>
This commit is contained in:
Guillaume Wenzek 2022-11-18 13:05:25 +01:00 committed by GitHub
parent 6c931c32bb
commit 5221c90164
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 230 additions and 0 deletions

View File

@ -770,3 +770,66 @@ void c_ptr_size_float_struct(Vector2 vec) {
Vector2 c_ret_ptr_size_float_struct(void) {
return (Vector2){3, 4};
}
/// Tests for Double + Char struct
struct DC { double v1; char v2; };
int c_assert_DC(struct DC lv){
if (lv.v1 != -0.25) return 1;
if (lv.v2 != 15) return 2;
return 0;
}
struct DC c_ret_DC(){
struct DC lv = { .v1 = -0.25, .v2 = 15 };
return lv;
}
int zig_assert_DC(struct DC);
int c_send_DC(){
return zig_assert_DC(c_ret_DC());
}
struct DC zig_ret_DC();
int c_assert_ret_DC(){
return c_assert_DC(zig_ret_DC());
}
/// Tests for Char + Float + Float struct
struct CFF { char v1; float v2; float v3; };
int c_assert_CFF(struct CFF lv){
if (lv.v1 != 39) return 1;
if (lv.v2 != 0.875) return 2;
if (lv.v3 != 1.0) return 3;
return 0;
}
struct CFF c_ret_CFF(){
struct CFF lv = { .v1 = 39, .v2 = 0.875, .v3 = 1.0 };
return lv;
}
int zig_assert_CFF(struct CFF);
int c_send_CFF(){
return zig_assert_CFF(c_ret_CFF());
}
struct CFF zig_ret_CFF();
int c_assert_ret_CFF(){
return c_assert_CFF(zig_ret_CFF());
}
struct PD { void* v1; double v2; };
int c_assert_PD(struct PD lv){
if (lv.v1 != 0) return 1;
if (lv.v2 != 0.5) return 2;
return 0;
}
struct PD c_ret_PD(){
struct PD lv = { .v1 = 0, .v2 = 0.5 };
return lv;
}
int zig_assert_PD(struct PD);
int c_send_PD(){
return zig_assert_PD(c_ret_PD());
}
struct PD zig_ret_PD();
int c_assert_ret_PD(){
return c_assert_PD(zig_ret_PD());
}

View File

@ -1,7 +1,15 @@
//! Tests for the C ABI.
//! Those tests are passing back and forth struct and values across C ABI
//! by combining Zig code here and its mirror in cfunc.c
//! To run all the tests on the tier 1 architecture you can use:
//! zig build test-c-abi -fqemu
//! To run the tests on a specific architecture:
//! zig test -fno-stage1 -lc main.zig cfuncs.c -target mips-linux --test-cmd qemu-mips --test-cmd-bin
const std = @import("std");
const builtin = @import("builtin");
const print = std.debug.print;
const expect = std.testing.expect;
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();
@ -831,3 +839,162 @@ test "C ABI pointer sized float struct" {
try expect(x.x == 3);
try expect(x.y == 4);
}
//=== Helpers for struct test ===//
pub inline fn expectOk(c_err: c_int) !void {
if (c_err != 0) {
std.debug.print("ABI mismatch on field v{d}.\n", .{c_err});
return error.TestExpectedEqual;
}
}
/// Tests for Double + Char struct
const DC = extern struct { v1: f64, v2: u8 };
test "DC: Zig passes to C" {
if (builtin.target.cpu.arch == .x86_64 and builtin.target.os.tag != .windows)
return error.SkipZigTest;
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_assert_DC(.{ .v1 = -0.25, .v2 = 15 }));
}
test "DC: Zig returns to C" {
if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_assert_ret_DC());
}
test "DC: C passes to Zig" {
if (builtin.target.cpu.arch == .x86_64 and builtin.target.os.tag != .windows)
return error.SkipZigTest;
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_send_DC());
}
test "DC: C returns to Zig" {
if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectEqual(c_ret_DC(), .{ .v1 = -0.25, .v2 = 15 });
}
pub extern fn c_assert_DC(lv: DC) c_int;
pub extern fn c_assert_ret_DC() c_int;
pub extern fn c_send_DC() c_int;
pub extern fn c_ret_DC() DC;
pub export fn zig_assert_DC(lv: DC) c_int {
var err: c_int = 0;
if (lv.v1 != -0.25) err = 1;
if (lv.v2 != 15) err = 2;
if (err != 0) std.debug.print("Received {}", .{lv});
return err;
}
pub export fn zig_ret_DC() DC {
return .{ .v1 = -0.25, .v2 = 15 };
}
/// Tests for Char + Float + FloatRect struct
const CFF = extern struct { v1: u8, v2: f32, v3: f32 };
test "CFF: Zig passes to C" {
if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows)
return error.SkipZigTest;
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_assert_CFF(.{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 }));
}
test "CFF: Zig returns to C" {
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_assert_ret_CFF());
}
test "CFF: C passes to Zig" {
if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows)
return error.SkipZigTest;
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_send_CFF());
}
test "CFF: C returns to Zig" {
// segfault on aarch64 and mips
if (builtin.target.cpu.arch == .aarch64) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectEqual(c_ret_CFF(), .{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 });
}
pub extern fn c_assert_CFF(lv: CFF) c_int;
pub extern fn c_assert_ret_CFF() c_int;
pub extern fn c_send_CFF() c_int;
pub extern fn c_ret_CFF() CFF;
pub export fn zig_assert_CFF(lv: CFF) c_int {
var err: c_int = 0;
if (lv.v1 != 39) err = 1;
if (lv.v2 != 0.875) err = 2;
if (lv.v3 != 1.0) err = 3;
if (err != 0) std.debug.print("Received {}", .{lv});
return err;
}
pub export fn zig_ret_CFF() CFF {
return .{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 };
}
/// Tests for Pointer + Double struct
const PD = extern struct { v1: ?*anyopaque, v2: f64 };
test "PD: Zig passes to C" {
if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows)
return error.SkipZigTest;
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_assert_PD(.{ .v1 = null, .v2 = 0.5 }));
}
test "PD: Zig returns to C" {
if (builtin.target.cpu.arch == .x86) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_assert_ret_PD());
}
test "PD: C passes to Zig" {
if (builtin.target.cpu.arch.isX86() and builtin.target.os.tag != .windows)
return error.SkipZigTest;
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectOk(c_send_PD());
}
test "PD: C returns to Zig" {
if (builtin.target.cpu.arch == .x86) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectEqual(c_ret_PD(), .{ .v1 = null, .v2 = 0.5 });
}
pub extern fn c_assert_PD(lv: PD) c_int;
pub extern fn c_assert_ret_PD() c_int;
pub extern fn c_send_PD() c_int;
pub extern fn c_ret_PD() PD;
pub export fn zig_c_assert_PD(lv: PD) c_int {
var err: c_int = 0;
if (lv.v1 != null) err = 1;
if (lv.v2 != 0.5) err = 2;
if (err != 0) std.debug.print("Received {}", .{lv});
return err;
}
pub export fn zig_ret_PD() PD {
return .{ .v1 = null, .v2 = 0.5 };
}
pub export fn zig_assert_PD(lv: PD) c_int {
var err: c_int = 0;
if (lv.v1 != null) err = 1;
if (lv.v2 != 0.5) err = 2;
if (err != 0) std.debug.print("Received {}", .{lv});
return err;
}