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 {...@@ -484,7 +484,20 @@ pub fn build(b: *Builder) !void {
484 ));484 ));
485485
486 toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));486 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 ));
488 toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, omit_stage2));501 toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, omit_stage2));
489 toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));502 toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
490 toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes));503 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 {...@@ -1674,7 +1674,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1674 try self.emitWValue(operand);1674 try self.emitWValue(operand);
1675 const opcode = buildOpcode(.{1675 const opcode = buildOpcode(.{
1676 .op = .load,1676 .op = .load,
1677 .width = @intCast(u8, scalar_type.abiSize(self.target)),1677 .width = @intCast(u8, scalar_type.abiSize(self.target) * 8),
1678 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,1678 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
1679 .valtype1 = typeToValtype(scalar_type, self.target),1679 .valtype1 = typeToValtype(scalar_type, self.target),
1680 });1680 });
src/arch/wasm/abi.zig+9-28
...@@ -23,8 +23,6 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {...@@ -23,8 +23,6 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
23 if (!ty.hasRuntimeBitsIgnoreComptime()) return none;23 if (!ty.hasRuntimeBitsIgnoreComptime()) return none;
24 switch (ty.zigTypeTag()) {24 switch (ty.zigTypeTag()) {
25 .Struct => {25 .Struct => {
26 // When the (maybe) scalar type exceeds max 'direct' integer size
27 if (ty.abiSize(target) > 8) return memory;
28 // When the struct type is non-scalar26 // When the struct type is non-scalar
29 if (ty.structFieldCount() > 1) return memory;27 if (ty.structFieldCount() > 1) return memory;
30 // When the struct's alignment is non-natural28 // When the struct's alignment is non-natural
...@@ -34,56 +32,39 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {...@@ -34,56 +32,39 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
34 return memory;32 return memory;
35 }33 }
36 }34 }
37 if (field.ty.isInt() or field.ty.isAnyFloat()) {
38 return direct;
39 }
40 return classifyType(field.ty, target);35 return classifyType(field.ty, target);
41 },36 },
42 .Int, .Enum, .ErrorSet, .Vector => {37 .Int, .Enum, .ErrorSet, .Vector => {
43 const int_bits = ty.intInfo(target).bits;38 const int_bits = ty.intInfo(target).bits;
44 if (int_bits <= 64) return direct;39 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 };
46 return memory;41 return memory;
47 },42 },
48 .Float => {43 .Float => {
49 const float_bits = ty.floatBits(target);44 const float_bits = ty.floatBits(target);
50 if (float_bits <= 64) return direct;45 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 };
52 return memory;47 return memory;
53 },48 },
54 .Bool => return direct,49 .Bool => return direct,
55 .Array => return memory,50 .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 },
65 .Optional => {51 .Optional => {
66 if (ty.isPtrLikeOptional()) return direct;52 std.debug.assert(ty.isPtrLikeOptional());
67 var buf: Type.Payload.ElemType = undefined;53 return direct;
68 const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime();
69 if (!pl_has_bits) return direct;
70 return memory;
71 },54 },
72 .Pointer => {55 .Pointer => {
73 // Slices act like struct and will be passed by reference56 std.debug.assert(!ty.isSlice());
74 if (ty.isSlice()) return memory;
75 return direct;57 return direct;
76 },58 },
77 .Union => {59 .Union => {
78 const layout = ty.unionGetLayout(target);60 const layout = ty.unionGetLayout(target);
79 if (layout.payload_size == 0 and layout.tag_size != 0) {61 std.debug.assert(layout.tag_size == 0);
80 return classifyType(ty.unionTagTypeSafety().?, target);
81 }
82 if (ty.unionFields().count() > 1) return memory;62 if (ty.unionFields().count() > 1) return memory;
83 return classifyType(ty.unionFields().values()[0].ty, target);63 return classifyType(ty.unionFields().values()[0].ty, target);
84 },64 },
85 .AnyFrame, .Frame => return direct,65 .ErrorUnion,
8666 .Frame,
67 .AnyFrame,
87 .NoReturn,68 .NoReturn,
88 .Void,69 .Void,
89 .Type,70 .Type,
src/codegen/llvm.zig+28
...@@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type;...@@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type;
22const LazySrcLoc = Module.LazySrcLoc;22const LazySrcLoc = Module.LazySrcLoc;
23const CType = @import("../type.zig").CType;23const CType = @import("../type.zig").CType;
24const x86_64_abi = @import("../arch/x86_64/abi.zig");24const x86_64_abi = @import("../arch/x86_64/abi.zig");
25const wasm_c_abi = @import("../arch/wasm/abi.zig");
2526
26const Error = error{ OutOfMemory, CodegenFail };27const Error = error{ OutOfMemory, CodegenFail };
2728
...@@ -9093,6 +9094,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool...@@ -9093,6 +9094,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
9093 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,9094 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
9094 else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory,9095 else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory,
9095 },9096 },
9097 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,
9096 else => return false, // TODO investigate C ABI for other architectures9098 else => return false, // TODO investigate C ABI for other architectures
9097 },9099 },
9098 else => return false,9100 else => return false,
...@@ -9197,6 +9199,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm....@@ -9197,6 +9199,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
9197 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);9199 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
9198 },9200 },
9199 },9201 },
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 },
9200 // TODO investigate C ABI for other architectures9216 // TODO investigate C ABI for other architectures
9201 else => return dg.lowerType(fn_info.return_type),9217 else => return dg.lowerType(fn_info.return_type),
9202 }9218 }
...@@ -9372,6 +9388,18 @@ const ParamTypeIterator = struct {...@@ -9372,6 +9388,18 @@ const ParamTypeIterator = struct {
9372 return .multiple_llvm_ints;9388 return .multiple_llvm_ints;
9373 },9389 },
9374 },9390 },
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 },
9375 // TODO investigate C ABI for other architectures9403 // TODO investigate C ABI for other architectures
9376 else => {9404 else => {
9377 it.zig_index += 1;9405 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 {...@@ -44,6 +44,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
44 cases.addBuildFile("test/stage1/c_abi/build.zig", .{});44 cases.addBuildFile("test/stage1/c_abi/build.zig", .{});
45 }45 }
46 }46 }
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
47 cases.addBuildFile("test/standalone/c_compiler/build.zig", .{53 cases.addBuildFile("test/standalone/c_compiler/build.zig", .{
48 .build_modes = true,54 .build_modes = true,
49 .cross_targets = true,55 .cross_targets = true,
test/tests.zig+34
...@@ -463,6 +463,11 @@ pub fn addStandaloneTests(...@@ -463,6 +463,11 @@ pub fn addStandaloneTests(
463 enable_macos_sdk: bool,463 enable_macos_sdk: bool,
464 target: std.zig.CrossTarget,464 target: std.zig.CrossTarget,
465 omit_stage2: bool,465 omit_stage2: bool,
466 enable_darling: bool,
467 enable_qemu: bool,
468 enable_rosetta: bool,
469 enable_wasmtime: bool,
470 enable_wine: bool,
466) *build.Step {471) *build.Step {
467 const cases = b.allocator.create(StandaloneContext) catch unreachable;472 const cases = b.allocator.create(StandaloneContext) catch unreachable;
468 cases.* = StandaloneContext{473 cases.* = StandaloneContext{
...@@ -475,6 +480,11 @@ pub fn addStandaloneTests(...@@ -475,6 +480,11 @@ pub fn addStandaloneTests(
475 .enable_macos_sdk = enable_macos_sdk,480 .enable_macos_sdk = enable_macos_sdk,
476 .target = target,481 .target = target,
477 .omit_stage2 = omit_stage2,482 .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,
478 };488 };
479489
480 standalone.addCases(cases);490 standalone.addCases(cases);
...@@ -962,6 +972,11 @@ pub const StandaloneContext = struct {...@@ -962,6 +972,11 @@ pub const StandaloneContext = struct {
962 enable_macos_sdk: bool,972 enable_macos_sdk: bool,
963 target: std.zig.CrossTarget,973 target: std.zig.CrossTarget,
964 omit_stage2: bool,974 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
966 pub fn addC(self: *StandaloneContext, root_src: []const u8) void {981 pub fn addC(self: *StandaloneContext, root_src: []const u8) void {
967 self.addAllArgs(root_src, true);982 self.addAllArgs(root_src, true);
...@@ -976,6 +991,7 @@ pub const StandaloneContext = struct {...@@ -976,6 +991,7 @@ pub const StandaloneContext = struct {
976 cross_targets: bool = false,991 cross_targets: bool = false,
977 requires_macos_sdk: bool = false,992 requires_macos_sdk: bool = false,
978 requires_stage2: bool = false,993 requires_stage2: bool = false,
994 use_emulation: bool = false,
979 }) void {995 }) void {
980 const b = self.b;996 const b = self.b;
981997
...@@ -1007,6 +1023,24 @@ pub const StandaloneContext = struct {...@@ -1007,6 +1023,24 @@ pub const StandaloneContext = struct {
1007 zig_args.append(target_arg) catch unreachable;1023 zig_args.append(target_arg) catch unreachable;
1008 }1024 }
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
1010 const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug};1044 const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug};
1011 for (modes) |mode| {1045 for (modes) |mode| {
1012 const arg = switch (mode) {1046 const arg = switch (mode) {