authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-08-22 23:24:37+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-09-17 00:04:51+02:00
log503c59263e358ae8ca16b271fc35799a508d03bb
tree18cb0be85a5bc0155cb6fffbf884bd743581a20a
parent235e20d85f599a502183ebaee6e9e44202f44a0c

wasm: better local tracking


1 files changed, 267 insertions(+), 259 deletions(-)

src/codegen/wasm/CodeGen.zig+267-259
......@@ -136,13 +136,10 @@ air: Air,
136136liveness: Air.Liveness,
137137gpa: mem.Allocator,
138138func_index: InternPool.Index,
139/// Contains a list of current branches.
140/// When we return from a branch, the branch will be popped from this list,
141/// which means branches can only contain references from within its own branch,
142/// or a branch higher (lower index) in the tree.
143branches: std.ArrayList(Branch) = .empty,
139/// Contains a list of current branches. Stores LocalPool before entering branch.
140branches: std.ArrayList(LocalPool) = .empty,
144141/// Table to save `WValue`'s generated by an `Air.Inst`
145// values: ValueTable,
142values: ValueTable = .empty,
146143/// Mapping from Air.Inst.Index to block ids
147144blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {
148145 label: u32,
......@@ -150,10 +147,6 @@ blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {
150147}) = .{},
151148/// Maps `loop` instructions to their label. `br` to here repeats the loop.
152149loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,
153/// The index the next local generated will have
154/// NOTE: arguments share the index with locals therefore the first variable
155/// will have the index that comes after the last argument's index
156local_index: u32,
157150/// The index of the current argument.
158151/// Used to track which argument is being referenced in `airArg`.
159152arg_index: u32 = 0,
......@@ -207,23 +200,8 @@ stack_size: u32 = 0,
207200/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.
208201stack_alignment: Alignment = .@"16",
209202
210// For each individual Wasm valtype we store a seperate free list which
211// allows us to re-use locals that are no longer used. e.g. a temporary local.
212/// A list of indexes which represents a local of valtype `i32`.
213/// It is illegal to store a non-i32 valtype in this list.
214free_locals_i32: std.ArrayList(u32) = .empty,
215/// A list of indexes which represents a local of valtype `i64`.
216/// It is illegal to store a non-i64 valtype in this list.
217free_locals_i64: std.ArrayList(u32) = .empty,
218/// A list of indexes which represents a local of valtype `f32`.
219/// It is illegal to store a non-f32 valtype in this list.
220free_locals_f32: std.ArrayList(u32) = .empty,
221/// A list of indexes which represents a local of valtype `f64`.
222/// It is illegal to store a non-f64 valtype in this list.
223free_locals_f64: std.ArrayList(u32) = .empty,
224/// A list of indexes which represents a local of valtype `v127`.
225/// It is illegal to store a non-v128 valtype in this list.
226free_locals_v128: std.ArrayList(u32) = .empty,
203local_pool: LocalPool = .{},
204wasm_param_count: u32 = undefined,
227205
228206/// When in debug mode, this tracks if no `finishAir` was missed.
229207/// Forgetting to call `finishAir` will cause the result to not be
......@@ -239,13 +217,7 @@ const WValue = union(enum) {
239217 none: void,
240218 /// The value lives on top of the stack
241219 stack: void,
242 /// Index of the local
243 local: struct {
244 /// Contains the index to the local
245 value: u32,
246 /// The amount of instructions referencing this `WValue`
247 references: u32,
248 },
220 local: u32, // index
249221 /// An immediate 32bit value
250222 imm32: u32,
251223 /// An immediate 64bit value
......@@ -290,7 +262,7 @@ const WValue = union(enum) {
290262 switch (value) {
291263 .stack => {
292264 const new_local = try gen.allocLocal(ty);
293 try gen.addLocal(.local_set, new_local.local.value);
265 try gen.addLocal(.local_set, new_local.local);
294266 return new_local;
295267 },
296268 else => return value,
......@@ -299,23 +271,9 @@ const WValue = union(enum) {
299271
300272 /// Marks a local as no longer being referenced and essentially allows
301273 /// us to re-use it somewhere else within the function.
302 /// The valtype of the local is deducted by using the index of the given `WValue`.
303 fn free(value: *WValue, gen: *CodeGen) void {
274 fn free(value: *WValue, cg: *CodeGen) void {
304275 if (value.* != .local) return;
305 const local_value = value.local.value;
306 const reserved = gen.args.len + @intFromBool(gen.return_value != .none) + @intFromBool(gen.varargs != .none);
307 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
308
309 const index = local_value - reserved;
310 const valtype = gen.mir_locals.items[index];
311 switch (valtype) {
312 .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead
313 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
314 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
315 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
316 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,
317 }
318 log.debug("freed local ({d}) of type {}", .{ local_value, valtype });
276 cg.releaseLocal(value.local);
319277 value.* = .dead;
320278 }
321279};
......@@ -323,6 +281,77 @@ const WValue = union(enum) {
323281/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
324282const ValueTable = std.array_hash_map.Auto(Air.Inst.Ref, WValue);
325283
284const LocalPool = struct {
285 const OptionalIndex = enum(u32) {
286 none = math.maxInt(u32),
287 _,
288
289 fn init(index: u32) OptionalIndex {
290 const result: OptionalIndex = @fromBackingInt(index);
291 assert(result != .none);
292 return result;
293 }
294
295 fn unwrap(index: OptionalIndex) ?u32 {
296 return if (index == .none) null else @backingInt(index);
297 }
298 };
299
300 const Local = union {
301 ref_count: u32,
302 next_free: OptionalIndex,
303 };
304
305 locals: std.ArrayList(Local) = .empty,
306 free_i32: OptionalIndex = .none,
307 free_i64: OptionalIndex = .none,
308 free_f32: OptionalIndex = .none,
309 free_f64: OptionalIndex = .none,
310 free_v128: OptionalIndex = .none,
311
312 fn deinit(pool: *LocalPool, gpa: Allocator) void {
313 pool.locals.deinit(gpa);
314 pool.* = undefined;
315 }
316
317 fn clone(pool: *LocalPool, gpa: Allocator) Allocator.Error!LocalPool {
318 return .{
319 .locals = try pool.locals.clone(gpa),
320 .free_i32 = pool.free_i32,
321 .free_i64 = pool.free_i64,
322 .free_f32 = pool.free_f32,
323 .free_f64 = pool.free_f64,
324 .free_v128 = pool.free_v128,
325 };
326 }
327
328 fn freeList(pool: *LocalPool, valtype: std.wasm.Valtype) *OptionalIndex {
329 return switch (valtype) {
330 .i32 => &pool.free_i32,
331 .i64 => &pool.free_i64,
332 .f32 => &pool.free_f32,
333 .f64 => &pool.free_f64,
334 .v128 => &pool.free_v128,
335 };
336 }
337
338 fn restore(snapshot: *LocalPool, cg: *CodeGen) void {
339 const local_pool = &cg.local_pool;
340 const old_local_count = snapshot.locals.items.len;
341 assert(local_pool.locals.items.len >= old_local_count);
342 @memcpy(local_pool.locals.items[0..old_local_count], snapshot.locals.items);
343 local_pool.free_i32 = snapshot.free_i32;
344 local_pool.free_i64 = snapshot.free_i64;
345 local_pool.free_f32 = snapshot.free_f32;
346 local_pool.free_f64 = snapshot.free_f64;
347 local_pool.free_v128 = snapshot.free_v128;
348
349 for (old_local_count..local_pool.locals.items.len) |index| {
350 cg.appendFreeLocal(cg.wasm_param_count + @as(u32, @intCast(index)));
351 }
352 }
353};
354
326355const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
327356
328357const InnerError = Error || error{
......@@ -336,14 +365,11 @@ pub fn deinit(cg: *CodeGen) void {
336365 const gpa = cg.gpa;
337366 for (cg.branches.items) |*branch| branch.deinit(gpa);
338367 cg.branches.deinit(gpa);
368 cg.values.deinit(gpa);
339369 cg.blocks.deinit(gpa);
340370 cg.loops.deinit(gpa);
341371 cg.simd_immediates.deinit(gpa);
342 cg.free_locals_i32.deinit(gpa);
343 cg.free_locals_i64.deinit(gpa);
344 cg.free_locals_f32.deinit(gpa);
345 cg.free_locals_f64.deinit(gpa);
346 cg.free_locals_v128.deinit(gpa);
372 cg.local_pool.deinit(gpa);
347373 cg.mir_instructions.deinit(gpa);
348374 cg.mir_extra.deinit(gpa);
349375 cg.mir_locals.deinit(gpa);
......@@ -362,19 +388,11 @@ pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error {
362388/// Resolves the `WValue` for the given instruction `inst`
363389/// When the given instruction has a `Value`, it returns a constant instead
364390fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
365 var branch_index = cg.branches.items.len;
366 while (branch_index > 0) : (branch_index -= 1) {
367 const branch = cg.branches.items[branch_index - 1];
368 if (branch.values.get(ref)) |value| {
369 return value;
370 }
371 }
391 if (cg.values.get(ref)) |value| return value;
372392
373393 // when we did not find an existing instruction, it
374394 // means we must generate it from a constant.
375 // We always store constants in the most outer branch as they must never
376 // be removed. The most outer branch is always at index 0.
377 const gop = try cg.branches.items[0].values.getOrPut(cg.gpa, ref);
395 const gop = try cg.values.getOrPut(cg.gpa, ref);
378396 assert(!gop.found_existing);
379397
380398 const pt = cg.pt;
......@@ -413,20 +431,33 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue {
413431
414432/// NOTE: if result == .stack, it will be stored in .local
415433fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void {
434 return cg.finishAirWithBranchDeaths(inst, result, operands, &.{});
435}
436
437/// Takes `branch_deaths` from continuing branch and calls `processDeath` after result retaining logic.
438fn finishAirWithBranchDeaths(
439 cg: *CodeGen,
440 inst: Air.Inst.Index,
441 result: WValue,
442 operands: []const Air.Inst.Ref,
443 branch_deaths: []const Air.Inst.Index,
444) InnerError!void {
416445 assert(operands.len <= Air.Liveness.bpi - 1);
417446
418 var final_result = result;
419 const aliased_operand: ?*WValue = if (result == .local) aliased: {
447 if (result == .local) aliased: {
420448 for (operands) |operand| {
421449 if (operand.toIndex() == null) continue;
422 const resolved_operand = cg.getResolvedInst(operand);
423 if (resolved_operand.* != .local or resolved_operand.local.value != result.local.value) continue;
450 const resolved_operand = cg.values.getPtr(operand) orelse unreachable;
451 if (resolved_operand.* != .local or resolved_operand.local != result.local) continue;
424452
425 resolved_operand.local.references += 1;
426 break :aliased resolved_operand;
453 if (result.local >= cg.wasm_param_count) {
454 const local = &cg.local_pool.locals.items[result.local - cg.wasm_param_count];
455 assert(local.ref_count > 0);
456 local.ref_count += 1;
457 }
458 break :aliased;
427459 }
428 break :aliased null;
429 } else null;
460 }
430461
431462 var tomb_bits = cg.liveness.getTombBits(inst);
432463 for (operands) |operand| {
......@@ -435,8 +466,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con
435466 if (!dies) continue;
436467 processDeath(cg, operand);
437468 }
438 if (aliased_operand) |operand| final_result.local.references = operand.local.references;
439 try cg.finishAirResult(inst, final_result);
469 for (branch_deaths) |death| cg.processDeath(death.toRef());
470 try cg.finishAirResult(inst, result);
440471}
441472
442473fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void {
......@@ -446,8 +477,7 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro
446477 result
447478 else
448479 try result.toLocal(cg, cg.typeOfIndex(inst));
449 const branch = cg.currentBranch();
450 branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result);
480 try cg.values.putNoClobber(cg.gpa, inst.toRef(), trackable_result);
451481 }
452482
453483 if (std.debug.runtime_safety) {
......@@ -455,17 +485,16 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro
455485 }
456486}
457487
458const Branch = struct {
459 values: ValueTable = .{},
460
461 fn deinit(branch: *Branch, gpa: Allocator) void {
462 branch.values.deinit(gpa);
463 branch.* = undefined;
464 }
465};
488fn pushBranch(cg: *CodeGen) InnerError!void {
489 var snapshot = try cg.local_pool.clone(cg.gpa);
490 errdefer snapshot.deinit(cg.gpa);
491 try cg.branches.append(cg.gpa, snapshot);
492}
466493
467inline fn currentBranch(cg: *CodeGen) *Branch {
468 return &cg.branches.items[cg.branches.items.len - 1];
494fn popBranch(cg: *CodeGen) void {
495 var snapshot = cg.branches.pop().?;
496 defer snapshot.deinit(cg.gpa);
497 snapshot.restore(cg);
469498}
470499
471500fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void {
......@@ -476,20 +505,9 @@ fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void {
476505
477506fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
478507 if (ref.toIndex() == null) return;
479 // Branches are currently only allowed to free locals allocated
480 // within their own branch.
481 // TODO: Upon branch consolidation free any locals if needed.
482 const value = cg.currentBranch().values.getPtr(ref) orelse return;
508 const value = cg.values.getPtr(ref) orelse unreachable;
483509 if (value.* != .local) return;
484 const reserved_indexes = cg.args.len + @intFromBool(cg.return_value != .none);
485 if (value.local.value < reserved_indexes) {
486 return; // function arguments can never be re-used
487 }
488 log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @backingInt(ref.toIndex().?), value.local.value });
489 value.local.references -= 1;
490 if (value.local.references == 0) {
491 value.free(cg);
492 }
510 cg.releaseLocal(value.local);
493511}
494512
495513pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
......@@ -708,7 +726,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
708726 switch (value) {
709727 .dead => unreachable, // reference to free'd `WValue`
710728 .none, .stack => {}, // no-op
711 .local => |idx| try cg.addLocal(.local_get, idx.value),
729 .local => |idx| try cg.addLocal(.local_get, idx),
712730 .imm32 => |val| try cg.addImm32(val),
713731 .imm64 => |val| try cg.addImm64(val),
714732 .imm128 => |val| try cg.addImm128(val),
......@@ -780,22 +798,28 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
780798/// Emits the base address for a memory instruction.
781799fn emitMemBase(cg: *CodeGen, ptr: WValue) InnerError!void {
782800 switch (ptr) {
783 .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local.value),
801 .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local),
784802 else => try cg.emitWValue(ptr),
785803 }
786804}
787805
788/// From a reference, returns its resolved `WValue`.
789/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.
790fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
791 var index = cg.branches.items.len;
792 while (index > 0) : (index -= 1) {
793 const branch = cg.branches.items[index - 1];
794 if (branch.values.getPtr(ref)) |value| {
795 return value;
796 }
797 }
798 unreachable; // developer-error: This can only be called on resolved instructions. Use `resolveInst` instead.
806fn appendFreeLocal(cg: *CodeGen, index: u32) void {
807 assert(index >= cg.wasm_param_count);
808 const mir_index = index - cg.wasm_param_count;
809 const valtype = cg.mir_locals.items[mir_index];
810 const free_list = cg.local_pool.freeList(valtype);
811 cg.local_pool.locals.items[mir_index] = .{ .next_free = free_list.* };
812 free_list.* = .init(index);
813 log.debug("freed local ({d}) of type {}", .{ index, valtype });
814}
815
816fn releaseLocal(cg: *CodeGen, index: u32) void {
817 if (index < cg.wasm_param_count) return;
818 const local = &cg.local_pool.locals.items[index - cg.wasm_param_count];
819 assert(local.ref_count > 0);
820 local.ref_count -= 1;
821 if (local.ref_count > 0) return;
822 cg.appendFreeLocal(index);
799823}
800824
801825/// Creates one locals for a given `Type`.
......@@ -803,16 +827,13 @@ fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
803827fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
804828 const zcu = cg.pt.zcu;
805829 const valtype = typeToValtype(ty, zcu, cg.target);
806 const index_or_null = switch (valtype) {
807 .i32 => cg.free_locals_i32.pop(),
808 .i64 => cg.free_locals_i64.pop(),
809 .f32 => cg.free_locals_f32.pop(),
810 .f64 => cg.free_locals_f64.pop(),
811 .v128 => cg.free_locals_v128.pop(),
812 };
813 if (index_or_null) |index| {
830 const free_list = cg.local_pool.freeList(valtype);
831 if (free_list.unwrap()) |index| {
832 const local = &cg.local_pool.locals.items[index - cg.wasm_param_count];
833 free_list.* = local.next_free;
834 local.* = .{ .ref_count = 1 };
814835 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
815 return .{ .local = .{ .value = index, .references = 1 } };
836 return .{ .local = index };
816837 }
817838 log.debug("new local of type {}", .{valtype});
818839 return cg.ensureAllocLocal(ty);
......@@ -822,10 +843,13 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
822843/// to use a zero-initialized local.
823844fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
824845 const zcu = cg.pt.zcu;
825 try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target));
826 const initial_index = cg.local_index;
827 cg.local_index += 1;
828 return .{ .local = .{ .value = initial_index, .references = 1 } };
846 try cg.mir_locals.ensureUnusedCapacity(cg.gpa, 1);
847 try cg.local_pool.locals.ensureUnusedCapacity(cg.gpa, 1);
848 assert(cg.mir_locals.items.len == cg.local_pool.locals.items.len);
849 const initial_index = cg.wasm_param_count + @as(u32, @intCast(cg.local_pool.locals.items.len));
850 cg.mir_locals.appendAssumeCapacity(typeToValtype(ty, zcu, cg.target));
851 cg.local_pool.locals.appendAssumeCapacity(.{ .ref_count = 1 });
852 return .{ .local = initial_index };
829853}
830854
831855pub const Error = codegen.Error;
......@@ -867,7 +891,6 @@ pub fn generate(
867891 .args = cc_result.args,
868892 .return_value = cc_result.return_value,
869893 .varargs = cc_result.varargs,
870 .local_index = cc_result.local_index,
871894 .mir_instructions = .empty,
872895 .mir_extra = .empty,
873896 .mir_locals = .empty,
......@@ -890,17 +913,10 @@ pub fn generate(
890913
891914fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
892915 const zcu = cg.pt.zcu;
893 // branch used for const values
894 try cg.branches.append(cg.gpa, .{});
895 // func scope branch
896 try cg.branches.append(cg.gpa, .{});
897 defer {
898 var func_branch = cg.branches.pop().?;
899 func_branch.deinit(cg.gpa);
900 var const_branch = cg.branches.pop().?;
901 const_branch.deinit(cg.gpa);
902 assert(cg.branches.items.len == 0); // missing branch merge
903 }
916 cg.wasm_param_count = @intCast(cg.args.len + @intFromBool(cg.return_value != .none) + @intFromBool(cg.varargs != .none));
917 cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize);
918 cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize);
919 defer assert(cg.branches.items.len == 0);
904920 // Generate MIR for function body
905921 try cg.genBody(cg.air.getMainBody());
906922
......@@ -925,11 +941,11 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
925941 .instructions = cg.mir_instructions.toOwnedSlice(),
926942 .extra = cg.mir_extra.toOwnedSliceAssert(),
927943 .locals = cg.mir_locals.toOwnedSliceAssert(),
928 .prologue = if (cg.initial_stack_value == .none) .none else .{
929 .sp_local = cg.initial_stack_value.local.value,
944 .prologue = .{
945 .sp_local = cg.initial_stack_value.local,
930946 .flags = .{ .stack_alignment = cg.stack_alignment },
931947 .stack_size = cg.stack_size,
932 .bottom_stack_local = cg.bottom_stack_value.local.value,
948 .bottom_stack_local = cg.bottom_stack_value.local,
933949 },
934950 .uavs = cg.mir_uavs.move(),
935951 .indirect_function_set = cg.mir_indirect_function_set.move(),
......@@ -976,7 +992,7 @@ fn resolveCallingConventionValues(
976992 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) {
977993 // the sret arg will be passed as first argument, therefore we
978994 // set the `return_value` before allocating locals for regular args.
979 result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } };
995 result.return_value = .{ .local = result.local_index };
980996 result.local_index += 1;
981997 }
982998
......@@ -987,7 +1003,7 @@ fn resolveCallingConventionValues(
9871003 continue;
9881004 }
9891005
990 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1006 try args.append(.{ .local = result.local_index });
9911007 result.local_index += 1;
9921008 }
9931009 },
......@@ -1000,17 +1016,17 @@ fn resolveCallingConventionValues(
10001016
10011017 switch (abi.classifyType(param_ty, zcu, target)) {
10021018 .direct, .indirect => {
1003 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1019 try args.append(.{ .local = result.local_index });
10041020 result.local_index += 1;
10051021 },
10061022 .double_i64 => {
1007 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1008 try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } });
1023 try args.append(.{ .local = result.local_index });
1024 try args.append(.{ .local = result.local_index + 1 });
10091025 result.local_index += 2;
10101026 },
10111027 .unrolled => |vector| {
10121028 for (0..vector.len) |_| {
1013 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1029 try args.append(.{ .local = result.local_index });
10141030 result.local_index += 1;
10151031 }
10161032 },
......@@ -1021,7 +1037,7 @@ fn resolveCallingConventionValues(
10211037 }
10221038
10231039 if (fn_info.is_var_args) {
1024 result.varargs = .{ .local = .{ .value = result.local_index, .references = 1 } };
1040 result.varargs = .{ .local = result.local_index };
10251041 result.local_index += 1;
10261042 }
10271043
......@@ -1088,23 +1104,9 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu
10881104 }
10891105}
10901106
1091/// Creates a local for the initial stack value
1092/// Asserts `initial_stack_value` is `.none`
1093fn initializeStack(cg: *CodeGen) !void {
1094 assert(cg.initial_stack_value == .none);
1095 // Reserve a local to store the current stack pointer
1096 // We can later use this local to set the stack pointer back to the value
1097 // we have stored here.
1098 cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize);
1099 // Also reserve a local to store the bottom stack value
1100 cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize);
1101}
1102
11031107/// Reads the stack pointer from `Context.initial_stack_value` and writes it
11041108/// to the global stack pointer variable
11051109fn restoreStackPointer(cg: *CodeGen) !void {
1106 // only restore the pointer if it was initialized
1107 if (cg.initial_stack_value == .none) return;
11081110 // Get the original stack pointer's value
11091111 try cg.emitWValue(cg.initial_stack_value);
11101112
......@@ -1144,10 +1146,6 @@ fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue {
11441146fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue {
11451147 assert(size > 0);
11461148
1147 if (cg.initial_stack_value == .none) {
1148 try cg.initializeStack();
1149 }
1150
11511149 cg.stack_alignment = cg.stack_alignment.max(alignment);
11521150
11531151 const offset: u32 = @intCast(alignment.forward(cg.stack_size));
......@@ -1166,10 +1164,6 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue {
11661164 const ptr_ty = cg.typeOfIndex(inst);
11671165 const pointee_ty = ptr_ty.childType(zcu);
11681166
1169 if (cg.initial_stack_value == .none) {
1170 try cg.initializeStack();
1171 }
1172
11731167 const abi_alignment = ptr_ty.ptrAlignment(zcu);
11741168 const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse {
11751169 return cg.fail("Type {f} with ABI size of {d} exceeds stack frame size", .{
......@@ -1934,7 +1928,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
19341928 continue;
19351929 }
19361930 const old_bookkeeping_value = cg.air_bookkeeping;
1937 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, 1);
19381931 try cg.genInst(inst);
19391932
19401933 if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) {
......@@ -2160,11 +2153,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21602153 .direct => |scalar_type| {
21612154 if (!isByRef(ret_ty, zcu, cg.target)) {
21622155 const result_local = try cg.allocLocal(ret_ty);
2163 try cg.addLocal(.local_set, result_local.local.value);
2156 try cg.addLocal(.local_set, result_local.local);
21642157 break :result_value result_local;
21652158 } else {
21662159 const result_local = try cg.allocLocal(scalar_type);
2167 try cg.addLocal(.local_set, result_local.local.value);
2160 try cg.addLocal(.local_set, result_local.local);
21682161 const result = try cg.allocStack(ret_ty);
21692162 try cg.store(result, result_local, scalar_type, .{});
21702163 break :result_value result;
......@@ -2175,7 +2168,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21752168 assert(vector.len == 1);
21762169 const result_local = try cg.allocLocal(vector.elem_type);
21772170 // save call result from operand stack
2178 try cg.addLocal(.local_set, result_local.local.value);
2171 try cg.addLocal(.local_set, result_local.local);
21792172 const result = try cg.allocStack(ret_ty);
21802173 try cg.store(result, result_local, vector.elem_type, .{});
21812174 break :result_value result;
......@@ -2183,7 +2176,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21832176 }
21842177 } else {
21852178 const result_local = try cg.allocLocal(ret_ty);
2186 try cg.addLocal(.local_set, result_local.local.value);
2179 try cg.addLocal(.local_set, result_local.local);
21872180 break :result_value result_local;
21882181 }
21892182 };
......@@ -2250,7 +2243,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22502243 }
22512244 }
22522245
2253 try cg.addLocal(.local_set, arg_ptr.local.value);
2246 try cg.addLocal(.local_set, arg_ptr.local);
22542247
22552248 try cg.emitWValue(operand);
22562249 try cg.emitWValue(arg_ptr);
......@@ -2272,7 +2265,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22722265 try cg.addTag(.f32_demote_f64);
22732266
22742267 const result_local = try cg.allocLocal(Type.f32);
2275 try cg.addLocal(.local_set, result_local.local.value);
2268 try cg.addLocal(.local_set, result_local.local);
22762269 break :result result_local;
22772270 } else try cg.load(arg_ptr, ty, .{});
22782271
......@@ -3300,7 +3293,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33003293
33013294 var mask = try cg.allocLocal(Type.i32);
33023295 defer mask.free(cg);
3303 try cg.addLocal(.local_tee, mask.local.value);
3296 try cg.addLocal(.local_tee, mask.local);
33043297
33053298 try cg.emitWValue(operand);
33063299 try cg.addTag(.i32_xor);
......@@ -3315,7 +3308,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33153308
33163309 var mask = try cg.allocLocal(Type.i64);
33173310 defer mask.free(cg);
3318 try cg.addLocal(.local_tee, mask.local.value);
3311 try cg.addLocal(.local_tee, mask.local);
33193312
33203313 try cg.emitWValue(operand);
33213314 try cg.addTag(.i64_xor);
......@@ -3337,7 +3330,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33373330
33383331 var tmp = try cg.allocLocal(Type.u64);
33393332 defer tmp.free(cg);
3340 try cg.addLocal(.local_tee, tmp.local.value);
3333 try cg.addLocal(.local_tee, tmp.local);
33413334 try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset });
33423335 try cg.emitWValue(tmp);
33433336 try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset + 8 });
......@@ -4033,7 +4026,7 @@ fn toLocalInt(cg: *CodeGen, value: WValue, int_ty: IntType) InnerError!WValue {
40334026 else => return cg.fail("TODO: Support toLocalInt for integer bitsize: {d}", .{int_ty.bits}),
40344027 };
40354028 const new_local = try cg.allocLocal(ty);
4036 try cg.addLocal(.local_set, new_local.local.value);
4029 try cg.addLocal(.local_set, new_local.local);
40374030 return new_local;
40384031 },
40394032 .local, .stack_offset => return value,
......@@ -4112,7 +4105,7 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
41124105fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult {
41134106 const overflow_bit = try cg.allocLocal(Type.u32);
41144107 try cg.addImm32(0);
4115 try cg.addLocal(.local_set, overflow_bit.local.value);
4108 try cg.addLocal(.local_set, overflow_bit.local);
41164109
41174110 const result_val = if (int_ty.bits <= 32) blk: {
41184111 const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 64 };
......@@ -4126,7 +4119,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41264119
41274120 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);
41284121 _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op);
4129 try cg.addLocal(.local_set, overflow_bit.local.value);
4122 try cg.addLocal(.local_set, overflow_bit.local);
41304123 break :blk res_tmp;
41314124 } else if (int_ty.bits <= 64) blk: {
41324125 const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 128 };
......@@ -4140,7 +4133,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41404133
41414134 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);
41424135 _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op);
4143 try cg.addLocal(.local_set, overflow_bit.local.value);
4136 try cg.addLocal(.local_set, overflow_bit.local);
41444137 break :blk res_tmp;
41454138 } else if (int_ty.bits == 128 and int_ty.is_signed) blk: {
41464139 const overflow_ret = try cg.allocStack(Type.i32);
......@@ -4151,7 +4144,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41514144 &.{ lhs, rhs, overflow_ret },
41524145 );
41534146 _ = try cg.load(overflow_ret, .i32, .{});
4154 try cg.addLocal(.local_set, overflow_bit.local.value);
4147 try cg.addLocal(.local_set, overflow_bit.local);
41554148 break :blk res;
41564149 } else {
41574150 const result = try cg.allocInt(int_ty);
......@@ -5232,17 +5225,17 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const
52325225 .label = cg.block_depth,
52335226 .value = block_result,
52345227 });
5228 defer assert(cg.blocks.swapRemove(inst));
52355229
52365230 {
5237 try cg.branches.append(cg.gpa, .{});
5238 defer {
5239 var branch = cg.branches.pop().?;
5240 branch.deinit(cg.gpa);
5241 }
5231 try cg.pushBranch();
5232 defer cg.popBranch();
52425233 try cg.genBody(body);
52435234 try cg.endBlock();
52445235 }
52455236
5237 for (cg.liveness.getBlock(inst).deaths) |death| cg.processDeath(death.toRef());
5238
52465239 return cg.finishAir(inst, block_result, &.{});
52475240}
52485241
......@@ -5282,6 +5275,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52825275 const condition = try cg.resolveInst(cond_br.condition);
52835276 const then_body = cond_br.then_body;
52845277 const else_body = cond_br.else_body;
5278 const branch_liveness = cg.liveness.getCondBr(inst);
52855279
52865280 // result type is always noreturn, so use `block_empty` as type.
52875281 try cg.startBlock(.block, .empty);
......@@ -5293,24 +5287,21 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52935287 // and continue with the then codepath
52945288 try cg.addLabel(.br_if, 0);
52955289
5296 try cg.branches.ensureUnusedCapacity(cg.gpa, 2);
5290 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(cond_br.condition);
5291
52975292 {
5298 cg.branches.appendAssumeCapacity(.{});
5299 defer {
5300 var else_stack = cg.branches.pop().?;
5301 else_stack.deinit(cg.gpa);
5302 }
5293 try cg.pushBranch();
5294 defer cg.popBranch();
5295 for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef());
53035296 try cg.genBody(else_body);
53045297 try cg.endBlock();
53055298 }
53065299
53075300 // Outer block that matches the condition
53085301 {
5309 cg.branches.appendAssumeCapacity(.{});
5310 defer {
5311 var then_stack = cg.branches.pop().?;
5312 then_stack.deinit(cg.gpa);
5313 }
5302 try cg.pushBranch();
5303 defer cg.popBranch();
5304 for (branch_liveness.then_deaths) |death| cg.processDeath(death.toRef());
53145305 try cg.genBody(then_body);
53155306 }
53165307
......@@ -5346,18 +5337,18 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne
53465337 try cg.startBlock(.block, .empty);
53475338
53485339 try cg.addImm32(if (op == .eq) 0 else 1);
5349 try cg.addLocal(.local_set, result.local.value);
5340 try cg.addLocal(.local_set, result.local);
53505341
53515342 _ = try cg.isNull(lhs, operand_ty, .i32_eq, .value);
5352 try cg.addLocal(.local_tee, lhs_null.local.value);
5343 try cg.addLocal(.local_tee, lhs_null.local);
53535344 _ = try cg.isNull(rhs, operand_ty, .i32_eq, .value);
53545345 try cg.addTag(.i32_ne);
53555346 try cg.addLabel(.br_if, 0);
53565347
53575348 try cg.addImm32(if (op == .eq) 1 else 0);
5358 try cg.addLocal(.local_set, result.local.value);
5349 try cg.addLocal(.local_set, result.local);
53595350
5360 try cg.addLocal(.local_get, lhs_null.local.value);
5351 try cg.addLocal(.local_get, lhs_null.local);
53615352 try cg.addLabel(.br_if, 0);
53625353
53635354 _ = try cg.load(lhs, payload_ty, .{});
......@@ -5369,10 +5360,10 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne
53695360 _ = try cg.intCmp(.fromType(cg, payload_ty), op, .stack, .stack);
53705361 }
53715362
5372 try cg.addLocal(.local_set, result.local.value);
5363 try cg.addLocal(.local_set, result.local);
53735364 try cg.endBlock();
53745365
5375 try cg.addLocal(.local_get, result.local.value);
5366 try cg.addLocal(.local_get, result.local);
53765367 try cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
53775368 } else {
53785369 const result = try cg.intCmp(.fromType(cg, operand_ty), op, lhs, rhs);
......@@ -5570,7 +5561,7 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55705561 if (block.value != .none) {
55715562 const operand = try cg.resolveInst(br.operand);
55725563 try cg.emitWValue(operand);
5573 try cg.addLocal(.local_set, block.value.local.value);
5564 try cg.addLocal(.local_set, block.value.local);
55745565 }
55755566
55765567 // We map every block to its block index.
......@@ -5854,24 +5845,29 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
58545845
58555846 const switch_br = cg.air.unwrapSwitch(inst);
58565847 const target_ty = cg.typeOf(switch_br.operand);
5848 const branch_liveness = try cg.liveness.getSwitchBr(cg.gpa, inst, switch_br.cases_len + 1);
5849 defer cg.gpa.free(branch_liveness.deaths);
58575850
58585851 assert(target_ty.hasRuntimeBits(zcu));
58595852
58605853 // swap target value with placeholder local, for dispatching
5854 var owned_target: WValue = .none;
5855 defer owned_target.free(cg);
58615856 const target = if (is_dispatch_loop) target: {
58625857 const initial_target = try cg.resolveInst(switch_br.operand);
5863 const target: WValue = try cg.allocLocal(target_ty);
5858 owned_target = try cg.allocLocal(target_ty);
58645859 try cg.emitWValue(initial_target);
5865 try cg.addLocal(.local_set, target.local.value);
5860 try cg.addLocal(.local_set, owned_target.local);
58665861
58675862 try cg.startBlock(.loop, .empty); // dispatch loop start
58685863 try cg.blocks.putNoClobber(cg.gpa, inst, .{
58695864 .label = cg.block_depth,
5870 .value = target,
5865 .value = owned_target,
58715866 });
58725867
5873 break :target target;
5868 break :target owned_target;
58745869 } else try cg.resolveInst(switch_br.operand);
5870 defer if (is_dispatch_loop) assert(cg.blocks.swapRemove(inst));
58755871
58765872 const has_else_body = switch_br.else_body_len != 0;
58775873 const branch_count = switch_br.cases_len + 1; // if else branch is missing, we trap when failing all conditions
......@@ -5883,12 +5879,14 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
58835879 var it = switch_br.iterateCases();
58845880 const else_body = it.elseBody();
58855881
5886 cg.branches.appendAssumeCapacity(.{});
5887 defer {
5888 var else_branch = cg.branches.pop().?;
5889 else_branch.deinit(cg.gpa);
5882 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand);
5883
5884 {
5885 try cg.pushBranch();
5886 defer cg.popBranch();
5887 for (branch_liveness.deaths[0]) |death| cg.processDeath(death.toRef());
5888 try cg.genBody(else_body);
58905889 }
5891 try cg.genBody(else_body);
58925890
58935891 if (is_dispatch_loop) {
58945892 try cg.endBlock(); // dispatch loop end
......@@ -6012,16 +6010,18 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
60126010 try cg.addLabel(.br, branch_count - 1);
60136011 }
60146012
6013 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand);
6014
60156015 var cases_it = switch_br.iterateCases();
60166016 while (cases_it.next()) |case| {
60176017 try cg.endBlock();
60186018
6019 cg.branches.appendAssumeCapacity(.{});
6020 defer {
6021 var case_branch = cg.branches.pop().?;
6022 case_branch.deinit(cg.gpa);
6019 {
6020 try cg.pushBranch();
6021 defer cg.popBranch();
6022 for (branch_liveness.deaths[case.idx]) |death| cg.processDeath(death.toRef());
6023 try cg.genBody(case.body);
60236024 }
6024 try cg.genBody(case.body);
60256025
60266026 try cg.addLabel(.br, branch_count - case.idx - 1); // matching case found and executed => exit switch
60276027 }
......@@ -6030,12 +6030,12 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
60306030 if (has_else_body) {
60316031 const else_body = cases_it.elseBody();
60326032
6033 cg.branches.appendAssumeCapacity(.{});
6034 defer {
6035 var else_branch = cg.branches.pop().?;
6036 else_branch.deinit(cg.gpa);
6033 {
6034 try cg.pushBranch();
6035 defer cg.popBranch();
6036 for (branch_liveness.deaths[branch_liveness.deaths.len - 1]) |death| cg.processDeath(death.toRef());
6037 try cg.genBody(else_body);
60376038 }
6038 try cg.genBody(else_body);
60396039 } else {
60406040 try cg.addTag(.@"unreachable");
60416041 }
......@@ -6055,7 +6055,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60556055
60566056 const operand = try cg.resolveInst(br.operand);
60576057 try cg.emitWValue(operand);
6058 try cg.addLocal(.local_set, switch_loop.value.local.value);
6058 try cg.addLocal(.local_set, switch_loop.value.local);
60596059
60606060 const idx: u32 = cg.block_depth - switch_loop.label;
60616061 try cg.addLabel(.br, idx);
......@@ -6630,7 +6630,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66306630 try cg.addTag(.i64_mul);
66316631 },
66326632 }
6633 try cg.addLocal(.local_set, new_len.local.value);
6633 try cg.addLocal(.local_set, new_len.local);
66346634 break :blk new_len;
66356635 } else len,
66366636 };
......@@ -6640,7 +6640,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66406640 var new_ptr = try cg.allocLocal(Type.usize);
66416641 defer new_ptr.free(cg);
66426642 try cg.emitWValue(ptr);
6643 try cg.addLocal(.local_set, new_ptr.local.value);
6643 try cg.addLocal(.local_set, new_ptr.local);
66446644
66456645 // get the loop conditional: if current pointer address equals final pointer's address
66466646 try cg.emitWValue(ptr);
......@@ -6649,7 +6649,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66496649 .wasm32 => try cg.addTag(.i32_add),
66506650 .wasm64 => try cg.addTag(.i64_add),
66516651 }
6652 try cg.addLocal(.local_set, end_ptr.local.value);
6652 try cg.addLocal(.local_set, end_ptr.local);
66536653
66546654 // outer block to jump to when loop is done
66556655 try cg.startBlock(.block, .empty);
......@@ -6679,7 +6679,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66796679 try cg.addTag(.i64_add);
66806680 },
66816681 }
6682 try cg.addLocal(.local_set, new_ptr.local.value);
6682 try cg.addLocal(.local_set, new_ptr.local);
66836683
66846684 // end of loop
66856685 try cg.addLabel(.br, 0); // jump to start of loop
......@@ -7186,7 +7186,7 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71867186 try cg.emitWValue(slice_len);
71877187 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
71887188 try cg.addTag(.i32_mul);
7189 try cg.addLocal(.local_set, slice_len.local.value);
7189 try cg.addLocal(.local_set, slice_len.local);
71907190 }
71917191 break :blk slice_len;
71927192 },
......@@ -7217,7 +7217,7 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72177217 try cg.emitWValue(slice_len);
72187218 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
72197219 try cg.addTag(.i32_mul);
7220 try cg.addLocal(.local_set, slice_len.local.value);
7220 try cg.addLocal(.local_set, slice_len.local);
72217221 }
72227222 break :blk slice_len;
72237223 },
......@@ -7303,8 +7303,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
73037303 const body = unwrapped_try.else_body;
73047304 const err_union = try cg.resolveInst(unwrapped_try.error_union);
73057305 const err_union_ty = cg.typeOf(unwrapped_try.error_union);
7306 const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false);
7307 return cg.finishAir(inst, result, &.{unwrapped_try.error_union});
7306 const result = try lowerTry(cg, inst, unwrapped_try.error_union, err_union, body, err_union_ty, false);
7307 return cg.finishAirWithBranchDeaths(
7308 inst,
7309 result,
7310 &.{unwrapped_try.error_union},
7311 cg.liveness.getCondBr(inst).then_deaths,
7312 );
73087313}
73097314
73107315fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -7313,20 +7318,26 @@ fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
73137318 const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr);
73147319 const body = unwrapped_try.else_body;
73157320 const err_union_ty = cg.typeOf(unwrapped_try.error_union_ptr).childType(zcu);
7316 const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true);
7317 return cg.finishAir(inst, result, &.{unwrapped_try.error_union_ptr});
7321 const result = try lowerTry(cg, inst, unwrapped_try.error_union_ptr, err_union_ptr, body, err_union_ty, true);
7322 return cg.finishAirWithBranchDeaths(
7323 inst,
7324 result,
7325 &.{unwrapped_try.error_union_ptr},
7326 cg.liveness.getCondBr(inst).then_deaths,
7327 );
73187328}
73197329
73207330fn lowerTry(
73217331 cg: *CodeGen,
73227332 inst: Air.Inst.Index,
7333 operand_ref: Air.Inst.Ref,
73237334 err_union: WValue,
73247335 body: []const Air.Inst.Index,
73257336 err_union_ty: Type,
73267337 operand_is_ptr: bool,
73277338) InnerError!WValue {
7328 _ = inst;
73297339 const zcu = cg.pt.zcu;
7340 const branch_liveness = cg.liveness.getCondBr(inst);
73307341
73317342 const pl_ty = err_union_ty.errorUnionPayload(zcu);
73327343 const pl_has_bits = pl_ty.hasRuntimeBits(zcu);
......@@ -7349,12 +7360,13 @@ fn lowerTry(
73497360 try cg.addTag(.i32_eqz);
73507361 try cg.addLabel(.br_if, 0); // jump out of block when error is '0'
73517362
7352 try cg.branches.append(cg.gpa, .{});
7353 defer {
7354 var branch = cg.branches.pop().?;
7355 branch.deinit(cg.gpa);
7363 {
7364 try cg.pushBranch();
7365 defer cg.popBranch();
7366 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(operand_ref);
7367 for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef());
7368 try cg.genBody(body);
73567369 }
7357 try cg.genBody(body);
73587370 try cg.endBlock();
73597371 }
73607372
......@@ -7367,8 +7379,7 @@ fn lowerTry(
73677379 if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {
73687380 return cg.offsetPointer(err_union, pl_offset);
73697381 }
7370 const payload = try cg.load(err_union, pl_ty, .{ .offset = pl_offset });
7371 return payload.toLocal(cg, pl_ty);
7382 return cg.load(err_union, pl_ty, .{ .offset = pl_offset });
73727383}
73737384
73747385/// Calls a compiler-rt intrinsic by creating an undefined symbol,
......@@ -7512,13 +7523,13 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
75127523 // 'false' branch (i.e. error set does not have value
75137524 // ensure we set local to 0 in case the local was re-used.
75147525 try cg.addImm32(0);
7515 try cg.addLocal(.local_set, result.local.value);
7526 try cg.addLocal(.local_set, result.local);
75167527 try cg.addLabel(.br, 1);
75177528 try cg.endBlock();
75187529
75197530 // 'true' branch
75207531 try cg.addImm32(1);
7521 try cg.addLocal(.local_set, result.local.value);
7532 try cg.addLocal(.local_set, result.local);
75227533 try cg.addLabel(.br, 0);
75237534 try cg.endBlock();
75247535
......@@ -7561,9 +7572,9 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
75617572 .offset = ptr_operand.memOffset(),
75627573 .alignment = ty.abiAlignment(zcu).toStdMem(),
75637574 });
7564 try cg.addLocal(.local_tee, val_local.local.value);
7575 try cg.addLocal(.local_tee, val_local.local);
75657576 _ = try cg.intCmp(int_ty, .eq, .stack, expected_val);
7566 try cg.addLocal(.local_set, cmp_result.local.value);
7577 try cg.addLocal(.local_set, cmp_result.local);
75677578 break :val val_local;
75687579 } else val: {
75697580 if (ty.abiSize(zcu) > 8) {
......@@ -7575,7 +7586,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
75757586 try cg.emitWValue(new_val);
75767587 try cg.emitWValue(ptr_val);
75777588 _ = try cg.intCmp(int_ty, .eq, ptr_val, expected_val);
7578 try cg.addLocal(.local_tee, cmp_result.local.value);
7589 try cg.addLocal(.local_tee, cmp_result.local);
75797590 try cg.addTag(.select);
75807591 try cg.store(.stack, .stack, ty, .{ .offset = ptr_operand.memOffset() });
75817592
......@@ -7685,11 +7696,11 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76857696 },
76867697 );
76877698 const select_res = try cg.allocLocal(ty);
7688 try cg.addLocal(.local_tee, select_res.local.value);
7699 try cg.addLocal(.local_tee, select_res.local);
76897700 _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if
76907701
76917702 try cg.emitWValue(select_res);
7692 try cg.addLocal(.local_set, value.local.value);
7703 try cg.addLocal(.local_set, value.local);
76937704
76947705 try cg.addLabel(.br_if, 0);
76957706 try cg.endBlock();
......@@ -7864,9 +7875,6 @@ fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
78647875}
78657876
78667877fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7867 if (cg.initial_stack_value == .none) {
7868 try cg.initializeStack();
7869 }
78707878 try cg.emitWValue(cg.bottom_stack_value);
78717879 return cg.finishAir(inst, .stack, &.{});
78727880}
......@@ -7912,7 +7920,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
79127920 return cg.fail("Self-hosted wasm backend requires output constraint to be equal \"=r\"", .{});
79137921 }
79147922
7915 const gop = try local_map.getOrPutValue(cg.gpa, name, result.local.value);
7923 const gop = try local_map.getOrPutValue(cg.gpa, name, result.local);
79167924 assert(!gop.found_existing); // first value
79177925
79187926 assert(it.next() == null);
......@@ -7933,7 +7941,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
79337941 try cg.emitWValue(operand);
79347942 const op_local = try WValue.toLocal(.stack, cg, cg.typeOf(input.operand));
79357943
7936 const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local.value);
7944 const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local);
79377945 if (gop.found_existing) {
79387946 return cg.fail("Duplicate asm variable name \"{s}\"", .{name});
79397947 }