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,...@@ -136,13 +136,10 @@ air: Air,
136liveness: Air.Liveness,136liveness: Air.Liveness,
137gpa: mem.Allocator,137gpa: mem.Allocator,
138func_index: InternPool.Index,138func_index: InternPool.Index,
139/// Contains a list of current branches.139/// Contains a list of current branches. Stores LocalPool before entering branch.
140/// When we return from a branch, the branch will be popped from this list,140branches: std.ArrayList(LocalPool) = .empty,
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,
144/// Table to save `WValue`'s generated by an `Air.Inst`141/// Table to save `WValue`'s generated by an `Air.Inst`
145// values: ValueTable,142values: ValueTable = .empty,
146/// Mapping from Air.Inst.Index to block ids143/// Mapping from Air.Inst.Index to block ids
147blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {144blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {
148 label: u32,145 label: u32,
...@@ -150,10 +147,6 @@ blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {...@@ -150,10 +147,6 @@ blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {
150}) = .{},147}) = .{},
151/// Maps `loop` instructions to their label. `br` to here repeats the loop.148/// Maps `loop` instructions to their label. `br` to here repeats the loop.
152loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,149loops: 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,
157/// The index of the current argument.150/// The index of the current argument.
158/// Used to track which argument is being referenced in `airArg`.151/// Used to track which argument is being referenced in `airArg`.
159arg_index: u32 = 0,152arg_index: u32 = 0,
...@@ -207,23 +200,8 @@ stack_size: u32 = 0,...@@ -207,23 +200,8 @@ stack_size: u32 = 0,
207/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.200/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.
208stack_alignment: Alignment = .@"16",201stack_alignment: Alignment = .@"16",
209202
210// For each individual Wasm valtype we store a seperate free list which203local_pool: LocalPool = .{},
211// allows us to re-use locals that are no longer used. e.g. a temporary local.204wasm_param_count: u32 = undefined,
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,
227205
228/// When in debug mode, this tracks if no `finishAir` was missed.206/// When in debug mode, this tracks if no `finishAir` was missed.
229/// Forgetting to call `finishAir` will cause the result to not be207/// Forgetting to call `finishAir` will cause the result to not be
...@@ -239,13 +217,7 @@ const WValue = union(enum) {...@@ -239,13 +217,7 @@ const WValue = union(enum) {
239 none: void,217 none: void,
240 /// The value lives on top of the stack218 /// The value lives on top of the stack
241 stack: void,219 stack: void,
242 /// Index of the local220 local: u32, // index
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 },
249 /// An immediate 32bit value221 /// An immediate 32bit value
250 imm32: u32,222 imm32: u32,
251 /// An immediate 64bit value223 /// An immediate 64bit value
...@@ -290,7 +262,7 @@ const WValue = union(enum) {...@@ -290,7 +262,7 @@ const WValue = union(enum) {
290 switch (value) {262 switch (value) {
291 .stack => {263 .stack => {
292 const new_local = try gen.allocLocal(ty);264 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);
294 return new_local;266 return new_local;
295 },267 },
296 else => return value,268 else => return value,
...@@ -299,23 +271,9 @@ const WValue = union(enum) {...@@ -299,23 +271,9 @@ const WValue = union(enum) {
299271
300 /// Marks a local as no longer being referenced and essentially allows272 /// Marks a local as no longer being referenced and essentially allows
301 /// us to re-use it somewhere else within the function.273 /// 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`.274 fn free(value: *WValue, cg: *CodeGen) void {
303 fn free(value: *WValue, gen: *CodeGen) void {
304 if (value.* != .local) return;275 if (value.* != .local) return;
305 const local_value = value.local.value;276 cg.releaseLocal(value.local);
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 });
319 value.* = .dead;277 value.* = .dead;
320 }278 }
321};279};
...@@ -323,6 +281,77 @@ const WValue = union(enum) {...@@ -323,6 +281,77 @@ const WValue = union(enum) {
323/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`281/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
324const ValueTable = std.array_hash_map.Auto(Air.Inst.Ref, WValue);282const 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
326const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};355const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
327356
328const InnerError = Error || error{357const InnerError = Error || error{
...@@ -336,14 +365,11 @@ pub fn deinit(cg: *CodeGen) void {...@@ -336,14 +365,11 @@ pub fn deinit(cg: *CodeGen) void {
336 const gpa = cg.gpa;365 const gpa = cg.gpa;
337 for (cg.branches.items) |*branch| branch.deinit(gpa);366 for (cg.branches.items) |*branch| branch.deinit(gpa);
338 cg.branches.deinit(gpa);367 cg.branches.deinit(gpa);
368 cg.values.deinit(gpa);
339 cg.blocks.deinit(gpa);369 cg.blocks.deinit(gpa);
340 cg.loops.deinit(gpa);370 cg.loops.deinit(gpa);
341 cg.simd_immediates.deinit(gpa);371 cg.simd_immediates.deinit(gpa);
342 cg.free_locals_i32.deinit(gpa);372 cg.local_pool.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);
347 cg.mir_instructions.deinit(gpa);373 cg.mir_instructions.deinit(gpa);
348 cg.mir_extra.deinit(gpa);374 cg.mir_extra.deinit(gpa);
349 cg.mir_locals.deinit(gpa);375 cg.mir_locals.deinit(gpa);
...@@ -362,19 +388,11 @@ pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error {...@@ -362,19 +388,11 @@ pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error {
362/// Resolves the `WValue` for the given instruction `inst`388/// Resolves the `WValue` for the given instruction `inst`
363/// When the given instruction has a `Value`, it returns a constant instead389/// When the given instruction has a `Value`, it returns a constant instead
364fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {390fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
365 var branch_index = cg.branches.items.len;391 if (cg.values.get(ref)) |value| return value;
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 }
372392
373 // when we did not find an existing instruction, it393 // when we did not find an existing instruction, it
374 // means we must generate it from a constant.394 // means we must generate it from a constant.
375 // We always store constants in the most outer branch as they must never395 const gop = try cg.values.getOrPut(cg.gpa, ref);
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);
378 assert(!gop.found_existing);396 assert(!gop.found_existing);
379397
380 const pt = cg.pt;398 const pt = cg.pt;
...@@ -413,20 +431,33 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue {...@@ -413,20 +431,33 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue {
413431
414/// NOTE: if result == .stack, it will be stored in .local432/// NOTE: if result == .stack, it will be stored in .local
415fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void {433fn 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 {
416 assert(operands.len <= Air.Liveness.bpi - 1);445 assert(operands.len <= Air.Liveness.bpi - 1);
417446
418 var final_result = result;447 if (result == .local) aliased: {
419 const aliased_operand: ?*WValue = if (result == .local) aliased: {
420 for (operands) |operand| {448 for (operands) |operand| {
421 if (operand.toIndex() == null) continue;449 if (operand.toIndex() == null) continue;
422 const resolved_operand = cg.getResolvedInst(operand);450 const resolved_operand = cg.values.getPtr(operand) orelse unreachable;
423 if (resolved_operand.* != .local or resolved_operand.local.value != result.local.value) continue;451 if (resolved_operand.* != .local or resolved_operand.local != result.local) continue;
424452
425 resolved_operand.local.references += 1;453 if (result.local >= cg.wasm_param_count) {
426 break :aliased resolved_operand;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;
427 }459 }
428 break :aliased null;460 }
429 } else null;
430461
431 var tomb_bits = cg.liveness.getTombBits(inst);462 var tomb_bits = cg.liveness.getTombBits(inst);
432 for (operands) |operand| {463 for (operands) |operand| {
...@@ -435,8 +466,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con...@@ -435,8 +466,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con
435 if (!dies) continue;466 if (!dies) continue;
436 processDeath(cg, operand);467 processDeath(cg, operand);
437 }468 }
438 if (aliased_operand) |operand| final_result.local.references = operand.local.references;469 for (branch_deaths) |death| cg.processDeath(death.toRef());
439 try cg.finishAirResult(inst, final_result);470 try cg.finishAirResult(inst, result);
440}471}
441472
442fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void {473fn 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...@@ -446,8 +477,7 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro
446 result477 result
447 else478 else
448 try result.toLocal(cg, cg.typeOfIndex(inst));479 try result.toLocal(cg, cg.typeOfIndex(inst));
449 const branch = cg.currentBranch();480 try cg.values.putNoClobber(cg.gpa, inst.toRef(), trackable_result);
450 branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result);
451 }481 }
452482
453 if (std.debug.runtime_safety) {483 if (std.debug.runtime_safety) {
...@@ -455,17 +485,16 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro...@@ -455,17 +485,16 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro
455 }485 }
456}486}
457487
458const Branch = struct {488fn pushBranch(cg: *CodeGen) InnerError!void {
459 values: ValueTable = .{},489 var snapshot = try cg.local_pool.clone(cg.gpa);
460490 errdefer snapshot.deinit(cg.gpa);
461 fn deinit(branch: *Branch, gpa: Allocator) void {491 try cg.branches.append(cg.gpa, snapshot);
462 branch.values.deinit(gpa);492}
463 branch.* = undefined;
464 }
465};
466493
467inline fn currentBranch(cg: *CodeGen) *Branch {494fn popBranch(cg: *CodeGen) void {
468 return &cg.branches.items[cg.branches.items.len - 1];495 var snapshot = cg.branches.pop().?;
496 defer snapshot.deinit(cg.gpa);
497 snapshot.restore(cg);
469}498}
470499
471fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void {500fn 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 {...@@ -476,20 +505,9 @@ fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void {
476505
477fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {506fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
478 if (ref.toIndex() == null) return;507 if (ref.toIndex() == null) return;
479 // Branches are currently only allowed to free locals allocated508 const value = cg.values.getPtr(ref) orelse unreachable;
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;
483 if (value.* != .local) return;509 if (value.* != .local) return;
484 const reserved_indexes = cg.args.len + @intFromBool(cg.return_value != .none);510 cg.releaseLocal(value.local);
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 }
493}511}
494512
495pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {513pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
...@@ -708,7 +726,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -708,7 +726,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
708 switch (value) {726 switch (value) {
709 .dead => unreachable, // reference to free'd `WValue`727 .dead => unreachable, // reference to free'd `WValue`
710 .none, .stack => {}, // no-op728 .none, .stack => {}, // no-op
711 .local => |idx| try cg.addLocal(.local_get, idx.value),729 .local => |idx| try cg.addLocal(.local_get, idx),
712 .imm32 => |val| try cg.addImm32(val),730 .imm32 => |val| try cg.addImm32(val),
713 .imm64 => |val| try cg.addImm64(val),731 .imm64 => |val| try cg.addImm64(val),
714 .imm128 => |val| try cg.addImm128(val),732 .imm128 => |val| try cg.addImm128(val),
...@@ -780,22 +798,28 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -780,22 +798,28 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
780/// Emits the base address for a memory instruction.798/// Emits the base address for a memory instruction.
781fn emitMemBase(cg: *CodeGen, ptr: WValue) InnerError!void {799fn emitMemBase(cg: *CodeGen, ptr: WValue) InnerError!void {
782 switch (ptr) {800 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),
784 else => try cg.emitWValue(ptr),802 else => try cg.emitWValue(ptr),
785 }803 }
786}804}
787805
788/// From a reference, returns its resolved `WValue`.806fn appendFreeLocal(cg: *CodeGen, index: u32) void {
789/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.807 assert(index >= cg.wasm_param_count);
790fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {808 const mir_index = index - cg.wasm_param_count;
791 var index = cg.branches.items.len;809 const valtype = cg.mir_locals.items[mir_index];
792 while (index > 0) : (index -= 1) {810 const free_list = cg.local_pool.freeList(valtype);
793 const branch = cg.branches.items[index - 1];811 cg.local_pool.locals.items[mir_index] = .{ .next_free = free_list.* };
794 if (branch.values.getPtr(ref)) |value| {812 free_list.* = .init(index);
795 return value;813 log.debug("freed local ({d}) of type {}", .{ index, valtype });
796 }814}
797 }815
798 unreachable; // developer-error: This can only be called on resolved instructions. Use `resolveInst` instead.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);
799}823}
800824
801/// Creates one locals for a given `Type`.825/// Creates one locals for a given `Type`.
...@@ -803,16 +827,13 @@ fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {...@@ -803,16 +827,13 @@ fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
803fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {827fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
804 const zcu = cg.pt.zcu;828 const zcu = cg.pt.zcu;
805 const valtype = typeToValtype(ty, zcu, cg.target);829 const valtype = typeToValtype(ty, zcu, cg.target);
806 const index_or_null = switch (valtype) {830 const free_list = cg.local_pool.freeList(valtype);
807 .i32 => cg.free_locals_i32.pop(),831 if (free_list.unwrap()) |index| {
808 .i64 => cg.free_locals_i64.pop(),832 const local = &cg.local_pool.locals.items[index - cg.wasm_param_count];
809 .f32 => cg.free_locals_f32.pop(),833 free_list.* = local.next_free;
810 .f64 => cg.free_locals_f64.pop(),834 local.* = .{ .ref_count = 1 };
811 .v128 => cg.free_locals_v128.pop(),
812 };
813 if (index_or_null) |index| {
814 log.debug("reusing local ({d}) of type {}", .{ index, valtype });835 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
815 return .{ .local = .{ .value = index, .references = 1 } };836 return .{ .local = index };
816 }837 }
817 log.debug("new local of type {}", .{valtype});838 log.debug("new local of type {}", .{valtype});
818 return cg.ensureAllocLocal(ty);839 return cg.ensureAllocLocal(ty);
...@@ -822,10 +843,13 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {...@@ -822,10 +843,13 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
822/// to use a zero-initialized local.843/// to use a zero-initialized local.
823fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {844fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
824 const zcu = cg.pt.zcu;845 const zcu = cg.pt.zcu;
825 try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target));846 try cg.mir_locals.ensureUnusedCapacity(cg.gpa, 1);
826 const initial_index = cg.local_index;847 try cg.local_pool.locals.ensureUnusedCapacity(cg.gpa, 1);
827 cg.local_index += 1;848 assert(cg.mir_locals.items.len == cg.local_pool.locals.items.len);
828 return .{ .local = .{ .value = initial_index, .references = 1 } };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 };
829}853}
830854
831pub const Error = codegen.Error;855pub const Error = codegen.Error;
...@@ -867,7 +891,6 @@ pub fn generate(...@@ -867,7 +891,6 @@ pub fn generate(
867 .args = cc_result.args,891 .args = cc_result.args,
868 .return_value = cc_result.return_value,892 .return_value = cc_result.return_value,
869 .varargs = cc_result.varargs,893 .varargs = cc_result.varargs,
870 .local_index = cc_result.local_index,
871 .mir_instructions = .empty,894 .mir_instructions = .empty,
872 .mir_extra = .empty,895 .mir_extra = .empty,
873 .mir_locals = .empty,896 .mir_locals = .empty,
...@@ -890,17 +913,10 @@ pub fn generate(...@@ -890,17 +913,10 @@ pub fn generate(
890913
891fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {914fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
892 const zcu = cg.pt.zcu;915 const zcu = cg.pt.zcu;
893 // branch used for const values916 cg.wasm_param_count = @intCast(cg.args.len + @intFromBool(cg.return_value != .none) + @intFromBool(cg.varargs != .none));
894 try cg.branches.append(cg.gpa, .{});917 cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize);
895 // func scope branch918 cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize);
896 try cg.branches.append(cg.gpa, .{});919 defer assert(cg.branches.items.len == 0);
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 }
904 // Generate MIR for function body920 // Generate MIR for function body
905 try cg.genBody(cg.air.getMainBody());921 try cg.genBody(cg.air.getMainBody());
906922
...@@ -925,11 +941,11 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {...@@ -925,11 +941,11 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
925 .instructions = cg.mir_instructions.toOwnedSlice(),941 .instructions = cg.mir_instructions.toOwnedSlice(),
926 .extra = cg.mir_extra.toOwnedSliceAssert(),942 .extra = cg.mir_extra.toOwnedSliceAssert(),
927 .locals = cg.mir_locals.toOwnedSliceAssert(),943 .locals = cg.mir_locals.toOwnedSliceAssert(),
928 .prologue = if (cg.initial_stack_value == .none) .none else .{944 .prologue = .{
929 .sp_local = cg.initial_stack_value.local.value,945 .sp_local = cg.initial_stack_value.local,
930 .flags = .{ .stack_alignment = cg.stack_alignment },946 .flags = .{ .stack_alignment = cg.stack_alignment },
931 .stack_size = cg.stack_size,947 .stack_size = cg.stack_size,
932 .bottom_stack_local = cg.bottom_stack_value.local.value,948 .bottom_stack_local = cg.bottom_stack_value.local,
933 },949 },
934 .uavs = cg.mir_uavs.move(),950 .uavs = cg.mir_uavs.move(),
935 .indirect_function_set = cg.mir_indirect_function_set.move(),951 .indirect_function_set = cg.mir_indirect_function_set.move(),
...@@ -976,7 +992,7 @@ fn resolveCallingConventionValues(...@@ -976,7 +992,7 @@ fn resolveCallingConventionValues(
976 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) {992 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) {
977 // the sret arg will be passed as first argument, therefore we993 // the sret arg will be passed as first argument, therefore we
978 // set the `return_value` before allocating locals for regular args.994 // 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 };
980 result.local_index += 1;996 result.local_index += 1;
981 }997 }
982998
...@@ -987,7 +1003,7 @@ fn resolveCallingConventionValues(...@@ -987,7 +1003,7 @@ fn resolveCallingConventionValues(
987 continue;1003 continue;
988 }1004 }
9891005
990 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });1006 try args.append(.{ .local = result.local_index });
991 result.local_index += 1;1007 result.local_index += 1;
992 }1008 }
993 },1009 },
...@@ -1000,17 +1016,17 @@ fn resolveCallingConventionValues(...@@ -1000,17 +1016,17 @@ fn resolveCallingConventionValues(
10001016
1001 switch (abi.classifyType(param_ty, zcu, target)) {1017 switch (abi.classifyType(param_ty, zcu, target)) {
1002 .direct, .indirect => {1018 .direct, .indirect => {
1003 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });1019 try args.append(.{ .local = result.local_index });
1004 result.local_index += 1;1020 result.local_index += 1;
1005 },1021 },
1006 .double_i64 => {1022 .double_i64 => {
1007 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });1023 try args.append(.{ .local = result.local_index });
1008 try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } });1024 try args.append(.{ .local = result.local_index + 1 });
1009 result.local_index += 2;1025 result.local_index += 2;
1010 },1026 },
1011 .unrolled => |vector| {1027 .unrolled => |vector| {
1012 for (0..vector.len) |_| {1028 for (0..vector.len) |_| {
1013 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });1029 try args.append(.{ .local = result.local_index });
1014 result.local_index += 1;1030 result.local_index += 1;
1015 }1031 }
1016 },1032 },
...@@ -1021,7 +1037,7 @@ fn resolveCallingConventionValues(...@@ -1021,7 +1037,7 @@ fn resolveCallingConventionValues(
1021 }1037 }
10221038
1023 if (fn_info.is_var_args) {1039 if (fn_info.is_var_args) {
1024 result.varargs = .{ .local = .{ .value = result.local_index, .references = 1 } };1040 result.varargs = .{ .local = result.local_index };
1025 result.local_index += 1;1041 result.local_index += 1;
1026 }1042 }
10271043
...@@ -1088,23 +1104,9 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu...@@ -1088,23 +1104,9 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu
1088 }1104 }
1089}1105}
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
1103/// Reads the stack pointer from `Context.initial_stack_value` and writes it1107/// Reads the stack pointer from `Context.initial_stack_value` and writes it
1104/// to the global stack pointer variable1108/// to the global stack pointer variable
1105fn restoreStackPointer(cg: *CodeGen) !void {1109fn restoreStackPointer(cg: *CodeGen) !void {
1106 // only restore the pointer if it was initialized
1107 if (cg.initial_stack_value == .none) return;
1108 // Get the original stack pointer's value1110 // Get the original stack pointer's value
1109 try cg.emitWValue(cg.initial_stack_value);1111 try cg.emitWValue(cg.initial_stack_value);
11101112
...@@ -1144,10 +1146,6 @@ fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue {...@@ -1144,10 +1146,6 @@ fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue {
1144fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue {1146fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue {
1145 assert(size > 0);1147 assert(size > 0);
11461148
1147 if (cg.initial_stack_value == .none) {
1148 try cg.initializeStack();
1149 }
1150
1151 cg.stack_alignment = cg.stack_alignment.max(alignment);1149 cg.stack_alignment = cg.stack_alignment.max(alignment);
11521150
1153 const offset: u32 = @intCast(alignment.forward(cg.stack_size));1151 const offset: u32 = @intCast(alignment.forward(cg.stack_size));
...@@ -1166,10 +1164,6 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue {...@@ -1166,10 +1164,6 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue {
1166 const ptr_ty = cg.typeOfIndex(inst);1164 const ptr_ty = cg.typeOfIndex(inst);
1167 const pointee_ty = ptr_ty.childType(zcu);1165 const pointee_ty = ptr_ty.childType(zcu);
11681166
1169 if (cg.initial_stack_value == .none) {
1170 try cg.initializeStack();
1171 }
1172
1173 const abi_alignment = ptr_ty.ptrAlignment(zcu);1167 const abi_alignment = ptr_ty.ptrAlignment(zcu);
1174 const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse {1168 const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse {
1175 return cg.fail("Type {f} with ABI size of {d} exceeds stack frame size", .{1169 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 {...@@ -1934,7 +1928,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
1934 continue;1928 continue;
1935 }1929 }
1936 const old_bookkeeping_value = cg.air_bookkeeping;1930 const old_bookkeeping_value = cg.air_bookkeeping;
1937 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, 1);
1938 try cg.genInst(inst);1931 try cg.genInst(inst);
19391932
1940 if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) {1933 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)...@@ -2160,11 +2153,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
2160 .direct => |scalar_type| {2153 .direct => |scalar_type| {
2161 if (!isByRef(ret_ty, zcu, cg.target)) {2154 if (!isByRef(ret_ty, zcu, cg.target)) {
2162 const result_local = try cg.allocLocal(ret_ty);2155 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);
2164 break :result_value result_local;2157 break :result_value result_local;
2165 } else {2158 } else {
2166 const result_local = try cg.allocLocal(scalar_type);2159 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);
2168 const result = try cg.allocStack(ret_ty);2161 const result = try cg.allocStack(ret_ty);
2169 try cg.store(result, result_local, scalar_type, .{});2162 try cg.store(result, result_local, scalar_type, .{});
2170 break :result_value result;2163 break :result_value result;
...@@ -2175,7 +2168,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)...@@ -2175,7 +2168,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
2175 assert(vector.len == 1);2168 assert(vector.len == 1);
2176 const result_local = try cg.allocLocal(vector.elem_type);2169 const result_local = try cg.allocLocal(vector.elem_type);
2177 // save call result from operand stack2170 // 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);
2179 const result = try cg.allocStack(ret_ty);2172 const result = try cg.allocStack(ret_ty);
2180 try cg.store(result, result_local, vector.elem_type, .{});2173 try cg.store(result, result_local, vector.elem_type, .{});
2181 break :result_value result;2174 break :result_value result;
...@@ -2183,7 +2176,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)...@@ -2183,7 +2176,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
2183 }2176 }
2184 } else {2177 } else {
2185 const result_local = try cg.allocLocal(ret_ty);2178 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);
2187 break :result_value result_local;2180 break :result_value result_local;
2188 }2181 }
2189 };2182 };
...@@ -2250,7 +2243,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2250,7 +2243,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2250 }2243 }
2251 }2244 }
22522245
2253 try cg.addLocal(.local_set, arg_ptr.local.value);2246 try cg.addLocal(.local_set, arg_ptr.local);
22542247
2255 try cg.emitWValue(operand);2248 try cg.emitWValue(operand);
2256 try cg.emitWValue(arg_ptr);2249 try cg.emitWValue(arg_ptr);
...@@ -2272,7 +2265,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2272,7 +2265,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2272 try cg.addTag(.f32_demote_f64);2265 try cg.addTag(.f32_demote_f64);
22732266
2274 const result_local = try cg.allocLocal(Type.f32);2267 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);
2276 break :result result_local;2269 break :result result_local;
2277 } else try cg.load(arg_ptr, ty, .{});2270 } else try cg.load(arg_ptr, ty, .{});
22782271
...@@ -3300,7 +3293,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {...@@ -3300,7 +3293,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33003293
3301 var mask = try cg.allocLocal(Type.i32);3294 var mask = try cg.allocLocal(Type.i32);
3302 defer mask.free(cg);3295 defer mask.free(cg);
3303 try cg.addLocal(.local_tee, mask.local.value);3296 try cg.addLocal(.local_tee, mask.local);
33043297
3305 try cg.emitWValue(operand);3298 try cg.emitWValue(operand);
3306 try cg.addTag(.i32_xor);3299 try cg.addTag(.i32_xor);
...@@ -3315,7 +3308,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {...@@ -3315,7 +3308,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33153308
3316 var mask = try cg.allocLocal(Type.i64);3309 var mask = try cg.allocLocal(Type.i64);
3317 defer mask.free(cg);3310 defer mask.free(cg);
3318 try cg.addLocal(.local_tee, mask.local.value);3311 try cg.addLocal(.local_tee, mask.local);
33193312
3320 try cg.emitWValue(operand);3313 try cg.emitWValue(operand);
3321 try cg.addTag(.i64_xor);3314 try cg.addTag(.i64_xor);
...@@ -3337,7 +3330,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {...@@ -3337,7 +3330,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33373330
3338 var tmp = try cg.allocLocal(Type.u64);3331 var tmp = try cg.allocLocal(Type.u64);
3339 defer tmp.free(cg);3332 defer tmp.free(cg);
3340 try cg.addLocal(.local_tee, tmp.local.value);3333 try cg.addLocal(.local_tee, tmp.local);
3341 try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset });3334 try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset });
3342 try cg.emitWValue(tmp);3335 try cg.emitWValue(tmp);
3343 try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset + 8 });3336 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 {...@@ -4033,7 +4026,7 @@ fn toLocalInt(cg: *CodeGen, value: WValue, int_ty: IntType) InnerError!WValue {
4033 else => return cg.fail("TODO: Support toLocalInt for integer bitsize: {d}", .{int_ty.bits}),4026 else => return cg.fail("TODO: Support toLocalInt for integer bitsize: {d}", .{int_ty.bits}),
4034 };4027 };
4035 const new_local = try cg.allocLocal(ty);4028 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);
4037 return new_local;4030 return new_local;
4038 },4031 },
4039 .local, .stack_offset => return value,4032 .local, .stack_offset => return value,
...@@ -4112,7 +4105,7 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro...@@ -4112,7 +4105,7 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
4112fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult {4105fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult {
4113 const overflow_bit = try cg.allocLocal(Type.u32);4106 const overflow_bit = try cg.allocLocal(Type.u32);
4114 try cg.addImm32(0);4107 try cg.addImm32(0);
4115 try cg.addLocal(.local_set, overflow_bit.local.value);4108 try cg.addLocal(.local_set, overflow_bit.local);
41164109
4117 const result_val = if (int_ty.bits <= 32) blk: {4110 const result_val = if (int_ty.bits <= 32) blk: {
4118 const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 64 };4111 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...@@ -4126,7 +4119,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41264119
4127 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);4120 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);
4128 _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op);4121 _ = 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);
4130 break :blk res_tmp;4123 break :blk res_tmp;
4131 } else if (int_ty.bits <= 64) blk: {4124 } else if (int_ty.bits <= 64) blk: {
4132 const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 128 };4125 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...@@ -4140,7 +4133,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41404133
4141 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);4134 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);
4142 _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op);4135 _ = 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);
4144 break :blk res_tmp;4137 break :blk res_tmp;
4145 } else if (int_ty.bits == 128 and int_ty.is_signed) blk: {4138 } else if (int_ty.bits == 128 and int_ty.is_signed) blk: {
4146 const overflow_ret = try cg.allocStack(Type.i32);4139 const overflow_ret = try cg.allocStack(Type.i32);
...@@ -4151,7 +4144,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner...@@ -4151,7 +4144,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
4151 &.{ lhs, rhs, overflow_ret },4144 &.{ lhs, rhs, overflow_ret },
4152 );4145 );
4153 _ = try cg.load(overflow_ret, .i32, .{});4146 _ = 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);
4155 break :blk res;4148 break :blk res;
4156 } else {4149 } else {
4157 const result = try cg.allocInt(int_ty);4150 const result = try cg.allocInt(int_ty);
...@@ -5232,17 +5225,17 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const...@@ -5232,17 +5225,17 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const
5232 .label = cg.block_depth,5225 .label = cg.block_depth,
5233 .value = block_result,5226 .value = block_result,
5234 });5227 });
5228 defer assert(cg.blocks.swapRemove(inst));
52355229
5236 {5230 {
5237 try cg.branches.append(cg.gpa, .{});5231 try cg.pushBranch();
5238 defer {5232 defer cg.popBranch();
5239 var branch = cg.branches.pop().?;
5240 branch.deinit(cg.gpa);
5241 }
5242 try cg.genBody(body);5233 try cg.genBody(body);
5243 try cg.endBlock();5234 try cg.endBlock();
5244 }5235 }
52455236
5237 for (cg.liveness.getBlock(inst).deaths) |death| cg.processDeath(death.toRef());
5238
5246 return cg.finishAir(inst, block_result, &.{});5239 return cg.finishAir(inst, block_result, &.{});
5247}5240}
52485241
...@@ -5282,6 +5275,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5282,6 +5275,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5282 const condition = try cg.resolveInst(cond_br.condition);5275 const condition = try cg.resolveInst(cond_br.condition);
5283 const then_body = cond_br.then_body;5276 const then_body = cond_br.then_body;
5284 const else_body = cond_br.else_body;5277 const else_body = cond_br.else_body;
5278 const branch_liveness = cg.liveness.getCondBr(inst);
52855279
5286 // result type is always noreturn, so use `block_empty` as type.5280 // result type is always noreturn, so use `block_empty` as type.
5287 try cg.startBlock(.block, .empty);5281 try cg.startBlock(.block, .empty);
...@@ -5293,24 +5287,21 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5293,24 +5287,21 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5293 // and continue with the then codepath5287 // and continue with the then codepath
5294 try cg.addLabel(.br_if, 0);5288 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
5297 {5292 {
5298 cg.branches.appendAssumeCapacity(.{});5293 try cg.pushBranch();
5299 defer {5294 defer cg.popBranch();
5300 var else_stack = cg.branches.pop().?;5295 for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef());
5301 else_stack.deinit(cg.gpa);
5302 }
5303 try cg.genBody(else_body);5296 try cg.genBody(else_body);
5304 try cg.endBlock();5297 try cg.endBlock();
5305 }5298 }
53065299
5307 // Outer block that matches the condition5300 // Outer block that matches the condition
5308 {5301 {
5309 cg.branches.appendAssumeCapacity(.{});5302 try cg.pushBranch();
5310 defer {5303 defer cg.popBranch();
5311 var then_stack = cg.branches.pop().?;5304 for (branch_liveness.then_deaths) |death| cg.processDeath(death.toRef());
5312 then_stack.deinit(cg.gpa);
5313 }
5314 try cg.genBody(then_body);5305 try cg.genBody(then_body);
5315 }5306 }
53165307
...@@ -5346,18 +5337,18 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne...@@ -5346,18 +5337,18 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne
5346 try cg.startBlock(.block, .empty);5337 try cg.startBlock(.block, .empty);
53475338
5348 try cg.addImm32(if (op == .eq) 0 else 1);5339 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
5351 _ = try cg.isNull(lhs, operand_ty, .i32_eq, .value);5342 _ = 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);
5353 _ = try cg.isNull(rhs, operand_ty, .i32_eq, .value);5344 _ = try cg.isNull(rhs, operand_ty, .i32_eq, .value);
5354 try cg.addTag(.i32_ne);5345 try cg.addTag(.i32_ne);
5355 try cg.addLabel(.br_if, 0);5346 try cg.addLabel(.br_if, 0);
53565347
5357 try cg.addImm32(if (op == .eq) 1 else 0);5348 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);
5361 try cg.addLabel(.br_if, 0);5352 try cg.addLabel(.br_if, 0);
53625353
5363 _ = try cg.load(lhs, payload_ty, .{});5354 _ = try cg.load(lhs, payload_ty, .{});
...@@ -5369,10 +5360,10 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne...@@ -5369,10 +5360,10 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne
5369 _ = try cg.intCmp(.fromType(cg, payload_ty), op, .stack, .stack);5360 _ = try cg.intCmp(.fromType(cg, payload_ty), op, .stack, .stack);
5370 }5361 }
53715362
5372 try cg.addLocal(.local_set, result.local.value);5363 try cg.addLocal(.local_set, result.local);
5373 try cg.endBlock();5364 try cg.endBlock();
53745365
5375 try cg.addLocal(.local_get, result.local.value);5366 try cg.addLocal(.local_get, result.local);
5376 try cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });5367 try cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
5377 } else {5368 } else {
5378 const result = try cg.intCmp(.fromType(cg, operand_ty), op, lhs, rhs);5369 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 {...@@ -5570,7 +5561,7 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5570 if (block.value != .none) {5561 if (block.value != .none) {
5571 const operand = try cg.resolveInst(br.operand);5562 const operand = try cg.resolveInst(br.operand);
5572 try cg.emitWValue(operand);5563 try cg.emitWValue(operand);
5573 try cg.addLocal(.local_set, block.value.local.value);5564 try cg.addLocal(.local_set, block.value.local);
5574 }5565 }
55755566
5576 // We map every block to its block index.5567 // 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...@@ -5854,24 +5845,29 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
58545845
5855 const switch_br = cg.air.unwrapSwitch(inst);5846 const switch_br = cg.air.unwrapSwitch(inst);
5856 const target_ty = cg.typeOf(switch_br.operand);5847 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
5858 assert(target_ty.hasRuntimeBits(zcu));5851 assert(target_ty.hasRuntimeBits(zcu));
58595852
5860 // swap target value with placeholder local, for dispatching5853 // swap target value with placeholder local, for dispatching
5854 var owned_target: WValue = .none;
5855 defer owned_target.free(cg);
5861 const target = if (is_dispatch_loop) target: {5856 const target = if (is_dispatch_loop) target: {
5862 const initial_target = try cg.resolveInst(switch_br.operand);5857 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);
5864 try cg.emitWValue(initial_target);5859 try cg.emitWValue(initial_target);
5865 try cg.addLocal(.local_set, target.local.value);5860 try cg.addLocal(.local_set, owned_target.local);
58665861
5867 try cg.startBlock(.loop, .empty); // dispatch loop start5862 try cg.startBlock(.loop, .empty); // dispatch loop start
5868 try cg.blocks.putNoClobber(cg.gpa, inst, .{5863 try cg.blocks.putNoClobber(cg.gpa, inst, .{
5869 .label = cg.block_depth,5864 .label = cg.block_depth,
5870 .value = target,5865 .value = owned_target,
5871 });5866 });
58725867
5873 break :target target;5868 break :target owned_target;
5874 } else try cg.resolveInst(switch_br.operand);5869 } else try cg.resolveInst(switch_br.operand);
5870 defer if (is_dispatch_loop) assert(cg.blocks.swapRemove(inst));
58755871
5876 const has_else_body = switch_br.else_body_len != 0;5872 const has_else_body = switch_br.else_body_len != 0;
5877 const branch_count = switch_br.cases_len + 1; // if else branch is missing, we trap when failing all conditions5873 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...@@ -5883,12 +5879,14 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
5883 var it = switch_br.iterateCases();5879 var it = switch_br.iterateCases();
5884 const else_body = it.elseBody();5880 const else_body = it.elseBody();
58855881
5886 cg.branches.appendAssumeCapacity(.{});5882 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand);
5887 defer {5883
5888 var else_branch = cg.branches.pop().?;5884 {
5889 else_branch.deinit(cg.gpa);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);
5890 }5889 }
5891 try cg.genBody(else_body);
58925890
5893 if (is_dispatch_loop) {5891 if (is_dispatch_loop) {
5894 try cg.endBlock(); // dispatch loop end5892 try cg.endBlock(); // dispatch loop end
...@@ -6012,16 +6010,18 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner...@@ -6012,16 +6010,18 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
6012 try cg.addLabel(.br, branch_count - 1);6010 try cg.addLabel(.br, branch_count - 1);
6013 }6011 }
60146012
6013 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand);
6014
6015 var cases_it = switch_br.iterateCases();6015 var cases_it = switch_br.iterateCases();
6016 while (cases_it.next()) |case| {6016 while (cases_it.next()) |case| {
6017 try cg.endBlock();6017 try cg.endBlock();
60186018
6019 cg.branches.appendAssumeCapacity(.{});6019 {
6020 defer {6020 try cg.pushBranch();
6021 var case_branch = cg.branches.pop().?;6021 defer cg.popBranch();
6022 case_branch.deinit(cg.gpa);6022 for (branch_liveness.deaths[case.idx]) |death| cg.processDeath(death.toRef());
6023 try cg.genBody(case.body);
6023 }6024 }
6024 try cg.genBody(case.body);
60256025
6026 try cg.addLabel(.br, branch_count - case.idx - 1); // matching case found and executed => exit switch6026 try cg.addLabel(.br, branch_count - case.idx - 1); // matching case found and executed => exit switch
6027 }6027 }
...@@ -6030,12 +6030,12 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner...@@ -6030,12 +6030,12 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
6030 if (has_else_body) {6030 if (has_else_body) {
6031 const else_body = cases_it.elseBody();6031 const else_body = cases_it.elseBody();
60326032
6033 cg.branches.appendAssumeCapacity(.{});6033 {
6034 defer {6034 try cg.pushBranch();
6035 var else_branch = cg.branches.pop().?;6035 defer cg.popBranch();
6036 else_branch.deinit(cg.gpa);6036 for (branch_liveness.deaths[branch_liveness.deaths.len - 1]) |death| cg.processDeath(death.toRef());
6037 try cg.genBody(else_body);
6037 }6038 }
6038 try cg.genBody(else_body);
6039 } else {6039 } else {
6040 try cg.addTag(.@"unreachable");6040 try cg.addTag(.@"unreachable");
6041 }6041 }
...@@ -6055,7 +6055,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6055,7 +6055,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60556055
6056 const operand = try cg.resolveInst(br.operand);6056 const operand = try cg.resolveInst(br.operand);
6057 try cg.emitWValue(operand);6057 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
6060 const idx: u32 = cg.block_depth - switch_loop.label;6060 const idx: u32 = cg.block_depth - switch_loop.label;
6061 try cg.addLabel(.br, idx);6061 try cg.addLabel(.br, idx);
...@@ -6630,7 +6630,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)...@@ -6630,7 +6630,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
6630 try cg.addTag(.i64_mul);6630 try cg.addTag(.i64_mul);
6631 },6631 },
6632 }6632 }
6633 try cg.addLocal(.local_set, new_len.local.value);6633 try cg.addLocal(.local_set, new_len.local);
6634 break :blk new_len;6634 break :blk new_len;
6635 } else len,6635 } else len,
6636 };6636 };
...@@ -6640,7 +6640,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)...@@ -6640,7 +6640,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
6640 var new_ptr = try cg.allocLocal(Type.usize);6640 var new_ptr = try cg.allocLocal(Type.usize);
6641 defer new_ptr.free(cg);6641 defer new_ptr.free(cg);
6642 try cg.emitWValue(ptr);6642 try cg.emitWValue(ptr);
6643 try cg.addLocal(.local_set, new_ptr.local.value);6643 try cg.addLocal(.local_set, new_ptr.local);
66446644
6645 // get the loop conditional: if current pointer address equals final pointer's address6645 // get the loop conditional: if current pointer address equals final pointer's address
6646 try cg.emitWValue(ptr);6646 try cg.emitWValue(ptr);
...@@ -6649,7 +6649,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)...@@ -6649,7 +6649,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
6649 .wasm32 => try cg.addTag(.i32_add),6649 .wasm32 => try cg.addTag(.i32_add),
6650 .wasm64 => try cg.addTag(.i64_add),6650 .wasm64 => try cg.addTag(.i64_add),
6651 }6651 }
6652 try cg.addLocal(.local_set, end_ptr.local.value);6652 try cg.addLocal(.local_set, end_ptr.local);
66536653
6654 // outer block to jump to when loop is done6654 // outer block to jump to when loop is done
6655 try cg.startBlock(.block, .empty);6655 try cg.startBlock(.block, .empty);
...@@ -6679,7 +6679,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)...@@ -6679,7 +6679,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
6679 try cg.addTag(.i64_add);6679 try cg.addTag(.i64_add);
6680 },6680 },
6681 }6681 }
6682 try cg.addLocal(.local_set, new_ptr.local.value);6682 try cg.addLocal(.local_set, new_ptr.local);
66836683
6684 // end of loop6684 // end of loop
6685 try cg.addLabel(.br, 0); // jump to start of loop6685 try cg.addLabel(.br, 0); // jump to start of loop
...@@ -7186,7 +7186,7 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7186,7 +7186,7 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7186 try cg.emitWValue(slice_len);7186 try cg.emitWValue(slice_len);
7187 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });7187 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
7188 try cg.addTag(.i32_mul);7188 try cg.addTag(.i32_mul);
7189 try cg.addLocal(.local_set, slice_len.local.value);7189 try cg.addLocal(.local_set, slice_len.local);
7190 }7190 }
7191 break :blk slice_len;7191 break :blk slice_len;
7192 },7192 },
...@@ -7217,7 +7217,7 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7217,7 +7217,7 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7217 try cg.emitWValue(slice_len);7217 try cg.emitWValue(slice_len);
7218 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });7218 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
7219 try cg.addTag(.i32_mul);7219 try cg.addTag(.i32_mul);
7220 try cg.addLocal(.local_set, slice_len.local.value);7220 try cg.addLocal(.local_set, slice_len.local);
7221 }7221 }
7222 break :blk slice_len;7222 break :blk slice_len;
7223 },7223 },
...@@ -7303,8 +7303,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7303,8 +7303,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7303 const body = unwrapped_try.else_body;7303 const body = unwrapped_try.else_body;
7304 const err_union = try cg.resolveInst(unwrapped_try.error_union);7304 const err_union = try cg.resolveInst(unwrapped_try.error_union);
7305 const err_union_ty = cg.typeOf(unwrapped_try.error_union);7305 const err_union_ty = cg.typeOf(unwrapped_try.error_union);
7306 const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false);7306 const result = try lowerTry(cg, inst, unwrapped_try.error_union, err_union, body, err_union_ty, false);
7307 return cg.finishAir(inst, result, &.{unwrapped_try.error_union});7307 return cg.finishAirWithBranchDeaths(
7308 inst,
7309 result,
7310 &.{unwrapped_try.error_union},
7311 cg.liveness.getCondBr(inst).then_deaths,
7312 );
7308}7313}
73097314
7310fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {7315fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -7313,20 +7318,26 @@ fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7313,20 +7318,26 @@ fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7313 const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr);7318 const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr);
7314 const body = unwrapped_try.else_body;7319 const body = unwrapped_try.else_body;
7315 const err_union_ty = cg.typeOf(unwrapped_try.error_union_ptr).childType(zcu);7320 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);7321 const result = try lowerTry(cg, inst, unwrapped_try.error_union_ptr, err_union_ptr, body, err_union_ty, true);
7317 return cg.finishAir(inst, result, &.{unwrapped_try.error_union_ptr});7322 return cg.finishAirWithBranchDeaths(
7323 inst,
7324 result,
7325 &.{unwrapped_try.error_union_ptr},
7326 cg.liveness.getCondBr(inst).then_deaths,
7327 );
7318}7328}
73197329
7320fn lowerTry(7330fn lowerTry(
7321 cg: *CodeGen,7331 cg: *CodeGen,
7322 inst: Air.Inst.Index,7332 inst: Air.Inst.Index,
7333 operand_ref: Air.Inst.Ref,
7323 err_union: WValue,7334 err_union: WValue,
7324 body: []const Air.Inst.Index,7335 body: []const Air.Inst.Index,
7325 err_union_ty: Type,7336 err_union_ty: Type,
7326 operand_is_ptr: bool,7337 operand_is_ptr: bool,
7327) InnerError!WValue {7338) InnerError!WValue {
7328 _ = inst;
7329 const zcu = cg.pt.zcu;7339 const zcu = cg.pt.zcu;
7340 const branch_liveness = cg.liveness.getCondBr(inst);
73307341
7331 const pl_ty = err_union_ty.errorUnionPayload(zcu);7342 const pl_ty = err_union_ty.errorUnionPayload(zcu);
7332 const pl_has_bits = pl_ty.hasRuntimeBits(zcu);7343 const pl_has_bits = pl_ty.hasRuntimeBits(zcu);
...@@ -7349,12 +7360,13 @@ fn lowerTry(...@@ -7349,12 +7360,13 @@ fn lowerTry(
7349 try cg.addTag(.i32_eqz);7360 try cg.addTag(.i32_eqz);
7350 try cg.addLabel(.br_if, 0); // jump out of block when error is '0'7361 try cg.addLabel(.br_if, 0); // jump out of block when error is '0'
73517362
7352 try cg.branches.append(cg.gpa, .{});7363 {
7353 defer {7364 try cg.pushBranch();
7354 var branch = cg.branches.pop().?;7365 defer cg.popBranch();
7355 branch.deinit(cg.gpa);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);
7356 }7369 }
7357 try cg.genBody(body);
7358 try cg.endBlock();7370 try cg.endBlock();
7359 }7371 }
73607372
...@@ -7367,8 +7379,7 @@ fn lowerTry(...@@ -7367,8 +7379,7 @@ fn lowerTry(
7367 if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {7379 if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {
7368 return cg.offsetPointer(err_union, pl_offset);7380 return cg.offsetPointer(err_union, pl_offset);
7369 }7381 }
7370 const payload = try cg.load(err_union, pl_ty, .{ .offset = pl_offset });7382 return cg.load(err_union, pl_ty, .{ .offset = pl_offset });
7371 return payload.toLocal(cg, pl_ty);
7372}7383}
73737384
7374/// Calls a compiler-rt intrinsic by creating an undefined symbol,7385/// Calls a compiler-rt intrinsic by creating an undefined symbol,
...@@ -7512,13 +7523,13 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7512,13 +7523,13 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7512 // 'false' branch (i.e. error set does not have value7523 // 'false' branch (i.e. error set does not have value
7513 // ensure we set local to 0 in case the local was re-used.7524 // ensure we set local to 0 in case the local was re-used.
7514 try cg.addImm32(0);7525 try cg.addImm32(0);
7515 try cg.addLocal(.local_set, result.local.value);7526 try cg.addLocal(.local_set, result.local);
7516 try cg.addLabel(.br, 1);7527 try cg.addLabel(.br, 1);
7517 try cg.endBlock();7528 try cg.endBlock();
75187529
7519 // 'true' branch7530 // 'true' branch
7520 try cg.addImm32(1);7531 try cg.addImm32(1);
7521 try cg.addLocal(.local_set, result.local.value);7532 try cg.addLocal(.local_set, result.local);
7522 try cg.addLabel(.br, 0);7533 try cg.addLabel(.br, 0);
7523 try cg.endBlock();7534 try cg.endBlock();
75247535
...@@ -7561,9 +7572,9 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7561,9 +7572,9 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7561 .offset = ptr_operand.memOffset(),7572 .offset = ptr_operand.memOffset(),
7562 .alignment = ty.abiAlignment(zcu).toStdMem(),7573 .alignment = ty.abiAlignment(zcu).toStdMem(),
7563 });7574 });
7564 try cg.addLocal(.local_tee, val_local.local.value);7575 try cg.addLocal(.local_tee, val_local.local);
7565 _ = try cg.intCmp(int_ty, .eq, .stack, expected_val);7576 _ = 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);
7567 break :val val_local;7578 break :val val_local;
7568 } else val: {7579 } else val: {
7569 if (ty.abiSize(zcu) > 8) {7580 if (ty.abiSize(zcu) > 8) {
...@@ -7575,7 +7586,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7575,7 +7586,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7575 try cg.emitWValue(new_val);7586 try cg.emitWValue(new_val);
7576 try cg.emitWValue(ptr_val);7587 try cg.emitWValue(ptr_val);
7577 _ = try cg.intCmp(int_ty, .eq, ptr_val, expected_val);7588 _ = 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);
7579 try cg.addTag(.select);7590 try cg.addTag(.select);
7580 try cg.store(.stack, .stack, ty, .{ .offset = ptr_operand.memOffset() });7591 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 {...@@ -7685,11 +7696,11 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7685 },7696 },
7686 );7697 );
7687 const select_res = try cg.allocLocal(ty);7698 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);
7689 _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if7700 _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if
76907701
7691 try cg.emitWValue(select_res);7702 try cg.emitWValue(select_res);
7692 try cg.addLocal(.local_set, value.local.value);7703 try cg.addLocal(.local_set, value.local);
76937704
7694 try cg.addLabel(.br_if, 0);7705 try cg.addLabel(.br_if, 0);
7695 try cg.endBlock();7706 try cg.endBlock();
...@@ -7864,9 +7875,6 @@ fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7864,9 +7875,6 @@ fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7864}7875}
78657876
7866fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {7877fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7867 if (cg.initial_stack_value == .none) {
7868 try cg.initializeStack();
7869 }
7870 try cg.emitWValue(cg.bottom_stack_value);7878 try cg.emitWValue(cg.bottom_stack_value);
7871 return cg.finishAir(inst, .stack, &.{});7879 return cg.finishAir(inst, .stack, &.{});
7872}7880}
...@@ -7912,7 +7920,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7912,7 +7920,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7912 return cg.fail("Self-hosted wasm backend requires output constraint to be equal \"=r\"", .{});7920 return cg.fail("Self-hosted wasm backend requires output constraint to be equal \"=r\"", .{});
7913 }7921 }
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);
7916 assert(!gop.found_existing); // first value7924 assert(!gop.found_existing); // first value
79177925
7918 assert(it.next() == null);7926 assert(it.next() == null);
...@@ -7933,7 +7941,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7933,7 +7941,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7933 try cg.emitWValue(operand);7941 try cg.emitWValue(operand);
7934 const op_local = try WValue.toLocal(.stack, cg, cg.typeOf(input.operand));7942 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);
7937 if (gop.found_existing) {7945 if (gop.found_existing) {
7938 return cg.fail("Duplicate asm variable name \"{s}\"", .{name});7946 return cg.fail("Duplicate asm variable name \"{s}\"", .{name});
7939 }7947 }