authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-07-31 02:28:42+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-08-02 03:28:09+02:00
log6db520a4cd1ce2391c79d0d55b2b2d5297e133a3
tree279f46702877d50bdfaa04a23c4923758e264c1c
parent47c5f556c08c1b15c74dbaca743d17a3297bcdef

stage2-wasm: pass cabi tests

- make ubsan to be linkable by linker - currently ignore bool vectors tests for `.stage2_wasm`, let defer handling it properly after supporting simd128 in backend (mainly tiny fixes + enabling it for matrix) - I needed to refactor `wasm/abi.zig`, still bad

9 files changed, 202 insertions(+), 64 deletions(-)

src/codegen/llvm/FuncGen.zig+2-2
...@@ -7156,7 +7156,7 @@ const ParamTypeIterator = struct {...@@ -7156,7 +7156,7 @@ const ParamTypeIterator = struct {
7156 },7156 },
7157 }7157 }
7158 },7158 },
7159 .wasm_mvp => switch (wasm_c_abi.classifyType(ty, zcu)) {7159 .wasm_mvp => switch (wasm_c_abi.classifyTypeForLlvm(ty, zcu)) {
7160 .direct => |scalar_ty| {7160 .direct => |scalar_ty| {
7161 if (isScalar(zcu, ty)) {7161 if (isScalar(zcu, ty)) {
7162 it.zig_index += 1;7162 it.zig_index += 1;
...@@ -7508,7 +7508,7 @@ pub fn fnReturnStrat(o: *Object, cc: std.lang.CallingConvention, ret_ty: Type) A...@@ -7508,7 +7508,7 @@ pub fn fnReturnStrat(o: *Object, cc: std.lang.CallingConvention, ret_ty: Type) A
7508 .simple_aggregate => unreachable,7508 .simple_aggregate => unreachable,
7509 .pointer => .sret,7509 .pointer => .sret,
7510 },7510 },
7511 .wasm_mvp => switch (wasm_c_abi.classifyType(ret_ty, zcu)) {7511 .wasm_mvp => switch (wasm_c_abi.classifyTypeForLlvm(ret_ty, zcu)) {
7512 .direct => |scalar_ty| if (scalar_ty.toIntern() == ret_ty.toIntern()) {7512 .direct => |scalar_ty| if (scalar_ty.toIntern() == ret_ty.toIntern()) {
7513 assert(!isByRef(ret_ty, zcu));7513 assert(!isByRef(ret_ty, zcu));
7514 return .by_val;7514 return .by_val;
src/codegen/wasm/CodeGen.zig+81-32
...@@ -927,21 +927,26 @@ fn resolveCallingConventionValues(...@@ -927,21 +927,26 @@ fn resolveCallingConventionValues(
927 },927 },
928 .wasm_mvp => {928 .wasm_mvp => {
929 for (fn_info.param_types.get(ip)) |ty| {929 for (fn_info.param_types.get(ip)) |ty| {
930 if (!Type.fromInterned(ty).hasRuntimeBits(zcu)) {930 const param_ty: Type = .fromInterned(ty);
931 if (!param_ty.hasRuntimeBits(zcu)) {
931 continue;932 continue;
932 }933 }
933 switch (abi.classifyType(.fromInterned(ty), zcu)) {934
934 .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) {935 switch (abi.classifyType(param_ty, zcu, target)) {
936 .direct, .indirect => {
935 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });937 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
936 result.local_index += 1;938 result.local_index += 1;
937 } else {939 },
940 .double_i64 => {
938 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });941 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
939 try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } });942 try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } });
940 result.local_index += 2;943 result.local_index += 2;
941 },944 },
942 .indirect => {945 .unrolled => |vector| {
943 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });946 for (0..vector.len) |_| {
944 result.local_index += 1;947 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
948 result.local_index += 1;
949 }
945 },950 },
946 }951 }
947 }952 }
...@@ -968,9 +973,10 @@ pub fn firstParamSRet(...@@ -968,9 +973,10 @@ pub fn firstParamSRet(
968 switch (cc) {973 switch (cc) {
969 .@"inline" => unreachable,974 .@"inline" => unreachable,
970 .auto => return isByRef(return_type, zcu, target),975 .auto => return isByRef(return_type, zcu, target),
971 .wasm_mvp => switch (abi.classifyType(return_type, zcu)) {976 .wasm_mvp => switch (abi.classifyType(return_type, zcu, target)) {
972 .direct => |scalar_ty| return abi.lowerAsDoubleI64(scalar_ty, zcu),977 .direct => return false,
973 .indirect => return true,978 .double_i64, .indirect => return true,
979 .unrolled => |vector| return vector.len > 1,
974 },980 },
975 else => return false,981 else => return false,
976 }982 }
...@@ -985,18 +991,15 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu...@@ -985,18 +991,15 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu
985991
986 const zcu = cg.pt.zcu;992 const zcu = cg.pt.zcu;
987993
988 switch (abi.classifyType(ty, zcu)) {994 switch (abi.classifyType(ty, zcu, cg.target)) {
989 .direct => |scalar_type| if (!abi.lowerAsDoubleI64(scalar_type, zcu)) {995 .direct => |scalar_ty| {
990 if (!isByRef(ty, zcu, cg.target)) {996 if (!isByRef(ty, zcu, cg.target)) {
991 return cg.lowerToStack(value);997 return cg.lowerToStack(value);
992 } else {998 } else {
993 switch (value) {999 _ = try cg.load(value, scalar_ty, 0);
994 .nav_ref, .stack_offset => _ = try cg.load(value, scalar_type, 0),
995 .dead => unreachable,
996 else => try cg.emitWValue(value),
997 }
998 }1000 }
999 } else {1001 },
1002 .double_i64 => {
1000 assert(ty.abiSize(zcu) == 16);1003 assert(ty.abiSize(zcu) == 16);
1001 // in this case we have an integer or float that must be lowered as 2 i64's.1004 // in this case we have an integer or float that must be lowered as 2 i64's.
1002 try cg.emitWValue(value);1005 try cg.emitWValue(value);
...@@ -1004,7 +1007,17 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu...@@ -1004,7 +1007,17 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu
1004 try cg.emitWValue(value);1007 try cg.emitWValue(value);
1005 try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });1008 try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
1006 },1009 },
1007 .indirect => return cg.lowerToStack(value),1010 .indirect => {
1011 const stack_copy = try cg.allocStack(ty);
1012 try cg.store(stack_copy, value, ty, 0);
1013 return cg.lowerToStack(stack_copy);
1014 },
1015 .unrolled => |vector| {
1016 const elem_size: u32 = @intCast(vector.elem_type.abiSize(zcu));
1017 for (0..vector.len) |index| {
1018 _ = try cg.load(value, vector.elem_type, @intCast(index * elem_size));
1019 }
1020 },
1008 }1021 }
1009}1022}
10101023
...@@ -1947,16 +1960,19 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1947,16 +1960,19 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1947 if (cg.return_value != .none) {1960 if (cg.return_value != .none) {
1948 try cg.store(cg.return_value, operand, ret_ty, 0);1961 try cg.store(cg.return_value, operand, ret_ty, 0);
1949 } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBits(zcu)) {1962 } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBits(zcu)) {
1950 switch (abi.classifyType(ret_ty, zcu)) {1963 switch (abi.classifyType(ret_ty, zcu, cg.target)) {
1951 .direct => |scalar_type| {1964 .direct => |scalar_type| {
1952 assert(!abi.lowerAsDoubleI64(scalar_type, zcu));
1953 if (!isByRef(ret_ty, zcu, cg.target)) {1965 if (!isByRef(ret_ty, zcu, cg.target)) {
1954 try cg.emitWValue(operand);1966 try cg.emitWValue(operand);
1955 } else {1967 } else {
1956 _ = try cg.load(operand, scalar_type, 0);1968 _ = try cg.load(operand, scalar_type, 0);
1957 }1969 }
1958 },1970 },
1959 .indirect => unreachable,1971 .double_i64, .indirect => unreachable,
1972 .unrolled => |vector| {
1973 assert(vector.len == 1);
1974 _ = try cg.load(operand, vector.elem_type, 0);
1975 },
1960 }1976 }
1961 } else {1977 } else {
1962 if (!ret_ty.hasRuntimeBits(zcu) and ret_ty.isError(zcu)) {1978 if (!ret_ty.hasRuntimeBits(zcu) and ret_ty.isError(zcu)) {
...@@ -2003,8 +2019,18 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2003,8 +2019,18 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2003 try cg.addImm32(0);2019 try cg.addImm32(0);
2004 }2020 }
2005 } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) {2021 } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) {
2006 // leave on the stack2022 if (fn_info.cc == .wasm_mvp) {
2007 _ = try cg.load(operand, ret_ty, 0);2023 switch (abi.classifyType(ret_ty, zcu, cg.target)) {
2024 .direct => |scalar_type| _ = try cg.load(operand, scalar_type, 0),
2025 .double_i64, .indirect => unreachable,
2026 .unrolled => |vector| {
2027 assert(vector.len == 1);
2028 _ = try cg.load(operand, vector.elem_type, 0);
2029 },
2030 }
2031 } else {
2032 _ = try cg.load(operand, ret_ty, 0);
2033 }
2008 }2034 }
20092035
2010 try cg.restoreStackPointer();2036 try cg.restoreStackPointer();
...@@ -2132,22 +2158,30 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)...@@ -2132,22 +2158,30 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
2132 } else if (first_param_sret) {2158 } else if (first_param_sret) {
2133 break :result_value sret;2159 break :result_value sret;
2134 } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp) {2160 } else if (zcu.typeToFunc(fn_ty).?.cc == .wasm_mvp) {
2135 switch (abi.classifyType(ret_ty, zcu)) {2161 switch (abi.classifyType(ret_ty, zcu, cg.target)) {
2136 .direct => |scalar_type| {2162 .direct => |scalar_type| {
2137 assert(!abi.lowerAsDoubleI64(scalar_type, zcu));
2138 if (!isByRef(ret_ty, zcu, cg.target)) {2163 if (!isByRef(ret_ty, zcu, cg.target)) {
2139 const result_local = try cg.allocLocal(ret_ty);2164 const result_local = try cg.allocLocal(ret_ty);
2140 try cg.addLocal(.local_set, result_local.local.value);2165 try cg.addLocal(.local_set, result_local.local.value);
2141 break :result_value result_local;2166 break :result_value result_local;
2142 } else {2167 } else {
2143 const result_local = try cg.allocLocal(ret_ty);2168 const result_local = try cg.allocLocal(scalar_type);
2144 try cg.addLocal(.local_set, result_local.local.value);2169 try cg.addLocal(.local_set, result_local.local.value);
2145 const result = try cg.allocStack(ret_ty);2170 const result = try cg.allocStack(ret_ty);
2146 try cg.store(result, result_local, scalar_type, 0);2171 try cg.store(result, result_local, scalar_type, 0);
2147 break :result_value result;2172 break :result_value result;
2148 }2173 }
2149 },2174 },
2150 .indirect => unreachable,2175 .double_i64, .indirect => unreachable,
2176 .unrolled => |vector| {
2177 assert(vector.len == 1);
2178 const result_local = try cg.allocLocal(vector.elem_type);
2179 // save call result from operand stack
2180 try cg.addLocal(.local_set, result_local.local.value);
2181 const result = try cg.allocStack(ret_ty);
2182 try cg.store(result, result_local, vector.elem_type, 0);
2183 break :result_value result;
2184 },
2151 }2185 }
2152 } else {2186 } else {
2153 const result_local = try cg.allocLocal(ret_ty);2187 const result_local = try cg.allocLocal(ret_ty);
...@@ -2450,17 +2484,32 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2450,17 +2484,32 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2450 const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc;2484 const cc = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?.cc;
2451 const arg_ty = cg.typeOfIndex(inst);2485 const arg_ty = cg.typeOfIndex(inst);
2452 if (cc == .wasm_mvp) {2486 if (cc == .wasm_mvp) {
2453 switch (abi.classifyType(arg_ty, zcu)) {2487 switch (abi.classifyType(arg_ty, zcu, cg.target)) {
2454 .direct => |scalar_ty| if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) {2488 .direct => |scalar_type| {
2455 cg.arg_index += 1;2489 cg.arg_index += 1;
2456 } else {2490 if (isByRef(arg_ty, zcu, cg.target)) {
2491 const result = try cg.allocStack(arg_ty);
2492 try cg.store(result, arg, scalar_type, 0);
2493 return cg.finishAir(inst, result, &.{});
2494 }
2495 },
2496 .indirect => cg.arg_index += 1,
2497 .double_i64 => {
2457 cg.arg_index += 2;2498 cg.arg_index += 2;
2458 const result = try cg.allocStack(arg_ty);2499 const result = try cg.allocStack(arg_ty);
2459 try cg.store(result, arg, Type.u64, 0);2500 try cg.store(result, arg, Type.u64, 0);
2460 try cg.store(result, cg.args[arg_index + 1], Type.u64, 8);2501 try cg.store(result, cg.args[arg_index + 1], Type.u64, 8);
2461 return cg.finishAir(inst, result, &.{});2502 return cg.finishAir(inst, result, &.{});
2462 },2503 },
2463 .indirect => cg.arg_index += 1,2504 .unrolled => |vector| {
2505 const result = try cg.allocStack(arg_ty);
2506 const elem_size: u32 = @intCast(vector.elem_type.abiSize(zcu));
2507 for (0..vector.len) |index| {
2508 try cg.store(result, cg.args[cg.arg_index], vector.elem_type, @intCast(index * elem_size));
2509 cg.arg_index += 1;
2510 }
2511 return cg.finishAir(inst, result, &.{});
2512 },
2464 }2513 }
2465 } else {2514 } else {
2466 cg.arg_index += 1;2515 cg.arg_index += 1;
src/codegen/wasm/abi.zig+35-11
...@@ -11,16 +11,44 @@ const assert = std.debug.assert;...@@ -11,16 +11,44 @@ const assert = std.debug.assert;
11const Type = @import("../../Type.zig");11const Type = @import("../../Type.zig");
12const Zcu = @import("../../Zcu.zig");12const Zcu = @import("../../Zcu.zig");
1313
14/// Defines how to pass a type as part of a function signature,14/// Describes how the Wasm backend represents a C ABI value.
15/// both for parameters as well as return values.
16pub const Class = union(enum) {15pub const Class = union(enum) {
17 direct: Type,16 direct: Type,
17 double_i64,
18 indirect,18 indirect,
19 unrolled: struct {
20 elem_type: Type,
21 len: u32,
22 },
19};23};
2024
21/// Classifies a given Zig type to determine how they must be passed25pub const LlvmClass = union(enum) {
22/// or returned as value within a wasm function.26 direct: Type,
23pub fn classifyType(ty: Type, zcu: *const Zcu) Class {27 indirect,
28};
29
30pub fn classifyType(ty: Type, zcu: *const Zcu, target: *const Target) Class {
31 if (ty.zigTypeTag(zcu) == .vector) {
32 if (!(ty.bitSize(zcu) == 128 and target.cpu.has(.wasm, .simd128))) {
33 const elem_type = ty.childType(zcu);
34 return .{ .unrolled = .{
35 .elem_type = elem_type,
36 .len = ty.vectorLen(zcu),
37 } };
38 }
39 return .{ .direct = ty };
40 }
41
42 return switch (classifyTypeForLlvm(ty, zcu)) {
43 .direct => |scalar_ty| if (scalar_ty.bitSize(zcu) > 64)
44 .double_i64
45 else
46 .{ .direct = scalar_ty },
47 .indirect => .indirect,
48 };
49}
50
51pub fn classifyTypeForLlvm(ty: Type, zcu: *const Zcu) LlvmClass {
24 const ip = &zcu.intern_pool;52 const ip = &zcu.intern_pool;
25 assert(ty.hasRuntimeBits(zcu));53 assert(ty.hasRuntimeBits(zcu));
26 switch (ty.zigTypeTag(zcu)) {54 switch (ty.zigTypeTag(zcu)) {
...@@ -56,7 +84,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {...@@ -56,7 +84,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
56 if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu)))84 if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu)))
57 return .indirect;85 return .indirect;
58 }86 }
59 return classifyType(field_ty, zcu);87 return classifyTypeForLlvm(field_ty, zcu);
60 },88 },
61 .@"union" => {89 .@"union" => {
62 const union_obj = zcu.typeToUnion(ty).?;90 const union_obj = zcu.typeToUnion(ty).?;
...@@ -67,7 +95,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {...@@ -67,7 +95,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
67 assert(layout.tag_size == 0);95 assert(layout.tag_size == 0);
68 if (union_obj.field_types.len > 1) return .indirect;96 if (union_obj.field_types.len > 1) return .indirect;
69 const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);97 const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);
70 return classifyType(first_field_ty, zcu);98 return classifyTypeForLlvm(first_field_ty, zcu);
71 },99 },
72 .error_union,100 .error_union,
73 .frame,101 .frame,
...@@ -86,7 +114,3 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {...@@ -86,7 +114,3 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
86 => unreachable,114 => unreachable,
87 }115 }
88}116}
89
90pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool {
91 return scalar_ty.bitSize(zcu) > 64;
92}
src/link/Wasm.zig+22-13
...@@ -4938,12 +4938,15 @@ fn convertZcuFnType(...@@ -4938,12 +4938,15 @@ fn convertZcuFnType(
4938 try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle4938 try params_buffer.append(gpa, .i32); // memory address is always a 32-bit handle
4939 } else if (return_type.hasRuntimeBits(zcu)) {4939 } else if (return_type.hasRuntimeBits(zcu)) {
4940 if (cc == .wasm_mvp) {4940 if (cc == .wasm_mvp) {
4941 switch (abi.classifyType(return_type, zcu)) {4941 switch (abi.classifyType(return_type, zcu, target)) {
4942 .direct => |scalar_ty| {4942 .direct => |scalar_type| {
4943 assert(!abi.lowerAsDoubleI64(scalar_ty, zcu));4943 try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target));
4944 try returns_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target));4944 },
4945 .double_i64, .indirect => unreachable,
4946 .unrolled => |vector| {
4947 assert(vector.len == 1);
4948 try returns_buffer.append(gpa, CodeGen.typeToValtype(vector.elem_type, zcu, target));
4945 },4949 },
4946 .indirect => unreachable,
4947 }4950 }
4948 } else {4951 } else {
4949 try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target));4952 try returns_buffer.append(gpa, CodeGen.typeToValtype(return_type, zcu, target));
...@@ -4959,16 +4962,22 @@ fn convertZcuFnType(...@@ -4959,16 +4962,22 @@ fn convertZcuFnType(
49594962
4960 switch (cc) {4963 switch (cc) {
4961 .wasm_mvp => {4964 .wasm_mvp => {
4962 switch (abi.classifyType(param_type, zcu)) {4965 switch (abi.classifyType(param_type, zcu, target)) {
4963 .direct => |scalar_ty| {4966 .direct => |scalar_type| {
4964 if (!abi.lowerAsDoubleI64(scalar_ty, zcu)) {4967 try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_type, zcu, target));
4965 try params_buffer.append(gpa, CodeGen.typeToValtype(scalar_ty, zcu, target));4968 },
4966 } else {4969 .double_i64 => {
4967 try params_buffer.append(gpa, .i64);4970 try params_buffer.append(gpa, .i64);
4968 try params_buffer.append(gpa, .i64);4971 try params_buffer.append(gpa, .i64);
4972 },
4973 .indirect => {
4974 try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target));
4975 },
4976 .unrolled => |vector| {
4977 for (0..vector.len) |_| {
4978 try params_buffer.append(gpa, CodeGen.typeToValtype(vector.elem_type, zcu, target));
4969 }4979 }
4970 },4980 },
4971 .indirect => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),
4972 }4981 }
4973 },4982 },
4974 else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),4983 else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),
src/link/Wasm/Flush.zig+33-4
...@@ -248,6 +248,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -248,6 +248,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
248248
249 if (comp.zcu) |zcu| {249 if (comp.zcu) |zcu| {
250 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!250 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
251 const function_imports_start = wasm.function_imports.entries.len;
252 const global_imports_start = wasm.global_imports.entries.len;
253 const data_imports_start = wasm.data_imports.entries.len;
251254
252 log.debug("total MIR instructions: {d}", .{wasm.mir_instructions.len});255 log.debug("total MIR instructions: {d}", .{wasm.mir_instructions.len});
253256
...@@ -464,6 +467,35 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -464,6 +467,35 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
464 else => continue,467 else => continue,
465 };468 };
466469
470 // marking above may discover additional imports
471 try f.function_imports.ensureUnusedCapacity(gpa, wasm.function_imports.entries.len - function_imports_start);
472 for (
473 wasm.function_imports.keys()[function_imports_start..],
474 wasm.function_imports.values()[function_imports_start..],
475 ) |name, id| {
476 if (!f.function_imports.contains(name) and Wasm.FunctionIndex.fromSymbolName(wasm, name) == null) {
477 f.function_imports.putAssumeCapacity(name, id);
478 }
479 }
480
481 try f.global_imports.ensureUnusedCapacity(gpa, wasm.global_imports.entries.len - global_imports_start);
482 for (
483 wasm.global_imports.keys()[global_imports_start..],
484 wasm.global_imports.values()[global_imports_start..],
485 ) |name, id| {
486 if (!f.global_imports.contains(name)) f.global_imports.putAssumeCapacity(name, id);
487 }
488
489 try f.data_imports.ensureUnusedCapacity(gpa, wasm.data_imports.entries.len - data_imports_start);
490 for (
491 wasm.data_imports.keys()[data_imports_start..],
492 wasm.data_imports.values()[data_imports_start..],
493 ) |name, id| {
494 if (!f.data_imports.contains(name) and !f.data_exports.contains(name)) {
495 f.data_imports.putAssumeCapacity(name, id);
496 }
497 }
498
467 for (f.missing_exports.keys()) |exp_name| {499 for (f.missing_exports.keys()) |exp_name| {
468 diags.addError("manually specified export name '{s}' undefined", .{exp_name.slice(wasm)});500 diags.addError("manually specified export name '{s}' undefined", .{exp_name.slice(wasm)});
469 }501 }
...@@ -1075,13 +1107,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -1075,13 +1107,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
1075 if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| {1107 if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| {
1076 try emitStartSection(gpa, binary_bytes, .fromFunctionIndex(wasm, @fromBackingInt(@intCast(func_index))));1108 try emitStartSection(gpa, binary_bytes, .fromFunctionIndex(wasm, @fromBackingInt(@intCast(func_index))));
1077 section_index += 1;1109 section_index += 1;
1078 } else if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| {
1079 try emitStartSection(gpa, binary_bytes, func_index);
1080 section_index += 1;
1081 }1110 }
10821111
1083 // element section1112 // element section
1084 if (f.indirect_function_table.entries.len > 0) {1113 if (!is_obj and f.indirect_function_table.entries.len > 0) {
1085 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);1114 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
10861115
1087 // indirect function table elements1116 // indirect function table elements
src/link/Wasm/Object.zig+1-1
...@@ -856,7 +856,7 @@ pub fn parse(...@@ -856,7 +856,7 @@ pub fn parse(
856 start_function = @fromBackingInt(@intCast(functions_start + index));856 start_function = @fromBackingInt(@intCast(functions_start + index));
857 },857 },
858 .element => {858 .element => {
859 log.warn("unimplemented: element section in {f} {?s}", .{ path, archive_member_name });859 // element section is not needed for linking, validating it serves no purpose
860 pos = section_end;860 pos = section_end;
861 },861 },
862 .code => {862 .code => {
src/target.zig+1-1
...@@ -450,7 +450,7 @@ pub fn canBuildLibUbsanRt(target: *const std.Target) enum { no, yes, llvm_only,...@@ -450,7 +450,7 @@ pub fn canBuildLibUbsanRt(target: *const std.Target) enum { no, yes, llvm_only,
450 else => {},450 else => {},
451 }451 }
452 return switch (zigBackend(target, false)) {452 return switch (zigBackend(target, false)) {
453 .stage2_wasm => .llvm_lld_only,453 .stage2_wasm => .yes,
454 .stage2_x86_64 => .yes,454 .stage2_x86_64 => .yes,
455 else => .llvm_only,455 else => .llvm_only,
456 };456 };
test/c_abi/main.zig+18
...@@ -451,6 +451,7 @@ test "long double" {...@@ -451,6 +451,7 @@ test "long double" {
451451
452comptime {452comptime {
453 skip: {453 skip: {
454 if (builtin.zig_backend == .stage2_wasm) break :skip;
454 if (builtin.cpu.arch == .hexagon) break :skip;455 if (builtin.cpu.arch == .hexagon) break :skip;
455 if (builtin.cpu.arch == .loongarch64) break :skip;456 if (builtin.cpu.arch == .loongarch64) break :skip;
456 if (builtin.cpu.arch.isMIPS()) break :skip;457 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -477,6 +478,7 @@ extern fn c_vector_2_bool(@Vector(2, bool)) void;...@@ -477,6 +478,7 @@ extern fn c_vector_2_bool(@Vector(2, bool)) void;
477extern fn c_test_vector_2_bool() void;478extern fn c_test_vector_2_bool() void;
478479
479test "@Vector(2, bool)" {480test "@Vector(2, bool)" {
481 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
480 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;482 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
481 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;483 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
482 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;484 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
...@@ -497,6 +499,7 @@ test "@Vector(2, bool)" {...@@ -497,6 +499,7 @@ test "@Vector(2, bool)" {
497499
498comptime {500comptime {
499 skip: {501 skip: {
502 if (builtin.zig_backend == .stage2_wasm) break :skip;
500 if (builtin.cpu.arch == .hexagon) break :skip;503 if (builtin.cpu.arch == .hexagon) break :skip;
501 if (builtin.cpu.arch == .loongarch64) break :skip;504 if (builtin.cpu.arch == .loongarch64) break :skip;
502 if (builtin.cpu.arch.isMIPS()) break :skip;505 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -527,6 +530,7 @@ extern fn c_vector_4_bool(@Vector(4, bool)) void;...@@ -527,6 +530,7 @@ extern fn c_vector_4_bool(@Vector(4, bool)) void;
527extern fn c_test_vector_4_bool() void;530extern fn c_test_vector_4_bool() void;
528531
529test "@Vector(4, bool)" {532test "@Vector(4, bool)" {
533 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
530 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;534 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
531 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;535 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
532 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;536 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
...@@ -551,6 +555,7 @@ test "@Vector(4, bool)" {...@@ -551,6 +555,7 @@ test "@Vector(4, bool)" {
551555
552comptime {556comptime {
553 skip: {557 skip: {
558 if (builtin.zig_backend == .stage2_wasm) break :skip;
554 if (builtin.cpu.arch == .hexagon) break :skip;559 if (builtin.cpu.arch == .hexagon) break :skip;
555 if (builtin.cpu.arch == .loongarch64) break :skip;560 if (builtin.cpu.arch == .loongarch64) break :skip;
556 if (builtin.cpu.arch.isMIPS()) break :skip;561 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -589,6 +594,7 @@ extern fn c_vector_8_bool(@Vector(8, bool)) void;...@@ -589,6 +594,7 @@ extern fn c_vector_8_bool(@Vector(8, bool)) void;
589extern fn c_test_vector_8_bool() void;594extern fn c_test_vector_8_bool() void;
590595
591test "@Vector(8, bool)" {596test "@Vector(8, bool)" {
597 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
592 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;598 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
593 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;599 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
594 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;600 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
...@@ -621,6 +627,7 @@ test "@Vector(8, bool)" {...@@ -621,6 +627,7 @@ test "@Vector(8, bool)" {
621627
622comptime {628comptime {
623 skip: {629 skip: {
630 if (builtin.zig_backend == .stage2_wasm) break :skip;
624 if (builtin.cpu.arch == .hexagon) break :skip;631 if (builtin.cpu.arch == .hexagon) break :skip;
625 if (builtin.cpu.arch == .loongarch64) break :skip;632 if (builtin.cpu.arch == .loongarch64) break :skip;
626 if (builtin.cpu.arch.isMIPS()) break :skip;633 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -675,6 +682,7 @@ extern fn c_vector_16_bool(@Vector(16, bool)) void;...@@ -675,6 +682,7 @@ extern fn c_vector_16_bool(@Vector(16, bool)) void;
675extern fn c_test_vector_16_bool() void;682extern fn c_test_vector_16_bool() void;
676683
677test "@Vector(16, bool)" {684test "@Vector(16, bool)" {
685 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
678 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;686 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
679 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;687 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
680 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;688 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
...@@ -723,6 +731,7 @@ test "@Vector(16, bool)" {...@@ -723,6 +731,7 @@ test "@Vector(16, bool)" {
723731
724comptime {732comptime {
725 skip: {733 skip: {
734 if (builtin.zig_backend == .stage2_wasm) break :skip;
726 if (builtin.cpu.arch == .hexagon) break :skip;735 if (builtin.cpu.arch == .hexagon) break :skip;
727 if (builtin.cpu.arch == .loongarch64) break :skip;736 if (builtin.cpu.arch == .loongarch64) break :skip;
728 if (builtin.cpu.arch.isMIPS()) break :skip;737 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -809,6 +818,7 @@ extern fn c_vector_32_bool(@Vector(32, bool)) void;...@@ -809,6 +818,7 @@ extern fn c_vector_32_bool(@Vector(32, bool)) void;
809extern fn c_test_vector_32_bool() void;818extern fn c_test_vector_32_bool() void;
810819
811test "@Vector(32, bool)" {820test "@Vector(32, bool)" {
821 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
812 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;822 if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
813 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;823 if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
814 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;824 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
...@@ -889,6 +899,7 @@ test "@Vector(32, bool)" {...@@ -889,6 +899,7 @@ test "@Vector(32, bool)" {
889899
890comptime {900comptime {
891 skip: {901 skip: {
902 if (builtin.zig_backend == .stage2_wasm) break :skip;
892 if (builtin.cpu.arch == .hexagon) break :skip;903 if (builtin.cpu.arch == .hexagon) break :skip;
893 if (builtin.cpu.arch == .loongarch64) break :skip;904 if (builtin.cpu.arch == .loongarch64) break :skip;
894 if (builtin.cpu.arch.isMIPS()) break :skip;905 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -1039,6 +1050,7 @@ extern fn c_vector_64_bool(@Vector(64, bool)) void;...@@ -1039,6 +1050,7 @@ extern fn c_vector_64_bool(@Vector(64, bool)) void;
1039extern fn c_test_vector_64_bool() void;1050extern fn c_test_vector_64_bool() void;
10401051
1041test "@Vector(64, bool)" {1052test "@Vector(64, bool)" {
1053 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1042 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;1054 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
1043 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;1055 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;
1044 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;1056 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
...@@ -1181,6 +1193,7 @@ test "@Vector(64, bool)" {...@@ -1181,6 +1193,7 @@ test "@Vector(64, bool)" {
11811193
1182comptime {1194comptime {
1183 skip: {1195 skip: {
1196 if (builtin.zig_backend == .stage2_wasm) break :skip;
1184 if (builtin.cpu.arch == .hexagon) break :skip;1197 if (builtin.cpu.arch == .hexagon) break :skip;
1185 if (builtin.cpu.arch == .loongarch64) break :skip;1198 if (builtin.cpu.arch == .loongarch64) break :skip;
1186 if (builtin.cpu.arch.isMIPS()) break :skip;1199 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -1459,6 +1472,7 @@ extern fn c_vector_128_bool(@Vector(128, bool)) void;...@@ -1459,6 +1472,7 @@ extern fn c_vector_128_bool(@Vector(128, bool)) void;
1459extern fn c_test_vector_128_bool() void;1472extern fn c_test_vector_128_bool() void;
14601473
1461test "@Vector(128, bool)" {1474test "@Vector(128, bool)" {
1475 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1462 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;1476 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
1463 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;1477 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;
1464 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;1478 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
...@@ -1729,6 +1743,7 @@ test "@Vector(128, bool)" {...@@ -1729,6 +1743,7 @@ test "@Vector(128, bool)" {
17291743
1730comptime {1744comptime {
1731 skip: {1745 skip: {
1746 if (builtin.zig_backend == .stage2_wasm) break :skip;
1732 if (builtin.cpu.arch == .hexagon) break :skip;1747 if (builtin.cpu.arch == .hexagon) break :skip;
1733 if (builtin.cpu.arch == .loongarch64) break :skip;1748 if (builtin.cpu.arch == .loongarch64) break :skip;
1734 if (builtin.cpu.arch.isMIPS()) break :skip;1749 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -2263,6 +2278,7 @@ extern fn c_vector_256_bool(@Vector(256, bool)) void;...@@ -2263,6 +2278,7 @@ extern fn c_vector_256_bool(@Vector(256, bool)) void;
2263extern fn c_test_vector_256_bool() void;2278extern fn c_test_vector_256_bool() void;
22642279
2265test "@Vector(256, bool)" {2280test "@Vector(256, bool)" {
2281 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
2266 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;2282 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
2267 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;2283 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;
2268 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;2284 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
...@@ -2789,6 +2805,7 @@ test "@Vector(256, bool)" {...@@ -2789,6 +2805,7 @@ test "@Vector(256, bool)" {
27892805
2790comptime {2806comptime {
2791 skip: {2807 skip: {
2808 if (builtin.zig_backend == .stage2_wasm) break :skip;
2792 if (builtin.cpu.arch == .hexagon) break :skip;2809 if (builtin.cpu.arch == .hexagon) break :skip;
2793 if (builtin.cpu.arch == .loongarch64) break :skip;2810 if (builtin.cpu.arch == .loongarch64) break :skip;
2794 if (builtin.cpu.arch.isMIPS()) break :skip;2811 if (builtin.cpu.arch.isMIPS()) break :skip;
...@@ -3835,6 +3852,7 @@ extern fn c_vector_512_bool(@Vector(512, bool)) void;...@@ -3835,6 +3852,7 @@ extern fn c_vector_512_bool(@Vector(512, bool)) void;
3835extern fn c_test_vector_512_bool() void;3852extern fn c_test_vector_512_bool() void;
38363853
3837test "@Vector(512, bool)" {3854test "@Vector(512, bool)" {
3855 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
3838 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;3856 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
3839 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;3857 if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest;
3840 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;3858 if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
test/tests.zig+9
...@@ -2060,6 +2060,15 @@ const c_abi_targets = blk: {...@@ -2060,6 +2060,15 @@ const c_abi_targets = blk: {
2060 .abi = .musl,2060 .abi = .musl,
2061 },2061 },
2062 },2062 },
2063 .{
2064 .target = .{
2065 .cpu_arch = .wasm32,
2066 .os_tag = .wasi,
2067 .abi = .musl,
2068 },
2069 .use_llvm = false,
2070 .use_lld = false,
2071 },
20632072
2064 // Windows Targets2073 // Windows Targets
20652074