authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-09-15 19:32:39+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-09-17 00:04:51+02:00
log65b732cd6118eaac95929c08f262514e58a4e3b0
treee8231e5551ea5e0e83959879631875e343ec8e5f
parent503c59263e358ae8ca16b271fc35799a508d03bb

wasm: local reuse and free verification + better blocks codegen


1 files changed, 151 insertions(+), 75 deletions(-)

src/codegen/wasm/CodeGen.zig+151-75
......@@ -473,11 +473,20 @@ fn finishAirWithBranchDeaths(
473473fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void {
474474 // results of `none` can never be referenced.
475475 if (result != .none) {
476 const trackable_result = if (result != .stack)
477 result
478 else
479 try result.toLocal(cg, cg.typeOfIndex(inst));
480 try cg.values.putNoClobber(cg.gpa, inst.toRef(), trackable_result);
476 if (cg.liveness.isUnused(inst)) {
477 if (result == .stack) {
478 try cg.addTag(.drop);
479 } else {
480 var unused_result = result;
481 unused_result.free(cg);
482 }
483 } else {
484 const trackable_result = if (result != .stack)
485 result
486 else
487 try result.toLocal(cg, cg.typeOfIndex(inst));
488 try cg.values.putNoClobber(cg.gpa, inst.toRef(), trackable_result);
489 }
481490 }
482491
483492 if (std.debug.runtime_safety) {
......@@ -510,6 +519,27 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
510519 cg.releaseLocal(value.local);
511520}
512521
522fn verifyLocalPool(cg: *CodeGen) void {
523 if (true) return; // TODO: test more
524 if (!std.debug.runtime_safety) return;
525
526 var free_count: usize = 0;
527 for ([_]std.wasm.Valtype{ .i32, .i64, .f32, .f64, .v128 }) |valtype| {
528 var next = cg.local_pool.freeList(valtype).*;
529 while (next.unwrap()) |index| {
530 assert(index >= cg.wasm_param_count);
531 const pool_index = index - cg.wasm_param_count;
532 assert(pool_index < cg.local_pool.locals.items.len);
533 assert(index != cg.initial_stack_value.local);
534 assert(index != cg.bottom_stack_value.local);
535 assert(cg.mir_locals.items[pool_index] == valtype);
536 free_count += 1;
537 next = cg.local_pool.locals.items[pool_index].next_free;
538 }
539 }
540 assert(free_count + 2 == cg.local_pool.locals.items.len);
541}
542
513543pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
514544 try cg.mir_instructions.append(cg.gpa, inst);
515545}
......@@ -656,10 +686,8 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w
656686/// Differently from `typeToValtype` this also allows `void` to create a block
657687/// with no return type
658688fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.BlockType {
659 return switch (ty.ip_index) {
660 .void_type, .noreturn_type => .empty,
661 else => .fromValtype(typeToValtype(ty, zcu, target)),
662 };
689 if (!ty.hasRuntimeBits(zcu)) return .empty;
690 return .fromValtype(typeToValtype(ty, zcu, target));
663691}
664692
665693/// Returns `ptr` with `offset` applied.
......@@ -1339,7 +1367,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13391367
13401368 try cg.emitWValue(vec_ptr);
13411369 try cg.emitScaledPointerOffset(elem_idx, elem_size, .add);
1342 const ptr = try WValue.toLocal(.stack, cg, Type.usize);
1370 var ptr = try WValue.toLocal(.stack, cg, Type.usize);
1371 defer ptr.free(cg);
13431372
13441373 try cg.store(ptr, elem_val, elem_ty, .{});
13451374
......@@ -1975,7 +2004,8 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19752004 try cg.restoreStackPointer();
19762005 try cg.addTag(.@"return");
19772006
1978 return cg.finishAir(inst, .none, &.{un_op});
2007 try cg.finishAir(inst, .none, &.{un_op});
2008 cg.verifyLocalPool();
19792009}
19802010
19812011fn airRetPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -2026,7 +2056,8 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20262056
20272057 try cg.restoreStackPointer();
20282058 try cg.addTag(.@"return");
2029 return cg.finishAir(inst, .none, &.{un_op});
2059 try cg.finishAir(inst, .none, &.{un_op});
2060 cg.verifyLocalPool();
20302061}
20312062
20322063fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) InnerError!void {
......@@ -2156,7 +2187,8 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21562187 try cg.addLocal(.local_set, result_local.local);
21572188 break :result_value result_local;
21582189 } else {
2159 const result_local = try cg.allocLocal(scalar_type);
2190 var result_local = try cg.allocLocal(scalar_type);
2191 defer result_local.free(cg);
21602192 try cg.addLocal(.local_set, result_local.local);
21612193 const result = try cg.allocStack(ret_ty);
21622194 try cg.store(result, result_local, scalar_type, .{});
......@@ -2166,7 +2198,8 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21662198 .double_i64, .indirect => unreachable,
21672199 .unrolled => |vector| {
21682200 assert(vector.len == 1);
2169 const result_local = try cg.allocLocal(vector.elem_type);
2201 var result_local = try cg.allocLocal(vector.elem_type);
2202 defer result_local.free(cg);
21702203 // save call result from operand stack
21712204 try cg.addLocal(.local_set, result_local.local);
21722205 const result = try cg.allocStack(ret_ty);
......@@ -2223,7 +2256,8 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22232256 const abi_size: u32 = @intCast(load_ty.abiSize(zcu));
22242257 const abi_align: u32 = @intCast(load_ty.abiAlignment(zcu).toByteUnits().?);
22252258
2226 const arg_ptr = try cg.allocLocal(.usize);
2259 var arg_ptr = try cg.allocLocal(.usize);
2260 defer arg_ptr.free(cg);
22272261 _ = try cg.load(operand, .usize, .{});
22282262
22292263 if (abi_align > 1) {
......@@ -3027,12 +3061,14 @@ fn intAnd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
30273061
30283062 const lhs_lsb = try cg.load(lhs, .u64, .{});
30293063 const rhs_lsb = try cg.load(rhs, .u64, .{});
3030 const and_lsb = try (try cg.intAnd(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3064 var and_lsb = try (try cg.intAnd(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3065 defer and_lsb.free(cg);
30313066 try cg.store(result, and_lsb, .u64, .{});
30323067
30333068 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
30343069 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
3035 const and_msb = try (try cg.intAnd(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3070 var and_msb = try (try cg.intAnd(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3071 defer and_msb.free(cg);
30363072 try cg.store(result, and_msb, .u64, .{ .offset = 8 });
30373073
30383074 return result;
......@@ -3071,12 +3107,14 @@ fn intOr(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
30713107
30723108 const lhs_lsb = try cg.load(lhs, .u64, .{});
30733109 const rhs_lsb = try cg.load(rhs, .u64, .{});
3074 const or_lsb = try (try cg.intOr(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3110 var or_lsb = try (try cg.intOr(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3111 defer or_lsb.free(cg);
30753112 try cg.store(result, or_lsb, .u64, .{});
30763113
30773114 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
30783115 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
3079 const or_msb = try (try cg.intOr(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3116 var or_msb = try (try cg.intOr(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3117 defer or_msb.free(cg);
30803118 try cg.store(result, or_msb, .u64, .{ .offset = 8 });
30813119
30823120 return result;
......@@ -3115,12 +3153,14 @@ fn intXor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
31153153
31163154 const lhs_lsb = try cg.load(lhs, .u64, .{});
31173155 const rhs_lsb = try cg.load(rhs, .u64, .{});
3118 const xor_lsb = try (try cg.intXor(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3156 var xor_lsb = try (try cg.intXor(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3157 defer xor_lsb.free(cg);
31193158 try cg.store(result, xor_lsb, .u64, .{});
31203159
31213160 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
31223161 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
3123 const xor_msb = try (try cg.intXor(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3162 var xor_msb = try (try cg.intXor(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3163 defer xor_msb.free(cg);
31243164 try cg.store(result, xor_msb, .u64, .{ .offset = 8 });
31253165
31263166 return result;
......@@ -4103,6 +4143,19 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
41034143}
41044144
41054145fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult {
4146 if (int_ty.bits > 64 and (int_ty.bits != 128 or !int_ty.is_signed)) {
4147 const result = try cg.allocInt(int_ty);
4148
4149 try cg.emitWValue(result);
4150 try cg.emitWValue(lhs);
4151 try cg.emitWValue(rhs);
4152 try cg.addImm32(@intFromBool(int_ty.is_signed));
4153 try cg.addImm32(int_ty.bits);
4154 try cg.addCallIntrinsic(.__mulo_limb64);
4155
4156 return .{ .result = result, .ov = .stack };
4157 }
4158
41064159 const overflow_bit = try cg.allocLocal(Type.u32);
41074160 try cg.addImm32(0);
41084161 try cg.addLocal(.local_set, overflow_bit.local);
......@@ -4112,7 +4165,8 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41124165 const lhs_upcast = try cg.intCast(new_ty, int_ty, lhs);
41134166 const rhs_upcast = try cg.intCast(new_ty, int_ty, rhs);
41144167 const mul_raw = try cg.intMul(new_ty, lhs_upcast, rhs_upcast);
4115 const bin_op = try cg.toLocalInt(mul_raw, new_ty);
4168 var bin_op = try cg.toLocalInt(mul_raw, new_ty);
4169 defer bin_op.free(cg);
41164170
41174171 const res = try cg.intTrunc(int_ty, new_ty, bin_op);
41184172 const res_tmp = try cg.toLocalInt(res, int_ty);
......@@ -4126,7 +4180,8 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41264180 const lhs_upcast = try cg.intCast(new_ty, int_ty, lhs);
41274181 const rhs_upcast = try cg.intCast(new_ty, int_ty, rhs);
41284182 const mul_raw = try cg.intMul(new_ty, lhs_upcast, rhs_upcast);
4129 const bin_op = try cg.toLocalInt(mul_raw, new_ty);
4183 var bin_op = try cg.toLocalInt(mul_raw, new_ty);
4184 defer bin_op.free(cg);
41304185
41314186 const res = try cg.intTrunc(int_ty, new_ty, bin_op);
41324187 const res_tmp = try cg.toLocalInt(res, int_ty);
......@@ -4135,7 +4190,8 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41354190 _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op);
41364191 try cg.addLocal(.local_set, overflow_bit.local);
41374192 break :blk res_tmp;
4138 } else if (int_ty.bits == 128 and int_ty.is_signed) blk: {
4193 } else blk: {
4194 assert(int_ty.bits == 128 and int_ty.is_signed);
41394195 const overflow_ret = try cg.allocStack(Type.i32);
41404196 const res = try cg.callIntrinsic(
41414197 .__muloti4,
......@@ -4146,17 +4202,6 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41464202 _ = try cg.load(overflow_ret, .i32, .{});
41474203 try cg.addLocal(.local_set, overflow_bit.local);
41484204 break :blk res;
4149 } else {
4150 const result = try cg.allocInt(int_ty);
4151
4152 try cg.emitWValue(result);
4153 try cg.emitWValue(lhs);
4154 try cg.emitWValue(rhs);
4155 try cg.addImm32(@intFromBool(int_ty.is_signed));
4156 try cg.addImm32(int_ty.bits);
4157 try cg.addCallIntrinsic(.__mulo_limb64);
4158
4159 return .{ .result = result, .ov = .stack };
41604205 };
41614206
41624207 return .{ .result = result_val, .ov = .{ .local = overflow_bit.local } };
......@@ -5212,13 +5257,19 @@ fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52125257
52135258fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {
52145259 const zcu = cg.pt.zcu;
5215 // if wasm_block_ty is non-empty, we create a register to store the temporary value
5260
5261 if (block_ty.isNoReturn(zcu)) {
5262 assert(cg.liveness.getBlock(inst).deaths.len == 0);
5263 try cg.genBody(body);
5264 return cg.finishAir(inst, .none, &.{});
5265 }
5266
52165267 const block_result: WValue = if (block_ty.hasRuntimeBits(zcu))
5217 try cg.allocLocal(block_ty)
5268 .stack
52185269 else
52195270 .none;
52205271
5221 try cg.startBlock(.block, .empty);
5272 try cg.startBlock(.block, genBlockType(block_ty, zcu, cg.target));
52225273 // Here we set the current block idx, so breaks know the depth to jump
52235274 // to when breaking out.
52245275 try cg.blocks.putNoClobber(cg.gpa, inst, .{
......@@ -5266,6 +5317,7 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52665317
52675318 try cg.genBody(block.body);
52685319 try cg.endBlock();
5320 try cg.addTag(.@"unreachable");
52695321
52705322 return cg.finishAir(inst, .none, &.{});
52715323}
......@@ -5557,11 +5609,13 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55575609 const br = cg.air.instructions.items(.data)[@backingInt(inst)].br;
55585610 const block = cg.blocks.get(br.block_inst).?;
55595611
5560 // if operand has codegen bits we should break with a value
5561 if (block.value != .none) {
5562 const operand = try cg.resolveInst(br.operand);
5563 try cg.emitWValue(operand);
5564 try cg.addLocal(.local_set, block.value.local);
5612 switch (block.value) {
5613 .none => {},
5614 .stack => {
5615 const operand = try cg.resolveInst(br.operand);
5616 try cg.emitWValue(operand);
5617 },
5618 else => unreachable,
55655619 }
55665620
55675621 // We map every block to its block index.
......@@ -5584,7 +5638,8 @@ fn airRepeat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55845638
55855639fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55865640 try cg.addTag(.@"unreachable");
5587 return cg.finishAir(inst, .none, &.{});
5641 try cg.finishAir(inst, .none, &.{});
5642 cg.verifyLocalPool();
55885643}
55895644
55905645fn airBreakpoint(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5596,7 +5651,8 @@ fn airBreakpoint(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55965651
55975652fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55985653 try cg.addTag(.@"unreachable");
5599 return cg.finishAir(inst, .none, &.{});
5654 try cg.finishAir(inst, .none, &.{});
5655 cg.verifyLocalPool();
56005656}
56015657
56025658fn airNopCast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5839,6 +5895,7 @@ fn airAggFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58395895 return cg.finishAir(inst, result, &.{struct_field.struct_operand});
58405896}
58415897
5898// Both `.switch_br` and `.loop_switch_br` are noreturn.
58425899fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) InnerError!void {
58435900 const pt = cg.pt;
58445901 const zcu = pt.zcu;
......@@ -5850,9 +5907,9 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
58505907
58515908 assert(target_ty.hasRuntimeBits(zcu));
58525909
5853 // swap target value with placeholder local, for dispatching
5910 const operand_dies = cg.liveness.operandDies(inst, 0);
58545911 var owned_target: WValue = .none;
5855 defer owned_target.free(cg);
5912 errdefer owned_target.free(cg);
58565913 const target = if (is_dispatch_loop) target: {
58575914 const initial_target = try cg.resolveInst(switch_br.operand);
58585915 owned_target = try cg.allocLocal(target_ty);
......@@ -5879,7 +5936,8 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
58795936 var it = switch_br.iterateCases();
58805937 const else_body = it.elseBody();
58815938
5882 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand);
5939 if (operand_dies) cg.processDeath(switch_br.operand);
5940 owned_target.free(cg);
58835941
58845942 {
58855943 try cg.pushBranch();
......@@ -5890,6 +5948,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
58905948
58915949 if (is_dispatch_loop) {
58925950 try cg.endBlock(); // dispatch loop end
5951 try cg.addTag(.@"unreachable");
58935952 }
58945953 return cg.finishAir(inst, .none, &.{});
58955954 }
......@@ -5934,8 +5993,6 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
59345993 break :width width_bigint.toConst().toInt(u32) catch null;
59355994 };
59365995
5937 try cg.startBlock(.block, .empty); // whole switch block start
5938
59395996 for (0..branch_count) |_| {
59405997 try cg.startBlock(.block, .empty);
59415998 }
......@@ -6010,7 +6067,8 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
60106067 try cg.addLabel(.br, branch_count - 1);
60116068 }
60126069
6013 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand);
6070 if (operand_dies) cg.processDeath(switch_br.operand);
6071 owned_target.free(cg);
60146072
60156073 var cases_it = switch_br.iterateCases();
60166074 while (cases_it.next()) |case| {
......@@ -6022,8 +6080,6 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
60226080 for (branch_liveness.deaths[case.idx]) |death| cg.processDeath(death.toRef());
60236081 try cg.genBody(case.body);
60246082 }
6025
6026 try cg.addLabel(.br, branch_count - case.idx - 1); // matching case found and executed => exit switch
60276083 }
60286084
60296085 try cg.endBlock();
......@@ -6040,10 +6096,9 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
60406096 try cg.addTag(.@"unreachable");
60416097 }
60426098
6043 try cg.endBlock(); // whole switch block end
6044
60456099 if (is_dispatch_loop) {
60466100 try cg.endBlock(); // dispatch loop end
6101 try cg.addTag(.@"unreachable");
60476102 }
60486103
60496104 return cg.finishAir(inst, .none, &.{});
......@@ -6547,11 +6602,6 @@ fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
65476602 const ptr = try cg.resolveInst(bin_op.lhs);
65486603 const ptr_ty = cg.typeOf(bin_op.lhs);
65496604 const value = try cg.resolveInst(bin_op.rhs);
6550 const len = switch (ptr_ty.ptrSize(zcu)) {
6551 .slice => try cg.sliceLen(ptr),
6552 .one => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }),
6553 .c, .many => unreachable,
6554 };
65556605
65566606 const elem_ty = if (ptr_ty.ptrSize(zcu) == .one)
65576607 ptr_ty.childType(zcu).childType(zcu)
......@@ -6562,7 +6612,14 @@ fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
65626612 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
65636613 }
65646614
6565 const dst_ptr = try cg.sliceOrArrayPtr(ptr, ptr_ty);
6615 var dst_ptr = try cg.sliceOrArrayPtr(ptr, ptr_ty);
6616 defer if (ptr_ty.isSlice(zcu)) dst_ptr.free(cg);
6617 var len = switch (ptr_ty.ptrSize(zcu)) {
6618 .slice => try cg.sliceLen(ptr),
6619 .one => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }),
6620 .c, .many => unreachable,
6621 };
6622 defer len.free(cg);
65666623 try cg.memset(elem_ty, dst_ptr, len, value);
65676624
65686625 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
......@@ -6614,11 +6671,13 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66146671 return;
66156672 }
66166673
6674 var owned_final_len: WValue = .none;
6675 defer owned_final_len.free(cg);
66176676 const final_len: WValue = switch (len) {
66186677 .imm32 => |val| .{ .imm32 = val * abi_size },
66196678 .imm64 => |val| .{ .imm64 = val * abi_size },
66206679 else => if (abi_size != 1) blk: {
6621 const new_len = try cg.ensureAllocLocal(Type.usize);
6680 owned_final_len = try cg.allocLocal(Type.usize);
66226681 try cg.emitWValue(len);
66236682 switch (cg.ptr_size) {
66246683 .wasm32 => {
......@@ -6630,8 +6689,8 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66306689 try cg.addTag(.i64_mul);
66316690 },
66326691 }
6633 try cg.addLocal(.local_set, new_len.local);
6634 break :blk new_len;
6692 try cg.addLocal(.local_set, owned_final_len.local);
6693 break :blk owned_final_len;
66356694 } else len,
66366695 };
66376696
......@@ -7179,7 +7238,7 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71797238 const ptr_elem_ty = dst_ty.childType(zcu);
71807239 const src = try cg.resolveInst(bin_op.rhs);
71817240 const src_ty = cg.typeOf(bin_op.rhs);
7182 const len = switch (dst_ty.ptrSize(zcu)) {
7241 var len = switch (dst_ty.ptrSize(zcu)) {
71837242 .slice => blk: {
71847243 const slice_len = try cg.sliceLen(dst);
71857244 if (ptr_elem_ty.abiSize(zcu) != 1) {
......@@ -7195,8 +7254,11 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71957254 }),
71967255 .c, .many => unreachable,
71977256 };
7198 const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
7199 const src_ptr = try cg.sliceOrArrayPtr(src, src_ty);
7257 defer len.free(cg);
7258 var dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
7259 defer if (dst_ty.isSlice(zcu)) dst_ptr.free(cg);
7260 var src_ptr = try cg.sliceOrArrayPtr(src, src_ty);
7261 defer if (src_ty.isSlice(zcu)) src_ptr.free(cg);
72007262 try cg.memcpy(dst_ptr, src_ptr, len);
72017263
72027264 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
......@@ -7210,7 +7272,7 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72107272 const ptr_elem_ty = dst_ty.childType(zcu);
72117273 const src = try cg.resolveInst(bin_op.rhs);
72127274 const src_ty = cg.typeOf(bin_op.rhs);
7213 const len = switch (dst_ty.ptrSize(zcu)) {
7275 var len = switch (dst_ty.ptrSize(zcu)) {
72147276 .slice => blk: {
72157277 const slice_len = try cg.sliceLen(dst);
72167278 if (ptr_elem_ty.abiSize(zcu) != 1) {
......@@ -7226,8 +7288,11 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72267288 }),
72277289 .c, .many => unreachable,
72287290 };
7229 const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
7230 const src_ptr = try cg.sliceOrArrayPtr(src, src_ty);
7291 defer len.free(cg);
7292 var dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
7293 defer if (dst_ty.isSlice(zcu)) dst_ptr.free(cg);
7294 var src_ptr = try cg.sliceOrArrayPtr(src, src_ty);
7295 defer if (src_ty.isSlice(zcu)) src_ptr.free(cg);
72317296 try cg.memmove(dst_ptr, src_ptr, len);
72327297
72337298 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
......@@ -7295,7 +7360,8 @@ fn airDbgVar(
72957360) InnerError!void {
72967361 _ = is_ptr;
72977362 _ = local_tag;
7298 return cg.finishAir(inst, .none, &.{});
7363 const pl_op = cg.air.instructions.items(.data)[@backingInt(inst)].pl_op;
7364 return cg.finishAir(inst, .none, &.{pl_op.operand});
72997365}
73007366
73017367fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -7555,9 +7621,10 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
75557621 const expected_val = try cg.resolveInst(extra.expected_value);
75567622 const new_val = try cg.resolveInst(extra.new_value);
75577623
7558 const cmp_result = try cg.allocLocal(Type.bool);
7624 var cmp_result = try cg.allocLocal(Type.bool);
7625 defer cmp_result.free(cg);
75597626
7560 const ptr_val = if (cg.useAtomicFeature()) val: {
7627 var ptr_val = if (cg.useAtomicFeature()) val: {
75617628 const val_local = try cg.allocLocal(ty);
75627629 try cg.emitMemBase(ptr_operand);
75637630 try cg.emitWValue(expected_val);
......@@ -7599,7 +7666,8 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
75997666 try cg.addTag(.i32_xor);
76007667 try cg.addImm32(1);
76017668 try cg.addTag(.i32_and);
7602 const and_result = try WValue.toLocal(.stack, cg, Type.bool);
7669 var and_result = try WValue.toLocal(.stack, cg, Type.bool);
7670 defer and_result.free(cg);
76037671 const result_ptr = try cg.allocStack(result_ty);
76047672 try cg.store(result_ptr, and_result, .bool, .{ .offset = ty.abiSize(zcu) });
76057673 try cg.store(result_ptr, ptr_val, ty, .{});
......@@ -7611,6 +7679,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76117679 try cg.addTag(.select);
76127680 break :val .stack;
76137681 };
7682 defer ptr_val.free(cg);
76147683
76157684 return cg.finishAir(inst, result, &.{ extra.ptr, extra.expected_value, extra.new_value });
76167685}
......@@ -7695,7 +7764,8 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76957764 .alignment = ty.abiAlignment(zcu).toStdMem(),
76967765 },
76977766 );
7698 const select_res = try cg.allocLocal(ty);
7767 var select_res = try cg.allocLocal(ty);
7768 defer select_res.free(cg);
76997769 try cg.addLocal(.local_tee, select_res.local);
77007770 _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if
77017771
......@@ -7908,6 +7978,11 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
79087978 if (unwrapped_asm.source.len != 0) {
79097979 var local_map: assembly.LocalMap = .empty;
79107980 defer local_map.deinit(cg.gpa);
7981 var input_locals: std.ArrayList(WValue) = try .initCapacity(cg.gpa, inputs.len);
7982 defer {
7983 for (input_locals.items) |*input_local| input_local.free(cg);
7984 input_locals.deinit(cg.gpa);
7985 }
79117986
79127987 {
79137988 var it = unwrapped_asm.iterateOutputs();
......@@ -7940,6 +8015,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
79408015
79418016 try cg.emitWValue(operand);
79428017 const op_local = try WValue.toLocal(.stack, cg, cg.typeOf(input.operand));
8018 input_locals.appendAssumeCapacity(op_local);
79438019
79448020 const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local);
79458021 if (gop.found_existing) {