authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-17 00:09:47+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-09-17 00:09:47+02:00
logd8147ba476217797d600b7e5f46f0a67a5b96188
tree3b72c66376a962ddf75ff319fecd8489633e8fb2
parent39512c3e5547b411294369c8eec8093045f74d64
parent5119f0a70de2565f22e1edc5d43d49b655c6f7a7

Merge pull request 'wasm: codegen improvements + better liveness handling' (#36846) from pavelverigo/zig:wasm-codegen-imporvements into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36846

4 files changed, 1080 insertions(+), 1036 deletions(-)

src/codegen/wasm/CodeGen.zig+1057-1020
......@@ -136,13 +136,10 @@ air: Air,
136136liveness: Air.Liveness,
137137gpa: mem.Allocator,
138138func_index: InternPool.Index,
139/// Contains a list of current branches.
140/// When we return from a branch, the branch will be popped from this list,
141/// which means branches can only contain references from within its own branch,
142/// or a branch higher (lower index) in the tree.
143branches: std.ArrayList(Branch) = .empty,
139/// Contains a list of current branches. Stores LocalPool before entering branch.
140branches: std.ArrayList(LocalPool) = .empty,
144141/// Table to save `WValue`'s generated by an `Air.Inst`
145// values: ValueTable,
142values: ValueTable = .empty,
146143/// Mapping from Air.Inst.Index to block ids
147144blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {
148145 label: u32,
......@@ -150,10 +147,6 @@ blocks: std.array_hash_map.Auto(Air.Inst.Index, struct {
150147}) = .{},
151148/// Maps `loop` instructions to their label. `br` to here repeats the loop.
152149loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty,
153/// The index the next local generated will have
154/// NOTE: arguments share the index with locals therefore the first variable
155/// will have the index that comes after the last argument's index
156local_index: u32,
157150/// The index of the current argument.
158151/// Used to track which argument is being referenced in `airArg`.
159152arg_index: u32 = 0,
......@@ -207,23 +200,8 @@ stack_size: u32 = 0,
207200/// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default.
208201stack_alignment: Alignment = .@"16",
209202
210// For each individual Wasm valtype we store a seperate free list which
211// allows us to re-use locals that are no longer used. e.g. a temporary local.
212/// A list of indexes which represents a local of valtype `i32`.
213/// It is illegal to store a non-i32 valtype in this list.
214free_locals_i32: std.ArrayList(u32) = .empty,
215/// A list of indexes which represents a local of valtype `i64`.
216/// It is illegal to store a non-i64 valtype in this list.
217free_locals_i64: std.ArrayList(u32) = .empty,
218/// A list of indexes which represents a local of valtype `f32`.
219/// It is illegal to store a non-f32 valtype in this list.
220free_locals_f32: std.ArrayList(u32) = .empty,
221/// A list of indexes which represents a local of valtype `f64`.
222/// It is illegal to store a non-f64 valtype in this list.
223free_locals_f64: std.ArrayList(u32) = .empty,
224/// A list of indexes which represents a local of valtype `v127`.
225/// It is illegal to store a non-v128 valtype in this list.
226free_locals_v128: std.ArrayList(u32) = .empty,
203local_pool: LocalPool = .{},
204wasm_param_count: u32 = undefined,
227205
228206/// When in debug mode, this tracks if no `finishAir` was missed.
229207/// Forgetting to call `finishAir` will cause the result to not be
......@@ -239,13 +217,7 @@ const WValue = union(enum) {
239217 none: void,
240218 /// The value lives on top of the stack
241219 stack: void,
242 /// Index of the local
243 local: struct {
244 /// Contains the index to the local
245 value: u32,
246 /// The amount of instructions referencing this `WValue`
247 references: u32,
248 },
220 local: u32, // index
249221 /// An immediate 32bit value
250222 imm32: u32,
251223 /// An immediate 64bit value
......@@ -271,23 +243,16 @@ const WValue = union(enum) {
271243 /// pointing to where the value lives.
272244 stack_offset: struct {
273245 /// Contains the actual value of the offset
274 value: u32,
275 /// The amount of instructions referencing this `WValue`
276 references: u32,
246 value: u64,
277247 },
278248
279 /// Returns the offset from the bottom of the stack. This is useful when
280 /// we use the load or store instruction to ensure we retrieve the value
281 /// from the correct position, rather than the value that lives at the
282 /// bottom of the stack. For instances where `WValue` is not `stack_value`
283 /// this will return 0, which allows us to simply call this function for all
284 /// loads and stores without requiring checks everywhere.
285 fn offset(value: WValue) u32 {
286 switch (value) {
287 .stack_offset => |stack_offset| return stack_offset.value,
249 /// Returns the offset from the bottom of the stack.
250 fn memOffset(value: WValue) u64 {
251 return switch (value) {
252 .stack_offset => |stack_offset| stack_offset.value,
288253 .dead => unreachable,
289 else => return 0,
290 }
254 else => 0,
255 };
291256 }
292257
293258 /// Promotes a `WValue` to a local when given value is on top of the stack.
......@@ -297,7 +262,7 @@ const WValue = union(enum) {
297262 switch (value) {
298263 .stack => {
299264 const new_local = try gen.allocLocal(ty);
300 try gen.addLocal(.local_set, new_local.local.value);
265 try gen.addLocal(.local_set, new_local.local);
301266 return new_local;
302267 },
303268 else => return value,
......@@ -306,23 +271,9 @@ const WValue = union(enum) {
306271
307272 /// Marks a local as no longer being referenced and essentially allows
308273 /// us to re-use it somewhere else within the function.
309 /// The valtype of the local is deducted by using the index of the given `WValue`.
310 fn free(value: *WValue, gen: *CodeGen) void {
274 fn free(value: *WValue, cg: *CodeGen) void {
311275 if (value.* != .local) return;
312 const local_value = value.local.value;
313 const reserved = gen.args.len + @intFromBool(gen.return_value != .none) + @intFromBool(gen.varargs != .none);
314 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
315
316 const index = local_value - reserved;
317 const valtype = gen.mir_locals.items[index];
318 switch (valtype) {
319 .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
320 .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return,
321 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
322 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
323 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,
324 }
325 log.debug("freed local ({d}) of type {}", .{ local_value, valtype });
276 cg.releaseLocal(value.local);
326277 value.* = .dead;
327278 }
328279};
......@@ -330,6 +281,77 @@ const WValue = union(enum) {
330281/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
331282const ValueTable = std.array_hash_map.Auto(Air.Inst.Ref, WValue);
332283
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
333355const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
334356
335357const InnerError = Error || error{
......@@ -343,14 +365,11 @@ pub fn deinit(cg: *CodeGen) void {
343365 const gpa = cg.gpa;
344366 for (cg.branches.items) |*branch| branch.deinit(gpa);
345367 cg.branches.deinit(gpa);
368 cg.values.deinit(gpa);
346369 cg.blocks.deinit(gpa);
347370 cg.loops.deinit(gpa);
348371 cg.simd_immediates.deinit(gpa);
349 cg.free_locals_i32.deinit(gpa);
350 cg.free_locals_i64.deinit(gpa);
351 cg.free_locals_f32.deinit(gpa);
352 cg.free_locals_f64.deinit(gpa);
353 cg.free_locals_v128.deinit(gpa);
372 cg.local_pool.deinit(gpa);
354373 cg.mir_instructions.deinit(gpa);
355374 cg.mir_extra.deinit(gpa);
356375 cg.mir_locals.deinit(gpa);
......@@ -369,19 +388,11 @@ pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error {
369388/// Resolves the `WValue` for the given instruction `inst`
370389/// When the given instruction has a `Value`, it returns a constant instead
371390fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
372 var branch_index = cg.branches.items.len;
373 while (branch_index > 0) : (branch_index -= 1) {
374 const branch = cg.branches.items[branch_index - 1];
375 if (branch.values.get(ref)) |value| {
376 return value;
377 }
378 }
391 if (cg.values.get(ref)) |value| return value;
379392
380393 // when we did not find an existing instruction, it
381394 // means we must generate it from a constant.
382 // We always store constants in the most outer branch as they must never
383 // be removed. The most outer branch is always at index 0.
384 const gop = try cg.branches.items[0].values.getOrPut(cg.gpa, ref);
395 const gop = try cg.values.getOrPut(cg.gpa, ref);
385396 assert(!gop.found_existing);
386397
387398 const pt = cg.pt;
......@@ -420,7 +431,34 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue {
420431
421432/// NOTE: if result == .stack, it will be stored in .local
422433fn 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 {
423445 assert(operands.len <= Air.Liveness.bpi - 1);
446
447 if (result == .local) aliased: {
448 for (operands) |operand| {
449 if (operand.toIndex() == null) continue;
450 const resolved_operand = cg.values.getPtr(operand) orelse unreachable;
451 if (resolved_operand.* != .local or resolved_operand.local != result.local) continue;
452
453 if (result.local >= cg.wasm_param_count) {
454 const local = &cg.local_pool.locals.items[result.local - cg.wasm_param_count];
455 assert(local.ref_count > 0);
456 local.ref_count += 1;
457 }
458 break :aliased;
459 }
460 }
461
424462 var tomb_bits = cg.liveness.getTombBits(inst);
425463 for (operands) |operand| {
426464 const dies = @as(u1, @truncate(tomb_bits)) != 0;
......@@ -428,18 +466,27 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con
428466 if (!dies) continue;
429467 processDeath(cg, operand);
430468 }
469 for (branch_deaths) |death| cg.processDeath(death.toRef());
431470 try cg.finishAirResult(inst, result);
432471}
433472
434473fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void {
435474 // results of `none` can never be referenced.
436475 if (result != .none) {
437 const trackable_result = if (result != .stack)
438 result
439 else
440 try result.toLocal(cg, cg.typeOfIndex(inst));
441 const branch = cg.currentBranch();
442 branch.values.putAssumeCapacityNoClobber(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 }
443490 }
444491
445492 if (std.debug.runtime_safety) {
......@@ -447,17 +494,16 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro
447494 }
448495}
449496
450const Branch = struct {
451 values: ValueTable = .{},
452
453 fn deinit(branch: *Branch, gpa: Allocator) void {
454 branch.values.deinit(gpa);
455 branch.* = undefined;
456 }
457};
497fn pushBranch(cg: *CodeGen) InnerError!void {
498 var snapshot = try cg.local_pool.clone(cg.gpa);
499 errdefer snapshot.deinit(cg.gpa);
500 try cg.branches.append(cg.gpa, snapshot);
501}
458502
459inline fn currentBranch(cg: *CodeGen) *Branch {
460 return &cg.branches.items[cg.branches.items.len - 1];
503fn popBranch(cg: *CodeGen) void {
504 var snapshot = cg.branches.pop().?;
505 defer snapshot.deinit(cg.gpa);
506 snapshot.restore(cg);
461507}
462508
463509fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void {
......@@ -468,20 +514,30 @@ fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void {
468514
469515fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
470516 if (ref.toIndex() == null) return;
471 // Branches are currently only allowed to free locals allocated
472 // within their own branch.
473 // TODO: Upon branch consolidation free any locals if needed.
474 const value = cg.currentBranch().values.getPtr(ref) orelse return;
517 const value = cg.values.getPtr(ref) orelse unreachable;
475518 if (value.* != .local) return;
476 const reserved_indexes = cg.args.len + @intFromBool(cg.return_value != .none);
477 if (value.local.value < reserved_indexes) {
478 return; // function arguments can never be re-used
479 }
480 log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @backingInt(ref.toIndex().?), value.local.value });
481 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
482 if (value.local.references == 0) {
483 value.free(cg);
519 cg.releaseLocal(value.local);
520}
521
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 }
484539 }
540 assert(free_count + 2 == cg.local_pool.locals.items.len);
485541}
486542
487543pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
......@@ -569,7 +625,7 @@ fn addCallIntrinsic(cg: *CodeGen, intrinsic: Mir.Intrinsic) error{OutOfMemory}!v
569625/// Returns the index into `mir_extra`
570626fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
571627 const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len;
572 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, field_count);
628 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, field_count * 2);
573629 return cg.addExtraAssumeCapacity(extra);
574630}
575631
......@@ -579,14 +635,22 @@ fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
579635 const info = @typeInfo(@TypeOf(extra)).@"struct";
580636 const result: u32 = @intCast(cg.mir_extra.items.len);
581637 inline for (info.field_names, info.field_types) |field_name, field_type| {
582 cg.mir_extra.appendAssumeCapacity(switch (field_type) {
583 u32 => @field(extra, field_name),
584 i32 => @bitCast(@field(extra, field_name)),
585 InternPool.Index,
586 InternPool.Nav.Index,
587 => @backingInt(@field(extra, field_name)),
588 else => @compileError("Unsupported field type " ++ @typeName(field_type)),
589 });
638 switch (field_type) {
639 u64 => {
640 const value = @field(extra, field_name);
641 cg.mir_extra.appendAssumeCapacity(@truncate(value));
642 cg.mir_extra.appendAssumeCapacity(@truncate(value >> 32));
643 },
644 else => cg.mir_extra.appendAssumeCapacity(switch (field_type) {
645 u32 => @field(extra, field_name),
646 i32 => @bitCast(@field(extra, field_name)),
647 InternPool.Index,
648 InternPool.Nav.Index,
649 Alignment,
650 => @backingInt(@field(extra, field_name)),
651 else => @compileError("Unsupported field type " ++ @typeName(field_type)),
652 }),
653 }
590654 }
591655 return result;
592656}
......@@ -622,18 +686,75 @@ pub fn typeToValtype(ty: Type, zcu: *const Zcu, target: *const std.Target) std.w
622686/// Differently from `typeToValtype` this also allows `void` to create a block
623687/// with no return type
624688fn genBlockType(ty: Type, zcu: *const Zcu, target: *const std.Target) std.wasm.BlockType {
625 return switch (ty.ip_index) {
626 .void_type, .noreturn_type => .empty,
627 else => .fromValtype(typeToValtype(ty, zcu, target)),
689 if (!ty.hasRuntimeBits(zcu)) return .empty;
690 return .fromValtype(typeToValtype(ty, zcu, target));
691}
692
693/// Returns `ptr` with `offset` applied.
694fn offsetPointer(cg: *CodeGen, ptr: WValue, offset: u64) InnerError!WValue {
695 if (offset == 0) return ptr;
696 return switch (ptr) {
697 .stack_offset => |stack_offset| .{ .stack_offset = .{
698 .value = stack_offset.value + offset,
699 } },
700 else => result: {
701 try cg.emitWValue(ptr);
702 switch (cg.ptr_size) {
703 .wasm32 => {
704 try cg.addImm32(@intCast(offset));
705 try cg.addTag(.i32_add);
706 },
707 .wasm64 => {
708 try cg.addImm64(offset);
709 try cg.addTag(.i64_add);
710 },
711 }
712 break :result .stack;
713 },
628714 };
629715}
630716
717const PointerOffsetOp = enum { add, sub };
718
719/// The offset may only be known at runtime. Base pointer must be already on the stack. Result is left on it also.
720fn emitScaledPointerOffset(cg: *CodeGen, offset: WValue, scale: u64, op: PointerOffsetOp) InnerError!void {
721 if (scale == 0 or switch (offset) {
722 .imm32 => |imm| imm == 0,
723 .imm64 => |imm| imm == 0,
724 else => false,
725 }) return;
726
727 try cg.emitWValue(offset);
728 switch (cg.ptr_size) {
729 .wasm32 => {
730 if (scale != 1) {
731 try cg.addImm32(@intCast(scale));
732 try cg.addTag(.i32_mul);
733 }
734 try cg.addTag(switch (op) {
735 .add => .i32_add,
736 .sub => .i32_sub,
737 });
738 },
739 .wasm64 => {
740 if (scale != 1) {
741 try cg.addImm64(scale);
742 try cg.addTag(.i64_mul);
743 }
744 try cg.addTag(switch (op) {
745 .add => .i64_add,
746 .sub => .i64_sub,
747 });
748 },
749 }
750}
751
631752/// Writes the bytecode depending on the given `WValue` in `val`
632753fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
633754 switch (value) {
634 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
755 .dead => unreachable, // reference to free'd `WValue`
635756 .none, .stack => {}, // no-op
636 .local => |idx| try cg.addLocal(.local_get, idx.value),
757 .local => |idx| try cg.addLocal(.local_get, idx),
637758 .imm32 => |val| try cg.addImm32(val),
638759 .imm64 => |val| try cg.addImm64(val),
639760 .imm128 => |val| try cg.addImm128(val),
......@@ -695,38 +816,38 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
695816 });
696817 }
697818 },
698 .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local.value), // caller must ensure to address the offset
819 .stack_offset => |stack_offset| {
820 try cg.emitWValue(cg.bottom_stack_value);
821 _ = try cg.offsetPointer(.stack, stack_offset.value);
822 },
699823 }
700824}
701825
702/// If given a local or stack-offset, increases the reference count by 1.
703/// The old `WValue` found at instruction `ref` is then replaced by the
704/// modified `WValue` and returned. When given a non-local or non-stack-offset,
705/// returns the given `operand` itfunc instead.
706fn reuseOperand(cg: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {
707 if (operand != .local and operand != .stack_offset) return operand;
708 var new_value = operand;
709 switch (new_value) {
710 .local => |*local| local.references += 1,
711 .stack_offset => |*stack_offset| stack_offset.references += 1,
712 else => unreachable,
826/// Emits the base address for a memory instruction.
827fn emitMemBase(cg: *CodeGen, ptr: WValue) InnerError!void {
828 switch (ptr) {
829 .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local),
830 else => try cg.emitWValue(ptr),
713831 }
714 const old_value = cg.getResolvedInst(ref);
715 old_value.* = new_value;
716 return new_value;
717832}
718833
719/// From a reference, returns its resolved `WValue`.
720/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.
721fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
722 var index = cg.branches.items.len;
723 while (index > 0) : (index -= 1) {
724 const branch = cg.branches.items[index - 1];
725 if (branch.values.getPtr(ref)) |value| {
726 return value;
727 }
728 }
729 unreachable; // developer-error: This can only be called on resolved instructions. Use `resolveInst` instead.
834fn appendFreeLocal(cg: *CodeGen, index: u32) void {
835 assert(index >= cg.wasm_param_count);
836 const mir_index = index - cg.wasm_param_count;
837 const valtype = cg.mir_locals.items[mir_index];
838 const free_list = cg.local_pool.freeList(valtype);
839 cg.local_pool.locals.items[mir_index] = .{ .next_free = free_list.* };
840 free_list.* = .init(index);
841 log.debug("freed local ({d}) of type {}", .{ index, valtype });
842}
843
844fn releaseLocal(cg: *CodeGen, index: u32) void {
845 if (index < cg.wasm_param_count) return;
846 const local = &cg.local_pool.locals.items[index - cg.wasm_param_count];
847 assert(local.ref_count > 0);
848 local.ref_count -= 1;
849 if (local.ref_count > 0) return;
850 cg.appendFreeLocal(index);
730851}
731852
732853/// Creates one locals for a given `Type`.
......@@ -734,16 +855,13 @@ fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue {
734855fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
735856 const zcu = cg.pt.zcu;
736857 const valtype = typeToValtype(ty, zcu, cg.target);
737 const index_or_null = switch (valtype) {
738 .i32 => cg.free_locals_i32.pop(),
739 .i64 => cg.free_locals_i64.pop(),
740 .f32 => cg.free_locals_f32.pop(),
741 .f64 => cg.free_locals_f64.pop(),
742 .v128 => cg.free_locals_v128.pop(),
743 };
744 if (index_or_null) |index| {
858 const free_list = cg.local_pool.freeList(valtype);
859 if (free_list.unwrap()) |index| {
860 const local = &cg.local_pool.locals.items[index - cg.wasm_param_count];
861 free_list.* = local.next_free;
862 local.* = .{ .ref_count = 1 };
745863 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
746 return .{ .local = .{ .value = index, .references = 1 } };
864 return .{ .local = index };
747865 }
748866 log.debug("new local of type {}", .{valtype});
749867 return cg.ensureAllocLocal(ty);
......@@ -753,10 +871,13 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
753871/// to use a zero-initialized local.
754872fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue {
755873 const zcu = cg.pt.zcu;
756 try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target));
757 const initial_index = cg.local_index;
758 cg.local_index += 1;
759 return .{ .local = .{ .value = initial_index, .references = 1 } };
874 try cg.mir_locals.ensureUnusedCapacity(cg.gpa, 1);
875 try cg.local_pool.locals.ensureUnusedCapacity(cg.gpa, 1);
876 assert(cg.mir_locals.items.len == cg.local_pool.locals.items.len);
877 const initial_index = cg.wasm_param_count + @as(u32, @intCast(cg.local_pool.locals.items.len));
878 cg.mir_locals.appendAssumeCapacity(typeToValtype(ty, zcu, cg.target));
879 cg.local_pool.locals.appendAssumeCapacity(.{ .ref_count = 1 });
880 return .{ .local = initial_index };
760881}
761882
762883pub const Error = codegen.Error;
......@@ -798,7 +919,6 @@ pub fn generate(
798919 .args = cc_result.args,
799920 .return_value = cc_result.return_value,
800921 .varargs = cc_result.varargs,
801 .local_index = cc_result.local_index,
802922 .mir_instructions = .empty,
803923 .mir_extra = .empty,
804924 .mir_locals = .empty,
......@@ -821,17 +941,10 @@ pub fn generate(
821941
822942fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
823943 const zcu = cg.pt.zcu;
824 // branch used for const values
825 try cg.branches.append(cg.gpa, .{});
826 // func scope branch
827 try cg.branches.append(cg.gpa, .{});
828 defer {
829 var func_branch = cg.branches.pop().?;
830 func_branch.deinit(cg.gpa);
831 var const_branch = cg.branches.pop().?;
832 const_branch.deinit(cg.gpa);
833 assert(cg.branches.items.len == 0); // missing branch merge
834 }
944 cg.wasm_param_count = @intCast(cg.args.len + @intFromBool(cg.return_value != .none) + @intFromBool(cg.varargs != .none));
945 cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize);
946 cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize);
947 defer assert(cg.branches.items.len == 0);
835948 // Generate MIR for function body
836949 try cg.genBody(cg.air.getMainBody());
837950
......@@ -856,11 +969,11 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir {
856969 .instructions = cg.mir_instructions.toOwnedSlice(),
857970 .extra = cg.mir_extra.toOwnedSliceAssert(),
858971 .locals = cg.mir_locals.toOwnedSliceAssert(),
859 .prologue = if (cg.initial_stack_value == .none) .none else .{
860 .sp_local = cg.initial_stack_value.local.value,
972 .prologue = .{
973 .sp_local = cg.initial_stack_value.local,
861974 .flags = .{ .stack_alignment = cg.stack_alignment },
862975 .stack_size = cg.stack_size,
863 .bottom_stack_local = cg.bottom_stack_value.local.value,
976 .bottom_stack_local = cg.bottom_stack_value.local,
864977 },
865978 .uavs = cg.mir_uavs.move(),
866979 .indirect_function_set = cg.mir_indirect_function_set.move(),
......@@ -907,7 +1020,7 @@ fn resolveCallingConventionValues(
9071020 if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) {
9081021 // the sret arg will be passed as first argument, therefore we
9091022 // set the `return_value` before allocating locals for regular args.
910 result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } };
1023 result.return_value = .{ .local = result.local_index };
9111024 result.local_index += 1;
9121025 }
9131026
......@@ -918,7 +1031,7 @@ fn resolveCallingConventionValues(
9181031 continue;
9191032 }
9201033
921 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1034 try args.append(.{ .local = result.local_index });
9221035 result.local_index += 1;
9231036 }
9241037 },
......@@ -931,17 +1044,17 @@ fn resolveCallingConventionValues(
9311044
9321045 switch (abi.classifyType(param_ty, zcu, target)) {
9331046 .direct, .indirect => {
934 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1047 try args.append(.{ .local = result.local_index });
9351048 result.local_index += 1;
9361049 },
9371050 .double_i64 => {
938 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
939 try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } });
1051 try args.append(.{ .local = result.local_index });
1052 try args.append(.{ .local = result.local_index + 1 });
9401053 result.local_index += 2;
9411054 },
9421055 .unrolled => |vector| {
9431056 for (0..vector.len) |_| {
944 try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } });
1057 try args.append(.{ .local = result.local_index });
9451058 result.local_index += 1;
9461059 }
9471060 },
......@@ -952,7 +1065,7 @@ fn resolveCallingConventionValues(
9521065 }
9531066
9541067 if (fn_info.is_var_args) {
955 result.varargs = .{ .local = .{ .value = result.local_index, .references = 1 } };
1068 result.varargs = .{ .local = result.local_index };
9561069 result.local_index += 1;
9571070 }
9581071
......@@ -983,7 +1096,7 @@ pub fn firstParamSRet(
9831096/// it matches the ABI.
9841097fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValue) !void {
9851098 if (cc != .wasm_mvp) {
986 return cg.lowerToStack(value);
1099 return cg.emitWValue(value);
9871100 }
9881101
9891102 const zcu = cg.pt.zcu;
......@@ -991,74 +1104,37 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu
9911104 switch (abi.classifyType(ty, zcu, cg.target)) {
9921105 .direct => |scalar_ty| {
9931106 if (!isByRef(ty, zcu, cg.target)) {
994 return cg.lowerToStack(value);
1107 return cg.emitWValue(value);
9951108 } else {
996 _ = try cg.load(value, scalar_ty, 0);
1109 _ = try cg.load(value, scalar_ty, .{});
9971110 }
9981111 },
9991112 .double_i64 => {
10001113 assert(ty.abiSize(zcu) == 16);
10011114 // in this case we have an integer or float that must be lowered as 2 i64's.
1002 try cg.emitWValue(value);
1003 try cg.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1004 try cg.emitWValue(value);
1005 try cg.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
1115 const base_offset = value.memOffset();
1116 try cg.emitMemBase(value);
1117 try cg.addMemArg(.i64_load, .{ .offset = base_offset, .alignment = .@"8" });
1118 try cg.emitMemBase(value);
1119 try cg.addMemArg(.i64_load, .{ .offset = base_offset + 8, .alignment = .@"8" });
10061120 },
10071121 .indirect => {
10081122 const stack_copy = try cg.allocStack(ty);
1009 try cg.store(stack_copy, value, ty, 0);
1010 return cg.lowerToStack(stack_copy);
1123 try cg.store(stack_copy, value, ty, .{});
1124 return cg.emitWValue(stack_copy);
10111125 },
10121126 .unrolled => |vector| {
1013 const elem_size: u32 = @intCast(vector.elem_type.abiSize(zcu));
1127 const elem_size = vector.elem_type.abiSize(zcu);
10141128 for (0..vector.len) |index| {
1015 _ = try cg.load(value, vector.elem_type, @intCast(index * elem_size));
1016 }
1017 },
1018 }
1019}
1020
1021/// Lowers a `WValue` to the stack. This means when the `value` results in
1022/// `.stack_offset` we calculate the pointer of this offset and use that.
1023/// The value is left on the stack, and not stored in any temporary.
1024fn lowerToStack(cg: *CodeGen, value: WValue) !void {
1025 switch (value) {
1026 .stack_offset => |offset| {
1027 try cg.emitWValue(value);
1028 if (offset.value > 0) {
1029 switch (cg.ptr_size) {
1030 .wasm32 => {
1031 try cg.addImm32(offset.value);
1032 try cg.addTag(.i32_add);
1033 },
1034 .wasm64 => {
1035 try cg.addImm64(offset.value);
1036 try cg.addTag(.i64_add);
1037 },
1038 }
1129 _ = try cg.load(value, vector.elem_type, .{ .offset = index * elem_size });
10391130 }
10401131 },
1041 else => try cg.emitWValue(value),
10421132 }
10431133}
10441134
1045/// Creates a local for the initial stack value
1046/// Asserts `initial_stack_value` is `.none`
1047fn initializeStack(cg: *CodeGen) !void {
1048 assert(cg.initial_stack_value == .none);
1049 // Reserve a local to store the current stack pointer
1050 // We can later use this local to set the stack pointer back to the value
1051 // we have stored here.
1052 cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize);
1053 // Also reserve a local to store the bottom stack value
1054 cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize);
1055}
1056
10571135/// Reads the stack pointer from `Context.initial_stack_value` and writes it
10581136/// to the global stack pointer variable
10591137fn restoreStackPointer(cg: *CodeGen) !void {
1060 // only restore the pointer if it was initialized
1061 if (cg.initial_stack_value == .none) return;
10621138 // Get the original stack pointer's value
10631139 try cg.emitWValue(cg.initial_stack_value);
10641140
......@@ -1098,16 +1174,12 @@ fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue {
10981174fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue {
10991175 assert(size > 0);
11001176
1101 if (cg.initial_stack_value == .none) {
1102 try cg.initializeStack();
1103 }
1104
11051177 cg.stack_alignment = cg.stack_alignment.max(alignment);
11061178
11071179 const offset: u32 = @intCast(alignment.forward(cg.stack_size));
11081180 defer cg.stack_size = offset + size;
11091181
1110 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
1182 return .{ .stack_offset = .{ .value = offset } };
11111183}
11121184
11131185/// From a given AIR instruction generates a pointer to the stack where
......@@ -1120,10 +1192,6 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue {
11201192 const ptr_ty = cg.typeOfIndex(inst);
11211193 const pointee_ty = ptr_ty.childType(zcu);
11221194
1123 if (cg.initial_stack_value == .none) {
1124 try cg.initializeStack();
1125 }
1126
11271195 const abi_alignment = ptr_ty.ptrAlignment(zcu);
11281196 const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse {
11291197 return cg.fail("Type {f} with ABI size of {d} exceeds stack frame size", .{
......@@ -1135,7 +1203,7 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue {
11351203 const offset: u32 = @intCast(abi_alignment.forward(cg.stack_size));
11361204 defer cg.stack_size = offset + abi_size;
11371205
1138 return .{ .stack_offset = .{ .value = offset, .references = 1 } };
1206 return .{ .stack_offset = .{ .value = offset } };
11391207}
11401208
11411209fn emitMemoryCopy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
......@@ -1165,8 +1233,8 @@ fn emitMemoryCopy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
11651233 try cg.addLabel(.br_if, 0);
11661234 }
11671235
1168 try cg.lowerToStack(dst);
1169 try cg.lowerToStack(src);
1236 try cg.emitWValue(dst);
1237 try cg.emitWValue(src);
11701238 try cg.emitWValue(len);
11711239 try cg.addExtended(.memory_copy);
11721240
......@@ -1176,26 +1244,26 @@ fn emitMemoryCopy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
11761244}
11771245
11781246fn memcpy(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1179 if (cg.target.cpu.has(.wasm, .bulk_memory)) {
1247 if (cg.target.cpu.has(.wasm, .bulk_memory_opt)) {
11801248 try cg.emitMemoryCopy(dst, src, len);
11811249 return;
11821250 }
11831251
1184 try cg.lowerToStack(dst);
1185 try cg.lowerToStack(src);
1252 try cg.emitWValue(dst);
1253 try cg.emitWValue(src);
11861254 try cg.emitWValue(len);
11871255 try cg.addCallIntrinsic(.memcpy);
11881256 try cg.addTag(.drop);
11891257}
11901258
11911259fn memmove(cg: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1192 if (cg.target.cpu.has(.wasm, .bulk_memory)) {
1260 if (cg.target.cpu.has(.wasm, .bulk_memory_opt)) {
11931261 try cg.emitMemoryCopy(dst, src, len);
11941262 return;
11951263 }
11961264
1197 try cg.lowerToStack(dst);
1198 try cg.lowerToStack(src);
1265 try cg.emitWValue(dst);
1266 try cg.emitWValue(src);
11991267 try cg.emitWValue(len);
12001268 try cg.addCallIntrinsic(.memmove);
12011269 try cg.addTag(.drop);
......@@ -1277,34 +1345,6 @@ pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.
12771345 return .unrolled;
12781346}
12791347
1280/// Creates a new local for a pointer that points to memory with given offset.
1281/// This can be used to get a pointer to a struct field, error payload, etc.
1282/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new
1283/// local value to store the pointer. This allows for local re-use and improves binary size.
1284fn buildPointerOffset(cg: *CodeGen, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue {
1285 // do not perform arithmetic when offset is 0.
1286 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;
1287 const result_ptr: WValue = switch (action) {
1288 .new => try cg.ensureAllocLocal(Type.usize),
1289 .modify => ptr_value,
1290 };
1291 try cg.emitWValue(ptr_value);
1292 if (offset + ptr_value.offset() > 0) {
1293 switch (cg.ptr_size) {
1294 .wasm32 => {
1295 try cg.addImm32(@intCast(offset + ptr_value.offset()));
1296 try cg.addTag(.i32_add);
1297 },
1298 .wasm64 => {
1299 try cg.addImm64(offset + ptr_value.offset());
1300 try cg.addTag(.i64_add);
1301 },
1302 }
1303 }
1304 try cg.addLocal(.local_set, result_ptr.local.value);
1305 return result_ptr;
1306}
1307
13081348fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13091349 const zcu = cg.pt.zcu;
13101350 const air_tags = cg.air.instructions.items(.tag);
......@@ -1325,14 +1365,12 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13251365 const elem_ty = cg.typeOf(bin_op.rhs);
13261366 const elem_size = elem_ty.abiSize(zcu);
13271367
1328 try cg.lowerToStack(vec_ptr);
1329 try cg.emitWValue(elem_idx);
1330 try cg.addImm32(@intCast(elem_size));
1331 try cg.addTag(.i32_mul);
1332 try cg.addTag(.i32_add);
1333 const ptr = try WValue.toLocal(.stack, cg, Type.usize);
1368 try cg.emitWValue(vec_ptr);
1369 try cg.emitScaledPointerOffset(elem_idx, elem_size, .add);
1370 var ptr = try WValue.toLocal(.stack, cg, Type.usize);
1371 defer ptr.free(cg);
13341372
1335 try cg.store(ptr, elem_val, elem_ty, 0);
1373 try cg.store(ptr, elem_val, elem_ty, .{});
13361374
13371375 return cg.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs });
13381376 },
......@@ -1509,8 +1547,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
15091547 const result = try cg.allocStack(cg.typeOfIndex(inst));
15101548 const offset: u32 = @intCast(ty.abiSize(cg.pt.zcu));
15111549
1512 try cg.store(result, res_tmp, ty, 0);
1513 try cg.store(result, ov_tmp, Type.u1, offset);
1550 try cg.store(result, res_tmp, ty, .{});
1551 try cg.store(result, ov_tmp, .u1, .{ .offset = offset });
15141552
15151553 try cg.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
15161554 },
......@@ -1598,17 +1636,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
15981636 const src_int_ty: IntType = .fromType(cg, src_ty);
15991637 const dest_int_ty: IntType = .fromType(cg, dest_ty);
16001638
1601 const src_bits = src_int_ty.bits;
1602 const dest_bits = dest_int_ty.bits;
1603
1604 const same_class: bool = (src_bits <= 32 and dest_bits <= 32) or
1605 (src_bits >= 33 and src_bits <= 64 and dest_bits >= 33 and dest_bits <= 64) or
1606 (src_bits >= 65 and src_bits <= 128 and dest_bits >= 65 and dest_bits <= 128);
1607
1608 const result = if (same_class)
1609 cg.reuseOperand(ty_op.operand, operand)
1610 else
1611 try cg.intCast(dest_int_ty, src_int_ty, operand);
1639 const result = try cg.intCast(dest_int_ty, src_int_ty, operand);
16121640
16131641 try cg.finishAir(inst, result, &.{ty_op.operand});
16141642 },
......@@ -1625,12 +1653,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
16251653
16261654 const src_int_ty: IntType = .fromType(cg, src_ty);
16271655 const dest_int_ty: IntType = .fromType(cg, dest_ty);
1628
1629 const result = if (src_int_ty.bits == dest_int_ty.bits)
1630 cg.reuseOperand(ty_op.operand, operand)
1631 else blk: {
1632 break :blk try cg.intTrunc(dest_int_ty, src_int_ty, operand);
1633 };
1656 const result = try cg.intTrunc(dest_int_ty, src_int_ty, operand);
16341657
16351658 try cg.finishAir(inst, result, &.{ty_op.operand});
16361659 },
......@@ -1934,7 +1957,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
19341957 continue;
19351958 }
19361959 const old_bookkeeping_value = cg.air_bookkeeping;
1937 try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, 1);
19381960 try cg.genInst(inst);
19391961
19401962 if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) {
......@@ -1956,20 +1978,20 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19561978 // result must be stored in the stack and we return a pointer
19571979 // to the stack instead
19581980 if (cg.return_value != .none) {
1959 try cg.store(cg.return_value, operand, ret_ty, 0);
1981 try cg.store(cg.return_value, operand, ret_ty, .{});
19601982 } else if (fn_info.cc == .wasm_mvp and ret_ty.hasRuntimeBits(zcu)) {
19611983 switch (abi.classifyType(ret_ty, zcu, cg.target)) {
19621984 .direct => |scalar_type| {
19631985 if (!isByRef(ret_ty, zcu, cg.target)) {
19641986 try cg.emitWValue(operand);
19651987 } else {
1966 _ = try cg.load(operand, scalar_type, 0);
1988 _ = try cg.load(operand, scalar_type, .{});
19671989 }
19681990 },
19691991 .double_i64, .indirect => unreachable,
19701992 .unrolled => |vector| {
19711993 assert(vector.len == 1);
1972 _ = try cg.load(operand, vector.elem_type, 0);
1994 _ = try cg.load(operand, vector.elem_type, .{});
19731995 },
19741996 }
19751997 } else {
......@@ -1982,7 +2004,8 @@ fn airRet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19822004 try cg.restoreStackPointer();
19832005 try cg.addTag(.@"return");
19842006
1985 return cg.finishAir(inst, .none, &.{un_op});
2007 try cg.finishAir(inst, .none, &.{un_op});
2008 cg.verifyLocalPool();
19862009}
19872010
19882011fn airRetPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -2019,21 +2042,22 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20192042 } else if (!firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, cg.target)) {
20202043 if (fn_info.cc == .wasm_mvp) {
20212044 switch (abi.classifyType(ret_ty, zcu, cg.target)) {
2022 .direct => |scalar_type| _ = try cg.load(operand, scalar_type, 0),
2045 .direct => |scalar_type| _ = try cg.load(operand, scalar_type, .{}),
20232046 .double_i64, .indirect => unreachable,
20242047 .unrolled => |vector| {
20252048 assert(vector.len == 1);
2026 _ = try cg.load(operand, vector.elem_type, 0);
2049 _ = try cg.load(operand, vector.elem_type, .{});
20272050 },
20282051 }
20292052 } else {
2030 _ = try cg.load(operand, ret_ty, 0);
2053 _ = try cg.load(operand, ret_ty, .{});
20312054 }
20322055 }
20332056
20342057 try cg.restoreStackPointer();
20352058 try cg.addTag(.@"return");
2036 return cg.finishAir(inst, .none, &.{un_op});
2059 try cg.finishAir(inst, .none, &.{un_op});
2060 cg.verifyLocalPool();
20372061}
20382062
20392063fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) InnerError!void {
......@@ -2109,7 +2133,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21092133 const arg_align = arg_ty.abiAlignment(zcu);
21102134
21112135 offset = @intCast(arg_align.forward(offset));
2112 try cg.store(buffer, arg_val, arg_ty, offset);
2136 try cg.store(buffer, arg_val, arg_ty, .{ .offset = offset });
21132137 offset += arg_size;
21142138 }
21152139
......@@ -2117,7 +2141,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21172141 } else .none;
21182142
21192143 if (first_param_sret) {
2120 try cg.lowerToStack(sret);
2144 try cg.emitWValue(sret);
21212145 }
21222146
21232147 for (args, 0..) |arg, arg_i| {
......@@ -2131,7 +2155,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21312155 }
21322156
21332157 if (fn_info.is_var_args) {
2134 try cg.lowerToStack(varargs_buf);
2158 try cg.emitWValue(varargs_buf);
21352159 }
21362160
21372161 if (callee) |nav_index| {
......@@ -2160,30 +2184,32 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier)
21602184 .direct => |scalar_type| {
21612185 if (!isByRef(ret_ty, zcu, cg.target)) {
21622186 const result_local = try cg.allocLocal(ret_ty);
2163 try cg.addLocal(.local_set, result_local.local.value);
2187 try cg.addLocal(.local_set, result_local.local);
21642188 break :result_value result_local;
21652189 } else {
2166 const result_local = try cg.allocLocal(scalar_type);
2167 try cg.addLocal(.local_set, result_local.local.value);
2190 var result_local = try cg.allocLocal(scalar_type);
2191 defer result_local.free(cg);
2192 try cg.addLocal(.local_set, result_local.local);
21682193 const result = try cg.allocStack(ret_ty);
2169 try cg.store(result, result_local, scalar_type, 0);
2194 try cg.store(result, result_local, scalar_type, .{});
21702195 break :result_value result;
21712196 }
21722197 },
21732198 .double_i64, .indirect => unreachable,
21742199 .unrolled => |vector| {
21752200 assert(vector.len == 1);
2176 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);
21772203 // save call result from operand stack
2178 try cg.addLocal(.local_set, result_local.local.value);
2204 try cg.addLocal(.local_set, result_local.local);
21792205 const result = try cg.allocStack(ret_ty);
2180 try cg.store(result, result_local, vector.elem_type, 0);
2206 try cg.store(result, result_local, vector.elem_type, .{});
21812207 break :result_value result;
21822208 },
21832209 }
21842210 } else {
21852211 const result_local = try cg.allocLocal(ret_ty);
2186 try cg.addLocal(.local_set, result_local.local.value);
2212 try cg.addLocal(.local_set, result_local.local);
21872213 break :result_value result_local;
21882214 }
21892215 };
......@@ -2208,7 +2234,7 @@ fn airVaCopy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22082234 const ty_op = cg.air.instructions.items(.data)[@backingInt(inst)].ty_op;
22092235 const operand = try cg.resolveInst(ty_op.operand);
22102236
2211 const result = try cg.load(operand, .usize, 0);
2237 const result = try cg.load(operand, .usize, .{});
22122238
22132239 return cg.finishAir(inst, result, &.{ty_op.operand});
22142240}
......@@ -2230,8 +2256,9 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22302256 const abi_size: u32 = @intCast(load_ty.abiSize(zcu));
22312257 const abi_align: u32 = @intCast(load_ty.abiAlignment(zcu).toByteUnits().?);
22322258
2233 const arg_ptr = try cg.allocLocal(.usize);
2234 _ = try cg.load(operand, .usize, 0);
2259 var arg_ptr = try cg.allocLocal(.usize);
2260 defer arg_ptr.free(cg);
2261 _ = try cg.load(operand, .usize, .{});
22352262
22362263 if (abi_align > 1) {
22372264 switch (cg.ptr_size) {
......@@ -2250,10 +2277,10 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22502277 }
22512278 }
22522279
2253 try cg.addLocal(.local_set, arg_ptr.local.value);
2280 try cg.addLocal(.local_set, arg_ptr.local);
22542281
2255 try cg.lowerToStack(operand);
2256 try cg.lowerToStack(arg_ptr);
2282 try cg.emitWValue(operand);
2283 try cg.emitWValue(arg_ptr);
22572284 switch (cg.ptr_size) {
22582285 .wasm32 => {
22592286 try cg.addImm32(abi_size);
......@@ -2264,17 +2291,17 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
22642291 try cg.addTag(.i64_add);
22652292 },
22662293 }
2267 try cg.store(.stack, .stack, .usize, 0);
2294 try cg.store(.stack, .stack, .usize, .{});
22682295
22692296 const result = if (is_f32_va_arg) result: {
2270 const promoted = try cg.load(arg_ptr, Type.f64, 0);
2297 const promoted = try cg.load(arg_ptr, .f64, .{});
22712298 try cg.emitWValue(promoted);
22722299 try cg.addTag(.f32_demote_f64);
22732300
22742301 const result_local = try cg.allocLocal(Type.f32);
2275 try cg.addLocal(.local_set, result_local.local.value);
2302 try cg.addLocal(.local_set, result_local.local);
22762303 break :result result_local;
2277 } else try cg.load(arg_ptr, ty, 0);
2304 } else try cg.load(arg_ptr, ty, .{});
22782305
22792306 return cg.finishAir(inst, result, &.{ty_op.operand});
22802307}
......@@ -2290,27 +2317,44 @@ fn airStore(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
22902317 const bin_op = cg.air.instructions.items(.data)[@backingInt(inst)].bin_op;
22912318
22922319 const lhs = try cg.resolveInst(bin_op.lhs);
2293 const rhs = try cg.resolveInst(bin_op.rhs);
22942320 const ptr_ty = cg.typeOf(bin_op.lhs);
22952321 const ptr_info = ptr_ty.ptrInfo(zcu);
22962322 const elem_ty = ptr_ty.childType(zcu);
2323 const rhs_is_undef = if (bin_op.rhs.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false;
22972324
2298 if (!safety and bin_op.rhs == .undef) {
2325 if (!safety and rhs_is_undef) {
22992326 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
23002327 }
23012328
2302 const offset: u32 = switch (ptr_info.flags.vector_index) {
2329 const rhs = try cg.resolveInst(bin_op.rhs);
2330
2331 const offset: u64 = switch (ptr_info.flags.vector_index) {
23032332 .none => offset: {
23042333 assert(ptr_info.packed_offset.host_size == 0); // legalize .expand_packed_store
23052334 break :offset 0;
23062335 },
2307 else => |index| @intCast(@backingInt(index) * elem_ty.abiSize(zcu)),
2336 else => |index| @backingInt(index) * elem_ty.abiSize(zcu),
23082337 };
2309 try cg.store(lhs, rhs, elem_ty, offset);
2338
2339 if (safety and rhs_is_undef and isByRef(elem_ty, zcu, cg.target)) {
2340 const dst = try cg.offsetPointer(lhs, offset);
2341 try cg.memset(.u8, dst, .{ .imm32 = @intCast(elem_ty.abiSize(zcu)) }, .{ .imm32 = 0xaa });
2342 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2343 }
2344
2345 try cg.store(lhs, rhs, elem_ty, .{
2346 .offset = offset,
2347 .alignment = ptr_ty.ptrAlignment(zcu),
2348 });
23102349 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
23112350}
23122351
2313fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
2352const MemArg = struct {
2353 offset: u64 = 0,
2354 alignment: Alignment = .none,
2355};
2356
2357fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, memarg: MemArg) InnerError!void {
23142358 assert(!(lhs != .stack and rhs == .stack));
23152359 const pt = cg.pt;
23162360 const zcu = pt.zcu;
......@@ -2318,29 +2362,31 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
23182362
23192363 if (!ty.hasRuntimeBits(zcu)) return;
23202364
2365 const natural_alignment = ty.abiAlignment(zcu);
2366 // wasm memory instruction alignment is <= access width
2367 const alignment = if (memarg.alignment == .none)
2368 natural_alignment
2369 else
2370 natural_alignment.minStrict(memarg.alignment);
2371
23212372 if (isByRef(ty, zcu, cg.target)) {
2322 const offset_ptr: WValue = switch (offset + lhs.offset()) {
2323 0 => lhs,
2324 else => |total_offset| ptr: {
2325 try cg.emitWValue(lhs);
2326 try cg.addImm32(total_offset);
2327 try cg.addTag(.i32_add);
2328 break :ptr .stack;
2329 },
2330 };
2373 const offset_ptr = try cg.offsetPointer(lhs, memarg.offset);
23312374 return cg.memcpy(offset_ptr, rhs, .{ .imm32 = @intCast(abi_size) });
23322375 }
23332376
2377 const base_offset = lhs.memOffset();
2378 try cg.emitMemBase(lhs);
2379
23342380 if (ty.zigTypeTag(zcu) == .vector) {
2335 try cg.emitWValue(lhs);
2336 try cg.lowerToStack(rhs);
2381 try cg.emitWValue(rhs);
23372382 // TODO: Add helper functions for simd opcodes
23382383 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
23392384 // stores as := opcode, offset, alignment (opcode::memarg)
2340 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2341 @backingInt(std.wasm.SimdOpcode.v128_store),
2342 offset + lhs.offset(),
2343 @intCast(ty.abiAlignment(zcu).toByteUnits().?),
2385 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 4);
2386 cg.mir_extra.appendAssumeCapacity(@backingInt(std.wasm.SimdOpcode.v128_store));
2387 _ = try cg.addExtraAssumeCapacity(Mir.MemArg{
2388 .offset = base_offset + memarg.offset,
2389 .alignment = alignment,
23442390 });
23452391 return cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
23462392 }
......@@ -2364,14 +2410,13 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
23642410 }
23652411 };
23662412
2367 try cg.emitWValue(lhs);
2368 try cg.lowerToStack(rhs);
2413 try cg.emitWValue(rhs);
23692414
23702415 try cg.addMemArg(
23712416 store_opcode,
23722417 .{
2373 .offset = offset + lhs.offset(),
2374 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
2418 .offset = base_offset + memarg.offset,
2419 .alignment = alignment,
23752420 },
23762421 );
23772422}
......@@ -2387,48 +2432,55 @@ fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
23872432
23882433 assert(elem_ty.hasRuntimeBits(zcu));
23892434
2390 const offset: u32 = switch (ptr_info.flags.vector_index) {
2435 const offset: u64 = switch (ptr_info.flags.vector_index) {
23912436 .none => offset: {
23922437 assert(ptr_info.packed_offset.host_size == 0); // legalize .expand_packed_load
23932438 break :offset 0;
23942439 },
2395 else => |index| @intCast(@backingInt(index) * elem_ty.abiSize(zcu)),
2440 else => |index| @backingInt(index) * elem_ty.abiSize(zcu),
23962441 };
2397 const result = try cg.load(operand, elem_ty, offset);
2442 const result = try cg.load(operand, elem_ty, .{
2443 .offset = offset,
2444 .alignment = ptr_ty.ptrAlignment(zcu),
2445 });
23982446 return cg.finishAir(inst, result, &.{ty_op.operand});
23992447}
24002448
24012449/// Loads an operand from the linear memory section.
24022450/// NOTE: Leaves the value on the stack, if isByRef == false.
2403fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
2451fn load(cg: *CodeGen, operand: WValue, ty: Type, memarg: MemArg) InnerError!WValue {
24042452 const zcu = cg.pt.zcu;
2453
2454 const natural_alignment = ty.abiAlignment(zcu);
2455 // wasm memory instruction alignment is <= access width
2456 const alignment = if (memarg.alignment == .none)
2457 natural_alignment
2458 else
2459 natural_alignment.minStrict(memarg.alignment);
2460
24052461 if (isByRef(ty, zcu, cg.target)) {
2406 const src_ptr_maybe_stack: WValue = switch (offset + operand.offset()) {
2407 0 => operand,
2408 else => |total_offset| ptr: {
2409 try cg.emitWValue(operand);
2410 try cg.addImm32(total_offset);
2411 try cg.addTag(.i32_add);
2412 break :ptr .stack;
2413 },
2414 };
2415 const src_ptr = try src_ptr_maybe_stack.toLocal(cg, .usize);
2462 var src_ptr = try cg.offsetPointer(operand, memarg.offset);
2463 const owns_src_ptr = src_ptr == .stack;
2464 src_ptr = try src_ptr.toLocal(cg, .usize);
2465 defer if (owns_src_ptr) src_ptr.free(cg);
2466
24162467 const new_ptr = try cg.allocStack(ty);
2417 try cg.store(new_ptr, src_ptr, ty, 0);
2468 try cg.memcpy(new_ptr, src_ptr, .{ .imm32 = @intCast(ty.abiSize(zcu)) });
24182469 return new_ptr;
24192470 }
24202471
2421 // load local's value from memory by its stack position
2422 try cg.emitWValue(operand);
2472 const base_offset = operand.memOffset();
2473 try cg.emitMemBase(operand);
24232474
24242475 if (ty.zigTypeTag(zcu) == .vector) {
24252476 // TODO: Add helper functions for simd opcodes
24262477 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
24272478 // stores as := opcode, offset, alignment (opcode::memarg)
2428 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
2429 @backingInt(std.wasm.SimdOpcode.v128_load),
2430 offset + operand.offset(),
2431 @intCast(ty.abiAlignment(zcu).toByteUnits().?),
2479 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 4);
2480 cg.mir_extra.appendAssumeCapacity(@backingInt(std.wasm.SimdOpcode.v128_load));
2481 _ = try cg.addExtraAssumeCapacity(Mir.MemArg{
2482 .offset = base_offset + memarg.offset,
2483 .alignment = alignment,
24322484 });
24332485 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
24342486 return .stack;
......@@ -2458,8 +2510,8 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue
24582510 try cg.addMemArg(
24592511 load_opcode,
24602512 .{
2461 .offset = offset + operand.offset(),
2462 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
2513 .offset = base_offset + memarg.offset,
2514 .alignment = alignment,
24632515 },
24642516 );
24652517
......@@ -2487,7 +2539,7 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
24872539 cg.arg_index += 1;
24882540 if (isByRef(arg_ty, zcu, cg.target)) {
24892541 const result = try cg.allocStack(arg_ty);
2490 try cg.store(result, arg, scalar_type, 0);
2542 try cg.store(result, arg, scalar_type, .{});
24912543 return cg.finishAir(inst, result, &.{});
24922544 }
24932545 },
......@@ -2495,15 +2547,15 @@ fn airArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
24952547 .double_i64 => {
24962548 cg.arg_index += 2;
24972549 const result = try cg.allocStack(arg_ty);
2498 try cg.store(result, arg, Type.u64, 0);
2499 try cg.store(result, cg.args[arg_index + 1], Type.u64, 8);
2550 try cg.store(result, arg, .u64, .{});
2551 try cg.store(result, cg.args[arg_index + 1], .u64, .{ .offset = 8 });
25002552 return cg.finishAir(inst, result, &.{});
25012553 },
25022554 .unrolled => |vector| {
25032555 const result = try cg.allocStack(arg_ty);
2504 const elem_size: u32 = @intCast(vector.elem_type.abiSize(zcu));
2556 const elem_size = vector.elem_type.abiSize(zcu);
25052557 for (0..vector.len) |index| {
2506 try cg.store(result, cg.args[cg.arg_index], vector.elem_type, @intCast(index * elem_size));
2558 try cg.store(result, cg.args[cg.arg_index], vector.elem_type, .{ .offset = index * elem_size });
25072559 cg.arg_index += 1;
25082560 }
25092561 return cg.finishAir(inst, result, &.{});
......@@ -2605,15 +2657,15 @@ fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
26052657 65...128 => {
26062658 const result = try cg.allocStack(Type.u128);
26072659
2608 var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64);
2660 var lhs_lsb = try (try cg.load(lhs, .u64, .{})).toLocal(cg, Type.u64);
26092661 defer lhs_lsb.free(cg);
2610 var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64);
2662 var rhs_lsb = try (try cg.load(rhs, .u64, .{})).toLocal(cg, Type.u64);
26112663 defer rhs_lsb.free(cg);
26122664 var op_lsb = try (try cg.intAdd(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
26132665 defer op_lsb.free(cg);
26142666
2615 const lhs_msb = try cg.load(lhs, Type.u64, 8);
2616 const rhs_msb = try cg.load(rhs, Type.u64, 8);
2667 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
2668 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
26172669 const op_msb = try cg.intAdd(.u64, lhs_msb, rhs_msb);
26182670
26192671 const lt = try cg.intCmp(.u64, .lt, op_lsb, rhs_lsb);
......@@ -2621,16 +2673,16 @@ fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
26212673 var tmp_op = try (try cg.intAdd(.u64, op_msb, tmp)).toLocal(cg, Type.u64);
26222674 defer tmp_op.free(cg);
26232675
2624 try cg.store(result, op_lsb, Type.u64, 0);
2625 try cg.store(result, tmp_op, Type.u64, 8);
2676 try cg.store(result, op_lsb, .u64, .{});
2677 try cg.store(result, tmp_op, .u64, .{ .offset = 8 });
26262678 return result;
26272679 },
26282680 else => {
26292681 const result = try cg.allocInt(ty);
26302682
2631 try cg.lowerToStack(result);
2632 try cg.lowerToStack(lhs);
2633 try cg.lowerToStack(rhs);
2683 try cg.emitWValue(result);
2684 try cg.emitWValue(lhs);
2685 try cg.emitWValue(rhs);
26342686 try cg.addImm32(@intFromBool(ty.is_signed));
26352687 try cg.addImm32(ty.bits);
26362688 try cg.addCallIntrinsic(.__addo_limb64);
......@@ -2658,15 +2710,15 @@ fn intSub(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
26582710 65...128 => {
26592711 const result = try cg.allocStack(Type.u128);
26602712
2661 var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64);
2713 var lhs_lsb = try (try cg.load(lhs, .u64, .{})).toLocal(cg, Type.u64);
26622714 defer lhs_lsb.free(cg);
2663 var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64);
2715 var rhs_lsb = try (try cg.load(rhs, .u64, .{})).toLocal(cg, Type.u64);
26642716 defer rhs_lsb.free(cg);
26652717 var op_lsb = try (try cg.intSub(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
26662718 defer op_lsb.free(cg);
26672719
2668 const lhs_msb = try cg.load(lhs, Type.u64, 8);
2669 const rhs_msb = try cg.load(rhs, Type.u64, 8);
2720 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
2721 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
26702722 const op_msb = try cg.intSub(.u64, lhs_msb, rhs_msb);
26712723
26722724 const lt = try cg.intCmp(.u64, .lt, lhs_lsb, rhs_lsb);
......@@ -2674,16 +2726,16 @@ fn intSub(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
26742726 var tmp_op = try (try cg.intSub(.u64, op_msb, tmp)).toLocal(cg, Type.u64);
26752727 defer tmp_op.free(cg);
26762728
2677 try cg.store(result, op_lsb, Type.u64, 0);
2678 try cg.store(result, tmp_op, Type.u64, 8);
2729 try cg.store(result, op_lsb, .u64, .{});
2730 try cg.store(result, tmp_op, .u64, .{ .offset = 8 });
26792731 return result;
26802732 },
26812733 else => {
26822734 const result = try cg.allocInt(ty);
26832735
2684 try cg.lowerToStack(result);
2685 try cg.lowerToStack(lhs);
2686 try cg.lowerToStack(rhs);
2736 try cg.emitWValue(result);
2737 try cg.emitWValue(lhs);
2738 try cg.emitWValue(rhs);
26872739 try cg.addImm32(@intFromBool(ty.is_signed));
26882740 try cg.addImm32(ty.bits);
26892741 try cg.addCallIntrinsic(.__subo_limb64);
......@@ -2713,9 +2765,9 @@ fn intMul(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
27132765 else => {
27142766 const result = try cg.allocInt(ty);
27152767
2716 try cg.lowerToStack(result);
2717 try cg.lowerToStack(lhs);
2718 try cg.lowerToStack(rhs);
2768 try cg.emitWValue(result);
2769 try cg.emitWValue(lhs);
2770 try cg.emitWValue(rhs);
27192771 try cg.addImm32(@intFromBool(ty.is_signed));
27202772 try cg.addImm32(ty.bits);
27212773 try cg.addCallIntrinsic(.__mulo_limb64);
......@@ -3007,24 +3059,26 @@ fn intAnd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
30073059 65...128 => {
30083060 const result = try cg.allocStack(Type.u128);
30093061
3010 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
3011 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
3012 const and_lsb = try (try cg.intAnd(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3013 try cg.store(result, and_lsb, Type.u64, 0);
3062 const lhs_lsb = try cg.load(lhs, .u64, .{});
3063 const rhs_lsb = try cg.load(rhs, .u64, .{});
3064 var and_lsb = try (try cg.intAnd(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3065 defer and_lsb.free(cg);
3066 try cg.store(result, and_lsb, .u64, .{});
30143067
3015 const lhs_msb = try cg.load(lhs, Type.u64, 8);
3016 const rhs_msb = try cg.load(rhs, Type.u64, 8);
3017 const and_msb = try (try cg.intAnd(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3018 try cg.store(result, and_msb, Type.u64, 8);
3068 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
3069 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
3070 var and_msb = try (try cg.intAnd(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3071 defer and_msb.free(cg);
3072 try cg.store(result, and_msb, .u64, .{ .offset = 8 });
30193073
30203074 return result;
30213075 },
30223076 else => {
30233077 const result = try cg.allocInt(ty);
30243078
3025 try cg.lowerToStack(result);
3026 try cg.lowerToStack(lhs);
3027 try cg.lowerToStack(rhs);
3079 try cg.emitWValue(result);
3080 try cg.emitWValue(lhs);
3081 try cg.emitWValue(rhs);
30283082 try cg.addImm32(ty.bits);
30293083 try cg.addCallIntrinsic(.__and_limb64);
30303084
......@@ -3051,24 +3105,26 @@ fn intOr(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
30513105 65...128 => {
30523106 const result = try cg.allocStack(Type.u128);
30533107
3054 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
3055 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
3056 const or_lsb = try (try cg.intOr(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3057 try cg.store(result, or_lsb, Type.u64, 0);
3108 const lhs_lsb = try cg.load(lhs, .u64, .{});
3109 const rhs_lsb = try cg.load(rhs, .u64, .{});
3110 var or_lsb = try (try cg.intOr(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3111 defer or_lsb.free(cg);
3112 try cg.store(result, or_lsb, .u64, .{});
30583113
3059 const lhs_msb = try cg.load(lhs, Type.u64, 8);
3060 const rhs_msb = try cg.load(rhs, Type.u64, 8);
3061 const or_msb = try (try cg.intOr(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3062 try cg.store(result, or_msb, Type.u64, 8);
3114 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
3115 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
3116 var or_msb = try (try cg.intOr(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3117 defer or_msb.free(cg);
3118 try cg.store(result, or_msb, .u64, .{ .offset = 8 });
30633119
30643120 return result;
30653121 },
30663122 else => {
30673123 const result = try cg.allocInt(ty);
30683124
3069 try cg.lowerToStack(result);
3070 try cg.lowerToStack(lhs);
3071 try cg.lowerToStack(rhs);
3125 try cg.emitWValue(result);
3126 try cg.emitWValue(lhs);
3127 try cg.emitWValue(rhs);
30723128 try cg.addImm32(ty.bits);
30733129 try cg.addCallIntrinsic(.__or_limb64);
30743130
......@@ -3095,24 +3151,26 @@ fn intXor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
30953151 65...128 => {
30963152 const result = try cg.allocStack(Type.u128);
30973153
3098 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
3099 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
3100 const xor_lsb = try (try cg.intXor(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3101 try cg.store(result, xor_lsb, Type.u64, 0);
3154 const lhs_lsb = try cg.load(lhs, .u64, .{});
3155 const rhs_lsb = try cg.load(rhs, .u64, .{});
3156 var xor_lsb = try (try cg.intXor(.u64, lhs_lsb, rhs_lsb)).toLocal(cg, Type.u64);
3157 defer xor_lsb.free(cg);
3158 try cg.store(result, xor_lsb, .u64, .{});
31023159
3103 const lhs_msb = try cg.load(lhs, Type.u64, 8);
3104 const rhs_msb = try cg.load(rhs, Type.u64, 8);
3105 const xor_msb = try (try cg.intXor(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3106 try cg.store(result, xor_msb, Type.u64, 8);
3160 const lhs_msb = try cg.load(lhs, .u64, .{ .offset = 8 });
3161 const rhs_msb = try cg.load(rhs, .u64, .{ .offset = 8 });
3162 var xor_msb = try (try cg.intXor(.u64, lhs_msb, rhs_msb)).toLocal(cg, Type.u64);
3163 defer xor_msb.free(cg);
3164 try cg.store(result, xor_msb, .u64, .{ .offset = 8 });
31073165
31083166 return result;
31093167 },
31103168 else => {
31113169 const result = try cg.allocInt(ty);
31123170
3113 try cg.lowerToStack(result);
3114 try cg.lowerToStack(lhs);
3115 try cg.lowerToStack(rhs);
3171 try cg.emitWValue(result);
3172 try cg.emitWValue(lhs);
3173 try cg.emitWValue(rhs);
31163174 try cg.addImm32(ty.bits);
31173175 try cg.addCallIntrinsic(.__xor_limb64);
31183176
......@@ -3157,29 +3215,30 @@ fn intNot(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
31573215 65...128 => {
31583216 const result = try cg.allocStack(Type.u128);
31593217
3160 try cg.emitWValue(result);
3161 _ = try cg.load(operand, Type.u64, 0);
3218 const result_offset = result.memOffset();
3219 try cg.emitMemBase(result);
3220 _ = try cg.load(operand, .u64, .{});
31623221 try cg.addImm64(~@as(u64, 0));
31633222 try cg.addTag(.i64_xor);
3164 try cg.store(.stack, .stack, Type.u64, result.offset());
3223 try cg.store(.stack, .stack, .u64, .{ .offset = result_offset });
31653224
3166 try cg.emitWValue(result);
3167 _ = try cg.load(operand, Type.u64, 8);
3225 try cg.emitMemBase(result);
3226 _ = try cg.load(operand, .u64, .{ .offset = 8 });
31683227 const high_mask: u64 = if (ty.is_signed)
31693228 ~@as(u64, 0)
31703229 else
31713230 ~@as(u64, 0) >> @intCast(128 - ty.bits);
31723231 try cg.addImm64(high_mask);
31733232 try cg.addTag(.i64_xor);
3174 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
3233 try cg.store(.stack, .stack, .u64, .{ .offset = result_offset + 8 });
31753234
31763235 return result;
31773236 },
31783237 else => {
31793238 const result = try cg.allocInt(ty);
31803239
3181 try cg.lowerToStack(result);
3182 try cg.lowerToStack(operand);
3240 try cg.emitWValue(result);
3241 try cg.emitWValue(operand);
31833242 try cg.addImm32(@intFromBool(ty.is_signed));
31843243 try cg.addImm32(ty.bits);
31853244 try cg.addCallIntrinsic(.__not_limb64);
......@@ -3211,9 +3270,9 @@ fn intShl(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
32113270 else => {
32123271 const result = try cg.allocInt(ty);
32133272
3214 try cg.lowerToStack(result);
3215 try cg.lowerToStack(lhs);
3216 try cg.lowerToStack(rhs);
3273 try cg.emitWValue(result);
3274 try cg.emitWValue(lhs);
3275 try cg.emitWValue(rhs);
32173276 try cg.addImm32(@intFromBool(ty.is_signed));
32183277 try cg.addImm32(ty.bits);
32193278 try cg.addCallIntrinsic(.__shlo_limb64);
......@@ -3251,9 +3310,9 @@ fn intShr(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
32513310 else => {
32523311 const result = try cg.allocInt(ty);
32533312
3254 try cg.lowerToStack(result);
3255 try cg.lowerToStack(lhs);
3256 try cg.lowerToStack(rhs);
3313 try cg.emitWValue(result);
3314 try cg.emitWValue(lhs);
3315 try cg.emitWValue(rhs);
32573316 try cg.addImm32(@intFromBool(ty.is_signed));
32583317 try cg.addImm32(ty.bits);
32593318 try cg.addCallIntrinsic(.__shr_limb64);
......@@ -3274,7 +3333,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
32743333
32753334 var mask = try cg.allocLocal(Type.i32);
32763335 defer mask.free(cg);
3277 try cg.addLocal(.local_tee, mask.local.value);
3336 try cg.addLocal(.local_tee, mask.local);
32783337
32793338 try cg.emitWValue(operand);
32803339 try cg.addTag(.i32_xor);
......@@ -3289,7 +3348,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
32893348
32903349 var mask = try cg.allocLocal(Type.i64);
32913350 defer mask.free(cg);
3292 try cg.addLocal(.local_tee, mask.local.value);
3351 try cg.addLocal(.local_tee, mask.local);
32933352
32943353 try cg.emitWValue(operand);
32953354 try cg.addTag(.i64_xor);
......@@ -3301,19 +3360,20 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33013360 const u128_ty: IntType = .{ .is_signed = false, .bits = 128 };
33023361
33033362 const mask = try cg.allocStack(Type.u128);
3304 try cg.emitWValue(mask);
3305 try cg.emitWValue(mask);
3363 const mask_offset = mask.memOffset();
3364 try cg.emitMemBase(mask);
3365 try cg.emitMemBase(mask);
33063366
3307 _ = try cg.load(operand, Type.u64, 8);
3367 _ = try cg.load(operand, .u64, .{ .offset = 8 });
33083368 try cg.addImm64(63);
33093369 try cg.addTag(.i64_shr_s);
33103370
33113371 var tmp = try cg.allocLocal(Type.u64);
33123372 defer tmp.free(cg);
3313 try cg.addLocal(.local_tee, tmp.local.value);
3314 try cg.store(.stack, .stack, Type.u64, mask.offset() + 0);
3373 try cg.addLocal(.local_tee, tmp.local);
3374 try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset });
33153375 try cg.emitWValue(tmp);
3316 try cg.store(.stack, .stack, Type.u64, mask.offset() + 8);
3376 try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset + 8 });
33173377
33183378 const a = try cg.intXor(u128_ty, operand, mask);
33193379 const b = try cg.intSub(u128_ty, a, mask);
......@@ -3322,8 +3382,8 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33223382 else => {
33233383 const result = try cg.allocInt(ty);
33243384
3325 try cg.lowerToStack(result);
3326 try cg.lowerToStack(operand);
3385 try cg.emitWValue(result);
3386 try cg.emitWValue(operand);
33273387 try cg.addImm32(ty.bits);
33283388 try cg.addCallIntrinsic(.__abs_limb64);
33293389
......@@ -3333,16 +3393,16 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33333393}
33343394
33353395fn intMax(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
3336 try cg.lowerToStack(lhs);
3337 try cg.lowerToStack(rhs);
3396 try cg.emitWValue(lhs);
3397 try cg.emitWValue(rhs);
33383398 _ = try cg.intCmp(ty, .gt, lhs, rhs);
33393399 try cg.addTag(.select);
33403400 return .stack;
33413401}
33423402
33433403fn intMin(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
3344 try cg.lowerToStack(lhs);
3345 try cg.lowerToStack(rhs);
3404 try cg.emitWValue(lhs);
3405 try cg.emitWValue(rhs);
33463406 _ = try cg.intCmp(ty, .lt, lhs, rhs);
33473407 try cg.addTag(.select);
33483408 return .stack;
......@@ -3381,7 +3441,7 @@ fn intClz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33813441 return .stack;
33823442 },
33833443 65...128 => {
3384 var msb = try (try cg.load(operand, Type.u64, 8)).toLocal(cg, Type.u64);
3444 var msb = try (try cg.load(operand, .u64, .{ .offset = 8 })).toLocal(cg, Type.u64);
33853445 defer msb.free(cg);
33863446
33873447 if (ty.is_signed and ty.bits < 128) {
......@@ -3392,7 +3452,7 @@ fn intClz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33923452 }
33933453
33943454 try cg.addTag(.i64_clz);
3395 _ = try cg.load(operand, Type.u64, 0);
3455 _ = try cg.load(operand, .u64, .{});
33963456 try cg.addTag(.i64_clz);
33973457 try cg.emitWValue(.{ .imm64 = 64 });
33983458 try cg.addTag(.i64_add);
......@@ -3408,7 +3468,7 @@ fn intClz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
34083468 return .stack;
34093469 },
34103470 else => {
3411 try cg.lowerToStack(operand);
3471 try cg.emitWValue(operand);
34123472 try cg.addImm32(ty.bits);
34133473 try cg.addCallIntrinsic(.__clz_limb64);
34143474
......@@ -3440,13 +3500,13 @@ fn intCtz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
34403500 return .stack;
34413501 },
34423502 65...128 => {
3443 var lsb = try (try cg.load(operand, Type.u64, 0)).toLocal(cg, Type.u64);
3503 var lsb = try (try cg.load(operand, .u64, .{})).toLocal(cg, Type.u64);
34443504 defer lsb.free(cg);
34453505
34463506 try cg.emitWValue(lsb);
34473507 try cg.addTag(.i64_ctz);
34483508
3449 _ = try cg.load(operand, Type.u64, 8);
3509 _ = try cg.load(operand, .u64, .{ .offset = 8 });
34503510 if (ty.bits < 128) {
34513511 try cg.addImm64(@as(u64, 1) << @intCast(ty.bits - 64));
34523512 try cg.addTag(.i64_or);
......@@ -3460,7 +3520,7 @@ fn intCtz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
34603520 return .stack;
34613521 },
34623522 else => {
3463 try cg.lowerToStack(operand);
3523 try cg.emitWValue(operand);
34643524 try cg.addImm32(ty.bits);
34653525 try cg.addCallIntrinsic(.__ctz_limb64);
34663526
......@@ -3492,9 +3552,9 @@ fn intPopCount(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
34923552 return .stack;
34933553 },
34943554 65...128 => {
3495 _ = try cg.load(operand, Type.u64, 0);
3555 _ = try cg.load(operand, .u64, .{});
34963556 try cg.addTag(.i64_popcnt);
3497 _ = try cg.load(operand, Type.u64, 8);
3557 _ = try cg.load(operand, .u64, .{ .offset = 8 });
34983558 if (ty.is_signed and ty.bits < 128) {
34993559 try cg.addImm64(128 - ty.bits);
35003560 try cg.addTag(.i64_shl);
......@@ -3506,7 +3566,7 @@ fn intPopCount(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
35063566 return .stack;
35073567 },
35083568 else => {
3509 try cg.lowerToStack(operand);
3569 try cg.emitWValue(operand);
35103570 try cg.addImm32(ty.bits);
35113571 try cg.addCallIntrinsic(.__popcount_limb64);
35123572
......@@ -3541,8 +3601,9 @@ fn intBitReverse(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
35413601 65...128 => {
35423602 const tmp = try cg.allocStack(Type.u128);
35433603
3544 try cg.emitWValue(tmp);
3545 const hi = try cg.load(operand, Type.u64, 8);
3604 const tmp_offset = tmp.memOffset();
3605 try cg.emitMemBase(tmp);
3606 const hi = try cg.load(operand, .u64, .{ .offset = 8 });
35463607 const hi_rev = try cg.callIntrinsic(
35473608 .__bitreversedi2,
35483609 &.{.u64_type},
......@@ -3550,10 +3611,10 @@ fn intBitReverse(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
35503611 &.{hi},
35513612 );
35523613 try cg.emitWValue(hi_rev);
3553 try cg.store(.stack, .stack, Type.u64, tmp.offset());
3614 try cg.store(.stack, .stack, .u64, .{ .offset = tmp_offset });
35543615
3555 try cg.emitWValue(tmp);
3556 const lo = try cg.load(operand, Type.u64, 0);
3616 try cg.emitMemBase(tmp);
3617 const lo = try cg.load(operand, .u64, .{});
35573618 const lo_rev = try cg.callIntrinsic(
35583619 .__bitreversedi2,
35593620 &.{.u64_type},
......@@ -3561,7 +3622,7 @@ fn intBitReverse(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
35613622 &.{lo},
35623623 );
35633624 try cg.emitWValue(lo_rev);
3564 try cg.store(.stack, .stack, Type.u64, tmp.offset() + 8);
3625 try cg.store(.stack, .stack, .u64, .{ .offset = tmp_offset + 8 });
35653626
35663627 if (ty.bits < 128) {
35673628 const shift_ty: IntType = .{ .is_signed = ty.is_signed, .bits = 128 };
......@@ -3573,8 +3634,8 @@ fn intBitReverse(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
35733634 else => {
35743635 const result = try cg.allocInt(ty);
35753636
3576 try cg.lowerToStack(result);
3577 try cg.lowerToStack(operand);
3637 try cg.emitWValue(result);
3638 try cg.emitWValue(operand);
35783639 try cg.addImm32(@intFromBool(ty.is_signed));
35793640 try cg.addImm32(ty.bits);
35803641 try cg.addCallIntrinsic(.__bitreverse_limb64);
......@@ -3610,27 +3671,28 @@ fn intByteSwap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
36103671 65...128 => {
36113672 const result = try cg.allocStack(Type.u128);
36123673
3613 try cg.emitWValue(result);
3674 const result_offset = result.memOffset();
3675 try cg.emitMemBase(result);
36143676
3615 const low = try cg.load(operand, Type.u64, 0);
3677 const low = try cg.load(operand, .u64, .{});
36163678 const swap_low = try cg.callIntrinsic(
36173679 .__bswapdi2,
36183680 &.{.u64_type},
36193681 Type.u64,
36203682 &.{low},
36213683 );
3622 try cg.store(.stack, swap_low, Type.u64, result.offset() + 8);
3684 try cg.store(.stack, swap_low, .u64, .{ .offset = result_offset + 8 });
36233685
3624 try cg.emitWValue(result);
3686 try cg.emitMemBase(result);
36253687
3626 const high = try cg.load(operand, Type.u64, 8);
3688 const high = try cg.load(operand, .u64, .{ .offset = 8 });
36273689 const swap_high = try cg.callIntrinsic(
36283690 .__bswapdi2,
36293691 &.{.u64_type},
36303692 Type.u64,
36313693 &.{high},
36323694 );
3633 try cg.store(.stack, swap_high, Type.u64, result.offset());
3695 try cg.store(.stack, swap_high, .u64, .{ .offset = result_offset });
36343696
36353697 if (ty.bits < 128) {
36363698 const shift_ty: IntType = .{ .is_signed = ty.is_signed, .bits = 128 };
......@@ -3642,8 +3704,8 @@ fn intByteSwap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
36423704 else => {
36433705 const result = try cg.allocInt(ty);
36443706
3645 try cg.lowerToStack(result);
3646 try cg.lowerToStack(operand);
3707 try cg.emitWValue(result);
3708 try cg.emitWValue(operand);
36473709 try cg.addImm32(@intFromBool(ty.is_signed));
36483710 try cg.addImm32(ty.bits);
36493711 try cg.addCallIntrinsic(.__byteswap_limb64);
......@@ -3687,12 +3749,13 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
36873749 65...127 => {
36883750 const result = try cg.allocStack(Type.u128);
36893751
3690 try cg.emitWValue(result);
3691 _ = try cg.load(operand, Type.u64, 0);
3692 try cg.store(.stack, .stack, Type.u64, result.offset());
3752 const result_offset = result.memOffset();
3753 try cg.emitMemBase(result);
3754 _ = try cg.load(operand, .u64, .{});
3755 try cg.store(.stack, .stack, .u64, .{ .offset = result_offset });
36933756
3694 try cg.emitWValue(result);
3695 _ = try cg.load(operand, Type.u64, 8);
3757 try cg.emitMemBase(result);
3758 _ = try cg.load(operand, .u64, .{ .offset = 8 });
36963759 if (ty.is_signed) {
36973760 try cg.addImm64(128 - ty.bits);
36983761 try cg.addTag(.i64_shl);
......@@ -3702,7 +3765,7 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
37023765 try cg.addImm64(~@as(u64, 0) >> @intCast(128 - ty.bits));
37033766 try cg.addTag(.i64_and);
37043767 }
3705 try cg.store(.stack, .stack, Type.u64, result.offset() + 8);
3768 try cg.store(.stack, .stack, .u64, .{ .offset = result_offset + 8 });
37063769
37073770 return result;
37083771 },
......@@ -3719,8 +3782,8 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
37193782 try cg.memcpy(result, operand, .{ .imm32 = used_len - 8 });
37203783 const pad = 64 - ty.bits % 64;
37213784
3722 try cg.emitWValue(result);
3723 _ = try cg.load(operand, Type.u64, used_len - 8);
3785 try cg.emitMemBase(result);
3786 _ = try cg.load(operand, .u64, .{ .offset = used_len - 8 });
37243787 if (ty.is_signed) {
37253788 try cg.addImm64(pad);
37263789 try cg.addTag(.i64_shl);
......@@ -3730,22 +3793,22 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
37303793 try cg.addImm64(~@as(u64, 0) >> @intCast(pad));
37313794 try cg.addTag(.i64_and);
37323795 }
3733 try cg.store(.stack, .stack, Type.u64, result.offset() + used_len - 8);
3796 try cg.store(.stack, .stack, .u64, .{ .offset = result.memOffset() + used_len - 8 });
37343797 } else {
37353798 try cg.memcpy(result, operand, .{ .imm32 = used_len });
37363799 }
37373800
37383801 const full_len = @divExact(bits, 8);
37393802 if (used_len + 8 == full_len) { // last limb needs sign extended
3740 try cg.emitWValue(result);
3803 try cg.emitMemBase(result);
37413804 if (ty.is_signed) {
3742 _ = try cg.load(result, Type.u64, used_len - 8);
3805 _ = try cg.load(result, .u64, .{ .offset = used_len - 8 });
37433806 try cg.addImm64(63);
37443807 try cg.addTag(.i64_shr_s);
37453808 } else {
37463809 try cg.addImm64(0);
37473810 }
3748 try cg.store(.stack, .stack, Type.u64, result.offset() + used_len);
3811 try cg.store(.stack, .stack, .u64, .{ .offset = result.memOffset() + used_len });
37493812 }
37503813
37513814 return result;
......@@ -3768,12 +3831,12 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
37683831 }
37693832 } else if (int_ty.bits <= 128) {
37703833 const result = try cg.allocInt(int_ty);
3771 try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, 0);
3834 try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, .u64, .{});
37723835
37733836 if (int_ty.is_signed) {
3774 try cg.store(result, .{ .imm64 = (~@as(u64, 0) >> @intCast(128 - int_ty.bits)) >> 1 }, Type.u64, 8);
3837 try cg.store(result, .{ .imm64 = (~@as(u64, 0) >> @intCast(128 - int_ty.bits)) >> 1 }, .u64, .{ .offset = 8 });
37753838 } else {
3776 try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(128 - int_ty.bits) }, Type.u64, 8);
3839 try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(128 - int_ty.bits) }, .u64, .{ .offset = 8 });
37773840 }
37783841 return result;
37793842 } else {
......@@ -3784,13 +3847,13 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
37843847 try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0xFF });
37853848
37863849 if (int_ty.is_signed) {
3787 try cg.store(result, .{ .imm64 = (~@as(u64, 0) >> @intCast(used_len * 8 - int_ty.bits)) >> 1 }, Type.u64, used_len - 8);
3850 try cg.store(result, .{ .imm64 = (~@as(u64, 0) >> @intCast(used_len * 8 - int_ty.bits)) >> 1 }, .u64, .{ .offset = used_len - 8 });
37883851 } else {
3789 try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(used_len * 8 - int_ty.bits) }, Type.u64, used_len - 8);
3852 try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(used_len * 8 - int_ty.bits) }, .u64, .{ .offset = used_len - 8 });
37903853 }
37913854
37923855 if (used_len + 8 == full_len) {
3793 try cg.store(result, .{ .imm64 = 0 }, Type.u64, full_len - 8);
3856 try cg.store(result, .{ .imm64 = 0 }, .u64, .{ .offset = full_len - 8 });
37943857 }
37953858
37963859 return result;
......@@ -3807,8 +3870,8 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
38073870 return .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 1) };
38083871 } else if (int_ty.bits <= 128) {
38093872 const result = try cg.allocInt(int_ty);
3810 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
3811 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 65) }, Type.u64, 8);
3873 try cg.store(result, .{ .imm64 = 0 }, .u64, .{});
3874 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 65) }, .u64, .{ .offset = 8 });
38123875 return result;
38133876 } else {
38143877 const result = try cg.allocInt(int_ty);
......@@ -3816,10 +3879,10 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
38163879 const used_len = @divCeil(int_ty.bits, 64) * 8;
38173880
38183881 try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0 });
3819 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - (used_len - 8) * 8 - 1) }, Type.u64, used_len - 8);
3882 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - (used_len - 8) * 8 - 1) }, .u64, .{ .offset = used_len - 8 });
38203883
38213884 if (used_len + 8 == full_len) {
3822 try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, full_len - 8);
3885 try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, .u64, .{ .offset = full_len - 8 });
38233886 }
38243887
38253888 return result;
......@@ -3839,20 +3902,20 @@ fn intAddSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
38393902 defer rhs_is_neg.free(cg);
38403903 const min_val = try cg.intMinValue(int_ty);
38413904
3842 try cg.lowerToStack(min_val);
3843 try cg.lowerToStack(max_val);
3905 try cg.emitWValue(min_val);
3906 try cg.emitWValue(max_val);
38443907 try cg.emitWValue(rhs_is_neg);
38453908 try cg.addTag(.select);
38463909
3847 try cg.lowerToStack(op_val);
3910 try cg.emitWValue(op_val);
38483911 const overflow_cmp = try cg.intCmp(int_ty, .lt, op_val, lhs);
38493912 const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp);
38503913 try cg.emitWValue(is_overflow);
38513914 try cg.addTag(.select);
38523915 return .stack;
38533916 } else {
3854 try cg.lowerToStack(max_val);
3855 try cg.lowerToStack(op_val);
3917 try cg.emitWValue(max_val);
3918 try cg.emitWValue(op_val);
38563919
38573920 const is_overflow = try cg.intCmp(int_ty, .lt, op_val, lhs);
38583921 try cg.emitWValue(is_overflow);
......@@ -3873,12 +3936,12 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
38733936 const max_val = try cg.intMaxValue(int_ty);
38743937 const min_val = try cg.intMinValue(int_ty);
38753938
3876 try cg.lowerToStack(max_val);
3877 try cg.lowerToStack(min_val);
3939 try cg.emitWValue(max_val);
3940 try cg.emitWValue(min_val);
38783941 try cg.emitWValue(rhs_is_neg);
38793942 try cg.addTag(.select);
38803943
3881 try cg.lowerToStack(op_val);
3944 try cg.emitWValue(op_val);
38823945 const overflow_cmp = try cg.intCmp(int_ty, .gt, op_val, lhs);
38833946 const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp);
38843947 try cg.emitWValue(is_overflow);
......@@ -3887,8 +3950,8 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
38873950 } else {
38883951 const zero = try cg.intZeroValue(int_ty);
38893952
3890 try cg.lowerToStack(zero);
3891 try cg.lowerToStack(op_val);
3953 try cg.emitWValue(zero);
3954 try cg.emitWValue(op_val);
38923955 const is_overflow = try cg.intCmp(int_ty, .lt, lhs, rhs);
38933956 try cg.emitWValue(is_overflow);
38943957 try cg.addTag(.select);
......@@ -3912,10 +3975,10 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
39123975 if (int_ty.is_signed) {
39133976 const min_val = try cg.intMinValue(int_ty);
39143977
3915 try cg.lowerToStack(min_val);
3978 try cg.emitWValue(min_val);
39163979
3917 try cg.lowerToStack(max_val);
3918 try cg.lowerToStack(op_val);
3980 try cg.emitWValue(max_val);
3981 try cg.emitWValue(op_val);
39193982 const max_ext = try cg.intCast(ext_ty, int_ty, max_val);
39203983 const ov_pos = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext);
39213984 try cg.emitWValue(ov_pos);
......@@ -3923,12 +3986,12 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
39233986
39243987 const min_ext = try cg.intCast(ext_ty, int_ty, min_val);
39253988 const ov_neg = try cg.intCmp(ext_ty, .gt, min_ext, mul_ext);
3926 try cg.lowerToStack(ov_neg);
3989 try cg.emitWValue(ov_neg);
39273990 try cg.addTag(.select);
39283991 return .stack;
39293992 } else {
3930 try cg.lowerToStack(max_val);
3931 try cg.lowerToStack(op_val);
3993 try cg.emitWValue(max_val);
3994 try cg.emitWValue(op_val);
39323995 const max_ext = try cg.intCast(ext_ty, int_ty, max_val);
39333996 const is_overflow = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext);
39343997 try cg.emitWValue(is_overflow);
......@@ -3951,20 +4014,20 @@ fn intShlSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError
39514014 const zero = try cg.intZeroValue(int_ty);
39524015 const min_val = try cg.intMinValue(int_ty);
39534016
3954 try cg.lowerToStack(min_val);
3955 try cg.lowerToStack(max_val);
4017 try cg.emitWValue(min_val);
4018 try cg.emitWValue(max_val);
39564019 const lhs_is_neg = try cg.intCmp(int_ty, .lt, lhs, zero);
39574020 try cg.emitWValue(lhs_is_neg);
39584021 try cg.addTag(.select);
39594022
3960 try cg.lowerToStack(op_val);
4023 try cg.emitWValue(op_val);
39614024 const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs);
39624025 try cg.emitWValue(is_overflow);
39634026 try cg.addTag(.select);
39644027 return .stack;
39654028 } else {
3966 try cg.lowerToStack(max_val);
3967 try cg.lowerToStack(op_val);
4029 try cg.emitWValue(max_val);
4030 try cg.emitWValue(op_val);
39684031 const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs);
39694032 try cg.emitWValue(is_overflow);
39704033 try cg.addTag(.select);
......@@ -3979,8 +4042,8 @@ fn intZeroValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
39794042 33...64 => return .{ .imm64 = 0 },
39804043 65...128 => {
39814044 const result = try cg.allocInt(int_ty);
3982 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
3983 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8);
4045 try cg.store(result, .{ .imm64 = 0 }, .u64, .{});
4046 try cg.store(result, .{ .imm64 = 0 }, .u64, .{ .offset = 8 });
39844047 return result;
39854048 },
39864049 else => {
......@@ -4003,7 +4066,7 @@ fn toLocalInt(cg: *CodeGen, value: WValue, int_ty: IntType) InnerError!WValue {
40034066 else => return cg.fail("TODO: Support toLocalInt for integer bitsize: {d}", .{int_ty.bits}),
40044067 };
40054068 const new_local = try cg.allocLocal(ty);
4006 try cg.addLocal(.local_set, new_local.local.value);
4069 try cg.addLocal(.local_set, new_local.local);
40074070 return new_local;
40084071 },
40094072 .local, .stack_offset => return value,
......@@ -4036,9 +4099,9 @@ fn intAddOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
40364099 else => {
40374100 const result = try cg.allocInt(ty);
40384101
4039 try cg.lowerToStack(result);
4040 try cg.lowerToStack(lhs);
4041 try cg.lowerToStack(rhs);
4102 try cg.emitWValue(result);
4103 try cg.emitWValue(lhs);
4104 try cg.emitWValue(rhs);
40424105 try cg.addImm32(@intFromBool(ty.is_signed));
40434106 try cg.addImm32(ty.bits);
40444107 try cg.addCallIntrinsic(.__addo_limb64);
......@@ -4068,9 +4131,9 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
40684131 else => {
40694132 const result = try cg.allocInt(ty);
40704133
4071 try cg.lowerToStack(result);
4072 try cg.lowerToStack(lhs);
4073 try cg.lowerToStack(rhs);
4134 try cg.emitWValue(result);
4135 try cg.emitWValue(lhs);
4136 try cg.emitWValue(rhs);
40744137 try cg.addImm32(@intFromBool(ty.is_signed));
40754138 try cg.addImm32(ty.bits);
40764139 try cg.addCallIntrinsic(.__subo_limb64);
......@@ -4080,39 +4143,55 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
40804143}
40814144
40824145fn 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
40834159 const overflow_bit = try cg.allocLocal(Type.u32);
40844160 try cg.addImm32(0);
4085 try cg.addLocal(.local_set, overflow_bit.local.value);
4161 try cg.addLocal(.local_set, overflow_bit.local);
40864162
40874163 const result_val = if (int_ty.bits <= 32) blk: {
40884164 const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 64 };
40894165 const lhs_upcast = try cg.intCast(new_ty, int_ty, lhs);
40904166 const rhs_upcast = try cg.intCast(new_ty, int_ty, rhs);
40914167 const mul_raw = try cg.intMul(new_ty, lhs_upcast, rhs_upcast);
4092 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);
40934170
40944171 const res = try cg.intTrunc(int_ty, new_ty, bin_op);
40954172 const res_tmp = try cg.toLocalInt(res, int_ty);
40964173
40974174 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);
40984175 _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op);
4099 try cg.addLocal(.local_set, overflow_bit.local.value);
4176 try cg.addLocal(.local_set, overflow_bit.local);
41004177 break :blk res_tmp;
41014178 } else if (int_ty.bits <= 64) blk: {
41024179 const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 128 };
41034180 const lhs_upcast = try cg.intCast(new_ty, int_ty, lhs);
41044181 const rhs_upcast = try cg.intCast(new_ty, int_ty, rhs);
41054182 const mul_raw = try cg.intMul(new_ty, lhs_upcast, rhs_upcast);
4106 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);
41074185
41084186 const res = try cg.intTrunc(int_ty, new_ty, bin_op);
41094187 const res_tmp = try cg.toLocalInt(res, int_ty);
41104188
41114189 const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp);
41124190 _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op);
4113 try cg.addLocal(.local_set, overflow_bit.local.value);
4191 try cg.addLocal(.local_set, overflow_bit.local);
41144192 break :blk res_tmp;
4115 } 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);
41164195 const overflow_ret = try cg.allocStack(Type.i32);
41174196 const res = try cg.callIntrinsic(
41184197 .__muloti4,
......@@ -4120,20 +4199,9 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner
41204199 Type.i128,
41214200 &.{ lhs, rhs, overflow_ret },
41224201 );
4123 _ = try cg.load(overflow_ret, Type.i32, 0);
4124 try cg.addLocal(.local_set, overflow_bit.local.value);
4202 _ = try cg.load(overflow_ret, .i32, .{});
4203 try cg.addLocal(.local_set, overflow_bit.local);
41254204 break :blk res;
4126 } else {
4127 const result = try cg.allocInt(int_ty);
4128
4129 try cg.lowerToStack(result);
4130 try cg.lowerToStack(lhs);
4131 try cg.lowerToStack(rhs);
4132 try cg.addImm32(@intFromBool(int_ty.is_signed));
4133 try cg.addImm32(int_ty.bits);
4134 try cg.addCallIntrinsic(.__mulo_limb64);
4135
4136 return .{ .result = result, .ov = .stack };
41374205 };
41384206
41394207 return .{ .result = result_val, .ov = .{ .local = overflow_bit.local } };
......@@ -4155,9 +4223,9 @@ fn intShlOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
41554223 else => {
41564224 const result = try cg.allocInt(ty);
41574225
4158 try cg.lowerToStack(result);
4159 try cg.lowerToStack(lhs);
4160 try cg.lowerToStack(rhs);
4226 try cg.emitWValue(result);
4227 try cg.emitWValue(lhs);
4228 try cg.emitWValue(rhs);
41614229 try cg.addImm32(@intFromBool(ty.is_signed));
41624230 try cg.addImm32(ty.bits);
41634231 try cg.addCallIntrinsic(.__shlo_limb64);
......@@ -4194,15 +4262,15 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
41944262 } else {
41954263 var src_len: u32 = undefined;
41964264 if (src_bits == 32) {
4197 try cg.emitWValue(result);
4265 try cg.emitMemBase(result);
41984266 try cg.emitWValue(operand);
41994267 try cg.addTag(if (src_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u);
4200 try cg.store(.stack, .stack, Type.u64, result.offset());
4268 try cg.store(.stack, .stack, .u64, .{ .offset = result.memOffset() });
42014269 src_len = 8;
42024270 } else if (src_bits == 64) {
4203 try cg.emitWValue(result);
4271 try cg.emitMemBase(result);
42044272 try cg.emitWValue(operand);
4205 try cg.store(.stack, .stack, Type.u64, result.offset());
4273 try cg.store(.stack, .stack, .u64, .{ .offset = result.memOffset() });
42064274 src_len = 8;
42074275 } else {
42084276 src_len = src_bits / 8;
......@@ -4211,7 +4279,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
42114279
42124280 if (dest_bits == 128) {
42134281 if (src_ty.is_signed) {
4214 try cg.emitWValue(result);
4282 try cg.emitMemBase(result);
42154283 if (src_bits == 32) {
42164284 try cg.emitWValue(operand);
42174285 try cg.addTag(if (dest_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u);
......@@ -4219,9 +4287,9 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
42194287 try cg.emitWValue(operand);
42204288 } else unreachable;
42214289 const shr = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 });
4222 try cg.store(.stack, shr, Type.u64, 8 + result.offset());
4290 try cg.store(.stack, shr, .u64, .{ .offset = result.memOffset() + 8 });
42234291 } else {
4224 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8);
4292 try cg.store(result, .{ .imm64 = 0 }, .u64, .{ .offset = 8 });
42254293 }
42264294 } else {
42274295 var pad = result;
......@@ -4236,7 +4304,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
42364304 _ = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 });
42374305 try cg.addTag(.i32_wrap_i64);
42384306 } else {
4239 _ = try cg.load(operand, Type.u64, src_len - 8);
4307 _ = try cg.load(operand, .u64, .{ .offset = src_len - 8 });
42404308 _ = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 });
42414309 try cg.addTag(.i32_wrap_i64);
42424310 }
......@@ -4254,11 +4322,12 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
42544322 assert(dest_bits <= 64);
42554323 assert(src_bits >= 128);
42564324 const load_ty = if (dest_bits == 32) Type.u32 else Type.u64;
4257 return cg.load(operand, load_ty, 0);
4325 return cg.load(operand, load_ty, .{});
42584326 }
42594327}
42604328
42614329fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue {
4330 assert(src_ty.bits > dest_ty.bits);
42624331 var result = try cg.intCast(dest_ty, src_ty, operand);
42634332
42644333 const dest_wasm_bits = cg.intBackingBits(dest_ty.bits);
......@@ -4622,20 +4691,22 @@ fn floatNeg(cg: *CodeGen, ty: FloatType, arg: WValue) InnerError!WValue {
46224691 },
46234692 .f80, .f128 => {
46244693 const result = try cg.allocStack(Type.f128);
4625 try cg.emitWValue(result);
4626 try cg.emitWValue(arg);
4627 try cg.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 });
4628 try cg.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 });
4629 try cg.emitWValue(result);
4630 try cg.emitWValue(arg);
4631 try cg.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 });
4694 const result_offset = result.memOffset();
4695 const arg_offset = arg.memOffset();
4696 try cg.emitMemBase(result);
4697 try cg.emitMemBase(arg);
4698 try cg.addMemArg(.i64_load, .{ .offset = arg_offset, .alignment = .@"2" });
4699 try cg.addMemArg(.i64_store, .{ .offset = result_offset, .alignment = .@"2" });
4700 try cg.emitMemBase(result);
4701 try cg.emitMemBase(arg);
4702 try cg.addMemArg(.i64_load, .{ .offset = arg_offset + 8, .alignment = .@"2" });
46324703 if (ty == .f80) {
46334704 try cg.addImm64(0x8000);
46344705 } else {
46354706 try cg.addImm64(0x8000000000000000);
46364707 }
46374708 try cg.addTag(.i64_xor);
4638 try cg.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 });
4709 try cg.addMemArg(.i64_store, .{ .offset = result_offset + 8, .alignment = .@"2" });
46394710 return result;
46404711 },
46414712 }
......@@ -4661,20 +4732,22 @@ fn floatAbs(cg: *CodeGen, ty: FloatType, arg: WValue) InnerError!WValue {
46614732 },
46624733 .f80, .f128 => {
46634734 const result = try cg.allocStack(Type.f128);
4664 try cg.emitWValue(result);
4665 try cg.emitWValue(arg);
4666 try cg.addMemArg(.i64_load, .{ .offset = 0 + arg.offset(), .alignment = 2 });
4667 try cg.addMemArg(.i64_store, .{ .offset = 0 + result.offset(), .alignment = 2 });
4668 try cg.emitWValue(result);
4669 try cg.emitWValue(arg);
4670 try cg.addMemArg(.i64_load, .{ .offset = 8 + arg.offset(), .alignment = 2 });
4735 const result_offset = result.memOffset();
4736 const arg_offset = arg.memOffset();
4737 try cg.emitMemBase(result);
4738 try cg.emitMemBase(arg);
4739 try cg.addMemArg(.i64_load, .{ .offset = arg_offset, .alignment = .@"2" });
4740 try cg.addMemArg(.i64_store, .{ .offset = result_offset, .alignment = .@"2" });
4741 try cg.emitMemBase(result);
4742 try cg.emitMemBase(arg);
4743 try cg.addMemArg(.i64_load, .{ .offset = arg_offset + 8, .alignment = .@"2" });
46714744 if (ty == .f80) {
46724745 try cg.addImm64(0x7FFF);
46734746 } else {
46744747 try cg.addImm64(0x7FFFFFFFFFFFFFFF);
46754748 }
46764749 try cg.addTag(.i64_and);
4677 try cg.addMemArg(.i64_store, .{ .offset = 8 + result.offset(), .alignment = 2 });
4750 try cg.addMemArg(.i64_store, .{ .offset = result_offset + 8, .alignment = .@"2" });
46784751 return result;
46794752 },
46804753 }
......@@ -5184,30 +5257,36 @@ fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51845257
51855258fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {
51865259 const zcu = cg.pt.zcu;
5187 // 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
51885267 const block_result: WValue = if (block_ty.hasRuntimeBits(zcu))
5189 try cg.allocLocal(block_ty)
5268 .stack
51905269 else
51915270 .none;
51925271
5193 try cg.startBlock(.block, .empty);
5272 try cg.startBlock(.block, genBlockType(block_ty, zcu, cg.target));
51945273 // Here we set the current block idx, so breaks know the depth to jump
51955274 // to when breaking out.
51965275 try cg.blocks.putNoClobber(cg.gpa, inst, .{
51975276 .label = cg.block_depth,
51985277 .value = block_result,
51995278 });
5279 defer assert(cg.blocks.swapRemove(inst));
52005280
52015281 {
5202 try cg.branches.append(cg.gpa, .{});
5203 defer {
5204 var branch = cg.branches.pop().?;
5205 branch.deinit(cg.gpa);
5206 }
5282 try cg.pushBranch();
5283 defer cg.popBranch();
52075284 try cg.genBody(body);
52085285 try cg.endBlock();
52095286 }
52105287
5288 for (cg.liveness.getBlock(inst).deaths) |death| cg.processDeath(death.toRef());
5289
52115290 return cg.finishAir(inst, block_result, &.{});
52125291}
52135292
......@@ -5238,6 +5317,7 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52385317
52395318 try cg.genBody(block.body);
52405319 try cg.endBlock();
5320 try cg.addTag(.@"unreachable");
52415321
52425322 return cg.finishAir(inst, .none, &.{});
52435323}
......@@ -5247,6 +5327,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52475327 const condition = try cg.resolveInst(cond_br.condition);
52485328 const then_body = cond_br.then_body;
52495329 const else_body = cond_br.else_body;
5330 const branch_liveness = cg.liveness.getCondBr(inst);
52505331
52515332 // result type is always noreturn, so use `block_empty` as type.
52525333 try cg.startBlock(.block, .empty);
......@@ -5258,24 +5339,21 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52585339 // and continue with the then codepath
52595340 try cg.addLabel(.br_if, 0);
52605341
5261 try cg.branches.ensureUnusedCapacity(cg.gpa, 2);
5342 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(cond_br.condition);
5343
52625344 {
5263 cg.branches.appendAssumeCapacity(.{});
5264 defer {
5265 var else_stack = cg.branches.pop().?;
5266 else_stack.deinit(cg.gpa);
5267 }
5345 try cg.pushBranch();
5346 defer cg.popBranch();
5347 for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef());
52685348 try cg.genBody(else_body);
52695349 try cg.endBlock();
52705350 }
52715351
52725352 // Outer block that matches the condition
52735353 {
5274 cg.branches.appendAssumeCapacity(.{});
5275 defer {
5276 var then_stack = cg.branches.pop().?;
5277 then_stack.deinit(cg.gpa);
5278 }
5354 try cg.pushBranch();
5355 defer cg.popBranch();
5356 for (branch_liveness.then_deaths) |death| cg.processDeath(death.toRef());
52795357 try cg.genBody(then_body);
52805358 }
52815359
......@@ -5311,22 +5389,22 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne
53115389 try cg.startBlock(.block, .empty);
53125390
53135391 try cg.addImm32(if (op == .eq) 0 else 1);
5314 try cg.addLocal(.local_set, result.local.value);
5392 try cg.addLocal(.local_set, result.local);
53155393
53165394 _ = try cg.isNull(lhs, operand_ty, .i32_eq, .value);
5317 try cg.addLocal(.local_tee, lhs_null.local.value);
5395 try cg.addLocal(.local_tee, lhs_null.local);
53185396 _ = try cg.isNull(rhs, operand_ty, .i32_eq, .value);
53195397 try cg.addTag(.i32_ne);
53205398 try cg.addLabel(.br_if, 0);
53215399
53225400 try cg.addImm32(if (op == .eq) 1 else 0);
5323 try cg.addLocal(.local_set, result.local.value);
5401 try cg.addLocal(.local_set, result.local);
53245402
5325 try cg.addLocal(.local_get, lhs_null.local.value);
5403 try cg.addLocal(.local_get, lhs_null.local);
53265404 try cg.addLabel(.br_if, 0);
53275405
5328 _ = try cg.load(lhs, payload_ty, 0);
5329 _ = try cg.load(rhs, payload_ty, 0);
5406 _ = try cg.load(lhs, payload_ty, .{});
5407 _ = try cg.load(rhs, payload_ty, .{});
53305408
53315409 if (payload_ty.isAnyFloat()) {
53325410 _ = try cg.floatCmp(.fromType(cg, payload_ty), op, .stack, .stack);
......@@ -5334,10 +5412,10 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne
53345412 _ = try cg.intCmp(.fromType(cg, payload_ty), op, .stack, .stack);
53355413 }
53365414
5337 try cg.addLocal(.local_set, result.local.value);
5415 try cg.addLocal(.local_set, result.local);
53385416 try cg.endBlock();
53395417
5340 try cg.addLocal(.local_get, result.local.value);
5418 try cg.addLocal(.local_get, result.local);
53415419 try cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
53425420 } else {
53435421 const result = try cg.intCmp(.fromType(cg, operand_ty), op, lhs, rhs);
......@@ -5357,8 +5435,8 @@ fn intCmp(cg: *CodeGen, ty: IntType, op: std.math.CompareOperator, lhs: WValue,
53575435 0 => unreachable,
53585436 1...32 => {
53595437 // lhs or rhs could be stack pointers
5360 try cg.lowerToStack(lhs);
5361 try cg.lowerToStack(rhs);
5438 try cg.emitWValue(lhs);
5439 try cg.emitWValue(rhs);
53625440 const opcode: Mir.Inst.Tag = switch (op) {
53635441 .eq => .i32_eq,
53645442 .neq => .i32_ne,
......@@ -5372,8 +5450,8 @@ fn intCmp(cg: *CodeGen, ty: IntType, op: std.math.CompareOperator, lhs: WValue,
53725450 },
53735451 33...64 => {
53745452 // lhs or rhs could be stack pointers
5375 try cg.lowerToStack(lhs);
5376 try cg.lowerToStack(rhs);
5453 try cg.emitWValue(lhs);
5454 try cg.emitWValue(rhs);
53775455 const opcode: Mir.Inst.Tag = switch (op) {
53785456 .eq => .i64_eq,
53795457 .neq => .i64_ne,
......@@ -5386,16 +5464,16 @@ fn intCmp(cg: *CodeGen, ty: IntType, op: std.math.CompareOperator, lhs: WValue,
53865464 return .stack;
53875465 },
53885466 65...128 => {
5389 var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64);
5467 var lhs_msb = try (try cg.load(lhs, .u64, .{ .offset = 8 })).toLocal(cg, Type.u64);
53905468 defer lhs_msb.free(cg);
5391 var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64);
5469 var rhs_msb = try (try cg.load(rhs, .u64, .{ .offset = 8 })).toLocal(cg, Type.u64);
53925470 defer rhs_msb.free(cg);
53935471
53945472 switch (op) {
53955473 .eq, .neq => {
53965474 const xor_high = try cg.intXor(.u64, lhs_msb, rhs_msb);
5397 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
5398 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
5475 const lhs_lsb = try cg.load(lhs, .u64, .{});
5476 const rhs_lsb = try cg.load(rhs, .u64, .{});
53995477 const xor_low = try cg.intXor(.u64, lhs_lsb, rhs_lsb);
54005478 const or_result = try cg.intOr(.u64, xor_high, xor_low);
54015479
......@@ -5408,8 +5486,8 @@ fn intCmp(cg: *CodeGen, ty: IntType, op: std.math.CompareOperator, lhs: WValue,
54085486 else => {
54095487 const word_int_ty: IntType = if (ty.is_signed) .i64 else .u64;
54105488
5411 const lhs_lsb = try cg.load(lhs, Type.u64, 0);
5412 const rhs_lsb = try cg.load(rhs, Type.u64, 0);
5489 const lhs_lsb = try cg.load(lhs, .u64, .{});
5490 const rhs_lsb = try cg.load(rhs, .u64, .{});
54135491
54145492 // leave values on stack for 'select'
54155493 _ = try cg.intCmp(.u64, op, lhs_lsb, rhs_lsb);
......@@ -5422,8 +5500,8 @@ fn intCmp(cg: *CodeGen, ty: IntType, op: std.math.CompareOperator, lhs: WValue,
54225500 return .stack;
54235501 },
54245502 else => {
5425 try cg.lowerToStack(lhs);
5426 try cg.lowerToStack(rhs);
5503 try cg.emitWValue(lhs);
5504 try cg.emitWValue(rhs);
54275505 try cg.addImm32(@intFromBool(ty.is_signed));
54285506 try cg.addImm32(ty.bits);
54295507 try cg.addCallIntrinsic(.__cmp_limb64);
......@@ -5531,11 +5609,13 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55315609 const br = cg.air.instructions.items(.data)[@backingInt(inst)].br;
55325610 const block = cg.blocks.get(br.block_inst).?;
55335611
5534 // if operand has codegen bits we should break with a value
5535 if (block.value != .none) {
5536 const operand = try cg.resolveInst(br.operand);
5537 try cg.lowerToStack(operand);
5538 try cg.addLocal(.local_set, block.value.local.value);
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,
55395619 }
55405620
55415621 // We map every block to its block index.
......@@ -5558,7 +5638,8 @@ fn airRepeat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55585638
55595639fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55605640 try cg.addTag(.@"unreachable");
5561 return cg.finishAir(inst, .none, &.{});
5641 try cg.finishAir(inst, .none, &.{});
5642 cg.verifyLocalPool();
55625643}
55635644
55645645fn airBreakpoint(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5570,7 +5651,8 @@ fn airBreakpoint(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55705651
55715652fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55725653 try cg.addTag(.@"unreachable");
5573 return cg.finishAir(inst, .none, &.{});
5654 try cg.finishAir(inst, .none, &.{});
5655 cg.verifyLocalPool();
55745656}
55755657
55765658fn airNopCast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5584,8 +5666,7 @@ fn airNopCast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55845666 assert(operand_ty.abiAlignment(zcu) == dest_ty.abiAlignment(zcu));
55855667
55865668 const operand = try cg.resolveInst(ty_op.operand);
5587 const result = cg.reuseOperand(ty_op.operand, operand);
5588 return cg.finishAir(inst, result, &.{ty_op.operand});
5669 return cg.finishAir(inst, operand, &.{ty_op.operand});
55895670}
55905671
55915672fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5599,11 +5680,12 @@ fn airIntFromPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55995680 assert(operand_ty.abiAlignment(zcu) == dest_ty.abiAlignment(zcu));
56005681
56015682 const operand = try cg.resolveInst(ty_op.operand);
5602 const result = switch (operand) {
5603 .stack_offset => try cg.buildPointerOffset(operand, 0, .new),
5604 else => cg.reuseOperand(ty_op.operand, operand),
5605 };
5606 return cg.finishAir(inst, result, &.{ty_op.operand});
5683 if (operand == .stack_offset) {
5684 // materialize operand, we assume precomputed value is more efficient
5685 try cg.emitWValue(operand);
5686 return cg.finishAir(inst, .stack, &.{ty_op.operand});
5687 }
5688 return cg.finishAir(inst, operand, &.{ty_op.operand});
56075689}
56085690
56095691fn airUnionFromEnum(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5616,7 +5698,7 @@ fn airUnionFromEnum(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56165698
56175699 const enum_value = try cg.resolveInst(ty_op.operand);
56185700 const result = try cg.allocStack(union_ty);
5619 try cg.store(result, enum_value, enum_ty, @intCast(layout.tagOffset()));
5701 try cg.store(result, enum_value, enum_ty, .{ .offset = layout.tagOffset() });
56205702
56215703 return cg.finishAir(inst, result, &.{ty_op.operand});
56225704}
......@@ -5627,7 +5709,7 @@ fn airBitcast(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
56275709 const dest_ty = cg.typeOfIndex(inst);
56285710 const src_ty = cg.typeOf(ty_op.operand);
56295711
5630 const result = (try cg.bitcast(dest_ty, src_ty, operand)) orelse cg.reuseOperand(ty_op.operand, operand);
5712 const result = try cg.bitcast(dest_ty, src_ty, operand);
56315713
56325714 return cg.finishAir(inst, result, &.{ty_op.operand});
56335715}
......@@ -5654,8 +5736,8 @@ fn bitcastClass(cg: *CodeGen, ty: Type) BitcastClass {
56545736 };
56555737}
56565738
5657fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerError!?WValue {
5658 if (dest_ty.eql(src_ty)) return null;
5739fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerError!WValue {
5740 if (dest_ty.eql(src_ty)) return operand;
56595741
56605742 const zcu = cg.pt.zcu;
56615743 const src_class = cg.bitcastClass(src_ty);
......@@ -5674,17 +5756,17 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
56745756
56755757 if (src_by_ref and dest_by_ref) {
56765758 if (needs_wrapping) return try cg.intWrap(dest_class.int, operand);
5677 return null;
5759 return operand;
56785760 }
56795761
56805762 if (dest_by_ref) {
56815763 const result = try cg.allocStack(src_ty);
5682 try cg.store(result, operand, src_ty, 0);
5764 try cg.store(result, operand, src_ty, .{});
56835765 return result;
56845766 }
56855767
56865768 if (src_by_ref) {
5687 return try cg.load(operand, dest_ty, 0);
5769 return try cg.load(operand, dest_ty, .{});
56885770 }
56895771
56905772 switch (src_class) {
......@@ -5708,7 +5790,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
57085790
57095791 switch (dest_class) {
57105792 .float => |float_ty| switch (float_ty) {
5711 .f16 => return null,
5793 .f16 => return operand,
57125794 .f32 => {
57135795 try cg.emitWValue(operand);
57145796 try cg.addTag(.f32_reinterpret_i32);
......@@ -5729,7 +5811,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
57295811 return try cg.intWrap(dest_class.int, operand);
57305812 }
57315813
5732 return null;
5814 return operand;
57335815}
57345816
57355817fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5740,7 +5822,7 @@ fn airStructFieldPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
57405822 const struct_ptr = try cg.resolveInst(extra.data.struct_operand);
57415823 const struct_ptr_ty = cg.typeOf(extra.data.struct_operand);
57425824 const struct_ty = struct_ptr_ty.childType(zcu);
5743 const result = try cg.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index);
5825 const result = try cg.structFieldPtr(inst, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index);
57445826 return cg.finishAir(inst, result, &.{extra.data.struct_operand});
57455827}
57465828
......@@ -5751,14 +5833,13 @@ fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, index: u32) InnerE
57515833 const struct_ptr_ty = cg.typeOf(ty_op.operand);
57525834 const struct_ty = struct_ptr_ty.childType(zcu);
57535835
5754 const result = try cg.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index);
5836 const result = try cg.structFieldPtr(inst, struct_ptr, struct_ptr_ty, struct_ty, index);
57555837 return cg.finishAir(inst, result, &.{ty_op.operand});
57565838}
57575839
57585840fn structFieldPtr(
57595841 cg: *CodeGen,
57605842 inst: Air.Inst.Index,
5761 ref: Air.Inst.Ref,
57625843 struct_ptr: WValue,
57635844 struct_ptr_ty: Type,
57645845 struct_ty: Type,
......@@ -5783,16 +5864,7 @@ fn structFieldPtr(
57835864 },
57845865 else => struct_ty.structFieldOffset(index, zcu),
57855866 };
5786 // save a load and store when we can simply reuse the operand
5787 if (offset == 0) {
5788 return cg.reuseOperand(ref, struct_ptr);
5789 }
5790 switch (struct_ptr) {
5791 .stack_offset => |stack_offset| {
5792 return .{ .stack_offset = .{ .value = stack_offset.value + @as(u32, @intCast(offset)), .references = 1 } };
5793 },
5794 else => return cg.buildPointerOffset(struct_ptr, offset, .new),
5795 }
5867 return cg.offsetPointer(struct_ptr, offset);
57965868}
57975869
57985870fn airAggFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5814,44 +5886,45 @@ fn airAggFieldVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58145886 return cg.fail("Field type '{f}' too big to fit into stack frame", .{field_ty.fmt(pt)});
58155887 };
58165888 if (isByRef(field_ty, zcu, cg.target)) {
5817 switch (operand) {
5818 .stack_offset => |stack_offset| {
5819 break :result .{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
5820 },
5821 else => break :result try cg.buildPointerOffset(operand, offset, .new),
5822 }
5889 break :result try cg.offsetPointer(operand, offset);
58235890 }
5824 break :result try cg.load(operand, field_ty, offset);
5891 break :result try cg.load(operand, field_ty, .{ .offset = offset });
58255892 },
58265893 };
58275894
58285895 return cg.finishAir(inst, result, &.{struct_field.struct_operand});
58295896}
58305897
5898// Both `.switch_br` and `.loop_switch_br` are noreturn.
58315899fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) InnerError!void {
58325900 const pt = cg.pt;
58335901 const zcu = pt.zcu;
58345902
58355903 const switch_br = cg.air.unwrapSwitch(inst);
58365904 const target_ty = cg.typeOf(switch_br.operand);
5905 const branch_liveness = try cg.liveness.getSwitchBr(cg.gpa, inst, switch_br.cases_len + 1);
5906 defer cg.gpa.free(branch_liveness.deaths);
58375907
58385908 assert(target_ty.hasRuntimeBits(zcu));
58395909
5840 // swap target value with placeholder local, for dispatching
5910 const operand_dies = cg.liveness.operandDies(inst, 0);
5911 var owned_target: WValue = .none;
5912 errdefer owned_target.free(cg);
58415913 const target = if (is_dispatch_loop) target: {
58425914 const initial_target = try cg.resolveInst(switch_br.operand);
5843 const target: WValue = try cg.allocLocal(target_ty);
5844 try cg.lowerToStack(initial_target);
5845 try cg.addLocal(.local_set, target.local.value);
5915 owned_target = try cg.allocLocal(target_ty);
5916 try cg.emitWValue(initial_target);
5917 try cg.addLocal(.local_set, owned_target.local);
58465918
58475919 try cg.startBlock(.loop, .empty); // dispatch loop start
58485920 try cg.blocks.putNoClobber(cg.gpa, inst, .{
58495921 .label = cg.block_depth,
5850 .value = target,
5922 .value = owned_target,
58515923 });
58525924
5853 break :target target;
5925 break :target owned_target;
58545926 } else try cg.resolveInst(switch_br.operand);
5927 defer if (is_dispatch_loop) assert(cg.blocks.swapRemove(inst));
58555928
58565929 const has_else_body = switch_br.else_body_len != 0;
58575930 const branch_count = switch_br.cases_len + 1; // if else branch is missing, we trap when failing all conditions
......@@ -5863,15 +5936,19 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
58635936 var it = switch_br.iterateCases();
58645937 const else_body = it.elseBody();
58655938
5866 cg.branches.appendAssumeCapacity(.{});
5867 defer {
5868 var else_branch = cg.branches.pop().?;
5869 else_branch.deinit(cg.gpa);
5939 if (operand_dies) cg.processDeath(switch_br.operand);
5940 owned_target.free(cg);
5941
5942 {
5943 try cg.pushBranch();
5944 defer cg.popBranch();
5945 for (branch_liveness.deaths[0]) |death| cg.processDeath(death.toRef());
5946 try cg.genBody(else_body);
58705947 }
5871 try cg.genBody(else_body);
58725948
58735949 if (is_dispatch_loop) {
58745950 try cg.endBlock(); // dispatch loop end
5951 try cg.addTag(.@"unreachable");
58755952 }
58765953 return cg.finishAir(inst, .none, &.{});
58775954 }
......@@ -5916,8 +5993,6 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
59165993 break :width width_bigint.toConst().toInt(u32) catch null;
59175994 };
59185995
5919 try cg.startBlock(.block, .empty); // whole switch block start
5920
59215996 for (0..branch_count) |_| {
59225997 try cg.startBlock(.block, .empty);
59235998 }
......@@ -5992,38 +6067,38 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner
59926067 try cg.addLabel(.br, branch_count - 1);
59936068 }
59946069
6070 if (operand_dies) cg.processDeath(switch_br.operand);
6071 owned_target.free(cg);
6072
59956073 var cases_it = switch_br.iterateCases();
59966074 while (cases_it.next()) |case| {
59976075 try cg.endBlock();
59986076
5999 cg.branches.appendAssumeCapacity(.{});
6000 defer {
6001 var case_branch = cg.branches.pop().?;
6002 case_branch.deinit(cg.gpa);
6077 {
6078 try cg.pushBranch();
6079 defer cg.popBranch();
6080 for (branch_liveness.deaths[case.idx]) |death| cg.processDeath(death.toRef());
6081 try cg.genBody(case.body);
60036082 }
6004 try cg.genBody(case.body);
6005
6006 try cg.addLabel(.br, branch_count - case.idx - 1); // matching case found and executed => exit switch
60076083 }
60086084
60096085 try cg.endBlock();
60106086 if (has_else_body) {
60116087 const else_body = cases_it.elseBody();
60126088
6013 cg.branches.appendAssumeCapacity(.{});
6014 defer {
6015 var else_branch = cg.branches.pop().?;
6016 else_branch.deinit(cg.gpa);
6089 {
6090 try cg.pushBranch();
6091 defer cg.popBranch();
6092 for (branch_liveness.deaths[branch_liveness.deaths.len - 1]) |death| cg.processDeath(death.toRef());
6093 try cg.genBody(else_body);
60176094 }
6018 try cg.genBody(else_body);
60196095 } else {
60206096 try cg.addTag(.@"unreachable");
60216097 }
60226098
6023 try cg.endBlock(); // whole switch block end
6024
60256099 if (is_dispatch_loop) {
60266100 try cg.endBlock(); // dispatch loop end
6101 try cg.addTag(.@"unreachable");
60276102 }
60286103
60296104 return cg.finishAir(inst, .none, &.{});
......@@ -6034,8 +6109,8 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60346109 const switch_loop = cg.blocks.get(br.block_inst).?;
60356110
60366111 const operand = try cg.resolveInst(br.operand);
6037 try cg.lowerToStack(operand);
6038 try cg.addLocal(.local_set, switch_loop.value.local.value);
6112 try cg.emitWValue(operand);
6113 try cg.addLocal(.local_set, switch_loop.value.local);
60396114
60406115 const idx: u32 = cg.block_depth - switch_loop.label;
60416116 try cg.addLabel(.br, idx);
......@@ -6062,12 +6137,14 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind
60626137 }
60636138 }
60646139
6065 try cg.emitWValue(operand);
60666140 if (op_kind == .ptr or pl_ty.hasRuntimeBits(zcu)) {
6141 try cg.emitMemBase(operand);
60676142 try cg.addMemArg(.i32_load16_u, .{
6068 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),
6069 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
6143 .offset = operand.memOffset() + errUnionErrorOffset(pl_ty, zcu),
6144 .alignment = Type.anyerror.abiAlignment(zcu),
60706145 });
6146 } else {
6147 try cg.emitWValue(operand);
60716148 }
60726149
60736150 // Compare the error value with '0'
......@@ -6092,18 +6169,18 @@ fn airUnwrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool)
60926169 const result: WValue = result: {
60936170 if (!payload_ty.hasRuntimeBits(zcu)) {
60946171 if (op_is_ptr) {
6095 break :result cg.reuseOperand(ty_op.operand, operand);
6172 break :result operand;
60966173 } else {
60976174 break :result .none;
60986175 }
60996176 }
61006177
6101 const pl_offset: u32 = @intCast(errUnionPayloadOffset(payload_ty, zcu));
6178 const pl_offset = errUnionPayloadOffset(payload_ty, zcu);
61026179 if (op_is_ptr or isByRef(payload_ty, zcu, cg.target)) {
6103 break :result try cg.buildPointerOffset(operand, pl_offset, .new);
6180 break :result try cg.offsetPointer(operand, pl_offset);
61046181 } else {
61056182 assert(isByRef(eu_ty, zcu, cg.target));
6106 break :result try cg.load(operand, payload_ty, pl_offset);
6183 break :result try cg.load(operand, payload_ty, .{ .offset = pl_offset });
61076184 }
61086185 };
61096186 return cg.finishAir(inst, result, &.{ty_op.operand});
......@@ -6128,10 +6205,10 @@ fn airUnwrapErrUnionError(cg: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) I
61286205
61296206 const err_offset: u32 = @intCast(errUnionErrorOffset(payload_ty, zcu));
61306207 if (op_is_ptr or isByRef(eu_ty, zcu, cg.target)) {
6131 break :result try cg.load(operand, Type.anyerror, err_offset);
6208 break :result try cg.load(operand, .anyerror, .{ .offset = err_offset });
61326209 } else {
61336210 assert(!payload_ty.hasRuntimeBits(zcu));
6134 break :result cg.reuseOperand(ty_op.operand, operand);
6211 break :result operand;
61356212 }
61366213 };
61376214 return cg.finishAir(inst, result, &.{ty_op.operand});
......@@ -6147,20 +6224,19 @@ fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61476224 const pl_ty = cg.typeOf(ty_op.operand);
61486225 const result = result: {
61496226 if (!pl_ty.hasRuntimeBits(zcu)) {
6150 break :result cg.reuseOperand(ty_op.operand, operand);
6227 break :result operand;
61516228 }
61526229
61536230 const err_union = try cg.allocStack(err_ty);
6154 const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);
6155 try cg.store(payload_ptr, operand, pl_ty, 0);
6231 try cg.store(err_union, operand, pl_ty, .{ .offset = errUnionPayloadOffset(pl_ty, zcu) });
61566232
61576233 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
6158 try cg.emitWValue(err_union);
6234 try cg.emitMemBase(err_union);
61596235 try cg.addImm32(0);
6160 const err_val_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
6236 const err_val_offset = errUnionErrorOffset(pl_ty, zcu);
61616237 try cg.addMemArg(.i32_store16, .{
6162 .offset = err_union.offset() + err_val_offset,
6163 .alignment = 2,
6238 .offset = err_union.memOffset() + err_val_offset,
6239 .alignment = .@"2",
61646240 });
61656241 break :result err_union;
61666242 };
......@@ -6177,15 +6253,15 @@ fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61776253
61786254 const result = result: {
61796255 if (!pl_ty.hasRuntimeBits(zcu)) {
6180 break :result cg.reuseOperand(ty_op.operand, operand);
6256 break :result operand;
61816257 }
61826258
61836259 const err_union = try cg.allocStack(err_ty);
61846260 // store error value
6185 try cg.store(err_union, operand, Type.anyerror, @intCast(errUnionErrorOffset(pl_ty, zcu)));
6261 try cg.store(err_union, operand, .anyerror, .{ .offset = errUnionErrorOffset(pl_ty, zcu) });
61866262
61876263 // write 'undefined' to the payload
6188 const payload_ptr = try cg.buildPointerOffset(err_union, @as(u32, @intCast(errUnionPayloadOffset(pl_ty, zcu))), .new);
6264 const payload_ptr = try cg.offsetPointer(err_union, errUnionPayloadOffset(pl_ty, zcu));
61896265 const len = @as(u32, @intCast(err_ty.errorUnionPayload(zcu).abiSize(zcu)));
61906266 try cg.memset(Type.u8, payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaa });
61916267
......@@ -6210,7 +6286,6 @@ fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kin
62106286fn isNull(cg: *CodeGen, operand: WValue, op_ty: Type, opcode: std.wasm.Opcode, op_kind: OpKind) InnerError!WValue {
62116287 const pt = cg.pt;
62126288 const zcu = pt.zcu;
6213 try cg.emitWValue(operand);
62146289 const optional_ty = switch (op_kind) {
62156290 .value => op_ty,
62166291 .ptr => op_ty.childType(zcu),
......@@ -6223,19 +6298,27 @@ fn isNull(cg: *CodeGen, operand: WValue, op_ty: Type, opcode: std.wasm.Opcode, o
62236298 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {
62246299 return cg.fail("Optional type {f} too big to fit into stack frame", .{optional_ty.fmt(pt)});
62256300 };
6226 try cg.addMemArg(.i32_load8_u, .{ .offset = operand.offset() + offset, .alignment = 1 });
6301 try cg.emitMemBase(operand);
6302 try cg.addMemArg(.i32_load8_u, .{ .offset = operand.memOffset() + offset, .alignment = .@"1" });
6303 } else {
6304 try cg.emitWValue(operand);
62276305 }
62286306 } else if (payload_ty.isSlice(zcu)) {
6307 const base_offset = operand.memOffset();
6308 try cg.emitMemBase(operand);
62296309 switch (cg.ptr_size) {
6230 .wasm32 => try cg.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }),
6231 .wasm64 => try cg.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }),
6310 .wasm32 => try cg.addMemArg(.i32_load, .{ .offset = base_offset, .alignment = .@"4" }),
6311 .wasm64 => try cg.addMemArg(.i64_load, .{ .offset = base_offset, .alignment = .@"8" }),
62326312 }
62336313 } else {
62346314 if (op_kind == .ptr) {
6315 try cg.emitMemBase(operand);
62356316 try cg.addMemArg(.i32_load, .{
6236 .offset = operand.offset(),
6237 .alignment = 4,
6317 .offset = operand.memOffset(),
6318 .alignment = .@"4",
62386319 });
6320 } else {
6321 try cg.emitWValue(operand);
62396322 }
62406323 }
62416324
......@@ -6257,13 +6340,13 @@ fn airOptionalPayload(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
62576340
62586341 const result = result: {
62596342 const operand = try cg.resolveInst(ty_op.operand);
6260 if (opt_ty.optionalReprIsPayload(zcu)) break :result cg.reuseOperand(ty_op.operand, operand);
6343 if (opt_ty.optionalReprIsPayload(zcu)) break :result operand;
62616344
62626345 if (isByRef(payload_ty, zcu, cg.target)) {
6263 break :result try cg.buildPointerOffset(operand, 0, .new);
6346 break :result operand;
62646347 }
62656348
6266 break :result try cg.load(operand, payload_ty, 0);
6349 break :result try cg.load(operand, payload_ty, .{});
62676350 };
62686351 return cg.finishAir(inst, result, &.{ty_op.operand});
62696352}
......@@ -6277,10 +6360,10 @@ fn airOptionalPayloadPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
62776360 const result = result: {
62786361 const payload_ty = opt_ty.optionalChild(zcu);
62796362 if (!payload_ty.hasRuntimeBits(zcu) or opt_ty.optionalReprIsPayload(zcu)) {
6280 break :result cg.reuseOperand(ty_op.operand, operand);
6363 break :result operand;
62816364 }
62826365
6283 break :result try cg.buildPointerOffset(operand, 0, .new);
6366 break :result operand;
62846367 };
62856368 return cg.finishAir(inst, result, &.{ty_op.operand});
62866369}
......@@ -6301,12 +6384,11 @@ fn airOptionalPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void
63016384 return cg.fail("Optional type {f} too big to fit into stack frame", .{opt_ty.fmt(pt)});
63026385 };
63036386
6304 try cg.emitWValue(operand);
6387 try cg.emitMemBase(operand);
63056388 try cg.addImm32(1);
6306 try cg.addMemArg(.i32_store8, .{ .offset = operand.offset() + offset, .alignment = 1 });
6389 try cg.addMemArg(.i32_store8, .{ .offset = operand.memOffset() + offset, .alignment = .@"1" });
63076390
6308 const result = try cg.buildPointerOffset(operand, 0, .new);
6309 return cg.finishAir(inst, result, &.{ty_op.operand});
6391 return cg.finishAir(inst, operand, &.{ty_op.operand});
63106392}
63116393
63126394fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -6318,16 +6400,16 @@ fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
63186400 const result = result: {
63196401 if (!payload_ty.hasRuntimeBits(zcu)) {
63206402 const non_null_bit = try cg.allocStack(Type.u1);
6321 try cg.emitWValue(non_null_bit);
6403 try cg.emitMemBase(non_null_bit);
63226404 try cg.addImm32(1);
6323 try cg.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
6405 try cg.addMemArg(.i32_store8, .{ .offset = non_null_bit.memOffset(), .alignment = .@"1" });
63246406 break :result non_null_bit;
63256407 }
63266408
63276409 const operand = try cg.resolveInst(ty_op.operand);
63286410 const op_ty = cg.typeOfIndex(inst);
63296411 if (op_ty.optionalReprIsPayload(zcu)) {
6330 break :result cg.reuseOperand(ty_op.operand, operand);
6412 break :result operand;
63316413 }
63326414 const offset = std.math.cast(u32, payload_ty.abiSize(zcu)) orelse {
63336415 return cg.fail("Optional type {f} too big to fit into stack frame", .{op_ty.fmt(pt)});
......@@ -6335,12 +6417,11 @@ fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
63356417
63366418 // Create optional type, set the non-null bit, and store the operand inside the optional type
63376419 const result_ptr = try cg.allocStack(op_ty);
6338 try cg.emitWValue(result_ptr);
6420 try cg.emitMemBase(result_ptr);
63396421 try cg.addImm32(1);
6340 try cg.addMemArg(.i32_store8, .{ .offset = result_ptr.offset() + offset, .alignment = 1 });
6422 try cg.addMemArg(.i32_store8, .{ .offset = result_ptr.memOffset() + offset, .alignment = .@"1" });
63416423
6342 const payload_ptr = try cg.buildPointerOffset(result_ptr, 0, .new);
6343 try cg.store(payload_ptr, operand, payload_ty, 0);
6424 try cg.store(result_ptr, operand, payload_ty, .{});
63446425 break :result result_ptr;
63456426 };
63466427
......@@ -6356,8 +6437,8 @@ fn airSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
63566437 const slice_ty = cg.typeOfIndex(inst);
63576438
63586439 const slice = try cg.allocStack(slice_ty);
6359 try cg.store(slice, lhs, Type.usize, 0);
6360 try cg.store(slice, rhs, Type.usize, cg.ptrSize());
6440 try cg.store(slice, lhs, .usize, .{});
6441 try cg.store(slice, rhs, .usize, .{ .offset = cg.ptrSize() });
63616442
63626443 return cg.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs });
63636444}
......@@ -6380,15 +6461,12 @@ fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
63806461 const elem_size = elem_ty.abiSize(zcu);
63816462
63826463 // load pointer onto stack
6383 _ = try cg.load(slice, Type.usize, 0);
6464 _ = try cg.load(slice, .usize, .{});
63846465
63856466 // calculate index into slice
6386 try cg.emitWValue(index);
6387 try cg.addImm32(@intCast(elem_size));
6388 try cg.addTag(.i32_mul);
6389 try cg.addTag(.i32_add);
6467 try cg.emitScaledPointerOffset(index, elem_size, .add);
63906468
6391 const elem_result = try cg.load(.stack, elem_ty, 0);
6469 const elem_result = try cg.load(.stack, elem_ty, .{});
63926470
63936471 return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
63946472}
......@@ -6404,13 +6482,10 @@ fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64046482 const slice = try cg.resolveInst(bin_op.lhs);
64056483 const index = try cg.resolveInst(bin_op.rhs);
64066484
6407 _ = try cg.load(slice, Type.usize, 0);
6485 _ = try cg.load(slice, .usize, .{});
64086486
64096487 // calculate index into slice
6410 try cg.emitWValue(index);
6411 try cg.addImm32(@intCast(elem_size));
6412 try cg.addTag(.i32_mul);
6413 try cg.addTag(.i32_add);
6488 try cg.emitScaledPointerOffset(index, elem_size, .add);
64146489
64156490 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
64166491}
......@@ -6422,12 +6497,12 @@ fn airSlicePtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64226497}
64236498
64246499fn slicePtr(cg: *CodeGen, operand: WValue) InnerError!WValue {
6425 const ptr = try cg.load(operand, Type.usize, 0);
6500 const ptr = try cg.load(operand, .usize, .{});
64266501 return ptr.toLocal(cg, Type.usize);
64276502}
64286503
64296504fn sliceLen(cg: *CodeGen, operand: WValue) InnerError!WValue {
6430 const len = try cg.load(operand, Type.usize, cg.ptrSize());
6505 const len = try cg.load(operand, .usize, .{ .offset = cg.ptrSize() });
64316506 return len.toLocal(cg, Type.usize);
64326507}
64336508
......@@ -6442,11 +6517,11 @@ fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64426517 // create a slice on the stack
64436518 const slice_local = try cg.allocStack(slice_ty);
64446519
6445 try cg.store(slice_local, operand, Type.usize, 0);
6520 try cg.store(slice_local, operand, .usize, .{});
64466521
64476522 // store the length of the array in the slice
64486523 const array_len: u32 = @intCast(array_ty.arrayLen(zcu));
6449 try cg.store(slice_local, .{ .imm32 = array_len }, Type.usize, cg.ptrSize());
6524 try cg.store(slice_local, .{ .imm32 = array_len }, .usize, .{ .offset = cg.ptrSize() });
64506525
64516526 return cg.finishAir(inst, slice_local, &.{ty_op.operand});
64526527}
......@@ -6463,18 +6538,15 @@ fn airPtrElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64636538
64646539 // load pointer onto the stack
64656540 if (ptr_ty.isSlice(zcu)) {
6466 _ = try cg.load(ptr, Type.usize, 0);
6541 _ = try cg.load(ptr, .usize, .{});
64676542 } else {
6468 try cg.lowerToStack(ptr);
6543 try cg.emitWValue(ptr);
64696544 }
64706545
64716546 // calculate index into slice
6472 try cg.emitWValue(index);
6473 try cg.addImm32(@intCast(elem_size));
6474 try cg.addTag(.i32_mul);
6475 try cg.addTag(.i32_add);
6547 try cg.emitScaledPointerOffset(index, elem_size, .add);
64766548
6477 const elem_result = try cg.load(.stack, elem_ty, 0);
6549 const elem_result = try cg.load(.stack, elem_ty, .{});
64786550
64796551 return cg.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
64806552}
......@@ -6493,21 +6565,18 @@ fn airPtrElemPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64936565
64946566 // load pointer onto the stack
64956567 if (ptr_ty.isSlice(zcu)) {
6496 _ = try cg.load(ptr, Type.usize, 0);
6568 _ = try cg.load(ptr, .usize, .{});
64976569 } else {
6498 try cg.lowerToStack(ptr);
6570 try cg.emitWValue(ptr);
64996571 }
65006572
65016573 // calculate index into ptr
6502 try cg.emitWValue(index);
6503 try cg.addImm32(@intCast(elem_size));
6504 try cg.addTag(.i32_mul);
6505 try cg.addTag(.i32_add);
6574 try cg.emitScaledPointerOffset(index, elem_size, .add);
65066575
65076576 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
65086577}
65096578
6510fn airPtrBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: enum { add, sub }) InnerError!void {
6579fn airPtrBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: PointerOffsetOp) InnerError!void {
65116580 const zcu = cg.pt.zcu;
65126581 const ty_pl = cg.air.instructions.items(.data)[@backingInt(inst)].ty_pl;
65136582 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -6520,27 +6589,8 @@ fn airPtrBinOp(cg: *CodeGen, inst: Air.Inst.Index, op: enum { add, sub }) InnerE
65206589 else => ptr_ty.childType(zcu),
65216590 };
65226591
6523 try cg.lowerToStack(ptr);
6524 try cg.emitWValue(offset);
6525
6526 switch (cg.ptr_size) {
6527 .wasm32 => {
6528 try cg.addImm32(@intCast(pointee_ty.abiSize(zcu)));
6529 try cg.addTag(.i32_mul);
6530 try cg.addTag(switch (op) {
6531 .add => .i32_add,
6532 .sub => .i32_sub,
6533 });
6534 },
6535 .wasm64 => {
6536 try cg.addImm64(pointee_ty.abiSize(zcu));
6537 try cg.addTag(.i64_mul);
6538 try cg.addTag(switch (op) {
6539 .add => .i64_add,
6540 .sub => .i64_sub,
6541 });
6542 },
6543 }
6592 try cg.emitWValue(ptr);
6593 try cg.emitScaledPointerOffset(offset, pointee_ty.abiSize(zcu), op);
65446594
65456595 return cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
65466596}
......@@ -6552,11 +6602,6 @@ fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
65526602 const ptr = try cg.resolveInst(bin_op.lhs);
65536603 const ptr_ty = cg.typeOf(bin_op.lhs);
65546604 const value = try cg.resolveInst(bin_op.rhs);
6555 const len = switch (ptr_ty.ptrSize(zcu)) {
6556 .slice => try cg.sliceLen(ptr),
6557 .one => @as(WValue, .{ .imm32 = @as(u32, @intCast(ptr_ty.childType(zcu).arrayLen(zcu))) }),
6558 .c, .many => unreachable,
6559 };
65606605
65616606 const elem_ty = if (ptr_ty.ptrSize(zcu) == .one)
65626607 ptr_ty.childType(zcu).childType(zcu)
......@@ -6567,26 +6612,38 @@ fn airMemset(cg: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void {
65676612 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
65686613 }
65696614
6570 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);
65716623 try cg.memset(elem_ty, dst_ptr, len, value);
65726624
65736625 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
65746626}
65756627
65766628/// Sets a region of memory at `ptr` to the value of `value`
6577/// When the user has enabled the bulk_memory feature, we lower
6629/// When the user has enabled the bulk_memory_opt feature, we lower
65786630/// this to wasm's memset instruction. When the feature is not present,
65796631/// we implement it manually.
65806632fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void {
65816633 const zcu = cg.pt.zcu;
65826634 const abi_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
6583
6584 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
6635 const len_known_neq_0 = switch (len) {
6636 .imm32 => |imm| if (imm != 0) true else return,
6637 .imm64 => |imm| if (imm != 0) true else return,
6638 else => false,
6639 };
6640 // When bulk_memory_opt is enabled, we lower it to wasm's memset instruction.
65856641 // If not, we lower it ourselves.
6586 if (cg.target.cpu.has(.wasm, .bulk_memory) and abi_size == 1) {
6642 if (cg.target.cpu.has(.wasm, .bulk_memory_opt) and abi_size == 1) {
65876643 const len0_ok = cg.target.cpu.has(.wasm, .nontrapping_bulk_memory_len0);
6644 const emit_check = !(len0_ok or len_known_neq_0);
65886645
6589 if (!len0_ok) {
6646 if (emit_check) {
65906647 try cg.startBlock(.block, .empty);
65916648
65926649 // Even if `len` is zero, the spec requires an implementation to trap if `ptr + len` is
......@@ -6602,23 +6659,25 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66026659 try cg.addLabel(.br_if, 0);
66036660 }
66046661
6605 try cg.lowerToStack(ptr);
6662 try cg.emitWValue(ptr);
66066663 try cg.emitWValue(value);
66076664 try cg.emitWValue(len);
66086665 try cg.addExtended(.memory_fill);
66096666
6610 if (!len0_ok) {
6667 if (emit_check) {
66116668 try cg.endBlock();
66126669 }
66136670
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,24 +6689,26 @@ 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.value);
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
66386697 var end_ptr = try cg.allocLocal(Type.usize);
66396698 defer end_ptr.free(cg);
6640 var new_ptr = try cg.buildPointerOffset(ptr, 0, .new);
6699 var new_ptr = try cg.allocLocal(Type.usize);
66416700 defer new_ptr.free(cg);
6701 try cg.emitWValue(ptr);
6702 try cg.addLocal(.local_set, new_ptr.local);
66426703
66436704 // get the loop conditional: if current pointer address equals final pointer's address
6644 try cg.lowerToStack(ptr);
6705 try cg.emitWValue(ptr);
66456706 try cg.emitWValue(final_len);
66466707 switch (cg.ptr_size) {
66476708 .wasm32 => try cg.addTag(.i32_add),
66486709 .wasm64 => try cg.addTag(.i64_add),
66496710 }
6650 try cg.addLocal(.local_set, end_ptr.local.value);
6711 try cg.addLocal(.local_set, end_ptr.local);
66516712
66526713 // outer block to jump to when loop is done
66536714 try cg.startBlock(.block, .empty);
......@@ -6663,7 +6724,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66636724 try cg.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
66646725
66656726 // store the value at the current position of the pointer
6666 try cg.store(new_ptr, value, elem_ty, 0);
6727 try cg.store(new_ptr, value, elem_ty, .{});
66676728
66686729 // move the pointer to the next element
66696730 try cg.emitWValue(new_ptr);
......@@ -6677,7 +6738,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue)
66776738 try cg.addTag(.i64_add);
66786739 },
66796740 }
6680 try cg.addLocal(.local_set, new_ptr.local.value);
6741 try cg.addLocal(.local_set, new_ptr.local);
66816742
66826743 // end of loop
66836744 try cg.addLabel(.br, 0); // jump to start of loop
......@@ -6696,11 +6757,8 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
66966757 const elem_size = elem_ty.abiSize(zcu);
66976758
66986759 if (isByRef(array_ty, zcu, cg.target)) {
6699 try cg.lowerToStack(array);
6700 try cg.emitWValue(index);
6701 try cg.addImm32(@intCast(elem_size));
6702 try cg.addTag(.i32_mul);
6703 try cg.addTag(.i32_add);
6760 try cg.emitWValue(array);
6761 try cg.emitScaledPointerOffset(index, elem_size, .add);
67046762 } else {
67056763 assert(array_ty.zigTypeTag(zcu) == .vector);
67066764
......@@ -6726,14 +6784,11 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67266784 },
67276785 else => {
67286786 const stack_vec = try cg.allocStack(array_ty);
6729 try cg.store(stack_vec, array, array_ty, 0);
6787 try cg.store(stack_vec, array, array_ty, .{});
67306788
67316789 // Is a non-unrolled vector (v128)
6732 try cg.lowerToStack(stack_vec);
6733 try cg.emitWValue(index);
6734 try cg.addImm32(@intCast(elem_size));
6735 try cg.addTag(.i32_mul);
6736 try cg.addTag(.i32_add);
6790 try cg.emitWValue(stack_vec);
6791 try cg.emitScaledPointerOffset(index, elem_size, .add);
67376792 },
67386793 }
67396794 }
......@@ -6741,7 +6796,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67416796 const result = if (isByRef(elem_ty, zcu, cg.target))
67426797 .stack
67436798 else
6744 try cg.load(.stack, elem_ty, 0);
6799 try cg.load(.stack, elem_ty, .{});
67456800 return cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
67466801}
67476802
......@@ -6765,13 +6820,14 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67656820 64 => @backingInt(std.wasm.SimdOpcode.v128_load64_splat),
67666821 else => break :blk, // Cannot make use of simd-instructions
67676822 };
6768 try cg.emitWValue(operand);
6823 try cg.emitMemBase(operand);
67696824 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
67706825 // stores as := opcode, offset, alignment (opcode::memarg)
6771 try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{
6772 opcode,
6773 operand.offset(),
6774 @intCast(elem_ty.abiAlignment(zcu).toByteUnits().?),
6826 try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 4);
6827 cg.mir_extra.appendAssumeCapacity(opcode);
6828 _ = try cg.addExtraAssumeCapacity(Mir.MemArg{
6829 .offset = operand.memOffset(),
6830 .alignment = elem_ty.abiAlignment(zcu),
67756831 });
67766832 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
67776833 return cg.finishAir(inst, .stack, &.{ty_op.operand});
......@@ -6800,7 +6856,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
68006856 var index: usize = 0;
68016857 var offset: u32 = 0;
68026858 while (index < vector_len) : (index += 1) {
6803 try cg.store(result, operand, elem_ty, offset);
6859 try cg.store(result, operand, elem_ty, .{ .offset = offset });
68046860 offset += elem_byte_size;
68056861 }
68066862
......@@ -6837,12 +6893,12 @@ fn airShuffleOne(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
68376893
68386894 const dest_alloc = try cg.allocStack(result_ty);
68396895 for (mask, 0..) |mask_elem, out_idx| {
6840 try cg.emitWValue(dest_alloc);
6896 try cg.emitMemBase(dest_alloc);
68416897 const elem_val = switch (mask_elem.unwrap()) {
6842 .elem => |idx| try cg.load(operand, elem_ty, @intCast(elem_size * idx)),
6898 .elem => |idx| try cg.load(operand, elem_ty, .{ .offset = elem_size * idx }),
68436899 .value => |val| try cg.lowerConstant(.fromInterned(val)),
68446900 };
6845 try cg.store(.stack, elem_val, elem_ty, @intCast(dest_alloc.offset() + elem_size * out_idx));
6901 try cg.store(.stack, elem_val, elem_ty, .{ .offset = dest_alloc.memOffset() + elem_size * out_idx });
68466902 }
68476903 return cg.finishAir(inst, dest_alloc, &.{unwrapped.operand});
68486904}
......@@ -6905,13 +6961,13 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69056961
69066962 const dest_alloc = try cg.allocStack(result_ty);
69076963 for (mask, 0..) |mask_elem, out_idx| {
6908 try cg.emitWValue(dest_alloc);
6964 try cg.emitMemBase(dest_alloc);
69096965 const elem_val = switch (mask_elem.unwrap()) {
6910 .a_elem => |idx| try cg.load(operand_a, elem_ty, @intCast(elem_size * idx)),
6911 .b_elem => |idx| try cg.load(operand_b, elem_ty, @intCast(elem_size * idx)),
6966 .a_elem => |idx| try cg.load(operand_a, elem_ty, .{ .offset = elem_size * idx }),
6967 .b_elem => |idx| try cg.load(operand_b, elem_ty, .{ .offset = elem_size * idx }),
69126968 .undef => try cg.emitUndefined(elem_ty),
69136969 };
6914 try cg.store(.stack, elem_val, elem_ty, @intCast(dest_alloc.offset() + elem_size * out_idx));
6970 try cg.store(.stack, elem_val, elem_ty, .{ .offset = dest_alloc.memOffset() + elem_size * out_idx });
69156971 }
69166972 return cg.finishAir(inst, dest_alloc, &.{ unwrapped.operand_a, unwrapped.operand_b });
69176973}
......@@ -6937,39 +6993,18 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69376993 .array, .vector => {
69386994 const result = try cg.allocStack(result_ty);
69396995 const elem_ty = result_ty.childType(zcu);
6940 const elem_size = @as(u32, @intCast(elem_ty.abiSize(zcu)));
6996 const elem_size = elem_ty.abiSize(zcu);
69416997 const sentinel = result_ty.sentinel(zcu);
69426998
6943 // When the element type is by reference, we must copy the entire
6944 // value. It is therefore safer to move the offset pointer and store
6945 // each value individually, instead of using store offsets.
6946 if (isByRef(elem_ty, zcu, cg.target)) {
6947 // copy stack pointer into a temporary local, which is
6948 // moved for each element to store each value in the right position.
6949 const offset = try cg.buildPointerOffset(result, 0, .new);
6950 for (elements, 0..) |elem, elem_index| {
6951 const elem_val = try cg.resolveInst(elem);
6952 try cg.store(offset, elem_val, elem_ty, 0);
6953
6954 if (elem_index < elements.len - 1 or sentinel != null) {
6955 _ = try cg.buildPointerOffset(offset, elem_size, .modify);
6956 }
6957 }
6958 if (sentinel) |s| {
6959 const val = try cg.resolveValue(s);
6960 try cg.store(offset, val, elem_ty, 0);
6961 }
6962 } else {
6963 var offset: u32 = 0;
6964 for (elements) |elem| {
6965 const elem_val = try cg.resolveInst(elem);
6966 try cg.store(result, elem_val, elem_ty, offset);
6967 offset += elem_size;
6968 }
6969 if (sentinel) |s| {
6970 const val = try cg.resolveValue(s);
6971 try cg.store(result, val, elem_ty, offset);
6972 }
6999 var offset: u64 = 0;
7000 for (elements) |elem| {
7001 const elem_val = try cg.resolveInst(elem);
7002 try cg.store(result, elem_val, elem_ty, .{ .offset = offset });
7003 offset += elem_size;
7004 }
7005 if (sentinel) |s| {
7006 const val = try cg.resolveValue(s);
7007 try cg.store(result, val, elem_ty, .{ .offset = offset });
69737008 }
69747009 break :result_value result;
69757010 },
......@@ -6982,10 +7017,9 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69827017
69837018 const elem_ty = result_ty.fieldType(elem_index, zcu);
69847019 const field_offset = result_ty.structFieldOffset(elem_index, zcu);
6985 const offset = try cg.buildPointerOffset(result, field_offset, .new);
69867020
69877021 const value = try cg.resolveInst(elem);
6988 try cg.store(offset, value, elem_ty, 0);
7022 try cg.store(result, value, elem_ty, .{ .offset = field_offset });
69897023 }
69907024
69917025 break :result_value result;
......@@ -7032,24 +7066,19 @@ fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70327066 const result_ptr = try cg.allocStack(union_ty);
70337067 const payload = try cg.resolveInst(extra.init);
70347068 if (layout.tag_align.compare(.gte, layout.payload_align)) {
7035 if (isByRef(field_ty, zcu, cg.target)) {
7036 const payload_ptr = try cg.buildPointerOffset(result_ptr, layout.tag_size, .new);
7037 try cg.store(payload_ptr, payload, field_ty, 0);
7038 } else {
7039 try cg.store(result_ptr, payload, field_ty, @intCast(layout.tag_size));
7040 }
7069 try cg.store(result_ptr, payload, field_ty, .{ .offset = layout.tag_size });
70417070
70427071 if (layout.tag_size > 0) {
7043 try cg.store(result_ptr, tag_int, .fromInterned(union_obj.enum_tag_type), 0);
7072 try cg.store(result_ptr, tag_int, .fromInterned(union_obj.enum_tag_type), .{});
70447073 }
70457074 } else {
7046 try cg.store(result_ptr, payload, field_ty, 0);
7075 try cg.store(result_ptr, payload, field_ty, .{});
70477076 if (layout.tag_size > 0) {
70487077 try cg.store(
70497078 result_ptr,
70507079 tag_int,
70517080 .fromInterned(union_obj.enum_tag_type),
7052 @intCast(layout.payload_size),
7081 .{ .offset = layout.payload_size },
70537082 );
70547083 }
70557084 }
......@@ -7095,7 +7124,7 @@ fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70957124 const union_ptr = try cg.resolveInst(bin_op.lhs);
70967125 const new_tag = try cg.resolveInst(bin_op.rhs);
70977126 if (layout.payload_size == 0) {
7098 try cg.store(union_ptr, new_tag, tag_ty, 0);
7127 try cg.store(union_ptr, new_tag, tag_ty, .{});
70997128 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
71007129 }
71017130
......@@ -7104,7 +7133,7 @@ fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71047133 const offset: u32 = if (layout.tag_align.compare(.lt, layout.payload_align)) blk: {
71057134 break :blk @intCast(layout.payload_size);
71067135 } else 0;
7107 try cg.store(union_ptr, new_tag, tag_ty, offset);
7136 try cg.store(union_ptr, new_tag, tag_ty, .{ .offset = offset });
71087137 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
71097138}
71107139
......@@ -7124,7 +7153,7 @@ fn airGetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71247153 @intCast(layout.payload_size)
71257154 else
71267155 0;
7127 const result = try cg.load(operand, tag_ty, offset);
7156 const result = try cg.load(operand, tag_ty, .{ .offset = offset });
71287157 return cg.finishAir(inst, result, &.{ty_op.operand});
71297158}
71307159
......@@ -7140,17 +7169,15 @@ fn airErrUnionPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void
71407169 try cg.store(
71417170 operand,
71427171 .{ .imm32 = 0 },
7143 Type.anyerror,
7144 @intCast(errUnionErrorOffset(payload_ty, zcu)),
7172 .anyerror,
7173 .{ .offset = errUnionErrorOffset(payload_ty, zcu) },
71457174 );
71467175
7147 const result = result: {
7148 if (!payload_ty.hasRuntimeBits(zcu)) {
7149 break :result cg.reuseOperand(ty_op.operand, operand);
7150 }
7176 if (!payload_ty.hasRuntimeBits(zcu)) {
7177 return cg.finishAir(inst, operand, &.{ty_op.operand});
7178 }
71517179
7152 break :result try cg.buildPointerOffset(operand, @as(u32, @intCast(errUnionPayloadOffset(payload_ty, zcu))), .new);
7153 };
7180 const result = try cg.offsetPointer(operand, errUnionPayloadOffset(payload_ty, zcu));
71547181 return cg.finishAir(inst, result, &.{ty_op.operand});
71557182}
71567183
......@@ -7175,16 +7202,23 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
71757202 },
71767203 };
71777204
7178 const result = if (field_offset != 0) result: {
7179 const base = try cg.buildPointerOffset(field_ptr, 0, .new);
7180 try cg.addLocal(.local_get, base.local.value);
7181 try cg.addImm32(@intCast(field_offset));
7182 try cg.addTag(.i32_sub);
7183 try cg.addLocal(.local_set, base.local.value);
7184 break :result base;
7185 } else cg.reuseOperand(extra.field_ptr, field_ptr);
7205 if (field_offset == 0) {
7206 return cg.finishAir(inst, field_ptr, &.{extra.field_ptr});
7207 }
71867208
7187 return cg.finishAir(inst, result, &.{extra.field_ptr});
7209 // maybe use offsetPointer? sub looks better in disasm
7210 try cg.emitWValue(field_ptr);
7211 switch (cg.ptr_size) {
7212 .wasm32 => {
7213 try cg.addImm32(@intCast(field_offset));
7214 try cg.addTag(.i32_sub);
7215 },
7216 .wasm64 => {
7217 try cg.addImm64(field_offset);
7218 try cg.addTag(.i64_sub);
7219 },
7220 }
7221 return cg.finishAir(inst, .stack, &.{extra.field_ptr});
71887222}
71897223
71907224fn sliceOrArrayPtr(cg: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue {
......@@ -7204,14 +7238,14 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72047238 const ptr_elem_ty = dst_ty.childType(zcu);
72057239 const src = try cg.resolveInst(bin_op.rhs);
72067240 const src_ty = cg.typeOf(bin_op.rhs);
7207 const len = switch (dst_ty.ptrSize(zcu)) {
7241 var len = switch (dst_ty.ptrSize(zcu)) {
72087242 .slice => blk: {
72097243 const slice_len = try cg.sliceLen(dst);
72107244 if (ptr_elem_ty.abiSize(zcu) != 1) {
72117245 try cg.emitWValue(slice_len);
72127246 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
72137247 try cg.addTag(.i32_mul);
7214 try cg.addLocal(.local_set, slice_len.local.value);
7248 try cg.addLocal(.local_set, slice_len.local);
72157249 }
72167250 break :blk slice_len;
72177251 },
......@@ -7220,8 +7254,11 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72207254 }),
72217255 .c, .many => unreachable,
72227256 };
7223 const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
7224 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);
72257262 try cg.memcpy(dst_ptr, src_ptr, len);
72267263
72277264 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
......@@ -7235,14 +7272,14 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72357272 const ptr_elem_ty = dst_ty.childType(zcu);
72367273 const src = try cg.resolveInst(bin_op.rhs);
72377274 const src_ty = cg.typeOf(bin_op.rhs);
7238 const len = switch (dst_ty.ptrSize(zcu)) {
7275 var len = switch (dst_ty.ptrSize(zcu)) {
72397276 .slice => blk: {
72407277 const slice_len = try cg.sliceLen(dst);
72417278 if (ptr_elem_ty.abiSize(zcu) != 1) {
72427279 try cg.emitWValue(slice_len);
72437280 try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) });
72447281 try cg.addTag(.i32_mul);
7245 try cg.addLocal(.local_set, slice_len.local.value);
7282 try cg.addLocal(.local_set, slice_len.local);
72467283 }
72477284 break :blk slice_len;
72487285 },
......@@ -7251,8 +7288,11 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72517288 }),
72527289 .c, .many => unreachable,
72537290 };
7254 const dst_ptr = try cg.sliceOrArrayPtr(dst, dst_ty);
7255 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);
72567296 try cg.memmove(dst_ptr, src_ptr, len);
72577297
72587298 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
......@@ -7283,19 +7323,7 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
72837323 // Lowers to a i32.const or i64.const with the error table memory address.
72847324 cg.error_name_table_ref_count += 1;
72857325 try cg.addTag(.error_name_table_ref);
7286 try cg.emitWValue(operand);
7287 switch (cg.ptr_size) {
7288 .wasm32 => {
7289 try cg.addImm32(@intCast(abi_size));
7290 try cg.addTag(.i32_mul);
7291 try cg.addTag(.i32_add);
7292 },
7293 .wasm64 => {
7294 try cg.addImm64(abi_size);
7295 try cg.addTag(.i64_mul);
7296 try cg.addTag(.i64_add);
7297 },
7298 }
7326 try cg.emitScaledPointerOffset(operand, abi_size, .add);
72997327
73007328 return cg.finishAir(inst, .stack, &.{un_op});
73017329}
......@@ -7303,7 +7331,7 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
73037331fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
73047332 const ty_op = cg.air.instructions.items(.data)[@backingInt(inst)].ty_op;
73057333 const slice_ptr = try cg.resolveInst(ty_op.operand);
7306 const result = try cg.buildPointerOffset(slice_ptr, offset, .new);
7334 const result = try cg.offsetPointer(slice_ptr, offset);
73077335 return cg.finishAir(inst, result, &.{ty_op.operand});
73087336}
73097337
......@@ -7332,7 +7360,8 @@ fn airDbgVar(
73327360) InnerError!void {
73337361 _ = is_ptr;
73347362 _ = local_tag;
7335 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});
73367365}
73377366
73387367fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -7340,8 +7369,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
73407369 const body = unwrapped_try.else_body;
73417370 const err_union = try cg.resolveInst(unwrapped_try.error_union);
73427371 const err_union_ty = cg.typeOf(unwrapped_try.error_union);
7343 const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false);
7344 return cg.finishAir(inst, result, &.{unwrapped_try.error_union});
7372 const result = try lowerTry(cg, inst, unwrapped_try.error_union, err_union, body, err_union_ty, false);
7373 return cg.finishAirWithBranchDeaths(
7374 inst,
7375 result,
7376 &.{unwrapped_try.error_union},
7377 cg.liveness.getCondBr(inst).then_deaths,
7378 );
73457379}
73467380
73477381fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -7350,20 +7384,26 @@ fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
73507384 const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr);
73517385 const body = unwrapped_try.else_body;
73527386 const err_union_ty = cg.typeOf(unwrapped_try.error_union_ptr).childType(zcu);
7353 const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true);
7354 return cg.finishAir(inst, result, &.{unwrapped_try.error_union_ptr});
7387 const result = try lowerTry(cg, inst, unwrapped_try.error_union_ptr, err_union_ptr, body, err_union_ty, true);
7388 return cg.finishAirWithBranchDeaths(
7389 inst,
7390 result,
7391 &.{unwrapped_try.error_union_ptr},
7392 cg.liveness.getCondBr(inst).then_deaths,
7393 );
73557394}
73567395
73577396fn lowerTry(
73587397 cg: *CodeGen,
73597398 inst: Air.Inst.Index,
7399 operand_ref: Air.Inst.Ref,
73607400 err_union: WValue,
73617401 body: []const Air.Inst.Index,
73627402 err_union_ty: Type,
73637403 operand_is_ptr: bool,
73647404) InnerError!WValue {
7365 _ = inst;
73667405 const zcu = cg.pt.zcu;
7406 const branch_liveness = cg.liveness.getCondBr(inst);
73677407
73687408 const pl_ty = err_union_ty.errorUnionPayload(zcu);
73697409 const pl_has_bits = pl_ty.hasRuntimeBits(zcu);
......@@ -7373,23 +7413,26 @@ fn lowerTry(
73737413 try cg.startBlock(.block, .empty);
73747414
73757415 // check if the error tag is set for the error union.
7376 try cg.emitWValue(err_union);
73777416 if (pl_has_bits or operand_is_ptr) {
7378 const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
7417 try cg.emitMemBase(err_union);
7418 const err_offset = errUnionErrorOffset(pl_ty, zcu);
73797419 try cg.addMemArg(.i32_load16_u, .{
7380 .offset = err_union.offset() + err_offset,
7381 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
7420 .offset = err_union.memOffset() + err_offset,
7421 .alignment = Type.anyerror.abiAlignment(zcu),
73827422 });
7423 } else {
7424 try cg.emitWValue(err_union);
73837425 }
73847426 try cg.addTag(.i32_eqz);
73857427 try cg.addLabel(.br_if, 0); // jump out of block when error is '0'
73867428
7387 try cg.branches.append(cg.gpa, .{});
7388 defer {
7389 var branch = cg.branches.pop().?;
7390 branch.deinit(cg.gpa);
7429 {
7430 try cg.pushBranch();
7431 defer cg.popBranch();
7432 if (cg.liveness.operandDies(inst, 0)) cg.processDeath(operand_ref);
7433 for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef());
7434 try cg.genBody(body);
73917435 }
7392 try cg.genBody(body);
73937436 try cg.endBlock();
73947437 }
73957438
......@@ -7398,12 +7441,11 @@ fn lowerTry(
73987441 return .none;
73997442 }
74007443
7401 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
7444 const pl_offset = errUnionPayloadOffset(pl_ty, zcu);
74027445 if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {
7403 return buildPointerOffset(cg, err_union, pl_offset, .new);
7446 return cg.offsetPointer(err_union, pl_offset);
74047447 }
7405 const payload = try cg.load(err_union, pl_ty, pl_offset);
7406 return payload.toLocal(cg, pl_ty);
7448 return cg.load(err_union, pl_ty, .{ .offset = pl_offset });
74077449}
74087450
74097451/// Calls a compiler-rt intrinsic by creating an undefined symbol,
......@@ -7429,7 +7471,7 @@ fn callIntrinsic(
74297471 // and emit it as our first argument
74307472 const sret = if (want_sret_param) blk: {
74317473 const sret_local = try cg.allocStack(return_type);
7432 try cg.lowerToStack(sret_local);
7474 try cg.emitWValue(sret_local);
74337475 break :blk sret_local;
74347476 } else .none;
74357477
......@@ -7457,21 +7499,9 @@ fn airTagName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
74577499 const enum_ty = cg.typeOf(un_op);
74587500
74597501 try cg.addInst(.{ .tag = .enum_tag_name_table_ref, .data = .{ .ip_index = enum_ty.toIntern() } });
7460 try cg.lowerToStack(operand);
7502 try cg.emitWValue(operand);
74617503 try cg.addInst(.{ .tag = .call_tag_index, .data = .{ .ip_index = enum_ty.toIntern() } });
7462
7463 switch (cg.ptr_size) {
7464 .wasm32 => {
7465 try cg.addImm32(@intCast(8));
7466 try cg.addTag(.i32_mul);
7467 try cg.addTag(.i32_add);
7468 },
7469 .wasm64 => {
7470 try cg.addImm64(8);
7471 try cg.addTag(.i64_mul);
7472 try cg.addTag(.i64_add);
7473 },
7474 }
7504 try cg.emitScaledPointerOffset(.stack, 8, .add);
74757505
74767506 return cg.finishAir(inst, .stack, &.{un_op});
74777507}
......@@ -7481,7 +7511,7 @@ fn airIsNamedEnumValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
74817511 const operand = try cg.resolveInst(un_op);
74827512 const enum_ty = cg.typeOf(un_op);
74837513
7484 try cg.lowerToStack(operand);
7514 try cg.emitWValue(operand);
74857515 try cg.addInst(.{ .tag = .call_tag_index, .data = .{ .ip_index = enum_ty.toIntern() } });
74867516 try cg.addImm32(~@as(u32, 0));
74877517 try cg.addTag(.i32_ne);
......@@ -7559,13 +7589,13 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
75597589 // 'false' branch (i.e. error set does not have value
75607590 // ensure we set local to 0 in case the local was re-used.
75617591 try cg.addImm32(0);
7562 try cg.addLocal(.local_set, result.local.value);
7592 try cg.addLocal(.local_set, result.local);
75637593 try cg.addLabel(.br, 1);
75647594 try cg.endBlock();
75657595
75667596 // 'true' branch
75677597 try cg.addImm32(1);
7568 try cg.addLocal(.local_set, result.local.value);
7598 try cg.addLocal(.local_set, result.local);
75697599 try cg.addLabel(.br, 0);
75707600 try cg.endBlock();
75717601
......@@ -7591,13 +7621,14 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
75917621 const expected_val = try cg.resolveInst(extra.expected_value);
75927622 const new_val = try cg.resolveInst(extra.new_value);
75937623
7594 const cmp_result = try cg.allocLocal(Type.bool);
7624 var cmp_result = try cg.allocLocal(Type.bool);
7625 defer cmp_result.free(cg);
75957626
7596 const ptr_val = if (cg.useAtomicFeature()) val: {
7627 var ptr_val = if (cg.useAtomicFeature()) val: {
75977628 const val_local = try cg.allocLocal(ty);
7598 try cg.emitWValue(ptr_operand);
7599 try cg.lowerToStack(expected_val);
7600 try cg.lowerToStack(new_val);
7629 try cg.emitMemBase(ptr_operand);
7630 try cg.emitWValue(expected_val);
7631 try cg.emitWValue(new_val);
76017632 try cg.addAtomicMemArg(switch (ty.abiSize(zcu)) {
76027633 1 => .i32_atomic_rmw8_cmpxchg_u,
76037634 2 => .i32_atomic_rmw16_cmpxchg_u,
......@@ -7605,26 +7636,26 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76057636 8 => .i32_atomic_rmw_cmpxchg,
76067637 else => |size| return cg.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}),
76077638 }, .{
7608 .offset = ptr_operand.offset(),
7609 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7639 .offset = ptr_operand.memOffset(),
7640 .alignment = ty.abiAlignment(zcu),
76107641 });
7611 try cg.addLocal(.local_tee, val_local.local.value);
7642 try cg.addLocal(.local_tee, val_local.local);
76127643 _ = try cg.intCmp(int_ty, .eq, .stack, expected_val);
7613 try cg.addLocal(.local_set, cmp_result.local.value);
7644 try cg.addLocal(.local_set, cmp_result.local);
76147645 break :val val_local;
76157646 } else val: {
76167647 if (ty.abiSize(zcu) > 8) {
76177648 return cg.fail("TODO: Implement `@cmpxchg` for types larger than abi size of 8 bytes", .{});
76187649 }
7619 const ptr_val = try WValue.toLocal(try cg.load(ptr_operand, ty, 0), cg, ty);
7650 const ptr_val = try WValue.toLocal(try cg.load(ptr_operand, ty, .{}), cg, ty);
76207651
7621 try cg.lowerToStack(ptr_operand);
7622 try cg.lowerToStack(new_val);
7652 try cg.emitMemBase(ptr_operand);
7653 try cg.emitWValue(new_val);
76237654 try cg.emitWValue(ptr_val);
76247655 _ = try cg.intCmp(int_ty, .eq, ptr_val, expected_val);
7625 try cg.addLocal(.local_tee, cmp_result.local.value);
7656 try cg.addLocal(.local_tee, cmp_result.local);
76267657 try cg.addTag(.select);
7627 try cg.store(.stack, .stack, ty, 0);
7658 try cg.store(.stack, .stack, ty, .{ .offset = ptr_operand.memOffset() });
76287659
76297660 break :val ptr_val;
76307661 };
......@@ -7635,10 +7666,11 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76357666 try cg.addTag(.i32_xor);
76367667 try cg.addImm32(1);
76377668 try cg.addTag(.i32_and);
7638 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);
76397671 const result_ptr = try cg.allocStack(result_ty);
7640 try cg.store(result_ptr, and_result, Type.bool, @as(u32, @intCast(ty.abiSize(zcu))));
7641 try cg.store(result_ptr, ptr_val, ty, 0);
7672 try cg.store(result_ptr, and_result, .bool, .{ .offset = ty.abiSize(zcu) });
7673 try cg.store(result_ptr, ptr_val, ty, .{});
76427674 break :val result_ptr;
76437675 } else val: {
76447676 try cg.addImm32(0);
......@@ -7647,6 +7679,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76477679 try cg.addTag(.select);
76487680 break :val .stack;
76497681 };
7682 defer ptr_val.free(cg);
76507683
76517684 return cg.finishAir(inst, result, &.{ extra.ptr, extra.expected_value, extra.new_value });
76527685}
......@@ -7665,13 +7698,13 @@ fn airAtomicLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76657698 8 => .i64_atomic_load,
76667699 else => |size| return cg.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),
76677700 };
7668 try cg.emitWValue(ptr);
7701 try cg.emitMemBase(ptr);
76697702 try cg.addAtomicMemArg(tag, .{
7670 .offset = ptr.offset(),
7671 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7703 .offset = ptr.memOffset(),
7704 .alignment = ty.abiAlignment(zcu),
76727705 });
76737706 } else {
7674 _ = try cg.load(ptr, ty, 0);
7707 _ = try cg.load(ptr, ty, .{});
76757708 }
76767709
76777710 return cg.finishAir(inst, .stack, &.{atomic_load.ptr});
......@@ -7694,13 +7727,13 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76947727 .Min,
76957728 .Nand,
76967729 => {
7697 const tmp = try cg.load(ptr, ty, 0);
7730 const tmp = try cg.load(ptr, ty, .{});
76987731 const value = try tmp.toLocal(cg, ty);
76997732
77007733 // create a loop to cmpxchg the new value
77017734 try cg.startBlock(.loop, .empty);
77027735
7703 try cg.emitWValue(ptr);
7736 try cg.emitMemBase(ptr);
77047737 try cg.emitWValue(value);
77057738 if (op == .Nand) {
77067739 const and_res = try cg.intAnd(int_ty, value, operand);
......@@ -7727,16 +7760,17 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
77277760 else => return cg.fail("TODO: implement `@atomicRmw` with operation `{s}` for types larger than 64 bits", .{@tagName(op)}),
77287761 },
77297762 .{
7730 .offset = ptr.offset(),
7731 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7763 .offset = ptr.memOffset(),
7764 .alignment = ty.abiAlignment(zcu),
77327765 },
77337766 );
7734 const select_res = try cg.allocLocal(ty);
7735 try cg.addLocal(.local_tee, select_res.local.value);
7767 var select_res = try cg.allocLocal(ty);
7768 defer select_res.free(cg);
7769 try cg.addLocal(.local_tee, select_res.local);
77367770 _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if
77377771
77387772 try cg.emitWValue(select_res);
7739 try cg.addLocal(.local_set, value.local.value);
7773 try cg.addLocal(.local_set, value.local);
77407774
77417775 try cg.addLabel(.br_if, 0);
77427776 try cg.endBlock();
......@@ -7745,7 +7779,7 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
77457779
77467780 // the other operations have their own instructions for Wasm.
77477781 else => {
7748 try cg.emitWValue(ptr);
7782 try cg.emitMemBase(ptr);
77497783 try cg.emitWValue(operand);
77507784 const tag: std.wasm.AtomicsOpcode = switch (ty.abiSize(zcu)) {
77517785 1 => switch (op) {
......@@ -7787,42 +7821,42 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
77877821 else => |size| return cg.fail("TODO: Implement `@atomicRmw` for types with abi size {d}", .{size}),
77887822 };
77897823 try cg.addAtomicMemArg(tag, .{
7790 .offset = ptr.offset(),
7791 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7824 .offset = ptr.memOffset(),
7825 .alignment = ty.abiAlignment(zcu),
77927826 });
77937827 return cg.finishAir(inst, .stack, &.{ pl_op.operand, extra.operand });
77947828 },
77957829 }
77967830 } else {
7797 const loaded = try cg.load(ptr, ty, 0);
7831 const loaded = try cg.load(ptr, ty, .{});
77987832 const result = try loaded.toLocal(cg, ty);
77997833
78007834 switch (op) {
78017835 .Xchg => {
7802 try cg.store(ptr, operand, ty, 0);
7836 try cg.store(ptr, operand, ty, .{});
78037837 },
78047838 .Add,
78057839 .Sub,
78067840 => {
78077841 if (ty.isAnyFloat()) {
78087842 const float_ty: FloatType = .fromType(cg, ty);
7809 try cg.emitWValue(ptr);
7843 try cg.emitMemBase(ptr);
78107844 _ = switch (op) {
78117845 .Add => try cg.floatAdd(float_ty, result, operand),
78127846 .Sub => try cg.floatSub(float_ty, result, operand),
78137847 else => unreachable,
78147848 };
7815 try cg.store(.stack, .stack, ty, ptr.offset());
7849 try cg.store(.stack, .stack, ty, .{ .offset = ptr.memOffset() });
78167850 } else {
78177851 const int_ty: IntType = .fromType(cg, ty);
7818 try cg.emitWValue(ptr);
7852 try cg.emitMemBase(ptr);
78197853 _ = switch (op) {
78207854 .Add => try cg.intAdd(int_ty, result, operand),
78217855 .Sub => try cg.intSub(int_ty, result, operand),
78227856 else => unreachable,
78237857 };
78247858 _ = try cg.intWrap(int_ty, .stack);
7825 try cg.store(.stack, .stack, ty, ptr.offset());
7859 try cg.store(.stack, .stack, ty, .{ .offset = ptr.memOffset() });
78267860 }
78277861 },
78287862 .And,
......@@ -7830,39 +7864,39 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
78307864 .Xor,
78317865 => {
78327866 const int_ty: IntType = .fromType(cg, ty);
7833 try cg.emitWValue(ptr);
7867 try cg.emitMemBase(ptr);
78347868 _ = switch (op) {
78357869 .And => try cg.intAnd(int_ty, result, operand),
78367870 .Or => try cg.intOr(int_ty, result, operand),
78377871 .Xor => try cg.intXor(int_ty, result, operand),
78387872 else => unreachable,
78397873 };
7840 try cg.store(.stack, .stack, ty, ptr.offset());
7874 try cg.store(.stack, .stack, ty, .{ .offset = ptr.memOffset() });
78417875 },
78427876 .Max,
78437877 .Min,
78447878 => {
78457879 if (ty.isAnyFloat()) {
78467880 const float_ty: FloatType = .fromType(cg, ty);
7847 try cg.emitWValue(ptr);
7881 try cg.emitMemBase(ptr);
78487882 try cg.emitWValue(result);
78497883 try cg.emitWValue(operand);
78507884 _ = try cg.floatCmp(float_ty, if (op == .Max) .gt else .lt, result, operand);
78517885 try cg.addTag(.select);
7852 try cg.store(.stack, .stack, ty, ptr.offset());
7886 try cg.store(.stack, .stack, ty, .{ .offset = ptr.memOffset() });
78537887 } else {
78547888 const int_ty: IntType = .fromType(cg, ty);
7855 try cg.emitWValue(ptr);
7889 try cg.emitMemBase(ptr);
78567890 try cg.emitWValue(result);
78577891 try cg.emitWValue(operand);
78587892 _ = try cg.intCmp(int_ty, if (op == .Max) .gt else .lt, result, operand);
78597893 try cg.addTag(.select);
7860 try cg.store(.stack, .stack, ty, ptr.offset());
7894 try cg.store(.stack, .stack, ty, .{ .offset = ptr.memOffset() });
78617895 }
78627896 },
78637897 .Nand => {
78647898 const int_ty: IntType = .fromType(cg, ty);
7865 try cg.emitWValue(ptr);
7899 try cg.emitMemBase(ptr);
78667900 const and_res = try cg.intAnd(int_ty, result, operand);
78677901 if (int_ty.bits <= 32) {
78687902 try cg.addImm32(~@as(u32, 0));
......@@ -7872,7 +7906,7 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
78727906 return cg.fail("TODO: `@atomicRmw` with operator `Nand` for types larger than 64 bits", .{});
78737907 }
78747908 _ = try cg.intXor(int_ty, and_res, .stack);
7875 try cg.store(.stack, .stack, ty, ptr.offset());
7909 try cg.store(.stack, .stack, ty, .{ .offset = ptr.memOffset() });
78767910 },
78777911 }
78787912
......@@ -7897,23 +7931,20 @@ fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
78977931 8 => .i64_atomic_store,
78987932 else => |size| return cg.fail("TODO: @atomicLoad for types with abi size {d}", .{size}),
78997933 };
7900 try cg.emitWValue(ptr);
7901 try cg.lowerToStack(operand);
7934 try cg.emitMemBase(ptr);
7935 try cg.emitWValue(operand);
79027936 try cg.addAtomicMemArg(tag, .{
7903 .offset = ptr.offset(),
7904 .alignment = @intCast(ty.abiAlignment(zcu).toByteUnits().?),
7937 .offset = ptr.memOffset(),
7938 .alignment = ty.abiAlignment(zcu),
79057939 });
79067940 } else {
7907 try cg.store(ptr, operand, ty, 0);
7941 try cg.store(ptr, operand, ty, .{});
79087942 }
79097943
79107944 return cg.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
79117945}
79127946
79137947fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7914 if (cg.initial_stack_value == .none) {
7915 try cg.initializeStack();
7916 }
79177948 try cg.emitWValue(cg.bottom_stack_value);
79187949 return cg.finishAir(inst, .stack, &.{});
79197950}
......@@ -7947,6 +7978,11 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
79477978 if (unwrapped_asm.source.len != 0) {
79487979 var local_map: assembly.LocalMap = .empty;
79497980 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 }
79507986
79517987 {
79527988 var it = unwrapped_asm.iterateOutputs();
......@@ -7959,7 +7995,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
79597995 return cg.fail("Self-hosted wasm backend requires output constraint to be equal \"=r\"", .{});
79607996 }
79617997
7962 const gop = try local_map.getOrPutValue(cg.gpa, name, result.local.value);
7998 const gop = try local_map.getOrPutValue(cg.gpa, name, result.local);
79637999 assert(!gop.found_existing); // first value
79648000
79658001 assert(it.next() == null);
......@@ -7977,10 +8013,11 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
79778013 return cg.fail("Self-hosted wasm backend requires input constraint to be equal \"r\"", .{});
79788014 }
79798015
7980 try cg.lowerToStack(operand);
8016 try cg.emitWValue(operand);
79818017 const op_local = try WValue.toLocal(.stack, cg, cg.typeOf(input.operand));
8018 input_locals.appendAssumeCapacity(op_local);
79828019
7983 const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local.value);
8020 const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local);
79848021 if (gop.found_existing) {
79858022 return cg.fail("Duplicate asm variable name \"{s}\"", .{name});
79868023 }
src/codegen/wasm/Emit.zig+10-10
......@@ -56,13 +56,15 @@ pub fn lower(emit: *Emit) Error!void {
5656 writeSleb128(code, aligned_stack);
5757 // subtract it from the current stack pointer
5858 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_sub));
59 // Get negative stack alignment
60 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
61 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const));
62 writeSleb128(code, neg_stack_align);
63 // Bitwise-and the value to get the new stack pointer to ensure the
64 // pointers are aligned with the abi alignment.
65 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_and));
59 if (align_bytes != 16) {
60 // Get negative stack alignment
61 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
62 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const));
63 writeSleb128(code, neg_stack_align);
64 // Bitwise-and the value to get the new stack pointer to ensure the
65 // pointers are aligned with the abi alignment.
66 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_and));
67 }
6668 // The bottom will be used to calculate all stack pointer offsets.
6769 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.local_tee));
6870 writeUleb128(code, mir.prologue.bottom_stack_local);
......@@ -988,9 +990,7 @@ pub fn lower(emit: *Emit) Error!void {
988990/// Asserts 20 unused capacity.
989991fn encodeMemArg(code: *ArrayList(u8), mem_arg: Mir.MemArg) void {
990992 assert(code.unusedCapacitySlice().len >= 20);
991 // Wasm encodes alignment as power of 2, rather than natural alignment.
992 const encoded_alignment = @ctz(mem_arg.alignment);
993 writeUleb128(code, encoded_alignment);
993 writeUleb128(code, mem_arg.alignment.toLog2Units());
994994 writeUleb128(code, mem_arg.offset);
995995}
996996
src/codegen/wasm/Mir.zig+9-2
......@@ -694,11 +694,18 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data
694694 inline for (info.field_names, info.field_types) |field_name, field_type| {
695695 @field(result, field_name) = switch (field_type) {
696696 u32 => self.extra[i],
697 u64 => value: {
698 const lo = @as(u64, self.extra[i]);
699 const hi = @as(u64, self.extra[i + 1]) << 32;
700 i += 1;
701 break :value hi | lo;
702 },
697703 i32 => @bitCast(self.extra[i]),
698704 Wasm.UavsObjIndex,
699705 Wasm.UavsExeIndex,
700706 InternPool.Nav.Index,
701707 InternPool.Index,
708 Alignment,
702709 => @fromBackingInt(@intCast(self.extra[i])),
703710 else => @compileError("Unsupported field type " ++ @typeName(field_type)),
704711 };
......@@ -747,8 +754,8 @@ pub const Float64 = struct {
747754};
748755
749756pub const MemArg = struct {
750 offset: u32,
751 alignment: u32,
757 offset: u64,
758 alignment: Alignment,
752759};
753760
754761pub const UavRefOff = struct {
src/codegen/wasm/assembly.zig+4-4
......@@ -679,20 +679,20 @@ fn parseMemArg(cg: *CodeGen, inst: []const u8, word_it: *mem.TokenIterator(u8, .
679679 const offset_str = oa[0];
680680 const alignment_str = oa[1];
681681
682 const offset = std.fmt.parseInt(i32, offset_str, 0) catch {
682 const offset = std.fmt.parseInt(i64, offset_str, 0) catch {
683683 return cg.fail("Malformed assembly, malformed offset \"{s}\"", .{offset_str});
684684 };
685685 const alignment_p2 = std.fmt.parseInt(u5, alignment_str, 0) catch {
686686 return cg.fail("Malformed assembly, malformed offset \"{s}\"", .{alignment_str});
687687 };
688688
689 return .{ .offset = @bitCast(offset), .alignment = @as(u32, 1) << alignment_p2 };
689 return .{ .offset = @bitCast(offset), .alignment = .fromLog2Units(alignment_p2) };
690690 } else {
691691 const offset_str = mem_arg_str;
692 const offset = std.fmt.parseInt(i32, offset_str, 0) catch {
692 const offset = std.fmt.parseInt(i64, offset_str, 0) catch {
693693 return cg.fail("Malformed assembly, malformed offset \"{s}\"", .{offset_str});
694694 };
695 return .{ .offset = @bitCast(offset), .alignment = 1 };
695 return .{ .offset = @bitCast(offset), .alignment = .@"1" };
696696 }
697697}
698698