| author | |
| committer | |
| log | 9a3dacc00ed805cb70b3b656f28de73185206b13 |
| tree | 275f0e96320c0839edfaca58be886d86e08d603d |
| parent | 75e5b38410e81ddf21ba37a874ee2bb13dea7477 |
| parent | a7417f783953c0f39e929a690926336e5c495f30 |
| signature |
stage2: llvm - Implement C ABI when targetting wasm327 files changed, 116 insertions(+), 30 deletions(-)
build.zig+14-1| ... | ... | @@ -484,7 +484,20 @@ pub fn build(b: *Builder) !void { |
| 484 | 484 | )); |
| 485 | 485 | |
| 486 | 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 | 501 | toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, omit_stage2)); |
| 489 | 502 | toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); |
| 490 | 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 | 1674 | try self.emitWValue(operand); |
| 1675 | 1675 | const opcode = buildOpcode(.{ |
| 1676 | 1676 | .op = .load, |
| 1677 | .width = @intCast(u8, scalar_type.abiSize(self.target)), | |
| 1677 | .width = @intCast(u8, scalar_type.abiSize(self.target) * 8), | |
| 1678 | 1678 | .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned, |
| 1679 | 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 | 23 | if (!ty.hasRuntimeBitsIgnoreComptime()) return none; |
| 24 | 24 | switch (ty.zigTypeTag()) { |
| 25 | 25 | .Struct => { |
| 26 | // When the (maybe) scalar type exceeds max 'direct' integer size | |
| 27 | if (ty.abiSize(target) > 8) return memory; | |
| 28 | 26 | // When the struct type is non-scalar |
| 29 | 27 | if (ty.structFieldCount() > 1) return memory; |
| 30 | 28 | // When the struct's alignment is non-natural |
| ... | ... | @@ -34,56 +32,39 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { |
| 34 | 32 | return memory; |
| 35 | 33 | } |
| 36 | 34 | } |
| 37 | if (field.ty.isInt() or field.ty.isAnyFloat()) { | |
| 38 | return direct; | |
| 39 | } | |
| 40 | 35 | return classifyType(field.ty, target); |
| 41 | 36 | }, |
| 42 | 37 | .Int, .Enum, .ErrorSet, .Vector => { |
| 43 | 38 | const int_bits = ty.intInfo(target).bits; |
| 44 | 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 | 41 | return memory; |
| 47 | 42 | }, |
| 48 | 43 | .Float => { |
| 49 | 44 | const float_bits = ty.floatBits(target); |
| 50 | 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 | 47 | return memory; |
| 53 | 48 | }, |
| 54 | 49 | .Bool => return direct, |
| 55 | 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 | 51 | .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; | |
| 71 | 54 | }, |
| 72 | 55 | .Pointer => { |
| 73 | // Slices act like struct and will be passed by reference | |
| 74 | if (ty.isSlice()) return memory; | |
| 56 | std.debug.assert(!ty.isSlice()); | |
| 75 | 57 | return direct; |
| 76 | 58 | }, |
| 77 | 59 | .Union => { |
| 78 | 60 | 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); | |
| 82 | 62 | if (ty.unionFields().count() > 1) return memory; |
| 83 | 63 | return classifyType(ty.unionFields().values()[0].ty, target); |
| 84 | 64 | }, |
| 85 | .AnyFrame, .Frame => return direct, | |
| 86 | ||
| 65 | .ErrorUnion, | |
| 66 | .Frame, | |
| 67 | .AnyFrame, | |
| 87 | 68 | .NoReturn, |
| 88 | 69 | .Void, |
| 89 | 70 | .Type, |
src/codegen/llvm.zig+28| ... | ... | @@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type; |
| 22 | 22 | const LazySrcLoc = Module.LazySrcLoc; |
| 23 | 23 | const CType = @import("../type.zig").CType; |
| 24 | 24 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 25 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); | |
| 25 | 26 | |
| 26 | 27 | const Error = error{ OutOfMemory, CodegenFail }; |
| 27 | 28 | |
| ... | ... | @@ -9093,6 +9094,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool |
| 9093 | 9094 | .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, |
| 9094 | 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 | 9098 | else => return false, // TODO investigate C ABI for other architectures |
| 9097 | 9099 | }, |
| 9098 | 9100 | else => return false, |
| ... | ... | @@ -9197,6 +9199,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. |
| 9197 | 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 | 9216 | // TODO investigate C ABI for other architectures |
| 9201 | 9217 | else => return dg.lowerType(fn_info.return_type), |
| 9202 | 9218 | } |
| ... | ... | @@ -9372,6 +9388,18 @@ const ParamTypeIterator = struct { |
| 9372 | 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 | 9403 | // TODO investigate C ABI for other architectures |
| 9376 | 9404 | else => { |
| 9377 | 9405 | it.zig_index += 1; |
test/stage1/c_abi/build_wasm.zig created+24| ... | ... | @@ -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+6| ... | ... | @@ -44,6 +44,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 44 | 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 | 53 | cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ |
| 48 | 54 | .build_modes = true, |
| 49 | 55 | .cross_targets = true, |
test/tests.zig+34| ... | ... | @@ -463,6 +463,11 @@ pub fn addStandaloneTests( |
| 463 | 463 | enable_macos_sdk: bool, |
| 464 | 464 | target: std.zig.CrossTarget, |
| 465 | 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 | 471 | ) *build.Step { |
| 467 | 472 | const cases = b.allocator.create(StandaloneContext) catch unreachable; |
| 468 | 473 | cases.* = StandaloneContext{ |
| ... | ... | @@ -475,6 +480,11 @@ pub fn addStandaloneTests( |
| 475 | 480 | .enable_macos_sdk = enable_macos_sdk, |
| 476 | 481 | .target = target, |
| 477 | 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 | }; |
| 479 | 489 | |
| 480 | 490 | standalone.addCases(cases); |
| ... | ... | @@ -962,6 +972,11 @@ pub const StandaloneContext = struct { |
| 962 | 972 | enable_macos_sdk: bool, |
| 963 | 973 | target: std.zig.CrossTarget, |
| 964 | 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, | |
| 965 | 980 | |
| 966 | 981 | pub fn addC(self: *StandaloneContext, root_src: []const u8) void { |
| 967 | 982 | self.addAllArgs(root_src, true); |
| ... | ... | @@ -976,6 +991,7 @@ pub const StandaloneContext = struct { |
| 976 | 991 | cross_targets: bool = false, |
| 977 | 992 | requires_macos_sdk: bool = false, |
| 978 | 993 | requires_stage2: bool = false, |
| 994 | use_emulation: bool = false, | |
| 979 | 995 | }) void { |
| 980 | 996 | const b = self.b; |
| 981 | 997 | |
| ... | ... | @@ -1007,6 +1023,24 @@ pub const StandaloneContext = struct { |
| 1007 | 1023 | zig_args.append(target_arg) catch unreachable; |
| 1008 | 1024 | } |
| 1009 | 1025 | |
| 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 | 1044 | const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug}; |
| 1011 | 1045 | for (modes) |mode| { |
| 1012 | 1046 | const arg = switch (mode) { |