authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-07-01 16:59:35+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-07-01 23:58:49+02:00
log4805b6f7acc07223a41b38e51a9a6e4f020dfdfe
treefe8d8a128c71db31972d0e097402b26a70fa471a
parent69d65ae472e725726d3c33f23c486d2c871ef8c7

stage2-wasm: support varargs


5 files changed, 190 insertions(+), 41 deletions(-)

src/codegen/wasm/CodeGen.zig+175-28
...@@ -196,6 +196,9 @@ args: []WValue,...@@ -196,6 +196,9 @@ args: []WValue,
196/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated196/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
197/// before this function returns its execution to the caller.197/// before this function returns its execution to the caller.
198return_value: WValue,198return_value: WValue,
199/// Only populated for variadic functions.
200/// Holds the hidden final parameter pointing to the varargs buffer.
201varargs: WValue,
199/// The size of the stack this function occupies. In the function prologue202/// The size of the stack this function occupies. In the function prologue
200/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`.203/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`.
201stack_size: u32 = 0,204stack_size: u32 = 0,
...@@ -308,7 +311,7 @@ const WValue = union(enum) {...@@ -308,7 +311,7 @@ const WValue = union(enum) {
308 fn free(value: *WValue, gen: *CodeGen) void {311 fn free(value: *WValue, gen: *CodeGen) void {
309 if (value.* != .local) return;312 if (value.* != .local) return;
310 const local_value = value.local.value;313 const local_value = value.local.value;
311 const reserved = gen.args.len + @intFromBool(gen.return_value != .none);314 const reserved = gen.args.len + @intFromBool(gen.return_value != .none) + @intFromBool(gen.varargs != .none);
312 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.315 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
313316
314 const index = local_value - reserved;317 const index = local_value - reserved;
...@@ -800,6 +803,7 @@ pub fn generate(...@@ -800,6 +803,7 @@ pub fn generate(
800 .func_index = func_index,803 .func_index = func_index,
801 .args = cc_result.args,804 .args = cc_result.args,
802 .return_value = cc_result.return_value,805 .return_value = cc_result.return_value,
806 .varargs = cc_result.varargs,
803 .local_index = cc_result.local_index,807 .local_index = cc_result.local_index,
804 .mir_instructions = .empty,808 .mir_instructions = .empty,
805 .mir_extra = .empty,809 .mir_extra = .empty,
...@@ -874,6 +878,7 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {...@@ -874,6 +878,7 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
874const CallWValues = struct {878const CallWValues = struct {
875 args: []WValue,879 args: []WValue,
876 return_value: WValue,880 return_value: WValue,
881 varargs: WValue,
877 local_index: u32,882 local_index: u32,
878883
879 fn deinit(values: *CallWValues, gpa: Allocator) void {884 fn deinit(values: *CallWValues, gpa: Allocator) void {
...@@ -895,6 +900,7 @@ fn resolveCallingConventionValues(...@@ -895,6 +900,7 @@ fn resolveCallingConventionValues(
895 var result: CallWValues = .{900 var result: CallWValues = .{
896 .args = &.{},901 .args = &.{},
897 .return_value = .none,902 .return_value = .none,
903 .varargs = .none,
898 .local_index = 0,904 .local_index = 0,
899 };905 };
900 if (cc == .naked) return result;906 if (cc == .naked) return result;
...@@ -945,6 +951,12 @@ fn resolveCallingConventionValues(...@@ -945,6 +951,12 @@ fn resolveCallingConventionValues(
945 },951 },
946 else => unreachable, // Frontend is responsible for emitting an error earlier.952 else => unreachable, // Frontend is responsible for emitting an error earlier.
947 }953 }
954
955 if (fn_info.is_var_args) {
956 result.varargs = .{ .local = .{ .value = result.local_index, .references = 1 } };
957 result.local_index += 1;
958 }
959
948 result.args = try args.toOwnedSlice();960 result.args = try args.toOwnedSlice();
949 return result;961 return result;
950}962}
...@@ -1056,9 +1068,6 @@ fn allocStack(cg: *CodeGen, ty: Type) !WValue {...@@ -1056,9 +1068,6 @@ fn allocStack(cg: *CodeGen, ty: Type) !WValue {
1056 const pt = cg.pt;1068 const pt = cg.pt;
1057 const zcu = pt.zcu;1069 const zcu = pt.zcu;
1058 assert(ty.hasRuntimeBits(zcu));1070 assert(ty.hasRuntimeBits(zcu));
1059 if (cg.initial_stack_value == .none) {
1060 try cg.initializeStack();
1061 }
10621071
1063 const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse {1072 const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse {
1064 return cg.fail("Type {f} with ABI size of {d} exceeds stack frame size", .{1073 return cg.fail("Type {f} with ABI size of {d} exceeds stack frame size", .{
...@@ -1067,28 +1076,29 @@ fn allocStack(cg: *CodeGen, ty: Type) !WValue {...@@ -1067,28 +1076,29 @@ fn allocStack(cg: *CodeGen, ty: Type) !WValue {
1067 };1076 };
1068 const abi_align = ty.abiAlignment(zcu);1077 const abi_align = ty.abiAlignment(zcu);
10691078
1070 cg.stack_alignment = cg.stack_alignment.max(abi_align);1079 return cg.allocStackBytes(abi_size, abi_align);
1071
1072 const offset: u32 = @intCast(abi_align.forward(cg.stack_size));
1073 defer cg.stack_size = offset + abi_size;
1074
1075 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
1076}1080}
10771081
1078fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue {1082fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue {
1079 if (cg.initial_stack_value == .none) {
1080 try cg.initializeStack();
1081 }
1082
1083 const abi_size = std.math.cast(u32, std.zig.target.intByteSize(cg.target, int_ty.bits)) orelse {1083 const abi_size = std.math.cast(u32, std.zig.target.intByteSize(cg.target, int_ty.bits)) orelse {
1084 return cg.fail("Integer ABI size exceeds max stack size", .{});1084 return cg.fail("Integer ABI size exceeds max stack size", .{});
1085 };1085 };
1086 const abi_align: Alignment = .fromByteUnits(std.zig.target.intAlignment(cg.target, int_ty.bits));1086 const abi_align: Alignment = .fromByteUnits(std.zig.target.intAlignment(cg.target, int_ty.bits));
10871087
1088 cg.stack_alignment = cg.stack_alignment.max(abi_align);1088 return cg.allocStackBytes(abi_size, abi_align);
1089}
1090
1091fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue {
1092 assert(size > 0);
10891093
1090 const offset: u32 = @intCast(abi_align.forward(cg.stack_size));1094 if (cg.initial_stack_value == .none) {
1091 defer cg.stack_size = offset + abi_size;1095 try cg.initializeStack();
1096 }
1097
1098 cg.stack_alignment = cg.stack_alignment.max(alignment);
1099
1100 const offset: u32 = @intCast(alignment.forward(cg.stack_size));
1101 defer cg.stack_size = offset + size;
10921102
1093 return .{ .stack_offset = .{ .value = offset, .references = 1 } };1103 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
1094}1104}
...@@ -1841,15 +1851,16 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1841,15 +1851,16 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18411851
1842 .assembly => cg.airAsm(inst),1852 .assembly => cg.airAsm(inst),
18431853
1854 .c_va_arg => try cg.airVaArg(inst),
1855 .c_va_copy => try cg.airVaCopy(inst),
1856 .c_va_end => try cg.airVaEnd(inst),
1857 .c_va_start => try cg.airVaStart(inst),
1858
1844 .err_return_trace,1859 .err_return_trace,
1845 .set_err_return_trace,1860 .set_err_return_trace,
1846 .save_err_return_trace_index,1861 .save_err_return_trace_index,
1847 .is_named_enum_value,1862 .is_named_enum_value,
1848 .addrspace_cast,1863 .addrspace_cast,
1849 .c_va_arg,
1850 .c_va_copy,
1851 .c_va_end,
1852 .c_va_start,
1853 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),1864 => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
18541865
1855 .atomic_load => cg.airAtomicLoad(inst),1866 .atomic_load => cg.airAtomicLoad(inst),
...@@ -2030,19 +2041,70 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)...@@ -2030,19 +2041,70 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
2030 return cg.fail("unable to lower callee to a function index", .{});2041 return cg.fail("unable to lower callee to a function index", .{});
2031 };2042 };
20322043
2033 const sret: WValue = if (first_param_sret) blk: {2044 const sret: WValue = if (first_param_sret)
2034 const sret_local = try cg.allocStack(ret_ty);2045 try cg.allocStack(ret_ty)
2035 try cg.lowerToStack(sret_local);2046 else
2036 break :blk sret_local;2047 .none;
2048
2049 const fixed_arg_count = fn_info.param_types.len;
2050
2051 const varargs_buf: WValue = if (fn_info.is_var_args) buf: {
2052 var varargs_size: u32 = 0;
2053 var varargs_align: Alignment = .fromByteUnits(1);
2054
2055 for (args[fixed_arg_count..]) |arg| {
2056 const arg_ty = cg.typeOf(arg);
2057 if (!arg_ty.hasRuntimeBits(zcu)) continue;
2058
2059 const arg_size = std.math.cast(u32, arg_ty.abiSize(zcu)) orelse {
2060 return cg.fail("argument type {f} too large for wasm varargs buffer", .{arg_ty.fmt(pt)});
2061 };
2062 const arg_align = arg_ty.abiAlignment(zcu);
2063
2064 varargs_align = varargs_align.max(arg_align);
2065 varargs_size = @intCast(arg_align.forward(varargs_size));
2066 varargs_size += arg_size;
2067 }
2068
2069 if (varargs_size == 0) varargs_size = 1;
2070
2071 const buffer = try cg.allocStackBytes(varargs_size, varargs_align);
2072
2073 var offset: u32 = 0;
2074 for (args[fixed_arg_count..]) |arg| {
2075 const arg_ty = cg.typeOf(arg);
2076 if (!arg_ty.hasRuntimeBits(zcu)) continue;
2077
2078 const arg_val = try cg.resolveInst(arg);
2079 const arg_size = std.math.cast(u32, arg_ty.abiSize(zcu)) orelse {
2080 return cg.fail("argument type {f} too large for wasm varargs buffer", .{arg_ty.fmt(pt)});
2081 };
2082 const arg_align = arg_ty.abiAlignment(zcu);
2083
2084 offset = @intCast(arg_align.forward(offset));
2085 try cg.store(buffer, arg_val, arg_ty, offset);
2086 offset += arg_size;
2087 }
2088
2089 break :buf buffer;
2037 } else .none;2090 } else .none;
20382091
2039 for (args) |arg| {2092 if (first_param_sret) {
2040 const arg_val = try cg.resolveInst(arg);2093 try cg.lowerToStack(sret);
2094 }
2095
2096 for (args, 0..) |arg, arg_i| {
2097 if (fn_info.is_var_args and arg_i >= fixed_arg_count) break;
20412098
2042 const arg_ty = cg.typeOf(arg);2099 const arg_ty = cg.typeOf(arg);
2043 if (!arg_ty.hasRuntimeBits(zcu)) continue;2100 if (!arg_ty.hasRuntimeBits(zcu)) continue;
20442101
2045 try cg.lowerArg(zcu.typeToFunc(fn_ty).?.cc, arg_ty, arg_val);2102 const arg_val = try cg.resolveInst(arg);
2103 try cg.lowerArg(fn_info.cc, arg_ty, arg_val);
2104 }
2105
2106 if (fn_info.is_var_args) {
2107 try cg.lowerToStack(varargs_buf);
2046 }2108 }
20472109
2048 if (callee) |nav_index| {2110 if (callee) |nav_index| {
...@@ -2097,6 +2159,91 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)...@@ -2097,6 +2159,91 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
2097 return cg.finishAirResult(inst, result_value);2159 return cg.finishAirResult(inst, result_value);
2098}2160}
20992161
2162fn airVaStart(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2163 try cg.emitWValue(cg.varargs);
2164 return cg.finishAir(inst, .stack, &.{});
2165}
2166
2167fn airVaEnd(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2168 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2169 return cg.finishAir(inst, .none, &.{un_op});
2170}
2171
2172fn airVaCopy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2173 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2174 const operand = try cg.resolveInst(ty_op.operand);
2175
2176 const result = try cg.load(operand, .usize, 0);
2177
2178 return cg.finishAir(inst, result, &.{ty_op.operand});
2179}
2180
2181fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2182 const zcu = cg.pt.zcu;
2183 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2184 const operand = try cg.resolveInst(ty_op.operand);
2185
2186 const ty = cg.typeOfIndex(inst);
2187
2188 if (!ty.hasRuntimeBits(zcu)) {
2189 return cg.finishAir(inst, .none, &.{ty_op.operand});
2190 }
2191
2192 const is_f32_va_arg = ty.toIntern() == .f32_type;
2193 const load_ty: Type = if (is_f32_va_arg) Type.f64 else ty;
2194
2195 const abi_size: u32 = @intCast(load_ty.abiSize(zcu));
2196 const abi_align: u32 = @intCast(load_ty.abiAlignment(zcu).toByteUnits().?);
2197
2198 const arg_ptr = try cg.allocLocal(.usize);
2199 _ = try cg.load(operand, .usize, 0);
2200
2201 if (abi_align > 1) {
2202 switch (cg.ptr_size) {
2203 .wasm32 => {
2204 try cg.addImm32(abi_align - 1);
2205 try cg.addTag(.i32_add);
2206 try cg.addImm32(~(abi_align - 1));
2207 try cg.addTag(.i32_and);
2208 },
2209 .wasm64 => {
2210 try cg.addImm64(abi_align - 1);
2211 try cg.addTag(.i64_add);
2212 try cg.addImm64(~@as(u64, abi_align - 1));
2213 try cg.addTag(.i64_and);
2214 },
2215 }
2216 }
2217
2218 try cg.addLocal(.local_set, arg_ptr.local.value);
2219
2220 try cg.lowerToStack(operand);
2221 try cg.lowerToStack(arg_ptr);
2222 switch (cg.ptr_size) {
2223 .wasm32 => {
2224 try cg.addImm32(abi_size);
2225 try cg.addTag(.i32_add);
2226 },
2227 .wasm64 => {
2228 try cg.addImm64(abi_size);
2229 try cg.addTag(.i64_add);
2230 },
2231 }
2232 try cg.store(.stack, .stack, .usize, 0);
2233
2234 const result = if (is_f32_va_arg) result: {
2235 const promoted = try cg.load(arg_ptr, Type.f64, 0);
2236 try cg.emitWValue(promoted);
2237 try cg.addTag(.f32_demote_f64);
2238
2239 const result_local = try cg.allocLocal(Type.f32);
2240 try cg.addLocal(.local_set, result_local.local.value);
2241 break :result result_local;
2242 } else try cg.load(arg_ptr, ty, 0);
2243
2244 return cg.finishAir(inst, result, &.{ty_op.operand});
2245}
2246
2100fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {2247fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2101 const value = try cg.allocStackPtr(inst);2248 const value = try cg.allocStackPtr(inst);
2102 return cg.finishAir(inst, value, &.{});2249 return cg.finishAir(inst, value, &.{});
src/codegen/wasm/Emit.zig+1
...@@ -186,6 +186,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -186,6 +186,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
186 fn_info.cc,186 fn_info.cc,
187 fn_info.param_types.get(&comp.zcu.?.intern_pool),187 fn_info.param_types.get(&comp.zcu.?.intern_pool),
188 .fromInterned(fn_info.return_type),188 .fromInterned(fn_info.return_type),
189 fn_info.is_var_args,
189 target,190 target,
190 ).?;191 ).?;
191 if (is_obj) {192 if (is_obj) {
src/link/Wasm.zig+13-6
...@@ -932,7 +932,7 @@ pub const ZcuFunc = union {...@@ -932,7 +932,7 @@ pub const ZcuFunc = union {
932 switch (ip.indexToKey(i.key(wasm).*)) {932 switch (ip.indexToKey(i.key(wasm).*)) {
933 .func => |func| {933 .func => |func| {
934 const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?;934 const fn_info = zcu.typeToFunc(.fromInterned(func.ty)).?;
935 return wasm.getExistingFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;935 return wasm.getExistingFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target).?;
936 },936 },
937 .enum_type => {937 .enum_type => {
938 return i.value(wasm).tag_name.type_index;938 return i.value(wasm).tag_name.type_index;
...@@ -2290,7 +2290,7 @@ pub const ZcuImportIndex = enum(u32) {...@@ -2290,7 +2290,7 @@ pub const ZcuImportIndex = enum(u32) {
2290 const nav_index = index.ptr(wasm).*;2290 const nav_index = index.ptr(wasm).*;
2291 const ext = ip.indexToKey(ip.getNav(nav_index).resolved.?.value).@"extern";2291 const ext = ip.indexToKey(ip.getNav(nav_index).resolved.?.value).@"extern";
2292 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;2292 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
2293 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target).?;2293 return getExistingFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target).?;
2294 }2294 }
22952295
2296 pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type {2296 pub fn globalType(index: ZcuImportIndex, wasm: *const Wasm) ObjectGlobal.Type {
...@@ -3209,7 +3209,7 @@ pub fn updateFunc(...@@ -3209,7 +3209,7 @@ pub fn updateFunc(
3209 for (mir.indirect_function_set.keys()) |nav| wasm.zcu_indirect_function_set.putAssumeCapacity(nav, {});3209 for (mir.indirect_function_set.keys()) |nav| wasm.zcu_indirect_function_set.putAssumeCapacity(nav, {});
3210 for (mir.func_tys.keys()) |func_ty| {3210 for (mir.func_tys.keys()) |func_ty| {
3211 const fn_info = zcu.typeToFunc(.fromInterned(func_ty)).?;3211 const fn_info = zcu.typeToFunc(.fromInterned(func_ty)).?;
3212 _ = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);3212 _ = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target);
3213 }3213 }
3214 wasm.error_name_table_ref_count += mir.error_name_table_ref_count;3214 wasm.error_name_table_ref_count += mir.error_name_table_ref_count;
3215 // We need to populate UAV data. In theory, we can lower the UAV values while we fill `mir.uavs`.3215 // We need to populate UAV data. In theory, we can lower the UAV values while we fill `mir.uavs`.
...@@ -3281,7 +3281,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -3281,7 +3281,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
3281 wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));3281 wasm.function_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));
3282 // Ensure there is a corresponding function type table entry.3282 // Ensure there is a corresponding function type table entry.
3283 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;3283 const fn_info = zcu.typeToFunc(.fromInterned(ext.ty)).?;
3284 _ = try internFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target);3284 _ = try internFunctionType(wasm, fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), fn_info.is_var_args, target);
3285 } else {3285 } else {
3286 wasm.data_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));3286 wasm.data_imports.putAssumeCapacity(name, .fromZcuImport(zcu_import, wasm));
3287 }3287 }
...@@ -3928,9 +3928,10 @@ pub fn internFunctionType(...@@ -3928,9 +3928,10 @@ pub fn internFunctionType(
3928 cc: std.lang.CallingConvention,3928 cc: std.lang.CallingConvention,
3929 params: []const InternPool.Index,3929 params: []const InternPool.Index,
3930 return_type: Zcu.Type,3930 return_type: Zcu.Type,
3931 is_var_args: bool,
3931 target: *const std.Target,3932 target: *const std.Target,
3932) Allocator.Error!FunctionType.Index {3933) Allocator.Error!FunctionType.Index {
3933 try convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch);3934 try convertZcuFnType(wasm.base.comp, cc, params, return_type, is_var_args, target, &wasm.params_scratch, &wasm.returns_scratch);
3934 return wasm.addFuncType(.{3935 return wasm.addFuncType(.{
3935 .params = try wasm.internValtypeList(wasm.params_scratch.items),3936 .params = try wasm.internValtypeList(wasm.params_scratch.items),
3936 .returns = try wasm.internValtypeList(wasm.returns_scratch.items),3937 .returns = try wasm.internValtypeList(wasm.returns_scratch.items),
...@@ -3942,9 +3943,10 @@ pub fn getExistingFunctionType(...@@ -3942,9 +3943,10 @@ pub fn getExistingFunctionType(
3942 cc: std.lang.CallingConvention,3943 cc: std.lang.CallingConvention,
3943 params: []const InternPool.Index,3944 params: []const InternPool.Index,
3944 return_type: Zcu.Type,3945 return_type: Zcu.Type,
3946 is_var_args: bool,
3945 target: *const std.Target,3947 target: *const std.Target,
3946) ?FunctionType.Index {3948) ?FunctionType.Index {
3947 convertZcuFnType(wasm.base.comp, cc, params, return_type, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) {3949 convertZcuFnType(wasm.base.comp, cc, params, return_type, is_var_args, target, &wasm.params_scratch, &wasm.returns_scratch) catch |err| switch (err) {
3948 error.OutOfMemory => return null,3950 error.OutOfMemory => return null,
3949 };3951 };
3950 return wasm.getExistingFuncType(.{3952 return wasm.getExistingFuncType(.{
...@@ -4176,6 +4178,7 @@ fn convertZcuFnType(...@@ -4176,6 +4178,7 @@ fn convertZcuFnType(
4176 cc: std.lang.CallingConvention,4178 cc: std.lang.CallingConvention,
4177 params: []const InternPool.Index,4179 params: []const InternPool.Index,
4178 return_type: Zcu.Type,4180 return_type: Zcu.Type,
4181 is_var_args: bool,
4179 target: *const std.Target,4182 target: *const std.Target,
4180 params_buffer: *std.ArrayList(std.wasm.Valtype),4183 params_buffer: *std.ArrayList(std.wasm.Valtype),
4181 returns_buffer: *std.ArrayList(std.wasm.Valtype),4184 returns_buffer: *std.ArrayList(std.wasm.Valtype),
...@@ -4226,6 +4229,10 @@ fn convertZcuFnType(...@@ -4226,6 +4229,10 @@ fn convertZcuFnType(
4226 else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),4229 else => try params_buffer.append(gpa, CodeGen.typeToValtype(param_type, zcu, target)),
4227 }4230 }
4228 }4231 }
4232
4233 if (is_var_args) {
4234 try params_buffer.append(gpa, .i32);
4235 }
4229}4236}
42304237
4231pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool {4238pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool {
src/link/Wasm/Flush.zig+1-1
...@@ -151,7 +151,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -151,7 +151,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
151 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);151 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
152 gop.value_ptr.* = .{ .tag_name = .{152 gop.value_ptr.* = .{ .tag_name = .{
153 .symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{data.ip_index}),153 .symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{data.ip_index}),
154 .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target),154 .type_index = try wasm.internFunctionType(.auto, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, false, target),
155 .table_index = @intCast(wasm.tag_name_offs.items.len),155 .table_index = @intCast(wasm.tag_name_offs.items.len),
156 } };156 } };
157 const tag_names = ip.loadEnumType(data.ip_index).field_names;157 const tag_names = ip.loadEnumType(data.ip_index).field_names;
test/behavior/var_args.zig-6
...@@ -94,7 +94,6 @@ fn doNothingWithFirstArg(args: anytype) void {...@@ -94,7 +94,6 @@ fn doNothingWithFirstArg(args: anytype) void {
94test "simple variadic function" {94test "simple variadic function" {
95 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO95 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
96 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO96 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
97 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;97 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;98 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
100 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {99 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
...@@ -156,7 +155,6 @@ test "simple variadic function" {...@@ -156,7 +155,6 @@ test "simple variadic function" {
156test "coerce reference to var arg" {155test "coerce reference to var arg" {
157 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO156 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
158 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO157 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
159 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
160 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;158 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
161 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;159 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
162 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {160 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
...@@ -188,7 +186,6 @@ test "coerce reference to var arg" {...@@ -188,7 +186,6 @@ test "coerce reference to var arg" {
188test "variadic functions" {186test "variadic functions" {
189 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO187 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
190 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO188 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
191 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
192 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;189 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
193 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;190 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
194 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {191 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
...@@ -241,7 +238,6 @@ test "variadic functions" {...@@ -241,7 +238,6 @@ test "variadic functions" {
241238
242test "copy VaList" {239test "copy VaList" {
243 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO240 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
244 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
245 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;241 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
246 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;242 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
247 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {243 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
...@@ -276,7 +272,6 @@ test "copy VaList" {...@@ -276,7 +272,6 @@ test "copy VaList" {
276272
277test "unused VaList arg" {273test "unused VaList arg" {
278 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO274 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
279 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
280 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;275 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
281 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;276 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
282 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {277 if (builtin.zig_backend == .stage2_llvm and !builtin.os.tag.isDarwin() and builtin.cpu.arch.isAARCH64()) {
...@@ -308,7 +303,6 @@ test "unused VaList arg" {...@@ -308,7 +303,6 @@ test "unused VaList arg" {
308303
309test "floating point VaList args" {304test "floating point VaList args" {
310 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO305 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
311 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
312 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;306 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
313 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;307 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
314 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/16961308 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/16961