| ... | ... | @@ -196,6 +196,9 @@ args: []WValue, |
| 196 | 196 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| 197 | 197 | /// before this function returns its execution to the caller. |
| 198 | 198 | return_value: WValue, |
| 199 | /// Only populated for variadic functions. |
| 200 | /// Holds the hidden final parameter pointing to the varargs buffer. |
| 201 | varargs: WValue, |
| 199 | 202 | /// The size of the stack this function occupies. In the function prologue |
| 200 | 203 | /// we will move the stack pointer by this number, forward aligned with the `stack_alignment`. |
| 201 | 204 | stack_size: u32 = 0, |
| ... | ... | @@ -308,7 +311,7 @@ const WValue = union(enum) { |
| 308 | 311 | fn free(value: *WValue, gen: *CodeGen) void { |
| 309 | 312 | if (value.* != .local) return; |
| 310 | 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 | 315 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. |
| 313 | 316 | |
| 314 | 317 | const index = local_value - reserved; |
| ... | ... | @@ -800,6 +803,7 @@ pub fn generate( |
| 800 | 803 | .func_index = func_index, |
| 801 | 804 | .args = cc_result.args, |
| 802 | 805 | .return_value = cc_result.return_value, |
| 806 | .varargs = cc_result.varargs, |
| 803 | 807 | .local_index = cc_result.local_index, |
| 804 | 808 | .mir_instructions = .empty, |
| 805 | 809 | .mir_extra = .empty, |
| ... | ... | @@ -874,6 +878,7 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { |
| 874 | 878 | const CallWValues = struct { |
| 875 | 879 | args: []WValue, |
| 876 | 880 | return_value: WValue, |
| 881 | varargs: WValue, |
| 877 | 882 | local_index: u32, |
| 878 | 883 | |
| 879 | 884 | fn deinit(values: *CallWValues, gpa: Allocator) void { |
| ... | ... | @@ -895,6 +900,7 @@ fn resolveCallingConventionValues( |
| 895 | 900 | var result: CallWValues = .{ |
| 896 | 901 | .args = &.{}, |
| 897 | 902 | .return_value = .none, |
| 903 | .varargs = .none, |
| 898 | 904 | .local_index = 0, |
| 899 | 905 | }; |
| 900 | 906 | if (cc == .naked) return result; |
| ... | ... | @@ -945,6 +951,12 @@ fn resolveCallingConventionValues( |
| 945 | 951 | }, |
| 946 | 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 | 960 | result.args = try args.toOwnedSlice(); |
| 949 | 961 | return result; |
| 950 | 962 | } |
| ... | ... | @@ -1056,9 +1068,6 @@ fn allocStack(cg: *CodeGen, ty: Type) !WValue { |
| 1056 | 1068 | const pt = cg.pt; |
| 1057 | 1069 | const zcu = pt.zcu; |
| 1058 | 1070 | assert(ty.hasRuntimeBits(zcu)); |
| 1059 | | if (cg.initial_stack_value == .none) { |
| 1060 | | try cg.initializeStack(); |
| 1061 | | } |
| 1062 | 1071 | |
| 1063 | 1072 | const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse { |
| 1064 | 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 | 1076 | }; |
| 1068 | 1077 | const abi_align = ty.abiAlignment(zcu); |
| 1069 | 1078 | |
| 1070 | | cg.stack_alignment = cg.stack_alignment.max(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 } }; |
| 1079 | return cg.allocStackBytes(abi_size, abi_align); |
| 1076 | 1080 | } |
| 1077 | 1081 | |
| 1078 | 1082 | fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue { |
| 1079 | | if (cg.initial_stack_value == .none) { |
| 1080 | | try cg.initializeStack(); |
| 1081 | | } |
| 1082 | | |
| 1083 | 1083 | const abi_size = std.math.cast(u32, std.zig.target.intByteSize(cg.target, int_ty.bits)) orelse { |
| 1084 | 1084 | return cg.fail("Integer ABI size exceeds max stack size", .{}); |
| 1085 | 1085 | }; |
| 1086 | 1086 | const abi_align: Alignment = .fromByteUnits(std.zig.target.intAlignment(cg.target, int_ty.bits)); |
| 1087 | 1087 | |
| 1088 | | cg.stack_alignment = cg.stack_alignment.max(abi_align); |
| 1088 | return cg.allocStackBytes(abi_size, abi_align); |
| 1089 | } |
| 1090 | |
| 1091 | fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue { |
| 1092 | assert(size > 0); |
| 1089 | 1093 | |
| 1090 | | const offset: u32 = @intCast(abi_align.forward(cg.stack_size)); |
| 1091 | | defer cg.stack_size = offset + abi_size; |
| 1094 | if (cg.initial_stack_value == .none) { |
| 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; |
| 1092 | 1102 | |
| 1093 | 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 | 1851 | |
| 1842 | 1852 | .assembly => cg.airAsm(inst), |
| 1843 | 1853 | |
| 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 | 1859 | .err_return_trace, |
| 1845 | 1860 | .set_err_return_trace, |
| 1846 | 1861 | .save_err_return_trace_index, |
| 1847 | 1862 | .is_named_enum_value, |
| 1848 | 1863 | .addrspace_cast, |
| 1849 | | .c_va_arg, |
| 1850 | | .c_va_copy, |
| 1851 | | .c_va_end, |
| 1852 | | .c_va_start, |
| 1853 | 1864 | => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 1854 | 1865 | |
| 1855 | 1866 | .atomic_load => cg.airAtomicLoad(inst), |
| ... | ... | @@ -2030,19 +2041,70 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2030 | 2041 | return cg.fail("unable to lower callee to a function index", .{}); |
| 2031 | 2042 | }; |
| 2032 | 2043 | |
| 2033 | | const sret: WValue = if (first_param_sret) blk: { |
| 2034 | | const sret_local = try cg.allocStack(ret_ty); |
| 2035 | | try cg.lowerToStack(sret_local); |
| 2036 | | break :blk sret_local; |
| 2044 | const sret: WValue = if (first_param_sret) |
| 2045 | try cg.allocStack(ret_ty) |
| 2046 | else |
| 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 | 2090 | } else .none; |
| 2038 | 2091 | |
| 2039 | | for (args) |arg| { |
| 2040 | | const arg_val = try cg.resolveInst(arg); |
| 2092 | if (first_param_sret) { |
| 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; |
| 2041 | 2098 | |
| 2042 | 2099 | const arg_ty = cg.typeOf(arg); |
| 2043 | 2100 | if (!arg_ty.hasRuntimeBits(zcu)) continue; |
| 2044 | 2101 | |
| 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 | } |
| 2047 | 2109 | |
| 2048 | 2110 | if (callee) |nav_index| { |
| ... | ... | @@ -2097,6 +2159,91 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2097 | 2159 | return cg.finishAirResult(inst, result_value); |
| 2098 | 2160 | } |
| 2099 | 2161 | |
| 2162 | fn airVaStart(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2163 | try cg.emitWValue(cg.varargs); |
| 2164 | return cg.finishAir(inst, .stack, &.{}); |
| 2165 | } |
| 2166 | |
| 2167 | fn 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 | |
| 2172 | fn 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 | |
| 2181 | fn 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 | |
| 2100 | 2247 | fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2101 | 2248 | const value = try cg.allocStackPtr(inst); |
| 2102 | 2249 | return cg.finishAir(inst, value, &.{}); |