Skip to content

Commit 9a3dacc

Browse files
authored
Merge pull request #12188 from Luukdegram/llvm-wasm-c-abi
stage2: llvm - Implement C ABI when targetting wasm32
2 parents 75e5b38 + a7417f7 commit 9a3dacc

File tree

7 files changed

+116
-30
lines changed

7 files changed

+116
-30
lines changed

build.zig

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,20 @@ pub fn build(b: *Builder) !void {
484484
));
485485

486486
toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
487-
toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target, omit_stage2));
487+
toolchain_step.dependOn(tests.addStandaloneTests(
488+
b,
489+
test_filter,
490+
modes,
491+
skip_non_native,
492+
enable_macos_sdk,
493+
target,
494+
omit_stage2,
495+
b.enable_darling,
496+
b.enable_qemu,
497+
b.enable_rosetta,
498+
b.enable_wasmtime,
499+
b.enable_wine,
500+
));
488501
toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, omit_stage2));
489502
toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
490503
toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes));

src/arch/wasm/CodeGen.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16741674
try self.emitWValue(operand);
16751675
const opcode = buildOpcode(.{
16761676
.op = .load,
1677-
.width = @intCast(u8, scalar_type.abiSize(self.target)),
1677+
.width = @intCast(u8, scalar_type.abiSize(self.target) * 8),
16781678
.signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
16791679
.valtype1 = typeToValtype(scalar_type, self.target),
16801680
});

src/arch/wasm/abi.zig

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
2323
if (!ty.hasRuntimeBitsIgnoreComptime()) return none;
2424
switch (ty.zigTypeTag()) {
2525
.Struct => {
26-
// When the (maybe) scalar type exceeds max 'direct' integer size
27-
if (ty.abiSize(target) > 8) return memory;
2826
// When the struct type is non-scalar
2927
if (ty.structFieldCount() > 1) return memory;
3028
// When the struct's alignment is non-natural
@@ -34,56 +32,39 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
3432
return memory;
3533
}
3634
}
37-
if (field.ty.isInt() or field.ty.isAnyFloat()) {
38-
return direct;
39-
}
4035
return classifyType(field.ty, target);
4136
},
4237
.Int, .Enum, .ErrorSet, .Vector => {
4338
const int_bits = ty.intInfo(target).bits;
4439
if (int_bits <= 64) return direct;
45-
if (int_bits > 64 and int_bits <= 128) return .{ .direct, .direct };
40+
if (int_bits <= 128) return .{ .direct, .direct };
4641
return memory;
4742
},
4843
.Float => {
4944
const float_bits = ty.floatBits(target);
5045
if (float_bits <= 64) return direct;
51-
if (float_bits > 64 and float_bits <= 128) return .{ .direct, .direct };
46+
if (float_bits <= 128) return .{ .direct, .direct };
5247
return memory;
5348
},
5449
.Bool => return direct,
5550
.Array => return memory,
56-
.ErrorUnion => {
57-
const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime();
58-
const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime();
59-
if (!has_pl) return direct;
60-
if (!has_tag) {
61-
return classifyType(ty.errorUnionPayload(), target);
62-
}
63-
return memory;
64-
},
6551
.Optional => {
66-
if (ty.isPtrLikeOptional()) return direct;
67-
var buf: Type.Payload.ElemType = undefined;
68-
const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime();
69-
if (!pl_has_bits) return direct;
70-
return memory;
52+
std.debug.assert(ty.isPtrLikeOptional());
53+
return direct;
7154
},
7255
.Pointer => {
73-
// Slices act like struct and will be passed by reference
74-
if (ty.isSlice()) return memory;
56+
std.debug.assert(!ty.isSlice());
7557
return direct;
7658
},
7759
.Union => {
7860
const layout = ty.unionGetLayout(target);
79-
if (layout.payload_size == 0 and layout.tag_size != 0) {
80-
return classifyType(ty.unionTagTypeSafety().?, target);
81-
}
61+
std.debug.assert(layout.tag_size == 0);
8262
if (ty.unionFields().count() > 1) return memory;
8363
return classifyType(ty.unionFields().values()[0].ty, target);
8464
},
85-
.AnyFrame, .Frame => return direct,
86-
65+
.ErrorUnion,
66+
.Frame,
67+
.AnyFrame,
8768
.NoReturn,
8869
.Void,
8970
.Type,

src/codegen/llvm.zig

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type;
2222
const LazySrcLoc = Module.LazySrcLoc;
2323
const CType = @import("../type.zig").CType;
2424
const x86_64_abi = @import("../arch/x86_64/abi.zig");
25+
const wasm_c_abi = @import("../arch/wasm/abi.zig");
2526

2627
const Error = error{ OutOfMemory, CodegenFail };
2728

