Zig: stage1 C ABI compatibility

Created on 6 Sep 2018  路  33Comments  路  Source: ziglang/zig

This issue is to track C ABI compatibility support in the stage1 compiler.

Here's what support looks like currently:

  • [x] integers, floats, pointers, enums, bools work on every target, as parameters and return values
  • [x] x86_64: struct & union parameters & return values larger than 16 bytes
  • [x] x86_64: struct & union parameters that have at least 1 integer in them
  • [ ] x86_64: struct & union parameters <= 16 bytes which break structs into parameters
  • [ ] x86_64: struct & union parameters <= 16 bytes which are all floats
  • [ ] x86_64: struct & union return values <= 16 bytes
  • [x] ARM: struct & union return values
  • [ ] ARM: struct & union parameters
  • [x] C ABI for parameters that is unsupported gives a compile error linking to this issue
  • [x] C ABI for return values that is unsupported gives a compile error linking to this issue
  • [ ] other architectures structs & unions parameters & return values

For those who find this issue from the compile error, leave a comment detailing your specific needs and I'll see if I can code those up for you to unblock you, so you don't have to wait for this issue to be 100% solved.

enhancement stage1

Most helpful comment

trying to use https://github.com/cimgui/cimgui
many functions take a struct parameter:

struct ImVec2
{
    float x, y;
};

All 33 comments

Update:

I just merged a branch into master which does the following things:

  • Unsupported C ABI gives a compile error, like this:
const ASmallUnion = extern union {
    r: u8,
};
extern fn foo(x: ASmallUnion) void;

pub fn main() void {
    const x = ASmallUnion{ .r = 0x12 };

    foo(col);
}
/home/andy/downloads/zig/build/test.zig:4:8: error: TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481
extern fn foo(x: ASmallUnion) void;
       ^
