Zig Version
0.14.0-dev.2649+77273103a
Steps to Reproduce and Observed Behavior
The zig compiler is generating wrong C ABI calls for amd64 linux when invoking functions with struct arguments.
Given the following source files:
main.c:
const std = @import("std");
const a = @import("a.zig");
pub fn main() !void {
std.debug.print("begin\n", .{});
const p1: ?*anyopaque = @ptrFromInt(1);
const v1: a.vec = .{ .a = 2.0, .b = 3.0 };
const v2: a.vec = .{ .a = 4.0, .b = 5.0 };
const f1: f64 = 6.0;
const s1: a.structure = .{ .a = 7, .b = 8, .c = 9 };
const p2: ?*anyopaque = @ptrFromInt(10);
const p3: ?*anyopaque = @ptrFromInt(11);
a.func(p1, v1, v2, f1, s1, p2, p3);
std.debug.print("end\n", .{});
}
a.h:
#include <stdint.h>
typedef struct { double a, b; } vec;
typedef struct { uintptr_t a; unsigned int b, c; } structure;
void func(void* p1, vec v1, vec v2, double f1, structure s1, void* p2, void* p3);
a.c:
#include <stdio.h>
#include "a.h"
void func(void *p1, vec v1, vec v2, double f1, structure s1, void *p2, void *p3) {
printf("p1=%p\n", p1);
printf("v1.a=%f\n", v1.a);
printf("v1.b=%f\n", v1.b);
printf("v2.a=%f\n", v2.a);
printf("v2.b=%f\n", v2.b);
printf("f1=%f\n", f1);
printf("s1.a=%lu\n", s1.a);
printf("s1.b=%u\n", s1.b);
printf("s1.c=%u\n", s1.c);
printf("p2=%p\n", p2);
printf("p3=%p\n", p3);
}
The following commands produce:
$ zig translate-c ./a.h > ./a.zig
$ zig run ./main.zig ./a.c -lc
begin
p1=0x1
v1.a=2.000000
v1.b=3.000000
v2.a=4.000000
v2.b=5.000000
f1=6.000000
s1.a=10
s1.b=11
s1.c=0
p2=0x7
p3=0x6
end
However the expected output would be:
p1=0x1
v1.a=2.000000
v1.b=3.000000
v2.a=4.000000
v2.b=5.000000
f1=6.000000
s1.a=7
s1.b=8
s1.c=9
p2=0xa
p3=0xb
Since structure has 16 bytes, s1 should be passed via 2 separate 64 bit registers. However the zig compiler seems to be passing s1 via the stack instead. In practice, that causes the s1 fields in the C function to receive the values passed as p2 and p3 .
If I add an additional field to the structure, forcing it to be bigger than 16 bytes, then the SystemV amd64 ABI expects that argument to be passed via the stack and the program works fine.
You can find more information about aggregate types in the SystemV amd64 ABI in the following article:
https://yorickpeterse.com/articles/the-mess-that-is-handling-structure-arguments-and-returns-in-llvm/
Expected Behavior
Expected output:
p1=0x1
v1.a=2.000000
v1.b=3.000000
v2.a=4.000000
v2.b=5.000000
f1=6.000000
s1.a=7
s1.b=8
s1.c=9
p2=0xa
p3=0xb
The 3 fields from the structure should be passed to the C function via 2 separate 64 bit registers.
Zig Version
0.14.0-dev.2649+77273103a
Steps to Reproduce and Observed Behavior
The zig compiler is generating wrong C ABI calls for amd64 linux when invoking functions with struct arguments.
Given the following source files:
main.c:a.h:a.c:The following commands produce:
However the expected output would be:
Since
structurehas 16 bytes,s1should be passed via 2 separate 64 bit registers. However the zig compiler seems to be passings1via the stack instead. In practice, that causes thes1fields in the C function to receive the values passed asp2andp3.If I add an additional field to the structure, forcing it to be bigger than 16 bytes, then the SystemV amd64 ABI expects that argument to be passed via the stack and the program works fine.
You can find more information about aggregate types in the SystemV amd64 ABI in the following article:
https://yorickpeterse.com/articles/the-mess-that-is-handling-structure-arguments-and-returns-in-llvm/
Expected Behavior
Expected output:
The 3 fields from the structure should be passed to the C function via 2 separate 64 bit registers.