@@ -9093,6 +9094,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
90939094
.windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
90949095
else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory,
90959096
},
9097+
.wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,
90969098
else => return false, // TODO investigate C ABI for other architectures
90979099
},
90989100
else => return false,
@@ -9197,6 +9199,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
91979199
return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
91989200
},
91999201
},
9202+
.wasm32 => {
9203+
if (is_scalar) {
9204+
return dg.lowerType(fn_info.return_type);
9205+
}
9206+
const classes = wasm_c_abi.classifyType(fn_info.return_type, target);
9207+
if (classes[0] == .indirect or classes[0] == .none) {
9208+
return dg.context.voidType();
9209+
}
9210+
9211+
assert(classes[0] == .direct and classes[1] == .none);
9212+
const scalar_type = wasm_c_abi.scalarType(fn_info.return_type, target);
9213+
const abi_size = scalar_type.abiSize(target);
9214+
return dg.context.intType(@intCast(c_uint, abi_size * 8));
9215+
},
92009216
// TODO investigate C ABI for other architectures
92019217
else => return dg.lowerType(fn_info.return_type),
92029218
}
@@ -9372,6 +9388,18 @@ const ParamTypeIterator = struct {
93729388
return .multiple_llvm_ints;
93739389
},
93749390
},
9391+
.wasm32 => {
9392+
it.zig_index += 1;
9393+
it.llvm_index += 1;
9394+
if (is_scalar) {
9395+
return .byval;
9396+
}
9397+
const classes = wasm_c_abi.classifyType(ty, it.target);
9398+
if (classes[0] == .indirect) {
9399+
return .byref;
9400+
}
9401+
return .abi_sized_int;
9402+
},
93759403
// TODO investigate C ABI for other architectures
93769404
else => {
93779405
it.zig_index += 1;

test/stage1/c_abi/build_wasm.zig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const std = @import("std");
2+
const Builder = std.build.Builder;
3+
4+
pub fn build(b: *Builder) void {
5+
const rel_opts = b.standardReleaseOptions();
6+
const target: std.zig.CrossTarget = .{ .cpu_arch = .wasm32, .os_tag = .wasi };
7+
b.use_stage1 = false;
8+
9+
const c_obj = b.addObject("cfuncs", null);
10+
c_obj.addCSourceFile("cfuncs.c", &[_][]const u8{"-std=c99"});
11+
c_obj.setBuildMode(rel_opts);
12+
c_obj.linkSystemLibrary("c");
13+
c_obj.setTarget(target);
14+
15+
const main = b.addTest("main.zig");
16+
main.setBuildMode(rel_opts);
17+
main.addObject(c_obj);
18+
main.setTarget(target);
19+
20+
const test_step = b.step("test", "Test the program");
21+
test_step.dependOn(&main.step);
22+
23+
b.default_step.dependOn(test_step);
24+
}

test/standalone.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
4444
cases.addBuildFile("test/stage1/c_abi/build.zig", .{});
4545
}
4646
}
47+
// C ABI tests only pass for the Wasm target when using stage2
48+
cases.addBuildFile("test/stage1/c_abi/build_wasm.zig", .{
49+
.requires_stage2 = true,
50+
.use_emulation = true,
51+
});
52+
4753
cases.addBuildFile("test/standalone/c_compiler/build.zig", .{
4854
.build_modes = true,
4955
.cross_targets = true,

test/tests.zig

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,11 @@ pub fn addStandaloneTests(
463463
enable_macos_sdk: bool,
464464
target: std.zig.CrossTarget,
465465
omit_stage2: bool,
466+
enable_darling: bool,
467+
enable_qemu: bool,
468+
enable_rosetta: bool,
469+
enable_wasmtime: bool,
470+
enable_wine: bool,
466471
) *build.Step {
467472
const cases = b.allocator.create(StandaloneContext) catch unreachable;
468473
cases.* = StandaloneContext{
@@ -475,6 +480,11 @@ pub fn addStandaloneTests(
475480
.enable_macos_sdk = enable_macos_sdk,
476481
.target = target,
477482
.omit_stage2 = omit_stage2,
483+
.enable_darling = enable_darling,
484+
.enable_qemu = enable_qemu,
485+
.enable_rosetta = enable_rosetta,
486+
.enable_wasmtime = enable_wasmtime,
487+
.enable_wine = enable_wine,
478488
};
479489

480490
standalone.addCases(cases);
@@ -962,6 +972,11 @@ pub const StandaloneContext = struct {
962972
enable_macos_sdk: bool,
963973
target: std.zig.CrossTarget,
964974
omit_stage2: bool,
975+
enable_darling: bool = false,
976+
enable_qemu: bool = false,
977+
enable_rosetta: bool = false,
978+
enable_wasmtime: bool = false,
979+
enable_wine: bool = false,
965980

966981
pub fn addC(self: *StandaloneContext, root_src: []const u8) void {
967982
self.addAllArgs(root_src, true);
@@ -976,6 +991,7 @@ pub const StandaloneContext = struct {
976991
cross_targets: bool = false,
977992
requires_macos_sdk: bool = false,
978993
requires_stage2: bool = false,
994+
use_emulation: bool = false,
979995
}) void {
980996
const b = self.b;
981997

@@ -1007,6 +1023,24 @@ pub const StandaloneContext = struct {
10071023
zig_args.append(target_arg) catch unreachable;
10081024
}
10091025

1026+
if (features.use_emulation) {
1027+
if (self.enable_darling) {
1028+
zig_args.append("-fdarling") catch unreachable;
1029+
}
1030+
if (self.enable_qemu) {
1031+
zig_args.append("-fqemu") catch unreachable;
1032+
}
1033+
if (self.enable_rosetta) {
1034+
zig_args.append("-frosetta") catch unreachable;
1035+
}
1036+
if (self.enable_wasmtime) {
1037+
zig_args.append("-fwasmtime") catch unreachable;
1038+
}
1039+
if (self.enable_wine) {
1040+
zig_args.append("-fwine") catch unreachable;
1041+
}
1042+
}
1043+
10101044
const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug};
10111045
for (modes) |mode| {
10121046
const arg = switch (mode) {

0 commit comments

Comments
 (0)