authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-25 16:12:36+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-25 16:12:36+02:00
log9a3dacc00ed805cb70b3b656f28de73185206b13
tree275f0e96320c0839edfaca58be886d86e08d603d
parent75e5b38410e81ddf21ba37a874ee2bb13dea7477
parenta7417f783953c0f39e929a690926336e5c495f30
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12188 from Luukdegram/llvm-wasm-c-abi

stage2: llvm - Implement C ABI when targetting wasm32

7 files changed, 116 insertions(+), 30 deletions(-)

build.zig+14-1
......@@ -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+1-1
......@@ -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+9-28
......@@ -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+28
......@@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type;
2222const LazySrcLoc = Module.LazySrcLoc;
2323const CType = @import("../type.zig").CType;
2424const x86_64_abi = @import("../arch/x86_64/abi.zig");
25const wasm_c_abi = @import("../arch/wasm/abi.zig");
2526
2627const 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 created+24
......@@ -0,0 +1,24 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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+6
......@@ -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+34
......@@ -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) {