/home/andy/downloads/zig/build/test.zig:4:8: note: pointers, integers, floats, bools, and enums work on all targets
extern fn foo(x: ASmallUnion) void;
       ^
  • There is a new type of test, C ABI tests, which covers the C ABI that we do suport. See test/stage1/c_abi/* for the source.
Test 1/9 C importing Zig ABI Tests...OK
Test 2/9 C ABI integers...OK
Test 3/9 C ABI floats...OK
Test 4/9 C ABI pointer...OK
Test 5/9 C ABI bool...OK
Test 6/9 C ABI array...OK
Test 7/9 C ABI big struct...OK
Test 8/9 C ABI big union...OK
Test 9/9 C ABI small struct of ints...OK
  • Added all targets C ABI support for integers, floats, bools, enums, and pointers
  • Added x86_64 C ABI support for big structs and unions (which are passed by memory)
  • Added x86_64 C ABI support for small structs which contain at least one integer

Zig will now either panic or emit a compile error when C ABI compatibility is required that it doesn't support yet. Therefore this issue is now an enhancement in order to provide additional C ABI compatibility and not a bug.

For those who find this issue from the compile error, leave a comment detailing your specific needs and I'll see if I can code those up for you to unblock you, so you don't have to wait for this issue to be 100% solved.

Running into this issue when trying to work with the godot native headers.
"TODO implement C ABI for x86_64 return types. type 'godot_string'"

https://github.com/GodotNativeTools/godot_headers

Running into this when bringing up the BGFX rendering library on 64-bit Linux.

My code calls the following function:

bgfx_create_vertex_buffer()

Error message:

TODO implement C ABI for x86_64 return types. type 'struct_bgfx_vertex_buffer_handle_s'

bgfx_vertex_buffer_handle_s is defined in this header, using a macro.

The macro evaluates to the following struct definition:

typedef struct bgfx_vertex_buffer_handle_s { 
    uint16_t idx; 
} bgfx_vertex_buffer_handle_t

This seems to correspond to "x86_64: struct & union return values <= 16 bytes," above.

Please advise on how to get unblocked. :smiley:

Thanks much for your hard work!

I'm using BGFX as well - not sure if @DavidYKay 's issue will affect me as well, but it will most likely end up blocking me too.

Trying to use the CSFML Network API -

const std = @import("std");
const defines = @import("defines.zig");
const ZealErrors = @import("error.zig").ZealErrors;
const sfml = @cImport({
    // See https://github.com/zig-lang/zig/issues/515
    @cDefine("_NO_CRT_STDIO_INLINE", "1");
    @cInclude("SFML/Network.h");
});

var listener: ?*sfml.sfTcpListener = undefined;

pub fn init() u8 {
    listener = sfml.sfTcpListener_create();
    if (listener == null) {
        return defines.QUIT_FAILURE;
    }

    var socket_status = sfml.sfTcpListener_listen(listener, 8080, sfml.sfIpAddress_Any);

    return defines.QUIT_SUCCESS;
}

I'm using the CSFML library - csfml: stable 2.4 (bottled), HEAD

I installed via homebrew on macOS High Sierra 10.13.6 (17G65)

Here is the related header file(s) - https://github.com/SFML/CSFML/blob/master/include/SFML/Network/TcpListener.h

Here's the offending function signature written in Zig - pub extern fn sfTcpListener_listen(listener: ?*sfTcpListener, port: c_ushort, address: sfIpAddress) sfSocketStatus;

And the error -

/Users/zachcarter/projects/zeal_zig/src/console_server.zig:23:5: error: TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481
pub extern fn sfTcpListener_listen(listener: ?*sfTcpListener, port: c_ushort, address: sfIpAddress) sfSocketStatus;
    ^
/Users/zachcarter/projects/zeal_zig/src/console_server.zig:23:5: note: pointers, integers, floats, bools, and enums work on all targets
pub extern fn sfTcpListener_listen(listener: ?*sfTcpListener, port: c_ushort, address: sfIpAddress) sfSocketStatus;

And here's the above example, without using @cimport -

const std = @import("std");
const defines = @import("defines.zig");
const ZealErrors = @import("error.zig").ZealErrors;

pub const struct_sfTcpListener = @OpaqueType();
pub const sfTcpListener = struct_sfTcpListener;

pub const sfIpAddress = extern struct {
    address: [16]u8,
};

pub const sfSocketStatus = extern enum {
    sfSocketDone = 0,
    sfSocketNotReady = 1,
    sfSocketPartial = 2,
    sfSocketDisconnected = 3,
    sfSocketError = 4,
};

pub extern const sfIpAddress_Any: sfIpAddress;

pub extern fn sfTcpListener_create() ?*sfTcpListener;
pub extern fn sfTcpListener_listen(listener: ?*sfTcpListener, port: c_ushort, address: sfIpAddress) sfSocketStatus;

var listener: ?*sfTcpListener = undefined;

pub fn init() u8 {
    listener = sfTcpListener_create();
    if (listener == null) {
        return defines.QUIT_FAILURE;
    }

    var socket_status = sfTcpListener_listen(listener, 8080, sfIpAddress_Any);

    return defines.QUIT_SUCCESS;
}

Trying to run @sizeOf builtin on packed structs from 2 bytes to 8 bytes, compiler emits the following: error: TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481

This is the relevant code (from x86_64.zig):

// Segment Selector
pub const SegmentSelector = packed struct {
    rpl: u2,
    ti: u1,
    index: u13,
};

From kzig.zig:

const m1 = "sizeof(SegmentSelector) == %lu\n";
efi_result = klib.uefi.AsciiPrint(&m1, @sizeOf(x86.SegmentSelector));

I'm trying to use XCB, translating this hello world program into Zig.

Here's what I have so far:

const std = @import("std");
const xcb = @cImport({
    @cInclude("xcb/xcb.h");
});

pub fn main() error!void {
    std.debug.warn("Starting...\n");
    const connection = xcb.xcb_connect(null, null);
    defer xcb.xcb_disconnect(connection);

    const screen = xcb
        .xcb_setup_roots_iterator(xcb.xcb_get_setup(connection)).data;

    const window = xcb.xcb_generate_id(connection);

    xcb.xcb_create_window(connection,
                          xcb.XCB_COPY_FROM_PARENT,
                          window,
                          screen.root,
                          0, 0,
                          150, 150,
                          10,
                          xcb.XCB_WINDOW_CLASS_INPUT_OUTPUT,
                          screen.root_visual,
                          0, null);

    xcb.xcb_map_window(connection, window);

    xcb.xcb_flush(connection);

    const stdin = try std.io.getStdIn();
    stdin.readByte();
}

And the error message:

TODO implement C ABI for x86_64 return types. type 'struct_xcb_screen_iterator_t'

Referring to the type of screen.

Also, I am a newcomer to Zig, so it's possible I'm doing something wrong.

Thank you for your time!

trying to use https://github.com/cimgui/cimgui
many functions take a struct parameter:

struct ImVec2
{
    float x, y;
};

Vector ABI depends on the compiler flags on x86_64, and current the c_abi tests have the compiler flags mixed up so that the zig version expects arguments to be passed in registers (such as %ymm0 and %zmm0 that don't exist on earlier CPUs, which only have %xmm0 et cetera) and the c version expects things to be passed on the stack.

But clearly the C can also pass on the stack with certain arguments https://godbolt.org/z/RCPb2c , we are passing -march=native so I don't know what we get the stack behavior https://godbolt.org/z/DzE_Px

just got here for the same reason as @xxxbxxx (also wrapping cimgui... ;))
I only hit this error when I define my own extern function with ImVec2 parameters, return values and calling functions taking ImVec2 work fine.

/my_module.zig:37:24: error: TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481
    linux.panic(c"%s", message);

This was me trying to use a variadic C function in x86_64-freestanding-gnu

x86_64: struct & union parameters <= 16 bytes which break structs into parameters
x86_64: struct & union parameters <= 16 bytes which are all floats
x86_64: struct & union return values <= 16 bytes

These would all be useful for C interop with the Chipmunk 2D game physics library: https://chipmunk-physics.net/

trying to use raylib but getting the error

TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481
pub extern fn DrawTextureV(texture: Texture2D, position: Vector2, tint: Color) void;
^

but calling the alternaitve (below) is valid -

void DrawTexture(Texture2D texture, int posX, int posY, Color tint);

matching the case

x86_64: struct & union parameters <= 16 bytes which are all floats

Vector2 defined as

typedef struct Vector2 {
    float x;
    float y;
} Vector2;

is there any way to bypass this? the issue makes the library unusable for now

I ran into this trying to use nuklear on x86_64.

[nix-shell:~/focus/memory]$ zig build run
Semantic Analysis [471/920] ./zig-cache/o/F6E4YJLCwGgr0e72_U8NRW-v320p6TKiGBLNKFajH8JlnEOq7xa_ZZvQhFtoC0mn/cimport.zig:19400:5: error: TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481
pub extern fn nk_begin(ctx: [*c]struct_nk_context, title: [*c]const u8, bounds: struct_nk_rect, flags: nk_flags) c_int;
    ^
./zig-cache/o/F6E4YJLCwGgr0e72_U8NRW-v320p6TKiGBLNKFajH8JlnEOq7xa_ZZvQhFtoC0mn/cimport.zig:19400:5: note: pointers, integers, floats, bools, and enums work on all targets
pub extern fn nk_begin(ctx: [*c]struct_nk_context, title: [*c]const u8, bounds: struct_nk_rect, flags: nk_flags) c_int;
    ^
focus...The following command exited with error code 1:
/home/jamie/zig-linux-x86_64-0.5.0+732c0cb58/zig build-exe /home/jamie/focus/memory/src/main.zig --library c --library GL --library GLU --library GL --library SDL2 --c-source -std=c99 -fno-sanitize=undefined /home/jamie/focus/memory/src/nk_main.c --cache-dir /home/jamie/focus/memory/zig-cache --name focus -I /nix/store/70ls7xnvbhwzl0w2ks9h860ipjkifjra-mesa-19.3.3-dev/include -I /nix/store/i8wrbvvya4s86jwi1w5jwzzp9rrlzadr-libglvnd-1.2.0-dev/include -I /nix/store/70ls7xnvbhwzl0w2ks9h860ipjkifjra-mesa-19.3.3-dev/include -I /nix/store/i8wrbvvya4s86jwi1w5jwzzp9rrlzadr-libglvnd-1.2.0-dev/include -I /nix/store/d8n81xp7v423d1sc8ch28k54rqfnn4wq-glu-9.0.1-dev/include -I /nix/store/gw3mxrhynd9lyr3qcvghg4sqxq9lrjcy-SDL2-2.0.10-dev/include/SDL2 -I /home/jamie/focus/memory/include -L /nix/store/nqv1gpi9q8gqxkgy1kr6gwqly77v4vnr-libglvnd-1.2.0/lib -L /nix/store/nqv1gpi9q8gqxkgy1kr6gwqly77v4vnr-libglvnd-1.2.0/lib -L /nix/store/1c4f4gjzxpmqfmaxwzyq4hxgi8pxy2qc-glu-9.0.1/lib -L /nix/store/2jjwgcqpacdla7iwhwd4l4zmjw655ljw-SDL2-2.0.10/lib -D _REENTRANT --cache on 

Build failed. The following command failed:
/home/jamie/focus/memory/zig-cache/o/Zr_C4R3GWir39zYwLrc_1cF1pueVmKYZRPELZhdNl2-asepcvvWs-CYZate2cW3u/build /home/jamie/zig-linux-x86_64-0.5.0+732c0cb58/zig /home/jamie/focus/memory /home/jamie/focus/memory/zig-cache run

I suppose the problem is:

struct nk_rect {float x,y,w,h;};

For now I can maybe just add a dummy field to the struct to make it bigger.

Also calling nk_rgb (which returns nk_color) does compile but produces a different result on each call. If I change the definition to struct nk_color {nk_byte r,g,b,a; nk_byte dummy[16];}; then I get the expected result.

Lmk if you need more details to repro, I can try to isolate it.

Hello, I stumbled upon this when needing to call variadic C functions. I worked around this by defining a simple C wrapper in a header that accepts the parameters I need but is not variadic, and use this wrapper instead of the variadic function.

Hey there, just as tglanz I'm trying to work with raylib and zig. Indeed I've released some manually tweaked bindings for it (shameless plug) and I'm trying to hack my way around this. Now, I was wondering what the difference between x86_64: struct & union parameters <= 16 bytes which break structs into parameters and x86_64: struct & union parameters <= 16 bytes which are all floats is. The first one is pretty clear, but I couldn't find any information on the second one.

I'm seeing this with Dear ImGui as well on macOS:

pub extern fn igButton(label: [*c]const u8, size: ImVec2) bool;

Trying to use libcurl, abstracting the C-ness away, but can't do it because C functions can't take the Zig strings.

fn make_request(url: []const u8) void {
  var curl = c.curl_easy_init();
  if(curl != null) {
    _  = c.curl_easy_setopt(curl, .CURLOPT_URL, url); // <- problem here
    var res = c.curl_easy_perform(curl);

    c.curl_easy_cleanup(curl);
  }
}

@matthewp This isn't an abi compatibility problem, it comes from curl_easy_setopt accepting varargs. Though we should probably validate that varargs parameters on ABI boundaries are ABI-compatible types.

You can use sentinel-terminated pointers or slices to solve your problem.
For a slice, url: [:0]const u8 and curl_easy_setopt(curl, .CURLOPT_URL, url.ptr)
For a pointer, url: [*:0]const u8 and curl_easy_setopt(curl, .CURLOPT_URL, url)

Discussion on the LLVM mailing list about this: http://lists.llvm.org/pipermail/llvm-dev/2020-June/142055.html

Hi, I'm trying to add support for powerpc64-linux target.

After adding the bits and os files, building zig errors with this message:

[ 98%] Built target zig0
make[2]: Entering directory '/home/koakuma/zig/src/zig/out'
make[2]: Leaving directory '/home/koakuma/zig/src/zig/out'
make[2]: Entering directory '/home/koakuma/zig/src/zig/out'
TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481
Aborted
make[2]: *** [CMakeFiles/zig_build_libstage2.dir/build.make:57: CMakeFiles/zig_build_libstage2] Error 134
make[2]: Leaving directory '/home/koakuma/zig/src/zig/out'
make[1]: *** [CMakeFiles/Makefile2:119: CMakeFiles/zig_build_libstage2.dir/all] Error 2
make[1]: Leaving directory '/home/koakuma/zig/src/zig/out'
make: *** [Makefile:130: all] Error 2

@koachan You have to read the code of the file with that error. PowerPC is easier than the other targets, but you still can't support "homogenous aggregates"/"non-homogenous aggreegates" easily, and look at the llvm-dev list above.

For those who find this issue from the compile error, leave a comment detailing your specific needs

I've come here for al_put_pixel(int, int, ALLEGRO_COLOR) where ALLEGRO_COLOR
https://github.com/liballeg/allegro5/blob/d7757184d335d400460808eff8e0d19c9f557673/include/allegro5/color.h#L15-L18
is an example of 'x86_64: struct & union parameters <= 16 bytes which are all floats' I suppose.
Thanks!

I'm running into this on macos.

Zig cimport:

pub extern fn c2CircletoCircle(A: c2Circle, B: c2Circle) c_int;
pub extern fn c2CircletoAABB(A: c2Circle, B: c2AABB) c_int;
pub extern fn c2CircletoCapsule(A: c2Circle, B: c2Capsule) c_int;
...

pub const struct_c2v = extern struct {
    x: f32,
    y: f32,
};
pub const c2v = struct_c2v;
pub const struct_c2Circle = extern struct {
    p: c2v,
    r: f32,
};
pub const c2Circle = struct_c2Circle;
pub const struct_c2AABB = extern struct {
    min: c2v,
    max: c2v,
};
pub const c2AABB = struct_c2AABB;
pub const struct_c2Capsule = extern struct {
    a: c2v,
    b: c2v,
    r: f32,
};
pub const c2Capsule = struct_c2Capsule;

C:

CUTE_C2_API int c2CircletoCircle(c2Circle A, c2Circle B);
CUTE_C2_API int c2CircletoAABB(c2Circle A, c2AABB B);
CUTE_C2_API int c2CircletoCapsule(c2Circle A, c2Capsule B);

typedef struct c2v
{
    float x;
    float y;
} c2v;
typedef struct c2Circle
{
    c2v p;
    float r;
} c2Circle;
typedef struct c2AABB
{
    c2v min;
    c2v max;
} c2AABB;
typedef struct c2Capsule
{
    c2v a;
    c2v b;
    float r;
} c2Capsule;

I am calling a function with the following signature:

pub export var RedisModule_Call: ?fn (?*RedisModuleCtx, [*c]const u8, [*c]const u8, ...) callconv(.C) ?*RedisModuleCallReply = undefined;

Like this:

const replyRemove = RedisModule_Call.?(ctx, "LREM", "sls", activeKey, -1, jobId);

zig complains with the following error:

/src/qdone.zig:94:79: error: TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481
        const replyRemove = RedisModule_Call.?(ctx, "LREM", "sls", activeKey, -1, jobId);
                                                                              ^
./src/qdone.zig:94:79: note: pointers, integers, floats, bools, and enums work on all targets
        const replyRemove = RedisModule_Call.?(ctx, "LREM", "sls", activeKey, -1, jobId);

It is passing an integer which is supposed to be supported, what gives?

@manast from looking at other reports in this thread it seems there may be a problem with variadic functions. The comment from @doppioandante (here) may be useful?

Working on translating some basic hello-world type serial code to zig.

Trying to open a serial port and set F_SETFL:

const clibs = @cImport({
  @cInclude("stdio.h");
  @cInclude("string.h");
  @cInclude("fcntl.h");
  @cInclude("errno.h");
  @cInclude("termios.h");
  @cInclude("unistd.h");
});

const std = @import("std");

pub fn main() void {
  var serial_port = clibs.open("/dev/ttyUSB3", clibs.O_RDWR| clibs.O_NOCTTY | clibs.O_NDELAY);
  if (-1 == clibs.fcntl(serial_port, clibs.F_SETFL, clibs.FNDELAY)) {
    @panic("Problem with FNDELAY setting");
  }

  var tty : clibs.termios = undefined;
  if(clibs.tcgetattr(serial_port, &tty) != 0) {
      @panic("Problem with tcgetattr");
  }
}

Results in compile error:

./d.zig:14:58: error: TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481
  if (-1 == clibs.fcntl(serial_port, clibs.F_SETFL, clibs.FNDELAY)) {
                                                         ^
./d.zig:14:58: note: pointers, integers, floats, bools, and enums work on all targets
  if (-1 == clibs.fcntl(serial_port, clibs.F_SETFL, clibs.FNDELAY)) {

On x86_64 musl libc/alpine linux, 0.7.0+98d5bfbd4.

I ran into this while playing with zig bindings for The Machinery.

We pass lots of small structs that are all floats by value, such as tm_vec3_t, tm_rect_t, etc -- both as parameters and return values. This blocked me from using Zig with The Machinery.

@niklas-ourmachinery it's a bit of a PITA, but you can hook zig up to The Machinery by adding wrapper C methods that take in a pointer to the tm_*_t types and then just call through to the real methods. I didn't make a full binding but was able to get things working back in beta 1.

@prime31 It's a bit more involved in this case, because I'm actually implementing a callback function. But yes, I could probably do something similar, create a C forwarding function that acts as the callback for the C code and in turn calls the zig function. I might try it just for proof of concept... but it becomes a bit tedious to do for all the API code.

Got it working for the callback too, code here for reference:

https://github.com/niklas-ourmachinery/zig-themachinery

@niklas-ourmachinery nice! By the way, The Machinery is really coming along nicely. I'm loving watching the progress.

Was this page helpful?
0 / 5 - 0 ratings