authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-17 07:51:07-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-17 07:51:07-04:00
logc010767311904177681280c6427eb360200df28f
tree0cd7953b997a12859c7d7d41dc1d95d370edb243
parent1e0f74a9e6a9071bfb82fa3ce5a40ac90bdb91cd
parent0aa23fe8b7b8ae3b3b0a4716e1d92a8116b1377e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13193 from Luukdegram/wasm-locals

stage2: Wasm - Integrate lifeness analysis for locals reusal

1 files changed, 2574 insertions(+), 2241 deletions(-)

src/arch/wasm/CodeGen.zig+2574-2241
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const Allocator = std.mem.Allocator;
34const ArrayList = std.ArrayList;
45const assert = std.debug.assert;
......@@ -31,8 +32,13 @@ const WValue = union(enum) {
3132 none: void,
3233 /// The value lives on top of the stack
3334 stack: void,
34 /// Index of the local variable
35 local: u32,
35 /// Index of the local
36 local: struct {
37 /// Contains the index to the local
38 value: u32,
39 /// The amount of instructions referencing this `WValue`
40 references: u32,
41 },
3642 /// An immediate 32bit value
3743 imm32: u32,
3844 /// An immediate 64bit value
......@@ -59,7 +65,12 @@ const WValue = union(enum) {
5965 function_index: u32,
6066 /// Offset from the bottom of the virtual stack, with the offset
6167 /// pointing to where the value lives.
62 stack_offset: u32,
68 stack_offset: struct {
69 /// Contains the actual value of the offset
70 value: u32,
71 /// The amount of instructions referencing this `WValue`
72 references: u32,
73 },
6374
6475 /// Returns the offset from the bottom of the stack. This is useful when
6576 /// we use the load or store instruction to ensure we retrieve the value
......@@ -67,9 +78,9 @@ const WValue = union(enum) {
6778 /// bottom of the stack. For instances where `WValue` is not `stack_value`
6879 /// this will return 0, which allows us to simply call this function for all
6980 /// loads and stores without requiring checks everywhere.
70 fn offset(self: WValue) u32 {
71 switch (self) {
72 .stack_offset => |stack_offset| return stack_offset,
81 fn offset(value: WValue) u32 {
82 switch (value) {
83 .stack_offset => |stack_offset| return stack_offset.value,
7384 else => return 0,
7485 }
7586 }
......@@ -77,12 +88,12 @@ const WValue = union(enum) {
7788 /// Promotes a `WValue` to a local when given value is on top of the stack.
7889 /// When encountering a `local` or `stack_offset` this is essentially a no-op.
7990 /// All other tags are illegal.
80 fn toLocal(value: WValue, gen: *Self, ty: Type) InnerError!WValue {
91 fn toLocal(value: WValue, gen: *CodeGen, ty: Type) InnerError!WValue {
8192 switch (value) {
8293 .stack => {
83 const local = try gen.allocLocal(ty);
84 try gen.addLabel(.local_set, local.local);
85 return local;
94 const new_local = try gen.allocLocal(ty);
95 try gen.addLabel(.local_set, new_local.local.value);
96 return new_local;
8697 },
8798 .local, .stack_offset => return value,
8899 else => unreachable,
......@@ -91,11 +102,14 @@ const WValue = union(enum) {
91102
92103 /// Marks a local as no longer being referenced and essentially allows
93104 /// us to re-use it somewhere else within the function.
94 /// The valtype of the local is deducted by using the index of the given.
95 fn free(value: *WValue, gen: *Self) void {
105 /// The valtype of the local is deducted by using the index of the given `WValue`.
106 fn free(value: *WValue, gen: *CodeGen) void {
96107 if (value.* != .local) return;
97 const local_value = value.local;
98 const index = local_value - gen.args.len - @boolToInt(gen.return_value != .none);
108 const local_value = value.local.value;
109 const reserved = gen.args.len + @boolToInt(gen.return_value != .none);
110 if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals.
111
112 const index = local_value - reserved;
99113 const valtype = @intToEnum(wasm.Valtype, gen.locals.items[index]);
100114 switch (valtype) {
101115 .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
......@@ -103,7 +117,7 @@ const WValue = union(enum) {
103117 .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return,
104118 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
105119 }
106 value.* = WValue{ .none = {} };
120 value.* = undefined;
107121 }
108122};
109123
......@@ -568,9 +582,9 @@ pub const Result = union(enum) {
568582};
569583
570584/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
571pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue);
585pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);
572586
573const Self = @This();
587const CodeGen = @This();
574588
575589/// Reference to the function declaration the code
576590/// section belongs to
......@@ -584,8 +598,13 @@ liveness: Liveness,
584598gpa: mem.Allocator,
585599debug_output: codegen.DebugInfoOutput,
586600mod_fn: *const Module.Fn,
601/// Contains a list of current branches.
602/// When we return from a branch, the branch will be popped from this list,
603/// which means branches can only contain references from within its own branch,
604/// or a branch higher (lower index) in the tree.
605branches: std.ArrayListUnmanaged(Branch) = .{},
587606/// Table to save `WValue`'s generated by an `Air.Inst`
588values: ValueTable,
607// values: ValueTable,
589608/// Mapping from Air.Inst.Index to block ids
590609blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
591610 label: u32,
......@@ -650,6 +669,13 @@ free_locals_f32: std.ArrayListUnmanaged(u32) = .{},
650669/// It is illegal to store a non-i32 valtype in this list.
651670free_locals_f64: std.ArrayListUnmanaged(u32) = .{},
652671
672/// When in debug mode, this tracks if no `finishAir` was missed.
673/// Forgetting to call `finishAir` will cause the result to not be
674/// stored in our `values` map and therefore cause bugs.
675air_bookkeeping: @TypeOf(bookkeeping_init) = bookkeeping_init,
676
677const bookkeeping_init = if (builtin.mode == .Debug) @as(usize, 0) else {};
678
653679const InnerError = error{
654680 OutOfMemory,
655681 /// An error occurred when trying to lower AIR to MIR.
......@@ -660,37 +686,48 @@ const InnerError = error{
660686 Overflow,
661687};
662688
663pub fn deinit(self: *Self) void {
664 self.values.deinit(self.gpa);
665 self.blocks.deinit(self.gpa);
666 self.locals.deinit(self.gpa);
667 self.mir_instructions.deinit(self.gpa);
668 self.mir_extra.deinit(self.gpa);
669 self.free_locals_i32.deinit(self.gpa);
670 self.free_locals_i64.deinit(self.gpa);
671 self.free_locals_f32.deinit(self.gpa);
672 self.free_locals_f64.deinit(self.gpa);
673 self.* = undefined;
689pub fn deinit(func: *CodeGen) void {
690 assert(func.branches.items.len == 0); // we should end with no branches left. Forgot a call to `branches.pop()`?
691 func.branches.deinit(func.gpa);
692 func.blocks.deinit(func.gpa);
693 func.locals.deinit(func.gpa);
694 func.mir_instructions.deinit(func.gpa);
695 func.mir_extra.deinit(func.gpa);
696 func.free_locals_i32.deinit(func.gpa);
697 func.free_locals_i64.deinit(func.gpa);
698 func.free_locals_f32.deinit(func.gpa);
699 func.free_locals_f64.deinit(func.gpa);
700 func.* = undefined;
674701}
675702
676703/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
677fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
704fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {
678705 const src = LazySrcLoc.nodeOffset(0);
679 const src_loc = src.toSrcLoc(self.decl);
680 self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
706 const src_loc = src.toSrcLoc(func.decl);
707 func.err_msg = try Module.ErrorMsg.create(func.gpa, src_loc, fmt, args);
681708 return error.CodegenFail;
682709}
683710
684711/// Resolves the `WValue` for the given instruction `inst`
685712/// When the given instruction has a `Value`, it returns a constant instead
686fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
687 const gop = try self.values.getOrPut(self.gpa, ref);
688 if (gop.found_existing) return gop.value_ptr.*;
713fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
714 var branch_index = func.branches.items.len;
715 while (branch_index > 0) : (branch_index -= 1) {
716 const branch = func.branches.items[branch_index - 1];
717 if (branch.values.get(ref)) |value| {
718 return value;
719 }
720 }
689721
690722 // when we did not find an existing instruction, it
691723 // means we must generate it from a constant.
692 const val = self.air.value(ref).?;
693 const ty = self.air.typeOf(ref);
724 // We always store constants in the most outer branch as they must never
725 // be removed. The most outer branch is always at index 0.
726 const gop = try func.branches.items[0].values.getOrPut(func.gpa, ref);
727 assert(!gop.found_existing);
728
729 const val = func.air.value(ref).?;
730 const ty = func.air.typeOf(ref);
694731 if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt() and !ty.isError()) {
695732 gop.value_ptr.* = WValue{ .none = {} };
696733 return gop.value_ptr.*;
......@@ -702,70 +739,152 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
702739 //
703740 // In the other cases, we will simply lower the constant to a value that fits
704741 // into a single local (such as a pointer, integer, bool, etc).
705 const result = if (isByRef(ty, self.target)) blk: {
706 const sym_index = try self.bin_file.lowerUnnamedConst(.{ .ty = ty, .val = val }, self.decl_index);
742 const result = if (isByRef(ty, func.target)) blk: {
743 const sym_index = try func.bin_file.lowerUnnamedConst(.{ .ty = ty, .val = val }, func.decl_index);
707744 break :blk WValue{ .memory = sym_index };
708 } else try self.lowerConstant(val, ty);
745 } else try func.lowerConstant(val, ty);
709746
710747 gop.value_ptr.* = result;
711748 return result;
712749}
713750
751fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) void {
752 assert(operands.len <= Liveness.bpi - 1);
753 var tomb_bits = func.liveness.getTombBits(inst);
754 for (operands) |operand| {
755 const dies = @truncate(u1, tomb_bits) != 0;
756 tomb_bits >>= 1;
757 if (!dies) continue;
758 processDeath(func, operand);
759 }
760
761 // results of `none` can never be referenced.
762 if (result != .none) {
763 assert(result != .stack); // it's illegal to store a stack value as we cannot track its position
764 const branch = func.currentBranch();
765 branch.values.putAssumeCapacityNoClobber(Air.indexToRef(inst), result);
766 }
767
768 if (builtin.mode == .Debug) {
769 func.air_bookkeeping += 1;
770 }
771}
772
773const Branch = struct {
774 values: ValueTable = .{},
775
776 fn deinit(branch: *Branch, gpa: Allocator) void {
777 branch.values.deinit(gpa);
778 }
779};
780
781inline fn currentBranch(func: *CodeGen) *Branch {
782 return &func.branches.items[func.branches.items.len - 1];
783}
784
785const BigTomb = struct {
786 gen: *CodeGen,
787 inst: Air.Inst.Index,
788 lbt: Liveness.BigTomb,
789
790 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
791 _ = Air.refToIndex(op_ref) orelse return; // constants do not have to be freed regardless
792 const dies = bt.lbt.feed();
793 if (!dies) return;
794 processDeath(bt.gen, op_ref);
795 }
796
797 fn finishAir(bt: *BigTomb, result: WValue) void {
798 assert(result != .stack);
799 if (result != .none) {
800 bt.gen.currentBranch().values.putAssumeCapacityNoClobber(Air.indexToRef(bt.inst), result);
801 }
802
803 if (builtin.mode == .Debug) {
804 bt.gen.air_bookkeeping += 1;
805 }
806 }
807};
808
809fn iterateBigTomb(func: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
810 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, operand_count + 1);
811 return BigTomb{
812 .gen = func,
813 .inst = inst,
814 .lbt = func.liveness.iterateBigTomb(inst),
815 };
816}
817
818fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {
819 const inst = Air.refToIndex(ref) orelse return;
820 if (func.air.instructions.items(.tag)[inst] == .constant) return;
821 // Branches are currently only allowed to free locals allocated
822 // within their own branch.
823 // TODO: Upon branch consolidation free any locals if needed.
824 const value = func.currentBranch().values.getPtr(ref) orelse return;
825 if (value.* != .local) return;
826 log.debug("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)});
827 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
828 if (value.local.references == 0) {
829 value.free(func);
830 }
831}
832
714833/// Appends a MIR instruction and returns its index within the list of instructions
715fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void {
716 try self.mir_instructions.append(self.gpa, inst);
834fn addInst(func: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
835 try func.mir_instructions.append(func.gpa, inst);
717836}
718837
719fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
720 try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
838fn addTag(func: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
839 try func.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
721840}
722841
723fn addExtended(self: *Self, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {
724 try self.addInst(.{ .tag = .extended, .secondary = @enumToInt(opcode), .data = .{ .tag = {} } });
842fn addExtended(func: *CodeGen, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {
843 try func.addInst(.{ .tag = .extended, .secondary = @enumToInt(opcode), .data = .{ .tag = {} } });
725844}
726845
727fn addLabel(self: *Self, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
728 try self.addInst(.{ .tag = tag, .data = .{ .label = label } });
846fn addLabel(func: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
847 try func.addInst(.{ .tag = tag, .data = .{ .label = label } });
729848}
730849
731fn addImm32(self: *Self, imm: i32) error{OutOfMemory}!void {
732 try self.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = imm } });
850fn addImm32(func: *CodeGen, imm: i32) error{OutOfMemory}!void {
851 try func.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = imm } });
733852}
734853
735854/// Accepts an unsigned 64bit integer rather than a signed integer to
736855/// prevent us from having to bitcast multiple times as most values
737856/// within codegen are represented as unsigned rather than signed.
738fn addImm64(self: *Self, imm: u64) error{OutOfMemory}!void {
739 const extra_index = try self.addExtra(Mir.Imm64.fromU64(imm));
740 try self.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
857fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void {
858 const extra_index = try func.addExtra(Mir.Imm64.fromU64(imm));
859 try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
741860}
742861
743fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void {
744 const extra_index = try self.addExtra(Mir.Float64.fromFloat64(float));
745 try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
862fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void {
863 const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float));
864 try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
746865}
747866
748867/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.
749fn addMemArg(self: *Self, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
750 const extra_index = try self.addExtra(mem_arg);
751 try self.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
868fn addMemArg(func: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
869 const extra_index = try func.addExtra(mem_arg);
870 try func.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
752871}
753872
754873/// Appends entries to `mir_extra` based on the type of `extra`.
755874/// Returns the index into `mir_extra`
756fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
875fn addExtra(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
757876 const fields = std.meta.fields(@TypeOf(extra));
758 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
759 return self.addExtraAssumeCapacity(extra);
877 try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len);
878 return func.addExtraAssumeCapacity(extra);
760879}
761880
762881/// Appends entries to `mir_extra` based on the type of `extra`.
763882/// Returns the index into `mir_extra`
764fn addExtraAssumeCapacity(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
883fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
765884 const fields = std.meta.fields(@TypeOf(extra));
766 const result = @intCast(u32, self.mir_extra.items.len);
885 const result = @intCast(u32, func.mir_extra.items.len);
767886 inline for (fields) |field| {
768 self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
887 func.mir_extra.appendAssumeCapacity(switch (field.field_type) {
769888 u32 => @field(extra, field.name),
770889 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
771890 });
......@@ -810,56 +929,91 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
810929}
811930
812931/// Writes the bytecode depending on the given `WValue` in `val`
813fn emitWValue(self: *Self, value: WValue) InnerError!void {
932fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
814933 switch (value) {
815934 .none, .stack => {}, // no-op
816 .local => |idx| try self.addLabel(.local_get, idx),
817 .imm32 => |val| try self.addImm32(@bitCast(i32, val)),
818 .imm64 => |val| try self.addImm64(val),
819 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
820 .float64 => |val| try self.addFloat64(val),
935 .local => |idx| try func.addLabel(.local_get, idx.value),
936 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),
937 .imm64 => |val| try func.addImm64(val),
938 .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
939 .float64 => |val| try func.addFloat64(val),
821940 .memory => |ptr| {
822 const extra_index = try self.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
823 try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
941 const extra_index = try func.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
942 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
824943 },
825944 .memory_offset => |mem_off| {
826 const extra_index = try self.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
827 try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
945 const extra_index = try func.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
946 try func.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
828947 },
829 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
830 .stack_offset => try self.addLabel(.local_get, self.bottom_stack_value.local), // caller must ensure to address the offset
948 .function_index => |index| try func.addLabel(.function_index, index), // write function index and generate relocation
949 .stack_offset => try func.addLabel(.local_get, func.bottom_stack_value.local.value), // caller must ensure to address the offset
950 }
951}
952
953/// If given a local or stack-offset, increases the reference count by 1.
954/// The old `WValue` found at instruction `ref` is then replaced by the
955/// modified `WValue` and returned. When given a non-local or non-stack-offset,
956/// returns the given `operand` itfunc instead.
957fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {
958 if (operand != .local and operand != .stack_offset) return operand;
959 var new_value = operand;
960 switch (new_value) {
961 .local => |*local| local.references += 1,
962 .stack_offset => |*stack_offset| stack_offset.references += 1,
963 else => unreachable,
964 }
965 const old_value = func.getResolvedInst(ref);
966 old_value.* = new_value;
967 return new_value;
968}
969
970/// From a reference, returns its resolved `WValue`.
971/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.
972fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {
973 var index = func.branches.items.len;
974 while (index > 0) : (index -= 1) {
975 const branch = func.branches.items[index - 1];
976 if (branch.values.getPtr(ref)) |value| {
977 return value;
978 }
831979 }
980 unreachable; // developer-error: This can only be called on resolved instructions. Use `resolveInst` instead.
832981}
833982
834983/// Creates one locals for a given `Type`.
835984/// Returns a corresponding `Wvalue` with `local` as active tag
836fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
837 const valtype = typeToValtype(ty, self.target);
985fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
986 const valtype = typeToValtype(ty, func.target);
838987 switch (valtype) {
839 .i32 => if (self.free_locals_i32.popOrNull()) |index| {
840 return WValue{ .local = index };
988 .i32 => if (func.free_locals_i32.popOrNull()) |index| {
989 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
990 return WValue{ .local = .{ .value = index, .references = 1 } };
841991 },
842 .i64 => if (self.free_locals_i64.popOrNull()) |index| {
843 return WValue{ .local = index };
992 .i64 => if (func.free_locals_i64.popOrNull()) |index| {
993 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
994 return WValue{ .local = .{ .value = index, .references = 1 } };
844995 },
845 .f32 => if (self.free_locals_f32.popOrNull()) |index| {
846 return WValue{ .local = index };
996 .f32 => if (func.free_locals_f32.popOrNull()) |index| {
997 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
998 return WValue{ .local = .{ .value = index, .references = 1 } };
847999 },
848 .f64 => if (self.free_locals_f64.popOrNull()) |index| {
849 return WValue{ .local = index };
1000 .f64 => if (func.free_locals_f64.popOrNull()) |index| {
1001 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1002 return WValue{ .local = .{ .value = index, .references = 1 } };
8501003 },
8511004 }
1005 log.debug("new local of type {}\n", .{valtype});
8521006 // no local was free to be re-used, so allocate a new local instead
853 return self.ensureAllocLocal(ty);
1007 return func.ensureAllocLocal(ty);
8541008}
8551009
8561010/// Ensures a new local will be created. This is useful when it's useful
8571011/// to use a zero-initialized local.
858fn ensureAllocLocal(self: *Self, ty: Type) InnerError!WValue {
859 try self.locals.append(self.gpa, genValtype(ty, self.target));
860 const initial_index = self.local_index;
861 self.local_index += 1;
862 return WValue{ .local = initial_index };
1012fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
1013 try func.locals.append(func.gpa, genValtype(ty, func.target));
1014 const initial_index = func.local_index;
1015 func.local_index += 1;
1016 return WValue{ .local = .{ .value = initial_index, .references = 1 } };
8631017}
8641018
8651019/// Generates a `wasm.Type` from a given function type.
......@@ -925,11 +1079,11 @@ pub fn generate(
9251079 debug_output: codegen.DebugInfoOutput,
9261080) codegen.GenerateSymbolError!codegen.FnResult {
9271081 _ = src_loc;
928 var code_gen: Self = .{
1082 var code_gen: CodeGen = .{
9291083 .gpa = bin_file.allocator,
9301084 .air = air,
9311085 .liveness = liveness,
932 .values = .{},
1086 // .values = .{},
9331087 .code = code,
9341088 .decl_index = func.owner_decl,
9351089 .decl = bin_file.options.module.?.declPtr(func.owner_decl),
......@@ -950,83 +1104,89 @@ pub fn generate(
9501104 return codegen.FnResult{ .appended = {} };
9511105}
9521106
953fn genFunc(self: *Self) InnerError!void {
954 const fn_info = self.decl.ty.fnInfo();
955 var func_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target);
956 defer func_type.deinit(self.gpa);
957 self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
1107fn genFunc(func: *CodeGen) InnerError!void {
1108 const fn_info = func.decl.ty.fnInfo();
1109 var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target);
1110 defer func_type.deinit(func.gpa);
1111 func.decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type);
9581112
959 var cc_result = try self.resolveCallingConventionValues(self.decl.ty);
960 defer cc_result.deinit(self.gpa);
1113 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);
1114 defer cc_result.deinit(func.gpa);
9611115
962 self.args = cc_result.args;
963 self.return_value = cc_result.return_value;
1116 func.args = cc_result.args;
1117 func.return_value = cc_result.return_value;
9641118
965 try self.addTag(.dbg_prologue_end);
1119 try func.addTag(.dbg_prologue_end);
9661120
1121 try func.branches.append(func.gpa, .{});
9671122 // Generate MIR for function body
968 try self.genBody(self.air.getMainBody());
1123 try func.genBody(func.air.getMainBody());
1124
1125 // clean up outer branch
1126 var outer_branch = func.branches.pop();
1127 outer_branch.deinit(func.gpa);
1128
9691129 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
9701130 // we emit an unreachable instruction to tell the stack validator that part will never be reached.
971 if (func_type.returns.len != 0 and self.air.instructions.len > 0) {
972 const inst = @intCast(u32, self.air.instructions.len - 1);
973 const last_inst_ty = self.air.typeOfIndex(inst);
1131 if (func_type.returns.len != 0 and func.air.instructions.len > 0) {
1132 const inst = @intCast(u32, func.air.instructions.len - 1);
1133 const last_inst_ty = func.air.typeOfIndex(inst);
9741134 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime() or last_inst_ty.isNoReturn()) {
975 try self.addTag(.@"unreachable");
1135 try func.addTag(.@"unreachable");
9761136 }
9771137 }
9781138 // End of function body
979 try self.addTag(.end);
1139 try func.addTag(.end);
9801140
981 try self.addTag(.dbg_epilogue_begin);
1141 try func.addTag(.dbg_epilogue_begin);
9821142
9831143 // check if we have to initialize and allocate anything into the stack frame.
9841144 // If so, create enough stack space and insert the instructions at the front of the list.
985 if (self.stack_size > 0) {
986 var prologue = std.ArrayList(Mir.Inst).init(self.gpa);
1145 if (func.stack_size > 0) {
1146 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
9871147 defer prologue.deinit();
9881148
9891149 // load stack pointer
9901150 try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });
9911151 // store stack pointer so we can restore it when we return from the function
992 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.initial_stack_value.local } });
1152 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });
9931153 // get the total stack size
994 const aligned_stack = std.mem.alignForwardGeneric(u32, self.stack_size, self.stack_alignment);
1154 const aligned_stack = std.mem.alignForwardGeneric(u32, func.stack_size, func.stack_alignment);
9951155 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, aligned_stack) } });
9961156 // substract it from the current stack pointer
9971157 try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } });
9981158 // Get negative stack aligment
999 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, self.stack_alignment) * -1 } });
1159 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, func.stack_alignment) * -1 } });
10001160 // Bitwise-and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment
10011161 try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } });
10021162 // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets
1003 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.bottom_stack_value.local } });
1163 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
10041164 // Store the current stack pointer value into the global stack pointer so other function calls will
10051165 // start from this value instead and not overwrite the current stack.
10061166 try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } });
10071167
10081168 // reserve space and insert all prologue instructions at the front of the instruction list
10091169 // We insert them in reserve order as there is no insertSlice in multiArrayList.
1010 try self.mir_instructions.ensureUnusedCapacity(self.gpa, prologue.items.len);
1170 try func.mir_instructions.ensureUnusedCapacity(func.gpa, prologue.items.len);
10111171 for (prologue.items) |_, index| {
10121172 const inst = prologue.items[prologue.items.len - 1 - index];
1013 self.mir_instructions.insertAssumeCapacity(0, inst);
1173 func.mir_instructions.insertAssumeCapacity(0, inst);
10141174 }
10151175 }
10161176
10171177 var mir: Mir = .{
1018 .instructions = self.mir_instructions.toOwnedSlice(),
1019 .extra = self.mir_extra.toOwnedSlice(self.gpa),
1178 .instructions = func.mir_instructions.toOwnedSlice(),
1179 .extra = func.mir_extra.toOwnedSlice(func.gpa),
10201180 };
1021 defer mir.deinit(self.gpa);
1181 defer mir.deinit(func.gpa);
10221182
10231183 var emit: Emit = .{
10241184 .mir = mir,
1025 .bin_file = &self.bin_file.base,
1026 .code = self.code,
1027 .locals = self.locals.items,
1028 .decl = self.decl,
1029 .dbg_output = self.debug_output,
1185 .bin_file = &func.bin_file.base,
1186 .code = func.code,
1187 .locals = func.locals.items,
1188 .decl = func.decl,
1189 .dbg_output = func.debug_output,
10301190 .prev_di_line = 0,
10311191 .prev_di_column = 0,
10321192 .prev_di_offset = 0,
......@@ -1034,7 +1194,7 @@ fn genFunc(self: *Self) InnerError!void {
10341194
10351195 emit.emitMir() catch |err| switch (err) {
10361196 error.EmitFail => {
1037 self.err_msg = emit.error_msg.?;
1197 func.err_msg = emit.error_msg.?;
10381198 return error.CodegenFail;
10391199 },
10401200 else => |e| return e,
......@@ -1045,16 +1205,16 @@ const CallWValues = struct {
10451205 args: []WValue,
10461206 return_value: WValue,
10471207
1048 fn deinit(self: *CallWValues, gpa: Allocator) void {
1049 gpa.free(self.args);
1050 self.* = undefined;
1208 fn deinit(values: *CallWValues, gpa: Allocator) void {
1209 gpa.free(values.args);
1210 values.* = undefined;
10511211 }
10521212};
10531213
1054fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValues {
1214fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues {
10551215 const cc = fn_ty.fnCallingConvention();
1056 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1057 defer self.gpa.free(param_types);
1216 const param_types = try func.gpa.alloc(Type, fn_ty.fnParamLen());
1217 defer func.gpa.free(param_types);
10581218 fn_ty.fnParamTypes(param_types);
10591219 var result: CallWValues = .{
10601220 .args = &.{},
......@@ -1062,17 +1222,17 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
10621222 };
10631223 if (cc == .Naked) return result;
10641224
1065 var args = std.ArrayList(WValue).init(self.gpa);
1225 var args = std.ArrayList(WValue).init(func.gpa);
10661226 defer args.deinit();
10671227
10681228 // Check if we store the result as a pointer to the stack rather than
10691229 // by value
10701230 const fn_info = fn_ty.fnInfo();
1071 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1231 if (firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
10721232 // the sret arg will be passed as first argument, therefore we
10731233 // set the `return_value` before allocating locals for regular args.
1074 result.return_value = .{ .local = self.local_index };
1075 self.local_index += 1;
1234 result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } };
1235 func.local_index += 1;
10761236 }
10771237
10781238 switch (cc) {
......@@ -1082,21 +1242,21 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
10821242 continue;
10831243 }
10841244
1085 try args.append(.{ .local = self.local_index });
1086 self.local_index += 1;
1245 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1246 func.local_index += 1;
10871247 }
10881248 },
10891249 .C => {
10901250 for (param_types) |ty| {
1091 const ty_classes = abi.classifyType(ty, self.target);
1251 const ty_classes = abi.classifyType(ty, func.target);
10921252 for (ty_classes) |class| {
10931253 if (class == .none) continue;
1094 try args.append(.{ .local = self.local_index });
1095 self.local_index += 1;
1254 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1255 func.local_index += 1;
10961256 }
10971257 }
10981258 },
1099 else => return self.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}),
1259 else => return func.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}),
11001260 }
11011261 result.args = args.toOwnedSlice();
11021262 return result;
......@@ -1117,14 +1277,14 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, target:
11171277
11181278/// For a given `Type`, add debug information to .debug_info at the current position.
11191279/// The actual bytes will be written to the position after relocation.
1120fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
1121 switch (self.debug_output) {
1280fn addDbgInfoTypeReloc(func: *CodeGen, ty: Type) !void {
1281 switch (func.debug_output) {
11221282 .dwarf => |dwarf| {
11231283 assert(ty.hasRuntimeBitsIgnoreComptime());
11241284 const dbg_info = &dwarf.dbg_info;
11251285 const index = dbg_info.items.len;
11261286 try dbg_info.resize(index + 4);
1127 const atom = &self.decl.link.wasm.dbg_info_atom;
1287 const atom = &func.decl.link.wasm.dbg_info_atom;
11281288 try dwarf.addTypeRelocGlobal(atom, ty, @intCast(u32, index));
11291289 },
11301290 .plan9 => unreachable,
......@@ -1134,96 +1294,96 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
11341294
11351295/// Lowers a Zig type and its value based on a given calling convention to ensure
11361296/// it matches the ABI.
1137fn lowerArg(self: *Self, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void {
1297fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void {
11381298 if (cc != .C) {
1139 return self.lowerToStack(value);
1299 return func.lowerToStack(value);
11401300 }
11411301
1142 const ty_classes = abi.classifyType(ty, self.target);
1302 const ty_classes = abi.classifyType(ty, func.target);
11431303 assert(ty_classes[0] != .none);
11441304 switch (ty.zigTypeTag()) {
11451305 .Struct, .Union => {
11461306 if (ty_classes[0] == .indirect) {
1147 return self.lowerToStack(value);
1307 return func.lowerToStack(value);
11481308 }
11491309 assert(ty_classes[0] == .direct);
1150 const scalar_type = abi.scalarType(ty, self.target);
1151 const abi_size = scalar_type.abiSize(self.target);
1310 const scalar_type = abi.scalarType(ty, func.target);
1311 const abi_size = scalar_type.abiSize(func.target);
11521312 const opcode = buildOpcode(.{
11531313 .op = .load,
11541314 .width = @intCast(u8, abi_size),
11551315 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
1156 .valtype1 = typeToValtype(scalar_type, self.target),
1316 .valtype1 = typeToValtype(scalar_type, func.target),
11571317 });
1158 try self.emitWValue(value);
1159 try self.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
1318 try func.emitWValue(value);
1319 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
11601320 .offset = value.offset(),
1161 .alignment = scalar_type.abiAlignment(self.target),
1321 .alignment = scalar_type.abiAlignment(func.target),
11621322 });
11631323 },
11641324 .Int, .Float => {
11651325 if (ty_classes[1] == .none) {
1166 return self.lowerToStack(value);
1326 return func.lowerToStack(value);
11671327 }
11681328 assert(ty_classes[0] == .direct and ty_classes[1] == .direct);
1169 assert(ty.abiSize(self.target) == 16);
1329 assert(ty.abiSize(func.target) == 16);
11701330 // in this case we have an integer or float that must be lowered as 2 i64's.
1171 try self.emitWValue(value);
1172 try self.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1173 try self.emitWValue(value);
1174 try self.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
1331 try func.emitWValue(value);
1332 try func.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1333 try func.emitWValue(value);
1334 try func.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 });
11751335 },
1176 else => return self.lowerToStack(value),
1336 else => return func.lowerToStack(value),
11771337 }
11781338}
11791339
11801340/// Lowers a `WValue` to the stack. This means when the `value` results in
11811341/// `.stack_offset` we calculate the pointer of this offset and use that.
11821342/// The value is left on the stack, and not stored in any temporary.
1183fn lowerToStack(self: *Self, value: WValue) !void {
1343fn lowerToStack(func: *CodeGen, value: WValue) !void {
11841344 switch (value) {
11851345 .stack_offset => |offset| {
1186 try self.emitWValue(value);
1187 if (offset > 0) {
1188 switch (self.arch()) {
1346 try func.emitWValue(value);
1347 if (offset.value > 0) {
1348 switch (func.arch()) {
11891349 .wasm32 => {
1190 try self.addImm32(@bitCast(i32, offset));
1191 try self.addTag(.i32_add);
1350 try func.addImm32(@bitCast(i32, offset.value));
1351 try func.addTag(.i32_add);
11921352 },
11931353 .wasm64 => {
1194 try self.addImm64(offset);
1195 try self.addTag(.i64_add);
1354 try func.addImm64(offset.value);
1355 try func.addTag(.i64_add);
11961356 },
11971357 else => unreachable,
11981358 }
11991359 }
12001360 },
1201 else => try self.emitWValue(value),
1361 else => try func.emitWValue(value),
12021362 }
12031363}
12041364
12051365/// Creates a local for the initial stack value
12061366/// Asserts `initial_stack_value` is `.none`
1207fn initializeStack(self: *Self) !void {
1208 assert(self.initial_stack_value == .none);
1367fn initializeStack(func: *CodeGen) !void {
1368 assert(func.initial_stack_value == .none);
12091369 // Reserve a local to store the current stack pointer
12101370 // We can later use this local to set the stack pointer back to the value
12111371 // we have stored here.
1212 self.initial_stack_value = try self.ensureAllocLocal(Type.usize);
1372 func.initial_stack_value = try func.ensureAllocLocal(Type.usize);
12131373 // Also reserve a local to store the bottom stack value
1214 self.bottom_stack_value = try self.ensureAllocLocal(Type.usize);
1374 func.bottom_stack_value = try func.ensureAllocLocal(Type.usize);
12151375}
12161376
12171377/// Reads the stack pointer from `Context.initial_stack_value` and writes it
12181378/// to the global stack pointer variable
1219fn restoreStackPointer(self: *Self) !void {
1379fn restoreStackPointer(func: *CodeGen) !void {
12201380 // only restore the pointer if it was initialized
1221 if (self.initial_stack_value == .none) return;
1381 if (func.initial_stack_value == .none) return;
12221382 // Get the original stack pointer's value
1223 try self.emitWValue(self.initial_stack_value);
1383 try func.emitWValue(func.initial_stack_value);
12241384
12251385 // save its value in the global stack pointer
1226 try self.addLabel(.global_set, 0);
1386 try func.addLabel(.global_set, 0);
12271387}
12281388
12291389/// From a given type, will create space on the virtual stack to store the value of such type.
......@@ -1232,61 +1392,61 @@ fn restoreStackPointer(self: *Self) !void {
12321392/// moveStack unless a local was already created to store the pointer.
12331393///
12341394/// Asserts Type has codegenbits
1235fn allocStack(self: *Self, ty: Type) !WValue {
1395fn allocStack(func: *CodeGen, ty: Type) !WValue {
12361396 assert(ty.hasRuntimeBitsIgnoreComptime());
1237 if (self.initial_stack_value == .none) {
1238 try self.initializeStack();
1397 if (func.initial_stack_value == .none) {
1398 try func.initializeStack();
12391399 }
12401400
1241 const abi_size = std.math.cast(u32, ty.abiSize(self.target)) orelse {
1242 const module = self.bin_file.base.options.module.?;
1243 return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1244 ty.fmt(module), ty.abiSize(self.target),
1401 const abi_size = std.math.cast(u32, ty.abiSize(func.target)) orelse {
1402 const module = func.bin_file.base.options.module.?;
1403 return func.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1404 ty.fmt(module), ty.abiSize(func.target),
12451405 });
12461406 };
1247 const abi_align = ty.abiAlignment(self.target);
1407 const abi_align = ty.abiAlignment(func.target);
12481408
1249 if (abi_align > self.stack_alignment) {
1250 self.stack_alignment = abi_align;
1409 if (abi_align > func.stack_alignment) {
1410 func.stack_alignment = abi_align;
12511411 }
12521412
1253 const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align);
1254 defer self.stack_size = offset + abi_size;
1413 const offset = std.mem.alignForwardGeneric(u32, func.stack_size, abi_align);
1414 defer func.stack_size = offset + abi_size;
12551415
1256 return WValue{ .stack_offset = offset };
1416 return WValue{ .stack_offset = .{ .value = offset, .references = 1 } };
12571417}
12581418
12591419/// From a given AIR instruction generates a pointer to the stack where
12601420/// the value of its type will live.
12611421/// This is different from allocStack where this will use the pointer's alignment
12621422/// if it is set, to ensure the stack alignment will be set correctly.
1263fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {
1264 const ptr_ty = self.air.typeOfIndex(inst);
1423fn allocStackPtr(func: *CodeGen, inst: Air.Inst.Index) !WValue {
1424 const ptr_ty = func.air.typeOfIndex(inst);
12651425 const pointee_ty = ptr_ty.childType();
12661426
1267 if (self.initial_stack_value == .none) {
1268 try self.initializeStack();
1427 if (func.initial_stack_value == .none) {
1428 try func.initializeStack();
12691429 }
12701430
12711431 if (!pointee_ty.hasRuntimeBitsIgnoreComptime()) {
1272 return self.allocStack(Type.usize); // create a value containing just the stack pointer.
1432 return func.allocStack(Type.usize); // create a value containing just the stack pointer.
12731433 }
12741434
1275 const abi_alignment = ptr_ty.ptrAlignment(self.target);
1276 const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) orelse {
1277 const module = self.bin_file.base.options.module.?;
1278 return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1279 pointee_ty.fmt(module), pointee_ty.abiSize(self.target),
1435 const abi_alignment = ptr_ty.ptrAlignment(func.target);
1436 const abi_size = std.math.cast(u32, pointee_ty.abiSize(func.target)) orelse {
1437 const module = func.bin_file.base.options.module.?;
1438 return func.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1439 pointee_ty.fmt(module), pointee_ty.abiSize(func.target),
12801440 });
12811441 };
1282 if (abi_alignment > self.stack_alignment) {
1283 self.stack_alignment = abi_alignment;
1442 if (abi_alignment > func.stack_alignment) {
1443 func.stack_alignment = abi_alignment;
12841444 }
12851445
1286 const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment);
1287 defer self.stack_size = offset + abi_size;
1446 const offset = std.mem.alignForwardGeneric(u32, func.stack_size, abi_alignment);
1447 defer func.stack_size = offset + abi_size;
12881448
1289 return WValue{ .stack_offset = offset };
1449 return WValue{ .stack_offset = .{ .value = offset, .references = 1 } };
12901450}
12911451
12921452/// From given zig bitsize, returns the wasm bitsize
......@@ -1298,14 +1458,14 @@ fn toWasmBits(bits: u16) ?u16 {
12981458
12991459/// Performs a copy of bytes for a given type. Copying all bytes
13001460/// from rhs to lhs.
1301fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
1461fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
13021462 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
13031463 // If not, we lower it ourselves manually
1304 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {
1305 try self.lowerToStack(dst);
1306 try self.lowerToStack(src);
1307 try self.emitWValue(len);
1308 try self.addExtended(.memory_copy);
1464 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) {
1465 try func.lowerToStack(dst);
1466 try func.lowerToStack(src);
1467 try func.emitWValue(len);
1468 try func.addExtended(.memory_copy);
13091469 return;
13101470 }
13111471
......@@ -1323,17 +1483,17 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
13231483 const rhs_base = src.offset();
13241484 while (offset < length) : (offset += 1) {
13251485 // get dst's address to store the result
1326 try self.emitWValue(dst);
1486 try func.emitWValue(dst);
13271487 // load byte from src's address
1328 try self.emitWValue(src);
1329 switch (self.arch()) {
1488 try func.emitWValue(src);
1489 switch (func.arch()) {
13301490 .wasm32 => {
1331 try self.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1332 try self.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
1491 try func.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1492 try func.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
13331493 },
13341494 .wasm64 => {
1335 try self.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1336 try self.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
1495 try func.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1496 try func.addMemArg(.i64_store8, .{ .offset = lhs_base + offset, .alignment = 1 });
13371497 },
13381498 else => unreachable,
13391499 }
......@@ -1342,50 +1502,50 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
13421502 else => {
13431503 // TODO: We should probably lower this to a call to compiler_rt
13441504 // But for now, we implement it manually
1345 var offset = try self.ensureAllocLocal(Type.usize); // local for counter
1346 defer offset.free(self);
1505 var offset = try func.ensureAllocLocal(Type.usize); // local for counter
1506 defer offset.free(func);
13471507
13481508 // outer block to jump to when loop is done
1349 try self.startBlock(.block, wasm.block_empty);
1350 try self.startBlock(.loop, wasm.block_empty);
1509 try func.startBlock(.block, wasm.block_empty);
1510 try func.startBlock(.loop, wasm.block_empty);
13511511
13521512 // loop condition (offset == length -> break)
13531513 {
1354 try self.emitWValue(offset);
1355 try self.emitWValue(len);
1356 switch (self.arch()) {
1357 .wasm32 => try self.addTag(.i32_eq),
1358 .wasm64 => try self.addTag(.i64_eq),
1514 try func.emitWValue(offset);
1515 try func.emitWValue(len);
1516 switch (func.arch()) {
1517 .wasm32 => try func.addTag(.i32_eq),
1518 .wasm64 => try func.addTag(.i64_eq),
13591519 else => unreachable,
13601520 }
1361 try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
1521 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
13621522 }
13631523
13641524 // get dst ptr
13651525 {
1366 try self.emitWValue(dst);
1367 try self.emitWValue(offset);
1368 switch (self.arch()) {
1369 .wasm32 => try self.addTag(.i32_add),
1370 .wasm64 => try self.addTag(.i64_add),
1526 try func.emitWValue(dst);
1527 try func.emitWValue(offset);
1528 switch (func.arch()) {
1529 .wasm32 => try func.addTag(.i32_add),
1530 .wasm64 => try func.addTag(.i64_add),
13711531 else => unreachable,
13721532 }
13731533 }
13741534
13751535 // get src value and also store in dst
13761536 {
1377 try self.emitWValue(src);
1378 try self.emitWValue(offset);
1379 switch (self.arch()) {
1537 try func.emitWValue(src);
1538 try func.emitWValue(offset);
1539 switch (func.arch()) {
13801540 .wasm32 => {
1381 try self.addTag(.i32_add);
1382 try self.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1383 try self.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
1541 try func.addTag(.i32_add);
1542 try func.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1543 try func.addMemArg(.i32_store8, .{ .offset = dst.offset(), .alignment = 1 });
13841544 },
13851545 .wasm64 => {
1386 try self.addTag(.i64_add);
1387 try self.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1388 try self.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
1546 try func.addTag(.i64_add);
1547 try func.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1548 try func.addMemArg(.i64_store8, .{ .offset = dst.offset(), .alignment = 1 });
13891549 },
13901550 else => unreachable,
13911551 }
......@@ -1393,33 +1553,33 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
13931553
13941554 // increment loop counter
13951555 {
1396 try self.emitWValue(offset);
1397 switch (self.arch()) {
1556 try func.emitWValue(offset);
1557 switch (func.arch()) {
13981558 .wasm32 => {
1399 try self.addImm32(1);
1400 try self.addTag(.i32_add);
1559 try func.addImm32(1);
1560 try func.addTag(.i32_add);
14011561 },
14021562 .wasm64 => {
1403 try self.addImm64(1);
1404 try self.addTag(.i64_add);
1563 try func.addImm64(1);
1564 try func.addTag(.i64_add);
14051565 },
14061566 else => unreachable,
14071567 }
1408 try self.addLabel(.local_set, offset.local);
1409 try self.addLabel(.br, 0); // jump to start of loop
1568 try func.addLabel(.local_set, offset.local.value);
1569 try func.addLabel(.br, 0); // jump to start of loop
14101570 }
1411 try self.endBlock(); // close off loop block
1412 try self.endBlock(); // close off outer block
1571 try func.endBlock(); // close off loop block
1572 try func.endBlock(); // close off outer block
14131573 },
14141574 }
14151575}
14161576
1417fn ptrSize(self: *const Self) u16 {
1418 return @divExact(self.target.cpu.arch.ptrBitWidth(), 8);
1577fn ptrSize(func: *const CodeGen) u16 {
1578 return @divExact(func.target.cpu.arch.ptrBitWidth(), 8);
14191579}
14201580
1421fn arch(self: *const Self) std.Target.Cpu.Arch {
1422 return self.target.cpu.arch;
1581fn arch(func: *const CodeGen) std.Target.Cpu.Arch {
1582 return func.target.cpu.arch;
14231583}
14241584
14251585/// For a given `Type`, will return true when the type will be passed
......@@ -1477,191 +1637,191 @@ fn isByRef(ty: Type, target: std.Target) bool {
14771637/// This can be used to get a pointer to a struct field, error payload, etc.
14781638/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new
14791639/// local value to store the pointer. This allows for local re-use and improves binary size.
1480fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue {
1640fn buildPointerOffset(func: *CodeGen, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue {
14811641 // do not perform arithmetic when offset is 0.
14821642 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;
14831643 const result_ptr: WValue = switch (action) {
1484 .new => try self.ensureAllocLocal(Type.usize),
1644 .new => try func.ensureAllocLocal(Type.usize),
14851645 .modify => ptr_value,
14861646 };
1487 try self.emitWValue(ptr_value);
1647 try func.emitWValue(ptr_value);
14881648 if (offset + ptr_value.offset() > 0) {
1489 switch (self.arch()) {
1649 switch (func.arch()) {
14901650 .wasm32 => {
1491 try self.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset())));
1492 try self.addTag(.i32_add);
1651 try func.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset())));
1652 try func.addTag(.i32_add);
14931653 },
14941654 .wasm64 => {
1495 try self.addImm64(offset + ptr_value.offset());
1496 try self.addTag(.i64_add);
1655 try func.addImm64(offset + ptr_value.offset());
1656 try func.addTag(.i64_add);
14971657 },
14981658 else => unreachable,
14991659 }
15001660 }
1501 try self.addLabel(.local_set, result_ptr.local);
1661 try func.addLabel(.local_set, result_ptr.local.value);
15021662 return result_ptr;
15031663}
15041664
1505fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1506 const air_tags = self.air.instructions.items(.tag);
1665fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1666 const air_tags = func.air.instructions.items(.tag);
15071667 return switch (air_tags[inst]) {
15081668 .constant => unreachable,
15091669 .const_ty => unreachable,
15101670
1511 .add => self.airBinOp(inst, .add),
1512 .add_sat => self.airSatBinOp(inst, .add),
1513 .addwrap => self.airWrapBinOp(inst, .add),
1514 .sub => self.airBinOp(inst, .sub),
1515 .sub_sat => self.airSatBinOp(inst, .sub),
1516 .subwrap => self.airWrapBinOp(inst, .sub),
1517 .mul => self.airBinOp(inst, .mul),
1518 .mulwrap => self.airWrapBinOp(inst, .mul),
1671 .add => func.airBinOp(inst, .add),
1672 .add_sat => func.airSatBinOp(inst, .add),
1673 .addwrap => func.airWrapBinOp(inst, .add),
1674 .sub => func.airBinOp(inst, .sub),
1675 .sub_sat => func.airSatBinOp(inst, .sub),
1676 .subwrap => func.airWrapBinOp(inst, .sub),
1677 .mul => func.airBinOp(inst, .mul),
1678 .mulwrap => func.airWrapBinOp(inst, .mul),
15191679 .div_float,
15201680 .div_exact,
15211681 .div_trunc,
1522 => self.airDiv(inst),
1523 .div_floor => self.airDivFloor(inst),
1524 .ceil => self.airCeilFloorTrunc(inst, .ceil),
1525 .floor => self.airCeilFloorTrunc(inst, .floor),
1526 .trunc_float => self.airCeilFloorTrunc(inst, .trunc),
1527 .bit_and => self.airBinOp(inst, .@"and"),
1528 .bit_or => self.airBinOp(inst, .@"or"),
1529 .bool_and => self.airBinOp(inst, .@"and"),
1530 .bool_or => self.airBinOp(inst, .@"or"),
1531 .rem => self.airBinOp(inst, .rem),
1532 .shl => self.airWrapBinOp(inst, .shl),
1533 .shl_exact => self.airBinOp(inst, .shl),
1534 .shl_sat => self.airShlSat(inst),
1535 .shr, .shr_exact => self.airBinOp(inst, .shr),
1536 .xor => self.airBinOp(inst, .xor),
1537 .max => self.airMaxMin(inst, .max),
1538 .min => self.airMaxMin(inst, .min),
1539 .mul_add => self.airMulAdd(inst),
1540
1541 .add_with_overflow => self.airAddSubWithOverflow(inst, .add),
1542 .sub_with_overflow => self.airAddSubWithOverflow(inst, .sub),
1543 .shl_with_overflow => self.airShlWithOverflow(inst),
1544 .mul_with_overflow => self.airMulWithOverflow(inst),
1545
1546 .clz => self.airClz(inst),
1547 .ctz => self.airCtz(inst),
1548
1549 .cmp_eq => self.airCmp(inst, .eq),
1550 .cmp_gte => self.airCmp(inst, .gte),
1551 .cmp_gt => self.airCmp(inst, .gt),
1552 .cmp_lte => self.airCmp(inst, .lte),
1553 .cmp_lt => self.airCmp(inst, .lt),
1554 .cmp_neq => self.airCmp(inst, .neq),
1555
1556 .cmp_vector => self.airCmpVector(inst),
1557 .cmp_lt_errors_len => self.airCmpLtErrorsLen(inst),
1558
1559 .array_elem_val => self.airArrayElemVal(inst),
1560 .array_to_slice => self.airArrayToSlice(inst),
1561 .alloc => self.airAlloc(inst),
1562 .arg => self.airArg(inst),
1563 .bitcast => self.airBitcast(inst),
1564 .block => self.airBlock(inst),
1565 .breakpoint => self.airBreakpoint(inst),
1566 .br => self.airBr(inst),
1567 .bool_to_int => self.airBoolToInt(inst),
1568 .cond_br => self.airCondBr(inst),
1569 .intcast => self.airIntcast(inst),
1570 .fptrunc => self.airFptrunc(inst),
1571 .fpext => self.airFpext(inst),
1572 .float_to_int => self.airFloatToInt(inst),
1573 .int_to_float => self.airIntToFloat(inst),
1574 .get_union_tag => self.airGetUnionTag(inst),
1575
1576 .@"try" => self.airTry(inst),
1577 .try_ptr => self.airTryPtr(inst),
1682 => func.airDiv(inst),
1683 .div_floor => func.airDivFloor(inst),
1684 .ceil => func.airCeilFloorTrunc(inst, .ceil),
1685 .floor => func.airCeilFloorTrunc(inst, .floor),
1686 .trunc_float => func.airCeilFloorTrunc(inst, .trunc),
1687 .bit_and => func.airBinOp(inst, .@"and"),
1688 .bit_or => func.airBinOp(inst, .@"or"),
1689 .bool_and => func.airBinOp(inst, .@"and"),
1690 .bool_or => func.airBinOp(inst, .@"or"),
1691 .rem => func.airBinOp(inst, .rem),
1692 .shl => func.airWrapBinOp(inst, .shl),
1693 .shl_exact => func.airBinOp(inst, .shl),
1694 .shl_sat => func.airShlSat(inst),
1695 .shr, .shr_exact => func.airBinOp(inst, .shr),
1696 .xor => func.airBinOp(inst, .xor),
1697 .max => func.airMaxMin(inst, .max),
1698 .min => func.airMaxMin(inst, .min),
1699 .mul_add => func.airMulAdd(inst),
1700
1701 .add_with_overflow => func.airAddSubWithOverflow(inst, .add),
1702 .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub),
1703 .shl_with_overflow => func.airShlWithOverflow(inst),
1704 .mul_with_overflow => func.airMulWithOverflow(inst),
1705
1706 .clz => func.airClz(inst),
1707 .ctz => func.airCtz(inst),
1708
1709 .cmp_eq => func.airCmp(inst, .eq),
1710 .cmp_gte => func.airCmp(inst, .gte),
1711 .cmp_gt => func.airCmp(inst, .gt),
1712 .cmp_lte => func.airCmp(inst, .lte),
1713 .cmp_lt => func.airCmp(inst, .lt),
1714 .cmp_neq => func.airCmp(inst, .neq),
1715
1716 .cmp_vector => func.airCmpVector(inst),
1717 .cmp_lt_errors_len => func.airCmpLtErrorsLen(inst),
1718
1719 .array_elem_val => func.airArrayElemVal(inst),
1720 .array_to_slice => func.airArrayToSlice(inst),
1721 .alloc => func.airAlloc(inst),
1722 .arg => func.airArg(inst),
1723 .bitcast => func.airBitcast(inst),
1724 .block => func.airBlock(inst),
1725 .breakpoint => func.airBreakpoint(inst),
1726 .br => func.airBr(inst),
1727 .bool_to_int => func.airBoolToInt(inst),
1728 .cond_br => func.airCondBr(inst),
1729 .intcast => func.airIntcast(inst),
1730 .fptrunc => func.airFptrunc(inst),
1731 .fpext => func.airFpext(inst),
1732 .float_to_int => func.airFloatToInt(inst),
1733 .int_to_float => func.airIntToFloat(inst),
1734 .get_union_tag => func.airGetUnionTag(inst),
1735
1736 .@"try" => func.airTry(inst),
1737 .try_ptr => func.airTryPtr(inst),
15781738
15791739 // TODO
15801740 .dbg_inline_begin,
15811741 .dbg_inline_end,
15821742 .dbg_block_begin,
15831743 .dbg_block_end,
1584 => WValue.none,
1585
1586 .dbg_var_ptr => self.airDbgVar(inst, true),
1587 .dbg_var_val => self.airDbgVar(inst, false),
1588
1589 .dbg_stmt => self.airDbgStmt(inst),
1590
1591 .call => self.airCall(inst, .auto),
1592 .call_always_tail => self.airCall(inst, .always_tail),
1593 .call_never_tail => self.airCall(inst, .never_tail),
1594 .call_never_inline => self.airCall(inst, .never_inline),
1595
1596 .is_err => self.airIsErr(inst, .i32_ne),
1597 .is_non_err => self.airIsErr(inst, .i32_eq),
1598
1599 .is_null => self.airIsNull(inst, .i32_eq, .value),
1600 .is_non_null => self.airIsNull(inst, .i32_ne, .value),
1601 .is_null_ptr => self.airIsNull(inst, .i32_eq, .ptr),
1602 .is_non_null_ptr => self.airIsNull(inst, .i32_ne, .ptr),
1603
1604 .load => self.airLoad(inst),
1605 .loop => self.airLoop(inst),
1606 .memset => self.airMemset(inst),
1607 .not => self.airNot(inst),
1608 .optional_payload => self.airOptionalPayload(inst),
1609 .optional_payload_ptr => self.airOptionalPayloadPtr(inst),
1610 .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst),
1611 .ptr_add => self.airPtrBinOp(inst, .add),
1612 .ptr_sub => self.airPtrBinOp(inst, .sub),
1613 .ptr_elem_ptr => self.airPtrElemPtr(inst),
1614 .ptr_elem_val => self.airPtrElemVal(inst),
1615 .ptrtoint => self.airPtrToInt(inst),
1616 .ret => self.airRet(inst),
1617 .ret_ptr => self.airRetPtr(inst),
1618 .ret_load => self.airRetLoad(inst),
1619 .splat => self.airSplat(inst),
1620 .select => self.airSelect(inst),
1621 .shuffle => self.airShuffle(inst),
1622 .reduce => self.airReduce(inst),
1623 .aggregate_init => self.airAggregateInit(inst),
1624 .union_init => self.airUnionInit(inst),
1625 .prefetch => self.airPrefetch(inst),
1626 .popcount => self.airPopcount(inst),
1627 .byte_swap => self.airByteSwap(inst),
1628
1629 .slice => self.airSlice(inst),
1630 .slice_len => self.airSliceLen(inst),
1631 .slice_elem_val => self.airSliceElemVal(inst),
1632 .slice_elem_ptr => self.airSliceElemPtr(inst),
1633 .slice_ptr => self.airSlicePtr(inst),
1634 .ptr_slice_len_ptr => self.airPtrSliceFieldPtr(inst, self.ptrSize()),
1635 .ptr_slice_ptr_ptr => self.airPtrSliceFieldPtr(inst, 0),
1636 .store => self.airStore(inst),
1637
1638 .set_union_tag => self.airSetUnionTag(inst),
1639 .struct_field_ptr => self.airStructFieldPtr(inst),
1640 .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0),
1641 .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1),
1642 .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2),
1643 .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3),
1644 .struct_field_val => self.airStructFieldVal(inst),
1645 .field_parent_ptr => self.airFieldParentPtr(inst),
1646
1647 .switch_br => self.airSwitchBr(inst),
1648 .trunc => self.airTrunc(inst),
1649 .unreach => self.airUnreachable(inst),
1650
1651 .wrap_optional => self.airWrapOptional(inst),
1652 .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst, false),
1653 .unwrap_errunion_payload_ptr => self.airUnwrapErrUnionPayload(inst, true),
1654 .unwrap_errunion_err => self.airUnwrapErrUnionError(inst, false),
1655 .unwrap_errunion_err_ptr => self.airUnwrapErrUnionError(inst, true),
1656 .wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
1657 .wrap_errunion_err => self.airWrapErrUnionErr(inst),
1658 .errunion_payload_ptr_set => self.airErrUnionPayloadPtrSet(inst),
1659 .error_name => self.airErrorName(inst),
1660
1661 .wasm_memory_size => self.airWasmMemorySize(inst),
1662 .wasm_memory_grow => self.airWasmMemoryGrow(inst),
1663
1664 .memcpy => self.airMemcpy(inst),
1744 => func.finishAir(inst, .none, &.{}),
1745
1746 .dbg_var_ptr => func.airDbgVar(inst, true),
1747 .dbg_var_val => func.airDbgVar(inst, false),
1748
1749 .dbg_stmt => func.airDbgStmt(inst),
1750
1751 .call => func.airCall(inst, .auto),
1752 .call_always_tail => func.airCall(inst, .always_tail),
1753 .call_never_tail => func.airCall(inst, .never_tail),
1754 .call_never_inline => func.airCall(inst, .never_inline),
1755
1756 .is_err => func.airIsErr(inst, .i32_ne),
1757 .is_non_err => func.airIsErr(inst, .i32_eq),
1758
1759 .is_null => func.airIsNull(inst, .i32_eq, .value),
1760 .is_non_null => func.airIsNull(inst, .i32_ne, .value),
1761 .is_null_ptr => func.airIsNull(inst, .i32_eq, .ptr),
1762 .is_non_null_ptr => func.airIsNull(inst, .i32_ne, .ptr),
1763
1764 .load => func.airLoad(inst),
1765 .loop => func.airLoop(inst),
1766 .memset => func.airMemset(inst),
1767 .not => func.airNot(inst),
1768 .optional_payload => func.airOptionalPayload(inst),
1769 .optional_payload_ptr => func.airOptionalPayloadPtr(inst),
1770 .optional_payload_ptr_set => func.airOptionalPayloadPtrSet(inst),
1771 .ptr_add => func.airPtrBinOp(inst, .add),
1772 .ptr_sub => func.airPtrBinOp(inst, .sub),
1773 .ptr_elem_ptr => func.airPtrElemPtr(inst),
1774 .ptr_elem_val => func.airPtrElemVal(inst),
1775 .ptrtoint => func.airPtrToInt(inst),
1776 .ret => func.airRet(inst),
1777 .ret_ptr => func.airRetPtr(inst),
1778 .ret_load => func.airRetLoad(inst),
1779 .splat => func.airSplat(inst),
1780 .select => func.airSelect(inst),
1781 .shuffle => func.airShuffle(inst),
1782 .reduce => func.airReduce(inst),
1783 .aggregate_init => func.airAggregateInit(inst),
1784 .union_init => func.airUnionInit(inst),
1785 .prefetch => func.airPrefetch(inst),
1786 .popcount => func.airPopcount(inst),
1787 .byte_swap => func.airByteSwap(inst),
1788
1789 .slice => func.airSlice(inst),
1790 .slice_len => func.airSliceLen(inst),
1791 .slice_elem_val => func.airSliceElemVal(inst),
1792 .slice_elem_ptr => func.airSliceElemPtr(inst),
1793 .slice_ptr => func.airSlicePtr(inst),
1794 .ptr_slice_len_ptr => func.airPtrSliceFieldPtr(inst, func.ptrSize()),
1795 .ptr_slice_ptr_ptr => func.airPtrSliceFieldPtr(inst, 0),
1796 .store => func.airStore(inst),
1797
1798 .set_union_tag => func.airSetUnionTag(inst),
1799 .struct_field_ptr => func.airStructFieldPtr(inst),
1800 .struct_field_ptr_index_0 => func.airStructFieldPtrIndex(inst, 0),
1801 .struct_field_ptr_index_1 => func.airStructFieldPtrIndex(inst, 1),
1802 .struct_field_ptr_index_2 => func.airStructFieldPtrIndex(inst, 2),
1803 .struct_field_ptr_index_3 => func.airStructFieldPtrIndex(inst, 3),
1804 .struct_field_val => func.airStructFieldVal(inst),
1805 .field_parent_ptr => func.airFieldParentPtr(inst),
1806
1807 .switch_br => func.airSwitchBr(inst),
1808 .trunc => func.airTrunc(inst),
1809 .unreach => func.airUnreachable(inst),
1810
1811 .wrap_optional => func.airWrapOptional(inst),
1812 .unwrap_errunion_payload => func.airUnwrapErrUnionPayload(inst, false),
1813 .unwrap_errunion_payload_ptr => func.airUnwrapErrUnionPayload(inst, true),
1814 .unwrap_errunion_err => func.airUnwrapErrUnionError(inst, false),
1815 .unwrap_errunion_err_ptr => func.airUnwrapErrUnionError(inst, true),
1816 .wrap_errunion_payload => func.airWrapErrUnionPayload(inst),
1817 .wrap_errunion_err => func.airWrapErrUnionErr(inst),
1818 .errunion_payload_ptr_set => func.airErrUnionPayloadPtrSet(inst),
1819 .error_name => func.airErrorName(inst),
1820
1821 .wasm_memory_size => func.airWasmMemorySize(inst),
1822 .wasm_memory_grow => func.airWasmMemoryGrow(inst),
1823
1824 .memcpy => func.airMemcpy(inst),
16651825
16661826 .mul_sat,
16671827 .mod,
......@@ -1700,7 +1860,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
17001860 .is_named_enum_value,
17011861 .error_set_has_value,
17021862 .addrspace_cast,
1703 => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
1863 => |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
17041864
17051865 .add_optimized,
17061866 .addwrap_optimized,
......@@ -1724,105 +1884,116 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
17241884 .cmp_vector_optimized,
17251885 .reduce_optimized,
17261886 .float_to_int_optimized,
1727 => return self.fail("TODO implement optimized float mode", .{}),
1887 => return func.fail("TODO implement optimized float mode", .{}),
17281888 };
17291889}
17301890
1731fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1891fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
17321892 for (body) |inst| {
1733 const result = try self.genInst(inst);
1734 if (result != .none) {
1735 assert(result != .stack); // not allowed to store stack values as we cannot keep track of where they are on the stack
1736 try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result);
1893 const old_bookkeeping_value = func.air_bookkeeping;
1894 // TODO: Determine why we need to pre-allocate an extra 4 possible values here.
1895 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi + 4);
1896 try func.genInst(inst);
1897
1898 if (builtin.mode == .Debug and func.air_bookkeeping < old_bookkeeping_value + 1) {
1899 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{
1900 inst,
1901 func.air.instructions.items(.tag)[inst],
1902 });
17371903 }
17381904 }
17391905}
17401906
1741fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1742 const un_op = self.air.instructions.items(.data)[inst].un_op;
1743 const operand = try self.resolveInst(un_op);
1744 const fn_info = self.decl.ty.fnInfo();
1907fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1908 const un_op = func.air.instructions.items(.data)[inst].un_op;
1909 const operand = try func.resolveInst(un_op);
1910 const fn_info = func.decl.ty.fnInfo();
17451911 const ret_ty = fn_info.return_type;
17461912
17471913 // result must be stored in the stack and we return a pointer
17481914 // to the stack instead
1749 if (self.return_value != .none) {
1750 try self.store(self.return_value, operand, ret_ty, 0);
1915 if (func.return_value != .none) {
1916 try func.store(func.return_value, operand, ret_ty, 0);
17511917 } else if (fn_info.cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime()) {
17521918 switch (ret_ty.zigTypeTag()) {
17531919 // Aggregate types can be lowered as a singular value
17541920 .Struct, .Union => {
1755 const scalar_type = abi.scalarType(ret_ty, self.target);
1756 try self.emitWValue(operand);
1921 const scalar_type = abi.scalarType(ret_ty, func.target);
1922 try func.emitWValue(operand);
17571923 const opcode = buildOpcode(.{
17581924 .op = .load,
1759 .width = @intCast(u8, scalar_type.abiSize(self.target) * 8),
1925 .width = @intCast(u8, scalar_type.abiSize(func.target) * 8),
17601926 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
1761 .valtype1 = typeToValtype(scalar_type, self.target),
1927 .valtype1 = typeToValtype(scalar_type, func.target),
17621928 });
1763 try self.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
1929 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
17641930 .offset = operand.offset(),
1765 .alignment = scalar_type.abiAlignment(self.target),
1931 .alignment = scalar_type.abiAlignment(func.target),
17661932 });
17671933 },
1768 else => try self.emitWValue(operand),
1934 else => try func.emitWValue(operand),
17691935 }
17701936 } else {
17711937 if (!ret_ty.hasRuntimeBitsIgnoreComptime() and ret_ty.isError()) {
1772 try self.addImm32(0);
1938 try func.addImm32(0);
17731939 } else {
1774 try self.emitWValue(operand);
1940 try func.emitWValue(operand);
17751941 }
17761942 }
1777 try self.restoreStackPointer();
1778 try self.addTag(.@"return");
1779 return WValue{ .none = {} };
1943 try func.restoreStackPointer();
1944 try func.addTag(.@"return");
1945
1946 func.finishAir(inst, .none, &.{un_op});
17801947}
17811948
1782fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1783 const child_type = self.air.typeOfIndex(inst).childType();
1949fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1950 const child_type = func.air.typeOfIndex(inst).childType();
17841951
1785 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime()) {
1786 return self.allocStack(Type.usize); // create pointer to void
1787 }
1952 var result = result: {
1953 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime()) {
1954 break :result try func.allocStack(Type.usize); // create pointer to void
1955 }
17881956
1789 const fn_info = self.decl.ty.fnInfo();
1790 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1791 return self.return_value;
1792 }
1957 const fn_info = func.decl.ty.fnInfo();
1958 if (firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
1959 break :result func.return_value;
1960 }
1961
1962 break :result try func.allocStackPtr(inst);
1963 };
17931964
1794 return self.allocStackPtr(inst);
1965 func.finishAir(inst, result, &.{});
17951966}
17961967
1797fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1798 const un_op = self.air.instructions.items(.data)[inst].un_op;
1799 const operand = try self.resolveInst(un_op);
1800 const ret_ty = self.air.typeOf(un_op).childType();
1968fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1969 const un_op = func.air.instructions.items(.data)[inst].un_op;
1970 const operand = try func.resolveInst(un_op);
1971 const ret_ty = func.air.typeOf(un_op).childType();
18011972 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {
18021973 if (ret_ty.isError()) {
1803 try self.addImm32(0);
1974 try func.addImm32(0);
18041975 } else {
1805 return WValue.none;
1976 return func.finishAir(inst, .none, &.{});
18061977 }
18071978 }
18081979
1809 const fn_info = self.decl.ty.fnInfo();
1810 if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1980 const fn_info = func.decl.ty.fnInfo();
1981 if (!firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
18111982 // leave on the stack
1812 _ = try self.load(operand, ret_ty, 0);
1983 _ = try func.load(operand, ret_ty, 0);
18131984 }
18141985
1815 try self.restoreStackPointer();
1816 try self.addTag(.@"return");
1817 return .none;
1986 try func.restoreStackPointer();
1987 try func.addTag(.@"return");
1988 return func.finishAir(inst, .none, &.{});
18181989}
18191990
1820fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) InnerError!WValue {
1821 if (modifier == .always_tail) return self.fail("TODO implement tail calls for wasm", .{});
1822 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1823 const extra = self.air.extraData(Air.Call, pl_op.payload);
1824 const args = self.air.extra[extra.end..][0..extra.data.args_len];
1825 const ty = self.air.typeOf(pl_op.operand);
1991fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) InnerError!void {
1992 if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{});
1993 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
1994 const extra = func.air.extraData(Air.Call, pl_op.payload);
1995 const args = @ptrCast([]const Air.Inst.Ref, func.air.extra[extra.end..][0..extra.data.args_len]);
1996 const ty = func.air.typeOf(pl_op.operand);
18261997
18271998 const fn_ty = switch (ty.zigTypeTag()) {
18281999 .Fn => ty,
......@@ -1831,21 +2002,21 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
18312002 };
18322003 const ret_ty = fn_ty.fnReturnType();
18332004 const fn_info = fn_ty.fnInfo();
1834 const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, self.target);
2005 const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, func.target);
18352006
18362007 const callee: ?*Decl = blk: {
1837 const func_val = self.air.value(pl_op.operand) orelse break :blk null;
1838 const module = self.bin_file.base.options.module.?;
2008 const func_val = func.air.value(pl_op.operand) orelse break :blk null;
2009 const module = func.bin_file.base.options.module.?;
18392010
1840 if (func_val.castTag(.function)) |func| {
1841 break :blk module.declPtr(func.data.owner_decl);
2011 if (func_val.castTag(.function)) |function| {
2012 break :blk module.declPtr(function.data.owner_decl);
18422013 } else if (func_val.castTag(.extern_fn)) |extern_fn| {
18432014 const ext_decl = module.declPtr(extern_fn.data.owner_decl);
18442015 const ext_info = ext_decl.ty.fnInfo();
1845 var func_type = try genFunctype(self.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, self.target);
1846 defer func_type.deinit(self.gpa);
1847 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
1848 try self.bin_file.addOrUpdateImport(
2016 var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target);
2017 defer func_type.deinit(func.gpa);
2018 ext_decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type);
2019 try func.bin_file.addOrUpdateImport(
18492020 mem.sliceTo(ext_decl.name, 0),
18502021 ext_decl.link.wasm.sym_index,
18512022 ext_decl.getExternFn().?.lib_name,
......@@ -1855,144 +2026,151 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
18552026 } else if (func_val.castTag(.decl_ref)) |decl_ref| {
18562027 break :blk module.declPtr(decl_ref.data);
18572028 }
1858 return self.fail("Expected a function, but instead found type '{}'", .{func_val.tag()});
2029 return func.fail("Expected a function, but instead found type '{}'", .{func_val.tag()});
18592030 };
18602031
18612032 const sret = if (first_param_sret) blk: {
1862 const sret_local = try self.allocStack(ret_ty);
1863 try self.lowerToStack(sret_local);
2033 const sret_local = try func.allocStack(ret_ty);
2034 try func.lowerToStack(sret_local);
18642035 break :blk sret_local;
18652036 } else WValue{ .none = {} };
18662037
18672038 for (args) |arg| {
1868 const arg_ref = @intToEnum(Air.Inst.Ref, arg);
1869 const arg_val = try self.resolveInst(arg_ref);
2039 const arg_val = try func.resolveInst(arg);
18702040
1871 const arg_ty = self.air.typeOf(arg_ref);
2041 const arg_ty = func.air.typeOf(arg);
18722042 if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue;
18732043
1874 try self.lowerArg(fn_ty.fnInfo().cc, arg_ty, arg_val);
2044 try func.lowerArg(fn_ty.fnInfo().cc, arg_ty, arg_val);
18752045 }
18762046
18772047 if (callee) |direct| {
1878 try self.addLabel(.call, direct.link.wasm.sym_index);
2048 try func.addLabel(.call, direct.link.wasm.sym_index);
18792049 } else {
18802050 // in this case we call a function pointer
18812051 // so load its value onto the stack
18822052 std.debug.assert(ty.zigTypeTag() == .Pointer);
1883 const operand = try self.resolveInst(pl_op.operand);
1884 try self.emitWValue(operand);
1885
1886 var fn_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target);
1887 defer fn_type.deinit(self.gpa);
1888
1889 const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);
1890 try self.addLabel(.call_indirect, fn_type_index);
1891 }
2053 const operand = try func.resolveInst(pl_op.operand);
2054 try func.emitWValue(operand);
2055
2056 var fn_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target);
2057 defer fn_type.deinit(func.gpa);
2058
2059 const fn_type_index = try func.bin_file.putOrGetFuncType(fn_type);
2060 try func.addLabel(.call_indirect, fn_type_index);
2061 }
2062
2063 const result_value = result_value: {
2064 if (func.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
2065 break :result_value WValue{ .none = {} };
2066 } else if (ret_ty.isNoReturn()) {
2067 try func.addTag(.@"unreachable");
2068 break :result_value WValue{ .none = {} };
2069 } else if (first_param_sret) {
2070 break :result_value sret;
2071 // TODO: Make this less fragile and optimize
2072 } else if (fn_ty.fnInfo().cc == .C and ret_ty.zigTypeTag() == .Struct or ret_ty.zigTypeTag() == .Union) {
2073 const result_local = try func.allocLocal(ret_ty);
2074 try func.addLabel(.local_set, result_local.local.value);
2075 const scalar_type = abi.scalarType(ret_ty, func.target);
2076 const result = try func.allocStack(scalar_type);
2077 try func.store(result, result_local, scalar_type, 0);
2078 break :result_value result;
2079 } else {
2080 const result_local = try func.allocLocal(ret_ty);
2081 try func.addLabel(.local_set, result_local.local.value);
2082 break :result_value result_local;
2083 }
2084 };
18922085
1893 if (self.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
1894 return WValue.none;
1895 } else if (ret_ty.isNoReturn()) {
1896 try self.addTag(.@"unreachable");
1897 return WValue.none;
1898 } else if (first_param_sret) {
1899 return sret;
1900 // TODO: Make this less fragile and optimize
1901 } else if (fn_ty.fnInfo().cc == .C and ret_ty.zigTypeTag() == .Struct or ret_ty.zigTypeTag() == .Union) {
1902 const result_local = try self.allocLocal(ret_ty);
1903 try self.addLabel(.local_set, result_local.local);
1904 const scalar_type = abi.scalarType(ret_ty, self.target);
1905 const result = try self.allocStack(scalar_type);
1906 try self.store(result, result_local, scalar_type, 0);
1907 return result;
1908 } else {
1909 const result_local = try self.allocLocal(ret_ty);
1910 try self.addLabel(.local_set, result_local.local);
1911 return result_local;
1912 }
2086 var bt = try func.iterateBigTomb(inst, 1 + args.len);
2087 bt.feed(pl_op.operand);
2088 for (args) |arg| bt.feed(arg);
2089 return bt.finishAir(result_value);
19132090}
19142091
1915fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1916 return self.allocStackPtr(inst);
2092fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2093 const value = try func.allocStackPtr(inst);
2094 func.finishAir(inst, value, &.{});
19172095}
19182096
1919fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1920 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2097fn airStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2098 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
19212099
1922 const lhs = try self.resolveInst(bin_op.lhs);
1923 const rhs = try self.resolveInst(bin_op.rhs);
1924 const ty = self.air.typeOf(bin_op.lhs).childType();
2100 const lhs = try func.resolveInst(bin_op.lhs);
2101 const rhs = try func.resolveInst(bin_op.rhs);
2102 const ty = func.air.typeOf(bin_op.lhs).childType();
19252103
1926 try self.store(lhs, rhs, ty, 0);
1927 return WValue{ .none = {} };
2104 try func.store(lhs, rhs, ty, 0);
2105 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
19282106}
19292107
1930fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
2108fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
19312109 assert(!(lhs != .stack and rhs == .stack));
19322110 switch (ty.zigTypeTag()) {
19332111 .ErrorUnion => {
19342112 const pl_ty = ty.errorUnionPayload();
19352113 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
1936 return self.store(lhs, rhs, Type.anyerror, 0);
2114 return func.store(lhs, rhs, Type.anyerror, 0);
19372115 }
19382116
1939 const len = @intCast(u32, ty.abiSize(self.target));
1940 return self.memcpy(lhs, rhs, .{ .imm32 = len });
2117 const len = @intCast(u32, ty.abiSize(func.target));
2118 return func.memcpy(lhs, rhs, .{ .imm32 = len });
19412119 },
19422120 .Optional => {
19432121 if (ty.isPtrLikeOptional()) {
1944 return self.store(lhs, rhs, Type.usize, 0);
2122 return func.store(lhs, rhs, Type.usize, 0);
19452123 }
19462124 var buf: Type.Payload.ElemType = undefined;
19472125 const pl_ty = ty.optionalChild(&buf);
19482126 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
1949 return self.store(lhs, rhs, Type.u8, 0);
2127 return func.store(lhs, rhs, Type.u8, 0);
19502128 }
19512129 if (pl_ty.zigTypeTag() == .ErrorSet) {
1952 return self.store(lhs, rhs, Type.anyerror, 0);
2130 return func.store(lhs, rhs, Type.anyerror, 0);
19532131 }
19542132
1955 const len = @intCast(u32, ty.abiSize(self.target));
1956 return self.memcpy(lhs, rhs, .{ .imm32 = len });
2133 const len = @intCast(u32, ty.abiSize(func.target));
2134 return func.memcpy(lhs, rhs, .{ .imm32 = len });
19572135 },
19582136 .Struct, .Array, .Union, .Vector => {
1959 const len = @intCast(u32, ty.abiSize(self.target));
1960 return self.memcpy(lhs, rhs, .{ .imm32 = len });
2137 const len = @intCast(u32, ty.abiSize(func.target));
2138 return func.memcpy(lhs, rhs, .{ .imm32 = len });
19612139 },
19622140 .Pointer => {
19632141 if (ty.isSlice()) {
19642142 // store pointer first
19652143 // lower it to the stack so we do not have to store rhs into a local first
1966 try self.emitWValue(lhs);
1967 const ptr_local = try self.load(rhs, Type.usize, 0);
1968 try self.store(.{ .stack = {} }, ptr_local, Type.usize, 0 + lhs.offset());
2144 try func.emitWValue(lhs);
2145 const ptr_local = try func.load(rhs, Type.usize, 0);
2146 try func.store(.{ .stack = {} }, ptr_local, Type.usize, 0 + lhs.offset());
19692147
19702148 // retrieve length from rhs, and store that alongside lhs as well
1971 try self.emitWValue(lhs);
1972 const len_local = try self.load(rhs, Type.usize, self.ptrSize());
1973 try self.store(.{ .stack = {} }, len_local, Type.usize, self.ptrSize() + lhs.offset());
2149 try func.emitWValue(lhs);
2150 const len_local = try func.load(rhs, Type.usize, func.ptrSize());
2151 try func.store(.{ .stack = {} }, len_local, Type.usize, func.ptrSize() + lhs.offset());
19742152 return;
19752153 }
19762154 },
1977 .Int => if (ty.intInfo(self.target).bits > 64) {
1978 try self.emitWValue(lhs);
1979 const lsb = try self.load(rhs, Type.u64, 0);
1980 try self.store(.{ .stack = {} }, lsb, Type.u64, 0 + lhs.offset());
1981
1982 try self.emitWValue(lhs);
1983 const msb = try self.load(rhs, Type.u64, 8);
1984 try self.store(.{ .stack = {} }, msb, Type.u64, 8 + lhs.offset());
2155 .Int => if (ty.intInfo(func.target).bits > 64) {
2156 try func.emitWValue(lhs);
2157 const lsb = try func.load(rhs, Type.u64, 0);
2158 try func.store(.{ .stack = {} }, lsb, Type.u64, 0 + lhs.offset());
2159
2160 try func.emitWValue(lhs);
2161 const msb = try func.load(rhs, Type.u64, 8);
2162 try func.store(.{ .stack = {} }, msb, Type.u64, 8 + lhs.offset());
19852163 return;
19862164 },
19872165 else => {},
19882166 }
1989 try self.emitWValue(lhs);
2167 try func.emitWValue(lhs);
19902168 // In this case we're actually interested in storing the stack position
19912169 // into lhs, so we calculate that and emit that instead
1992 try self.lowerToStack(rhs);
2170 try func.lowerToStack(rhs);
19932171
1994 const valtype = typeToValtype(ty, self.target);
1995 const abi_size = @intCast(u8, ty.abiSize(self.target));
2172 const valtype = typeToValtype(ty, func.target);
2173 const abi_size = @intCast(u8, ty.abiSize(func.target));
19962174
19972175 const opcode = buildOpcode(.{
19982176 .valtype1 = valtype,
......@@ -2001,61 +2179,64 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
20012179 });
20022180
20032181 // store rhs value at stack pointer's location in memory
2004 try self.addMemArg(
2182 try func.addMemArg(
20052183 Mir.Inst.Tag.fromOpcode(opcode),
2006 .{ .offset = offset + lhs.offset(), .alignment = ty.abiAlignment(self.target) },
2184 .{ .offset = offset + lhs.offset(), .alignment = ty.abiAlignment(func.target) },
20072185 );
20082186}
20092187
2010fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2011 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2012 const operand = try self.resolveInst(ty_op.operand);
2013 const ty = self.air.getRefType(ty_op.ty);
2188fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2189 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
2190 const operand = try func.resolveInst(ty_op.operand);
2191 const ty = func.air.getRefType(ty_op.ty);
20142192
2015 if (!ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
2193 if (!ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{ty_op.operand});
20162194
2017 if (isByRef(ty, self.target)) {
2018 const new_local = try self.allocStack(ty);
2019 try self.store(new_local, operand, ty, 0);
2020 return new_local;
2021 }
2195 const result = result: {
2196 if (isByRef(ty, func.target)) {
2197 const new_local = try func.allocStack(ty);
2198 try func.store(new_local, operand, ty, 0);
2199 break :result new_local;
2200 }
20222201
2023 const stack_loaded = try self.load(operand, ty, 0);
2024 return stack_loaded.toLocal(self, ty);
2202 const stack_loaded = try func.load(operand, ty, 0);
2203 break :result try stack_loaded.toLocal(func, ty);
2204 };
2205 func.finishAir(inst, result, &.{ty_op.operand});
20252206}
20262207
20272208/// Loads an operand from the linear memory section.
20282209/// NOTE: Leaves the value on the stack.
2029fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
2210fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
20302211 // load local's value from memory by its stack position
2031 try self.emitWValue(operand);
2212 try func.emitWValue(operand);
20322213
2033 const abi_size = @intCast(u8, ty.abiSize(self.target));
2214 const abi_size = @intCast(u8, ty.abiSize(func.target));
20342215 const opcode = buildOpcode(.{
2035 .valtype1 = typeToValtype(ty, self.target),
2216 .valtype1 = typeToValtype(ty, func.target),
20362217 .width = abi_size * 8,
20372218 .op = .load,
20382219 .signedness = .unsigned,
20392220 });
20402221
2041 try self.addMemArg(
2222 try func.addMemArg(
20422223 Mir.Inst.Tag.fromOpcode(opcode),
2043 .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(self.target) },
2224 .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(func.target) },
20442225 );
20452226
20462227 return WValue{ .stack = {} };
20472228}
20482229
2049fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2050 const arg_index = self.arg_index;
2051 const arg = self.args[arg_index];
2052 const cc = self.decl.ty.fnInfo().cc;
2053 const arg_ty = self.air.typeOfIndex(inst);
2230fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2231 const arg_index = func.arg_index;
2232 const arg = func.args[arg_index];
2233 const cc = func.decl.ty.fnInfo().cc;
2234 const arg_ty = func.air.typeOfIndex(inst);
20542235 if (cc == .C) {
2055 const arg_classes = abi.classifyType(arg_ty, self.target);
2236 const arg_classes = abi.classifyType(arg_ty, func.target);
20562237 for (arg_classes) |class| {
20572238 if (class != .none) {
2058 self.arg_index += 1;
2239 func.arg_index += 1;
20592240 }
20602241 }
20612242
......@@ -2063,25 +2244,25 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
20632244 // we combine them into a single stack value
20642245 if (arg_classes[0] == .direct and arg_classes[1] == .direct) {
20652246 if (arg_ty.zigTypeTag() != .Int) {
2066 return self.fail(
2247 return func.fail(
20672248 "TODO: Implement C-ABI argument for type '{}'",
2068 .{arg_ty.fmt(self.bin_file.base.options.module.?)},
2249 .{arg_ty.fmt(func.bin_file.base.options.module.?)},
20692250 );
20702251 }
2071 const result = try self.allocStack(arg_ty);
2072 try self.store(result, arg, Type.u64, 0);
2073 try self.store(result, self.args[arg_index + 1], Type.u64, 8);
2074 return result;
2252 const result = try func.allocStack(arg_ty);
2253 try func.store(result, arg, Type.u64, 0);
2254 try func.store(result, func.args[arg_index + 1], Type.u64, 8);
2255 return func.finishAir(inst, arg, &.{});
20752256 }
20762257 } else {
2077 self.arg_index += 1;
2258 func.arg_index += 1;
20782259 }
20792260
2080 switch (self.debug_output) {
2261 switch (func.debug_output) {
20812262 .dwarf => |dwarf| {
20822263 // TODO: Get the original arg index rather than wasm arg index
2083 const name = self.mod_fn.getParamName(self.bin_file.base.options.module.?, arg_index);
2084 const leb_size = link.File.Wasm.getULEB128Size(arg.local);
2264 const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, arg_index);
2265 const leb_size = link.File.Wasm.getULEB128Size(arg.local.value);
20852266 const dbg_info = &dwarf.dbg_info;
20862267 try dbg_info.ensureUnusedCapacity(3 + leb_size + 5 + name.len + 1);
20872268 // wasm locations are encoded as follow:
......@@ -2095,194 +2276,197 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
20952276 std.dwarf.OP.WASM_location,
20962277 std.dwarf.OP.WASM_local,
20972278 });
2098 leb.writeULEB128(dbg_info.writer(), arg.local) catch unreachable;
2099 try self.addDbgInfoTypeReloc(arg_ty);
2279 leb.writeULEB128(dbg_info.writer(), arg.local.value) catch unreachable;
2280 try func.addDbgInfoTypeReloc(arg_ty);
21002281 dbg_info.appendSliceAssumeCapacity(name);
21012282 dbg_info.appendAssumeCapacity(0);
21022283 },
21032284 else => {},
21042285 }
2105 return arg;
2106}
21072286
2108fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
2109 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2287 func.finishAir(inst, arg, &.{});
2288}
21102289
2111 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2112 const lhs = try self.resolveInst(bin_op.lhs);
2113 const rhs = try self.resolveInst(bin_op.rhs);
2114 const ty = self.air.typeOf(bin_op.lhs);
2290fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2291 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
2292 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2293 const lhs = try func.resolveInst(bin_op.lhs);
2294 const rhs = try func.resolveInst(bin_op.rhs);
2295 const ty = func.air.typeOf(bin_op.lhs);
21152296
2116 const stack_value = try self.binOp(lhs, rhs, ty, op);
2117 return stack_value.toLocal(self, ty);
2297 const stack_value = try func.binOp(lhs, rhs, ty, op);
2298 func.finishAir(inst, try stack_value.toLocal(func, ty), &.{ bin_op.lhs, bin_op.rhs });
21182299}
21192300
21202301/// Performs a binary operation on the given `WValue`'s
21212302/// NOTE: THis leaves the value on top of the stack.
2122fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2303fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
21232304 assert(!(lhs != .stack and rhs == .stack));
2124 if (isByRef(ty, self.target)) {
2305 if (isByRef(ty, func.target)) {
21252306 if (ty.zigTypeTag() == .Int) {
2126 return self.binOpBigInt(lhs, rhs, ty, op);
2307 return func.binOpBigInt(lhs, rhs, ty, op);
21272308 } else {
2128 return self.fail(
2309 return func.fail(
21292310 "TODO: Implement binary operation for type: {}",
2130 .{ty.fmt(self.bin_file.base.options.module.?)},
2311 .{ty.fmt(func.bin_file.base.options.module.?)},
21312312 );
21322313 }
21332314 }
21342315
2135 if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) {
2136 return self.binOpFloat16(lhs, rhs, op);
2316 if (ty.isAnyFloat() and ty.floatBits(func.target) == 16) {
2317 return func.binOpFloat16(lhs, rhs, op);
21372318 }
21382319
21392320 const opcode: wasm.Opcode = buildOpcode(.{
21402321 .op = op,
2141 .valtype1 = typeToValtype(ty, self.target),
2322 .valtype1 = typeToValtype(ty, func.target),
21422323 .signedness = if (ty.isSignedInt()) .signed else .unsigned,
21432324 });
2144 try self.emitWValue(lhs);
2145 try self.emitWValue(rhs);
2325 try func.emitWValue(lhs);
2326 try func.emitWValue(rhs);
21462327
2147 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2328 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
21482329
21492330 return WValue{ .stack = {} };
21502331}
21512332
21522333/// Performs a binary operation for 16-bit floats.
21532334/// NOTE: Leaves the result value on the stack
2154fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue {
2335fn binOpFloat16(func: *CodeGen, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue {
21552336 const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned });
2156 _ = try self.fpext(lhs, Type.f16, Type.f32);
2157 _ = try self.fpext(rhs, Type.f16, Type.f32);
2158 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2337 _ = try func.fpext(lhs, Type.f16, Type.f32);
2338 _ = try func.fpext(rhs, Type.f16, Type.f32);
2339 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
21592340
2160 return self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
2341 return func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
21612342}
21622343
2163fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2164 if (ty.intInfo(self.target).bits > 128) {
2165 return self.fail("TODO: Implement binary operation for big integer", .{});
2344fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2345 if (ty.intInfo(func.target).bits > 128) {
2346 return func.fail("TODO: Implement binary operation for big integer", .{});
21662347 }
21672348
21682349 if (op != .add and op != .sub) {
2169 return self.fail("TODO: Implement binary operation for big integers", .{});
2350 return func.fail("TODO: Implement binary operation for big integers", .{});
21702351 }
21712352
2172 const result = try self.allocStack(ty);
2173 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
2174 defer lhs_high_bit.free(self);
2175 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
2176 defer rhs_high_bit.free(self);
2177 var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
2178 defer high_op_res.free(self);
2353 const result = try func.allocStack(ty);
2354 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
2355 defer lhs_high_bit.free(func);
2356 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
2357 defer rhs_high_bit.free(func);
2358 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);
2359 defer high_op_res.free(func);
21792360
2180 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
2181 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
2182 const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
2361 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2362 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2363 const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
21832364
21842365 const lt = if (op == .add) blk: {
2185 break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);
2366 break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);
21862367 } else if (op == .sub) blk: {
2187 break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
2368 break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
21882369 } else unreachable;
2189 const tmp = try self.intcast(lt, Type.u32, Type.u64);
2190 var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
2191 defer tmp_op.free(self);
2370 const tmp = try func.intcast(lt, Type.u32, Type.u64);
2371 var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);
2372 defer tmp_op.free(func);
21922373
2193 try self.store(result, high_op_res, Type.u64, 0);
2194 try self.store(result, tmp_op, Type.u64, 8);
2374 try func.store(result, high_op_res, Type.u64, 0);
2375 try func.store(result, tmp_op, Type.u64, 8);
21952376 return result;
21962377}
21972378
2198fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
2199 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2200 const lhs = try self.resolveInst(bin_op.lhs);
2201 const rhs = try self.resolveInst(bin_op.rhs);
2379fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2380 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
2381 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2382
2383 const lhs = try func.resolveInst(bin_op.lhs);
2384 const rhs = try func.resolveInst(bin_op.rhs);
2385 const ty = func.air.typeOf(bin_op.lhs);
22022386
2203 const ty = self.air.typeOf(bin_op.lhs);
22042387 if (ty.zigTypeTag() == .Vector) {
2205 return self.fail("TODO: Implement wrapping arithmetic for vectors", .{});
2388 return func.fail("TODO: Implement wrapping arithmetic for vectors", .{});
22062389 }
22072390
2208 return (try self.wrapBinOp(lhs, rhs, ty, op)).toLocal(self, ty);
2391 const result = try (try func.wrapBinOp(lhs, rhs, ty, op)).toLocal(func, ty);
2392 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
22092393}
22102394
22112395/// Performs a wrapping binary operation.
22122396/// Asserts rhs is not a stack value when lhs also isn't.
22132397/// NOTE: Leaves the result on the stack when its Type is <= 64 bits
2214fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2215 const bin_local = try self.binOp(lhs, rhs, ty, op);
2216 return self.wrapOperand(bin_local, ty);
2398fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2399 const bin_local = try func.binOp(lhs, rhs, ty, op);
2400 return func.wrapOperand(bin_local, ty);
22172401}
22182402
22192403/// Wraps an operand based on a given type's bitsize.
22202404/// Asserts `Type` is <= 128 bits.
22212405/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.
2222fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
2223 assert(ty.abiSize(self.target) <= 16);
2224 const bitsize = ty.intInfo(self.target).bits;
2406fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
2407 assert(ty.abiSize(func.target) <= 16);
2408 const bitsize = ty.intInfo(func.target).bits;
22252409 const wasm_bits = toWasmBits(bitsize) orelse {
2226 return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
2410 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
22272411 };
22282412
22292413 if (wasm_bits == bitsize) return operand;
22302414
22312415 if (wasm_bits == 128) {
22322416 assert(operand != .stack);
2233 const lsb = try self.load(operand, Type.u64, 8);
2417 const lsb = try func.load(operand, Type.u64, 8);
22342418
2235 const result_ptr = try self.allocStack(ty);
2236 try self.emitWValue(result_ptr);
2237 try self.store(.{ .stack = {} }, lsb, Type.u64, 8 + result_ptr.offset());
2419 const result_ptr = try func.allocStack(ty);
2420 try func.emitWValue(result_ptr);
2421 try func.store(.{ .stack = {} }, lsb, Type.u64, 8 + result_ptr.offset());
22382422 const result = (@as(u64, 1) << @intCast(u6, 64 - (wasm_bits - bitsize))) - 1;
2239 try self.emitWValue(result_ptr);
2240 _ = try self.load(operand, Type.u64, 0);
2241 try self.addImm64(result);
2242 try self.addTag(.i64_and);
2243 try self.addMemArg(.i64_store, .{ .offset = result_ptr.offset(), .alignment = 8 });
2423 try func.emitWValue(result_ptr);
2424 _ = try func.load(operand, Type.u64, 0);
2425 try func.addImm64(result);
2426 try func.addTag(.i64_and);
2427 try func.addMemArg(.i64_store, .{ .offset = result_ptr.offset(), .alignment = 8 });
22442428 return result_ptr;
22452429 }
22462430
22472431 const result = (@as(u64, 1) << @intCast(u6, bitsize)) - 1;
2248 try self.emitWValue(operand);
2432 try func.emitWValue(operand);
22492433 if (bitsize <= 32) {
2250 try self.addImm32(@bitCast(i32, @intCast(u32, result)));
2251 try self.addTag(.i32_and);
2434 try func.addImm32(@bitCast(i32, @intCast(u32, result)));
2435 try func.addTag(.i32_and);
22522436 } else if (bitsize <= 64) {
2253 try self.addImm64(result);
2254 try self.addTag(.i64_and);
2437 try func.addImm64(result);
2438 try func.addTag(.i64_and);
22552439 } else unreachable;
22562440
22572441 return WValue{ .stack = {} };
22582442}
22592443
2260fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue {
2444fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue {
22612445 switch (ptr_val.tag()) {
22622446 .decl_ref_mut => {
22632447 const decl_index = ptr_val.castTag(.decl_ref_mut).?.data.decl_index;
2264 return self.lowerParentPtrDecl(ptr_val, decl_index);
2448 return func.lowerParentPtrDecl(ptr_val, decl_index);
22652449 },
22662450 .decl_ref => {
22672451 const decl_index = ptr_val.castTag(.decl_ref).?.data;
2268 return self.lowerParentPtrDecl(ptr_val, decl_index);
2452 return func.lowerParentPtrDecl(ptr_val, decl_index);
22692453 },
22702454 .variable => {
22712455 const decl_index = ptr_val.castTag(.variable).?.data.owner_decl;
2272 return self.lowerParentPtrDecl(ptr_val, decl_index);
2456 return func.lowerParentPtrDecl(ptr_val, decl_index);
22732457 },
22742458 .field_ptr => {
22752459 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
22762460 const parent_ty = field_ptr.container_ty;
2277 const parent_ptr = try self.lowerParentPtr(field_ptr.container_ptr, parent_ty);
2461 const parent_ptr = try func.lowerParentPtr(field_ptr.container_ptr, parent_ty);
22782462
22792463 const offset = switch (parent_ty.zigTypeTag()) {
22802464 .Struct => blk: {
2281 const offset = parent_ty.structFieldOffset(field_ptr.field_index, self.target);
2465 const offset = parent_ty.structFieldOffset(field_ptr.field_index, func.target);
22822466 break :blk offset;
22832467 },
22842468 .Union => blk: {
2285 const layout: Module.Union.Layout = parent_ty.unionGetLayout(self.target);
2469 const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target);
22862470 if (layout.payload_size == 0) break :blk 0;
22872471 if (layout.payload_align > layout.tag_align) break :blk 0;
22882472
......@@ -2293,7 +2477,7 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
22932477 .Pointer => switch (parent_ty.ptrSize()) {
22942478 .Slice => switch (field_ptr.field_index) {
22952479 0 => 0,
2296 1 => self.ptrSize(),
2480 1 => func.ptrSize(),
22972481 else => unreachable,
22982482 },
22992483 else => unreachable,
......@@ -2320,8 +2504,8 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
23202504 .elem_ptr => {
23212505 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
23222506 const index = elem_ptr.index;
2323 const offset = index * ptr_child_ty.abiSize(self.target);
2324 const array_ptr = try self.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
2507 const offset = index * ptr_child_ty.abiSize(func.target);
2508 const array_ptr = try func.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
23252509
23262510 return WValue{ .memory_offset = .{
23272511 .pointer = array_ptr.memory,
......@@ -2330,27 +2514,27 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
23302514 },
23312515 .opt_payload_ptr => {
23322516 const payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
2333 const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty);
2517 const parent_ptr = try func.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty);
23342518 var buf: Type.Payload.ElemType = undefined;
23352519 const payload_ty = payload_ptr.container_ty.optionalChild(&buf);
23362520 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.optionalReprIsPayload()) {
23372521 return parent_ptr;
23382522 }
23392523
2340 const abi_size = payload_ptr.container_ty.abiSize(self.target);
2341 const offset = abi_size - payload_ty.abiSize(self.target);
2524 const abi_size = payload_ptr.container_ty.abiSize(func.target);
2525 const offset = abi_size - payload_ty.abiSize(func.target);
23422526
23432527 return WValue{ .memory_offset = .{
23442528 .pointer = parent_ptr.memory,
23452529 .offset = @intCast(u32, offset),
23462530 } };
23472531 },
2348 else => |tag| return self.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
2532 else => |tag| return func.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
23492533 }
23502534}
23512535
2352fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl_index: Module.Decl.Index) InnerError!WValue {
2353 const module = self.bin_file.base.options.module.?;
2536fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.Index) InnerError!WValue {
2537 const module = func.bin_file.base.options.module.?;
23542538 const decl = module.declPtr(decl_index);
23552539 module.markDeclAlive(decl);
23562540 var ptr_ty_payload: Type.Payload.ElemType = .{
......@@ -2358,15 +2542,15 @@ fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl_index: Module.Decl.Index
23582542 .data = decl.ty,
23592543 };
23602544 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2361 return self.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index);
2545 return func.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index);
23622546}
23632547
2364fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!WValue {
2548fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!WValue {
23652549 if (tv.ty.isSlice()) {
2366 return WValue{ .memory = try self.bin_file.lowerUnnamedConst(tv, decl_index) };
2550 return WValue{ .memory = try func.bin_file.lowerUnnamedConst(tv, decl_index) };
23672551 }
23682552
2369 const module = self.bin_file.base.options.module.?;
2553 const module = func.bin_file.base.options.module.?;
23702554 const decl = module.declPtr(decl_index);
23712555 if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
23722556 return WValue{ .imm32 = 0xaaaaaaaa };
......@@ -2376,7 +2560,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index)
23762560
23772561 const target_sym_index = decl.link.wasm.sym_index;
23782562 if (decl.ty.zigTypeTag() == .Fn) {
2379 try self.bin_file.addTableFunction(target_sym_index);
2563 try func.bin_file.addTableFunction(target_sym_index);
23802564 return WValue{ .function_index = target_sym_index };
23812565 } else return WValue{ .memory = target_sym_index };
23822566}
......@@ -2397,21 +2581,21 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(
23972581 return @intCast(WantedT, result);
23982582}
23992583
2400fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
2401 if (val.isUndefDeep()) return self.emitUndefined(ty);
2584fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
2585 if (val.isUndefDeep()) return func.emitUndefined(ty);
24022586 if (val.castTag(.decl_ref)) |decl_ref| {
24032587 const decl_index = decl_ref.data;
2404 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
2588 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
24052589 }
24062590 if (val.castTag(.decl_ref_mut)) |decl_ref_mut| {
24072591 const decl_index = decl_ref_mut.data.decl_index;
2408 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
2592 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
24092593 }
2410 const target = self.target;
2594 const target = func.target;
24112595 switch (ty.zigTypeTag()) {
24122596 .Void => return WValue{ .none = {} },
24132597 .Int => {
2414 const int_info = ty.intInfo(self.target);
2598 const int_info = ty.intInfo(func.target);
24152599 switch (int_info.signedness) {
24162600 .signed => switch (int_info.bits) {
24172601 0...32 => return WValue{ .imm32 = @intCast(u32, toTwosComplement(
......@@ -2432,7 +2616,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
24322616 }
24332617 },
24342618 .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
2435 .Float => switch (ty.floatBits(self.target)) {
2619 .Float => switch (ty.floatBits(func.target)) {
24362620 16 => return WValue{ .imm32 = @bitCast(u16, val.toFloat(f16)) },
24372621 32 => return WValue{ .float32 = val.toFloat(f32) },
24382622 64 => return WValue{ .float64 = val.toFloat(f64) },
......@@ -2440,11 +2624,11 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
24402624 },
24412625 .Pointer => switch (val.tag()) {
24422626 .field_ptr, .elem_ptr, .opt_payload_ptr => {
2443 return self.lowerParentPtr(val, ty.childType());
2627 return func.lowerParentPtr(val, ty.childType());
24442628 },
24452629 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
24462630 .zero, .null_value => return WValue{ .imm32 = 0 },
2447 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}),
2631 else => return func.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}),
24482632 },
24492633 .Enum => {
24502634 if (val.castTag(.enum_field_index)) |field_index| {
......@@ -2454,7 +2638,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
24542638 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
24552639 if (enum_full.values.count() != 0) {
24562640 const tag_val = enum_full.values.keys()[field_index.data];
2457 return self.lowerConstant(tag_val, enum_full.tag_ty);
2641 return func.lowerConstant(tag_val, enum_full.tag_ty);
24582642 } else {
24592643 return WValue{ .imm32 = field_index.data };
24602644 }
......@@ -2463,19 +2647,19 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
24632647 const index = field_index.data;
24642648 const enum_data = ty.castTag(.enum_numbered).?.data;
24652649 const enum_val = enum_data.values.keys()[index];
2466 return self.lowerConstant(enum_val, enum_data.tag_ty);
2650 return func.lowerConstant(enum_val, enum_data.tag_ty);
24672651 },
2468 else => return self.fail("TODO: lowerConstant for enum tag: {}", .{ty.tag()}),
2652 else => return func.fail("TODO: lowerConstant for enum tag: {}", .{ty.tag()}),
24692653 }
24702654 } else {
24712655 var int_tag_buffer: Type.Payload.Bits = undefined;
24722656 const int_tag_ty = ty.intTagType(&int_tag_buffer);
2473 return self.lowerConstant(val, int_tag_ty);
2657 return func.lowerConstant(val, int_tag_ty);
24742658 }
24752659 },
24762660 .ErrorSet => switch (val.tag()) {
24772661 .@"error" => {
2478 const kv = try self.bin_file.base.options.module.?.getErrorValue(val.getError().?);
2662 const kv = try func.bin_file.base.options.module.?.getErrorValue(val.getError().?);
24792663 return WValue{ .imm32 = kv.value };
24802664 },
24812665 else => return WValue{ .imm32 = 0 },
......@@ -2484,41 +2668,41 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
24842668 const error_type = ty.errorUnionSet();
24852669 const is_pl = val.errorUnionIsPayload();
24862670 const err_val = if (!is_pl) val else Value.initTag(.zero);
2487 return self.lowerConstant(err_val, error_type);
2671 return func.lowerConstant(err_val, error_type);
24882672 },
24892673 .Optional => if (ty.optionalReprIsPayload()) {
24902674 var buf: Type.Payload.ElemType = undefined;
24912675 const pl_ty = ty.optionalChild(&buf);
24922676 if (val.castTag(.opt_payload)) |payload| {
2493 return self.lowerConstant(payload.data, pl_ty);
2677 return func.lowerConstant(payload.data, pl_ty);
24942678 } else if (val.isNull()) {
24952679 return WValue{ .imm32 = 0 };
24962680 } else {
2497 return self.lowerConstant(val, pl_ty);
2681 return func.lowerConstant(val, pl_ty);
24982682 }
24992683 } else {
25002684 const is_pl = val.tag() == .opt_payload;
25012685 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
25022686 },
2503 else => |zig_type| return self.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
2687 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
25042688 }
25052689}
25062690
2507fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
2691fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
25082692 switch (ty.zigTypeTag()) {
25092693 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },
2510 .Int => switch (ty.intInfo(self.target).bits) {
2694 .Int => switch (ty.intInfo(func.target).bits) {
25112695 0...32 => return WValue{ .imm32 = 0xaaaaaaaa },
25122696 33...64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
25132697 else => unreachable,
25142698 },
2515 .Float => switch (ty.floatBits(self.target)) {
2699 .Float => switch (ty.floatBits(func.target)) {
25162700 16 => return WValue{ .imm32 = 0xaaaaaaaa },
25172701 32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) },
25182702 64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) },
25192703 else => unreachable,
25202704 },
2521 .Pointer => switch (self.arch()) {
2705 .Pointer => switch (func.arch()) {
25222706 .wasm32 => return WValue{ .imm32 = 0xaaaaaaaa },
25232707 .wasm64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
25242708 else => unreachable,
......@@ -2527,22 +2711,22 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
25272711 var buf: Type.Payload.ElemType = undefined;
25282712 const pl_ty = ty.optionalChild(&buf);
25292713 if (ty.optionalReprIsPayload()) {
2530 return self.emitUndefined(pl_ty);
2714 return func.emitUndefined(pl_ty);
25312715 }
25322716 return WValue{ .imm32 = 0xaaaaaaaa };
25332717 },
25342718 .ErrorUnion => {
25352719 return WValue{ .imm32 = 0xaaaaaaaa };
25362720 },
2537 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),
2721 else => return func.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),
25382722 }
25392723}
25402724
25412725/// Returns a `Value` as a signed 32 bit value.
25422726/// It's illegal to provide a value with a type that cannot be represented
25432727/// as an integer value.
2544fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
2545 const target = self.target;
2728fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 {
2729 const target = func.target;
25462730 switch (ty.zigTypeTag()) {
25472731 .Enum => {
25482732 if (val.castTag(.enum_field_index)) |field_index| {
......@@ -2552,28 +2736,28 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
25522736 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
25532737 if (enum_full.values.count() != 0) {
25542738 const tag_val = enum_full.values.keys()[field_index.data];
2555 return self.valueAsI32(tag_val, enum_full.tag_ty);
2739 return func.valueAsI32(tag_val, enum_full.tag_ty);
25562740 } else return @bitCast(i32, field_index.data);
25572741 },
25582742 .enum_numbered => {
25592743 const index = field_index.data;
25602744 const enum_data = ty.castTag(.enum_numbered).?.data;
2561 return self.valueAsI32(enum_data.values.keys()[index], enum_data.tag_ty);
2745 return func.valueAsI32(enum_data.values.keys()[index], enum_data.tag_ty);
25622746 },
25632747 else => unreachable,
25642748 }
25652749 } else {
25662750 var int_tag_buffer: Type.Payload.Bits = undefined;
25672751 const int_tag_ty = ty.intTagType(&int_tag_buffer);
2568 return self.valueAsI32(val, int_tag_ty);
2752 return func.valueAsI32(val, int_tag_ty);
25692753 }
25702754 },
2571 .Int => switch (ty.intInfo(self.target).signedness) {
2755 .Int => switch (ty.intInfo(func.target).signedness) {
25722756 .signed => return @truncate(i32, val.toSignedInt()),
25732757 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt(target))),
25742758 },
25752759 .ErrorSet => {
2576 const kv = self.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
2760 const kv = func.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
25772761 return @bitCast(i32, kv.value);
25782762 },
25792763 .Bool => return @intCast(i32, val.toSignedInt()),
......@@ -2582,103 +2766,139 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
25822766 }
25832767}
25842768
2585fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2586 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2587 const block_ty = self.air.getRefType(ty_pl.ty);
2588 const wasm_block_ty = genBlockType(block_ty, self.target);
2589 const extra = self.air.extraData(Air.Block, ty_pl.payload);
2590 const body = self.air.extra[extra.end..][0..extra.data.body_len];
2769fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2770 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
2771 const block_ty = func.air.getRefType(ty_pl.ty);
2772 const wasm_block_ty = genBlockType(block_ty, func.target);
2773 const extra = func.air.extraData(Air.Block, ty_pl.payload);
2774 const body = func.air.extra[extra.end..][0..extra.data.body_len];
25912775
25922776 // if wasm_block_ty is non-empty, we create a register to store the temporary value
25932777 const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {
2594 const ty: Type = if (isByRef(block_ty, self.target)) Type.u32 else block_ty;
2595 break :blk try self.allocLocal(ty);
2778 const ty: Type = if (isByRef(block_ty, func.target)) Type.u32 else block_ty;
2779 break :blk try func.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten
25962780 } else WValue.none;
25972781
2598 try self.startBlock(.block, wasm.block_empty);
2782 try func.startBlock(.block, wasm.block_empty);
25992783 // Here we set the current block idx, so breaks know the depth to jump
26002784 // to when breaking out.
2601 try self.blocks.putNoClobber(self.gpa, inst, .{
2602 .label = self.block_depth,
2785 try func.blocks.putNoClobber(func.gpa, inst, .{
2786 .label = func.block_depth,
26032787 .value = block_result,
26042788 });
2605 try self.genBody(body);
2606 try self.endBlock();
2789 try func.genBody(body);
2790 try func.endBlock();
26072791
2608 return block_result;
2792 func.finishAir(inst, block_result, &.{});
26092793}
26102794
26112795/// appends a new wasm block to the code section and increases the `block_depth` by 1
2612fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8) !void {
2613 self.block_depth += 1;
2614 try self.addInst(.{
2796fn startBlock(func: *CodeGen, block_tag: wasm.Opcode, valtype: u8) !void {
2797 func.block_depth += 1;
2798 try func.addInst(.{
26152799 .tag = Mir.Inst.Tag.fromOpcode(block_tag),
26162800 .data = .{ .block_type = valtype },
26172801 });
26182802}
26192803
26202804/// Ends the current wasm block and decreases the `block_depth` by 1
2621fn endBlock(self: *Self) !void {
2622 try self.addTag(.end);
2623 self.block_depth -= 1;
2805fn endBlock(func: *CodeGen) !void {
2806 try func.addTag(.end);
2807 func.block_depth -= 1;
26242808}
26252809
2626fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2627 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2628 const loop = self.air.extraData(Air.Block, ty_pl.payload);
2629 const body = self.air.extra[loop.end..][0..loop.data.body_len];
2810fn airLoop(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2811 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
2812 const loop = func.air.extraData(Air.Block, ty_pl.payload);
2813 const body = func.air.extra[loop.end..][0..loop.data.body_len];
26302814
26312815 // result type of loop is always 'noreturn', meaning we can always
26322816 // emit the wasm type 'block_empty'.
2633 try self.startBlock(.loop, wasm.block_empty);
2634 try self.genBody(body);
2817 try func.startBlock(.loop, wasm.block_empty);
2818 try func.genBody(body);
26352819
26362820 // breaking to the index of a loop block will continue the loop instead
2637 try self.addLabel(.br, 0);
2638 try self.endBlock();
2821 try func.addLabel(.br, 0);
2822 try func.endBlock();
26392823
2640 return .none;
2824 func.finishAir(inst, .none, &.{});
26412825}
26422826
2643fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2644 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2645 const condition = try self.resolveInst(pl_op.operand);
2646 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
2647 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2648 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
2649 // TODO: Handle death instructions for then and else body
2827fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2828 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
2829 const condition = try func.resolveInst(pl_op.operand);
2830 const extra = func.air.extraData(Air.CondBr, pl_op.payload);
2831 const then_body = func.air.extra[extra.end..][0..extra.data.then_body_len];
2832 const else_body = func.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
2833 const liveness_condbr = func.liveness.getCondBr(inst);
26502834
26512835 // result type is always noreturn, so use `block_empty` as type.
2652 try self.startBlock(.block, wasm.block_empty);
2836 try func.startBlock(.block, wasm.block_empty);
26532837 // emit the conditional value
2654 try self.emitWValue(condition);
2838 try func.emitWValue(condition);
26552839
26562840 // we inserted the block in front of the condition
26572841 // so now check if condition matches. If not, break outside this block
26582842 // and continue with the then codepath
2659 try self.addLabel(.br_if, 0);
2843 try func.addLabel(.br_if, 0);
26602844
2661 try self.genBody(else_body);
2662 try self.endBlock();
2845 try func.branches.ensureUnusedCapacity(func.gpa, 2);
2846
2847 func.branches.appendAssumeCapacity(.{});
2848 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
2849 for (liveness_condbr.else_deaths) |death| {
2850 func.processDeath(Air.indexToRef(death));
2851 }
2852 try func.genBody(else_body);
2853 try func.endBlock();
2854 var else_stack = func.branches.pop();
2855 defer else_stack.deinit(func.gpa);
26632856
26642857 // Outer block that matches the condition
2665 try self.genBody(then_body);
2858 func.branches.appendAssumeCapacity(.{});
2859 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
2860 for (liveness_condbr.then_deaths) |death| {
2861 func.processDeath(Air.indexToRef(death));
2862 }
2863 try func.genBody(then_body);
2864 var then_stack = func.branches.pop();
2865 defer then_stack.deinit(func.gpa);
2866
2867 try func.mergeBranch(&else_stack);
2868 try func.mergeBranch(&then_stack);
2869
2870 func.finishAir(inst, .none, &.{});
2871}
2872
2873fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
2874 const parent = func.currentBranch();
26662875
2667 return .none;
2876 const target_slice = branch.values.entries.slice();
2877 const target_keys = target_slice.items(.key);
2878 const target_values = target_slice.items(.value);
2879
2880 try parent.values.ensureUnusedCapacity(func.gpa, branch.values.count());
2881 for (target_keys) |key, index| {
2882 // TODO: process deaths from branches
2883 parent.values.putAssumeCapacity(key, target_values[index]);
2884 }
26682885}
26692886
2670fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue {
2671 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2672 const lhs = try self.resolveInst(bin_op.lhs);
2673 const rhs = try self.resolveInst(bin_op.rhs);
2674 const operand_ty = self.air.typeOf(bin_op.lhs);
2675 return (try self.cmp(lhs, rhs, operand_ty, op)).toLocal(self, Type.u32); // comparison result is always 32 bits
2887fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
2888 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
2889 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2890
2891 const lhs = try func.resolveInst(bin_op.lhs);
2892 const rhs = try func.resolveInst(bin_op.rhs);
2893 const operand_ty = func.air.typeOf(bin_op.lhs);
2894 const result = try (try func.cmp(lhs, rhs, operand_ty, op)).toLocal(func, Type.u32); // comparison result is always 32 bits
2895 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
26762896}
26772897
26782898/// Compares two operands.
26792899/// Asserts rhs is not a stack value when the lhs isn't a stack value either
26802900/// NOTE: This leaves the result on top of the stack, rather than a new local.
2681fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue {
2901fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue {
26822902 assert(!(lhs != .stack and rhs == .stack));
26832903 if (ty.zigTypeTag() == .Optional and !ty.optionalReprIsPayload()) {
26842904 var buf: Type.Payload.ElemType = undefined;
......@@ -2687,28 +2907,28 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper
26872907 // When we hit this case, we must check the value of optionals
26882908 // that are not pointers. This means first checking against non-null for
26892909 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
2690 return self.cmpOptionals(lhs, rhs, ty, op);
2910 return func.cmpOptionals(lhs, rhs, ty, op);
26912911 }
2692 } else if (isByRef(ty, self.target)) {
2693 return self.cmpBigInt(lhs, rhs, ty, op);
2694 } else if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) {
2695 return self.cmpFloat16(lhs, rhs, op);
2912 } else if (isByRef(ty, func.target)) {
2913 return func.cmpBigInt(lhs, rhs, ty, op);
2914 } else if (ty.isAnyFloat() and ty.floatBits(func.target) == 16) {
2915 return func.cmpFloat16(lhs, rhs, op);
26962916 }
26972917
26982918 // ensure that when we compare pointers, we emit
26992919 // the true pointer of a stack value, rather than the stack pointer.
2700 try self.lowerToStack(lhs);
2701 try self.lowerToStack(rhs);
2920 try func.lowerToStack(lhs);
2921 try func.lowerToStack(rhs);
27022922
27032923 const signedness: std.builtin.Signedness = blk: {
27042924 // by default we tell the operand type is unsigned (i.e. bools and enum values)
27052925 if (ty.zigTypeTag() != .Int) break :blk .unsigned;
27062926
27072927 // incase of an actual integer, we emit the correct signedness
2708 break :blk ty.intInfo(self.target).signedness;
2928 break :blk ty.intInfo(func.target).signedness;
27092929 };
27102930 const opcode: wasm.Opcode = buildOpcode(.{
2711 .valtype1 = typeToValtype(ty, self.target),
2931 .valtype1 = typeToValtype(ty, func.target),
27122932 .op = switch (op) {
27132933 .lt => .lt,
27142934 .lte => .le,
......@@ -2719,14 +2939,14 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper
27192939 },
27202940 .signedness = signedness,
27212941 });
2722 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2942 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
27232943
27242944 return WValue{ .stack = {} };
27252945}
27262946
27272947/// Compares 16-bit floats
27282948/// NOTE: The result value remains on top of the stack.
2729fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue {
2949fn cmpFloat16(func: *CodeGen, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue {
27302950 const opcode: wasm.Opcode = buildOpcode(.{
27312951 .op = switch (op) {
27322952 .lt => .lt,
......@@ -2739,186 +2959,201 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato
27392959 .valtype1 = .f32,
27402960 .signedness = .unsigned,
27412961 });
2742 _ = try self.fpext(lhs, Type.f16, Type.f32);
2743 _ = try self.fpext(rhs, Type.f16, Type.f32);
2744 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2962 _ = try func.fpext(lhs, Type.f16, Type.f32);
2963 _ = try func.fpext(rhs, Type.f16, Type.f32);
2964 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
27452965
27462966 return WValue{ .stack = {} };
27472967}
27482968
2749fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2969fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
27502970 _ = inst;
2751 return self.fail("TODO implement airCmpVector for wasm", .{});
2971 return func.fail("TODO implement airCmpVector for wasm", .{});
27522972}
27532973
2754fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2755 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2756
2757 const un_op = self.air.instructions.items(.data)[inst].un_op;
2758 const operand = try self.resolveInst(un_op);
2974fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2975 const un_op = func.air.instructions.items(.data)[inst].un_op;
2976 const operand = try func.resolveInst(un_op);
27592977
27602978 _ = operand;
2761 return self.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
2979 return func.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
27622980}
27632981
2764fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2765 const br = self.air.instructions.items(.data)[inst].br;
2766 const block = self.blocks.get(br.block_inst).?;
2982fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2983 const br = func.air.instructions.items(.data)[inst].br;
2984 const block = func.blocks.get(br.block_inst).?;
27672985
27682986 // if operand has codegen bits we should break with a value
2769 if (self.air.typeOf(br.operand).hasRuntimeBitsIgnoreComptime()) {
2770 const operand = try self.resolveInst(br.operand);
2771 try self.lowerToStack(operand);
2987 if (func.air.typeOf(br.operand).hasRuntimeBitsIgnoreComptime()) {
2988 const operand = try func.resolveInst(br.operand);
2989 try func.lowerToStack(operand);
27722990
27732991 if (block.value != .none) {
2774 try self.addLabel(.local_set, block.value.local);
2992 try func.addLabel(.local_set, block.value.local.value);
27752993 }
27762994 }
27772995
27782996 // We map every block to its block index.
27792997 // We then determine how far we have to jump to it by subtracting it from current block depth
2780 const idx: u32 = self.block_depth - block.label;
2781 try self.addLabel(.br, idx);
2998 const idx: u32 = func.block_depth - block.label;
2999 try func.addLabel(.br, idx);
27823000
2783 return .none;
3001 func.finishAir(inst, .none, &.{br.operand});
27843002}
27853003
2786fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2787 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3004fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3005 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3006 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
27883007
2789 const operand = try self.resolveInst(ty_op.operand);
2790 const operand_ty = self.air.typeOf(ty_op.operand);
3008 const operand = try func.resolveInst(ty_op.operand);
3009 const operand_ty = func.air.typeOf(ty_op.operand);
27913010
2792 if (operand_ty.zigTypeTag() == .Bool) {
2793 try self.emitWValue(operand);
2794 try self.addTag(.i32_eqz);
2795 const not_tmp = try self.allocLocal(operand_ty);
2796 try self.addLabel(.local_set, not_tmp.local);
2797 return not_tmp;
2798 } else {
2799 const operand_bits = operand_ty.intInfo(self.target).bits;
2800 const wasm_bits = toWasmBits(operand_bits) orelse {
2801 return self.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});
2802 };
3011 const result = result: {
3012 if (operand_ty.zigTypeTag() == .Bool) {
3013 try func.emitWValue(operand);
3014 try func.addTag(.i32_eqz);
3015 const not_tmp = try func.allocLocal(operand_ty);
3016 try func.addLabel(.local_set, not_tmp.local.value);
3017 break :result not_tmp;
3018 } else {
3019 const operand_bits = operand_ty.intInfo(func.target).bits;
3020 const wasm_bits = toWasmBits(operand_bits) orelse {
3021 return func.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});
3022 };
28033023
2804 switch (wasm_bits) {
2805 32 => {
2806 const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);
2807 return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
2808 },
2809 64 => {
2810 const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);
2811 return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
2812 },
2813 128 => {
2814 const result_ptr = try self.allocStack(operand_ty);
2815 try self.emitWValue(result_ptr);
2816 const msb = try self.load(operand, Type.u64, 0);
2817 const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2818 try self.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset());
2819
2820 try self.emitWValue(result_ptr);
2821 const lsb = try self.load(operand, Type.u64, 8);
2822 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
2823 try self.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset());
2824 return result_ptr;
2825 },
2826 else => unreachable,
3024 switch (wasm_bits) {
3025 32 => {
3026 const bin_op = try func.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);
3027 break :result try (try func.wrapOperand(bin_op, operand_ty)).toLocal(func, operand_ty);
3028 },
3029 64 => {
3030 const bin_op = try func.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);
3031 break :result try (try func.wrapOperand(bin_op, operand_ty)).toLocal(func, operand_ty);
3032 },
3033 128 => {
3034 const result_ptr = try func.allocStack(operand_ty);
3035 try func.emitWValue(result_ptr);
3036 const msb = try func.load(operand, Type.u64, 0);
3037 const msb_xor = try func.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
3038 try func.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset());
3039
3040 try func.emitWValue(result_ptr);
3041 const lsb = try func.load(operand, Type.u64, 8);
3042 const lsb_xor = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
3043 try func.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset());
3044 break :result result_ptr;
3045 },
3046 else => unreachable,
3047 }
28273048 }
2828 }
3049 };
3050 func.finishAir(inst, result, &.{ty_op.operand});
28293051}
28303052
2831fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2832 _ = self;
2833 _ = inst;
2834 // unsupported by wasm itself. Can be implemented once we support DWARF
3053fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3054 // unsupported by wasm itfunc. Can be implemented once we support DWARF
28353055 // for wasm
2836 return .none;
3056 func.finishAir(inst, .none, &.{});
28373057}
28383058
2839fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2840 _ = inst;
2841 try self.addTag(.@"unreachable");
2842 return .none;
3059fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3060 try func.addTag(.@"unreachable");
3061 func.finishAir(inst, .none, &.{});
28433062}
28443063
2845fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2846 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2847 return self.resolveInst(ty_op.operand);
3064fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3065 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3066 const result = if (!func.liveness.isUnused(inst)) result: {
3067 const operand = try func.resolveInst(ty_op.operand);
3068 break :result func.reuseOperand(ty_op.operand, operand);
3069 } else WValue{ .none = {} };
3070 func.finishAir(inst, result, &.{});
28483071}
28493072
2850fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2851 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2852 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
2853 const struct_ptr = try self.resolveInst(extra.data.struct_operand);
2854 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
2855 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) orelse {
2856 const module = self.bin_file.base.options.module.?;
2857 return self.fail("Field type '{}' too big to fit into stack frame", .{
3073fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3074 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3075 const extra = func.air.extraData(Air.StructField, ty_pl.payload);
3076 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.data.struct_operand});
3077
3078 const struct_ptr = try func.resolveInst(extra.data.struct_operand);
3079 const struct_ty = func.air.typeOf(extra.data.struct_operand).childType();
3080 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, func.target)) orelse {
3081 const module = func.bin_file.base.options.module.?;
3082 return func.fail("Field type '{}' too big to fit into stack frame", .{
28583083 struct_ty.structFieldType(extra.data.field_index).fmt(module),
28593084 });
28603085 };
2861 return self.structFieldPtr(struct_ptr, offset);
3086 const result = try func.structFieldPtr(struct_ptr, offset);
3087 func.finishAir(inst, result, &.{extra.data.struct_operand});
28623088}
28633089
2864fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue {
2865 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2866 const struct_ptr = try self.resolveInst(ty_op.operand);
2867 const struct_ty = self.air.typeOf(ty_op.operand).childType();
3090fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void {
3091 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3092 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
3093 const struct_ptr = try func.resolveInst(ty_op.operand);
3094 const struct_ty = func.air.typeOf(ty_op.operand).childType();
28683095 const field_ty = struct_ty.structFieldType(index);
2869 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) orelse {
2870 const module = self.bin_file.base.options.module.?;
2871 return self.fail("Field type '{}' too big to fit into stack frame", .{
3096 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, func.target)) orelse {
3097 const module = func.bin_file.base.options.module.?;
3098 return func.fail("Field type '{}' too big to fit into stack frame", .{
28723099 field_ty.fmt(module),
28733100 });
28743101 };
2875 return self.structFieldPtr(struct_ptr, offset);
3102 const result = try func.structFieldPtr(struct_ptr, offset);
3103 func.finishAir(inst, result, &.{ty_op.operand});
28763104}
28773105
2878fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {
3106fn structFieldPtr(func: *CodeGen, struct_ptr: WValue, offset: u32) InnerError!WValue {
28793107 switch (struct_ptr) {
28803108 .stack_offset => |stack_offset| {
2881 return WValue{ .stack_offset = stack_offset + offset };
3109 return WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
28823110 },
2883 else => return self.buildPointerOffset(struct_ptr, offset, .new),
3111 else => return func.buildPointerOffset(struct_ptr, offset, .new),
28843112 }
28853113}
28863114
2887fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2888 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3115fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3116 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3117 const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data;
3118 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{struct_field.struct_operand});
28893119
2890 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2891 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
2892 const struct_ty = self.air.typeOf(struct_field.struct_operand);
2893 const operand = try self.resolveInst(struct_field.struct_operand);
3120 const struct_ty = func.air.typeOf(struct_field.struct_operand);
3121 const operand = try func.resolveInst(struct_field.struct_operand);
28943122 const field_index = struct_field.field_index;
28953123 const field_ty = struct_ty.structFieldType(field_index);
2896 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
2897 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) orelse {
2898 const module = self.bin_file.base.options.module.?;
2899 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
3124 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{struct_field.struct_operand});
3125
3126 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, func.target)) orelse {
3127 const module = func.bin_file.base.options.module.?;
3128 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
29003129 };
29013130
2902 if (isByRef(field_ty, self.target)) {
2903 switch (operand) {
2904 .stack_offset => |stack_offset| {
2905 return WValue{ .stack_offset = stack_offset + offset };
2906 },
2907 else => return self.buildPointerOffset(operand, offset, .new),
3131 const result = result: {
3132 if (isByRef(field_ty, func.target)) {
3133 switch (operand) {
3134 .stack_offset => |stack_offset| {
3135 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
3136 },
3137 else => break :result try func.buildPointerOffset(operand, offset, .new),
3138 }
29083139 }
2909 }
29103140
2911 const field = try self.load(operand, field_ty, offset);
2912 return field.toLocal(self, field_ty);
3141 const field = try func.load(operand, field_ty, offset);
3142 break :result try field.toLocal(func, field_ty);
3143 };
3144 func.finishAir(inst, result, &.{struct_field.struct_operand});
29133145}
29143146
2915fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3147fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
29163148 // result type is always 'noreturn'
29173149 const blocktype = wasm.block_empty;
2918 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2919 const target = try self.resolveInst(pl_op.operand);
2920 const target_ty = self.air.typeOf(pl_op.operand);
2921 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
3150 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
3151 const target = try func.resolveInst(pl_op.operand);
3152 const target_ty = func.air.typeOf(pl_op.operand);
3153 const switch_br = func.air.extraData(Air.SwitchBr, pl_op.payload);
3154 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.data.cases_len + 1);
3155 defer func.gpa.free(liveness.deaths);
3156
29223157 var extra_index: usize = switch_br.end;
29233158 var case_i: u32 = 0;
29243159
......@@ -2927,24 +3162,24 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29273162 var case_list = try std.ArrayList(struct {
29283163 values: []const CaseValue,
29293164 body: []const Air.Inst.Index,
2930 }).initCapacity(self.gpa, switch_br.data.cases_len);
3165 }).initCapacity(func.gpa, switch_br.data.cases_len);
29313166 defer for (case_list.items) |case| {
2932 self.gpa.free(case.values);
3167 func.gpa.free(case.values);
29333168 } else case_list.deinit();
29343169
29353170 var lowest_maybe: ?i32 = null;
29363171 var highest_maybe: ?i32 = null;
29373172 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
2938 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
2939 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
2940 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
3173 const case = func.air.extraData(Air.SwitchBr.Case, extra_index);
3174 const items = @ptrCast([]const Air.Inst.Ref, func.air.extra[case.end..][0..case.data.items_len]);
3175 const case_body = func.air.extra[case.end + items.len ..][0..case.data.body_len];
29413176 extra_index = case.end + items.len + case_body.len;
2942 const values = try self.gpa.alloc(CaseValue, items.len);
2943 errdefer self.gpa.free(values);
3177 const values = try func.gpa.alloc(CaseValue, items.len);
3178 errdefer func.gpa.free(values);
29443179
29453180 for (items) |ref, i| {
2946 const item_val = self.air.value(ref).?;
2947 const int_val = self.valueAsI32(item_val, target_ty);
3181 const item_val = func.air.value(ref).?;
3182 const int_val = func.valueAsI32(item_val, target_ty);
29483183 if (lowest_maybe == null or int_val < lowest_maybe.?) {
29493184 lowest_maybe = int_val;
29503185 }
......@@ -2955,7 +3190,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29553190 }
29563191
29573192 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });
2958 try self.startBlock(.block, blocktype);
3193 try func.startBlock(.block, blocktype);
29593194 }
29603195
29613196 // When highest and lowest are null, we have no cases and can use a jump table
......@@ -2966,12 +3201,12 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29663201 // When the target is an integer size larger than u32, we have no way to use the value
29673202 // as an index, therefore we also use an if/else-chain for those cases.
29683203 // TODO: Benchmark this to find a proper value, LLVM seems to draw the line at '40~45'.
2969 const is_sparse = highest - lowest > 50 or target_ty.bitSize(self.target) > 32;
3204 const is_sparse = highest - lowest > 50 or target_ty.bitSize(func.target) > 32;
29703205
2971 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
3206 const else_body = func.air.extra[extra_index..][0..switch_br.data.else_body_len];
29723207 const has_else_body = else_body.len != 0;
29733208 if (has_else_body) {
2974 try self.startBlock(.block, blocktype);
3209 try func.startBlock(.block, blocktype);
29753210 }
29763211
29773212 if (!is_sparse) {
......@@ -2979,25 +3214,25 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29793214 // The value 'target' represents the index into the table.
29803215 // Each index in the table represents a label to the branch
29813216 // to jump to.
2982 try self.startBlock(.block, blocktype);
2983 try self.emitWValue(target);
3217 try func.startBlock(.block, blocktype);
3218 try func.emitWValue(target);
29843219 if (lowest < 0) {
29853220 // since br_table works using indexes, starting from '0', we must ensure all values
29863221 // we put inside, are atleast 0.
2987 try self.addImm32(lowest * -1);
2988 try self.addTag(.i32_add);
3222 try func.addImm32(lowest * -1);
3223 try func.addTag(.i32_add);
29893224 } else if (lowest > 0) {
29903225 // make the index start from 0 by substracting the lowest value
2991 try self.addImm32(lowest);
2992 try self.addTag(.i32_sub);
3226 try func.addImm32(lowest);
3227 try func.addTag(.i32_sub);
29933228 }
29943229
29953230 // Account for default branch so always add '1'
29963231 const depth = @intCast(u32, highest - lowest + @boolToInt(has_else_body)) + 1;
29973232 const jump_table: Mir.JumpTable = .{ .length = depth };
2998 const table_extra_index = try self.addExtra(jump_table);
2999 try self.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
3000 try self.mir_extra.ensureUnusedCapacity(self.gpa, depth);
3233 const table_extra_index = try func.addExtra(jump_table);
3234 try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
3235 try func.mir_extra.ensureUnusedCapacity(func.gpa, depth);
30013236 var value = lowest;
30023237 while (value <= highest) : (value += 1) {
30033238 // idx represents the branch we jump to
......@@ -3013,11 +3248,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30133248 // by using a jump table for this instead of if-else chains.
30143249 break :blk if (has_else_body or target_ty.zigTypeTag() == .ErrorSet) case_i else unreachable;
30153250 };
3016 self.mir_extra.appendAssumeCapacity(idx);
3251 func.mir_extra.appendAssumeCapacity(idx);
30173252 } else if (has_else_body) {
3018 self.mir_extra.appendAssumeCapacity(case_i); // default branch
3253 func.mir_extra.appendAssumeCapacity(case_i); // default branch
30193254 }
3020 try self.endBlock();
3255 try func.endBlock();
30213256 }
30223257
30233258 const signedness: std.builtin.Signedness = blk: {
......@@ -3025,199 +3260,235 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30253260 if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
30263261
30273262 // incase of an actual integer, we emit the correct signedness
3028 break :blk target_ty.intInfo(self.target).signedness;
3263 break :blk target_ty.intInfo(func.target).signedness;
30293264 };
30303265
3031 for (case_list.items) |case| {
3266 try func.branches.ensureUnusedCapacity(func.gpa, case_list.items.len + @boolToInt(has_else_body));
3267 for (case_list.items) |case, index| {
30323268 // when sparse, we use if/else-chain, so emit conditional checks
30333269 if (is_sparse) {
30343270 // for single value prong we can emit a simple if
30353271 if (case.values.len == 1) {
3036 try self.emitWValue(target);
3037 const val = try self.lowerConstant(case.values[0].value, target_ty);
3038 try self.emitWValue(val);
3272 try func.emitWValue(target);
3273 const val = try func.lowerConstant(case.values[0].value, target_ty);
3274 try func.emitWValue(val);
30393275 const opcode = buildOpcode(.{
3040 .valtype1 = typeToValtype(target_ty, self.target),
3276 .valtype1 = typeToValtype(target_ty, func.target),
30413277 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
30423278 .signedness = signedness,
30433279 });
3044 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3045 try self.addLabel(.br_if, 0);
3280 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3281 try func.addLabel(.br_if, 0);
30463282 } else {
30473283 // in multi-value prongs we must check if any prongs match the target value.
3048 try self.startBlock(.block, blocktype);
3284 try func.startBlock(.block, blocktype);
30493285 for (case.values) |value| {
3050 try self.emitWValue(target);
3051 const val = try self.lowerConstant(value.value, target_ty);
3052 try self.emitWValue(val);
3286 try func.emitWValue(target);
3287 const val = try func.lowerConstant(value.value, target_ty);
3288 try func.emitWValue(val);
30533289 const opcode = buildOpcode(.{
3054 .valtype1 = typeToValtype(target_ty, self.target),
3290 .valtype1 = typeToValtype(target_ty, func.target),
30553291 .op = .eq,
30563292 .signedness = signedness,
30573293 });
3058 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3059 try self.addLabel(.br_if, 0);
3294 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3295 try func.addLabel(.br_if, 0);
30603296 }
30613297 // value did not match any of the prong values
3062 try self.addLabel(.br, 1);
3063 try self.endBlock();
3298 try func.addLabel(.br, 1);
3299 try func.endBlock();
30643300 }
30653301 }
3066 try self.genBody(case.body);
3067 try self.endBlock();
3302 func.branches.appendAssumeCapacity(.{});
3303
3304 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len);
3305 for (liveness.deaths[index]) |operand| {
3306 func.processDeath(Air.indexToRef(operand));
3307 }
3308 try func.genBody(case.body);
3309 try func.endBlock();
3310 var case_branch = func.branches.pop();
3311 defer case_branch.deinit(func.gpa);
3312 try func.mergeBranch(&case_branch);
30683313 }
30693314
30703315 if (has_else_body) {
3071 try self.genBody(else_body);
3072 try self.endBlock();
3316 func.branches.appendAssumeCapacity(.{});
3317 const else_deaths = liveness.deaths.len - 1;
3318 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len);
3319 for (liveness.deaths[else_deaths]) |operand| {
3320 func.processDeath(Air.indexToRef(operand));
3321 }
3322 try func.genBody(else_body);
3323 try func.endBlock();
3324 var else_branch = func.branches.pop();
3325 defer else_branch.deinit(func.gpa);
3326 try func.mergeBranch(&else_branch);
30733327 }
3074 return .none;
3328 func.finishAir(inst, .none, &.{});
30753329}
30763330
3077fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
3078 const un_op = self.air.instructions.items(.data)[inst].un_op;
3079 const operand = try self.resolveInst(un_op);
3080 const err_union_ty = self.air.typeOf(un_op);
3331fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {
3332 const un_op = func.air.instructions.items(.data)[inst].un_op;
3333 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
3334 const operand = try func.resolveInst(un_op);
3335 const err_union_ty = func.air.typeOf(un_op);
30813336 const pl_ty = err_union_ty.errorUnionPayload();
30823337
3083 if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
3084 switch (opcode) {
3085 .i32_ne => return WValue{ .imm32 = 0 },
3086 .i32_eq => return WValue{ .imm32 = 1 },
3087 else => unreachable,
3338 const result = result: {
3339 if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
3340 switch (opcode) {
3341 .i32_ne => break :result WValue{ .imm32 = 0 },
3342 .i32_eq => break :result WValue{ .imm32 = 1 },
3343 else => unreachable,
3344 }
30883345 }
3089 }
30903346
3091 try self.emitWValue(operand);
3092 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
3093 try self.addMemArg(.i32_load16_u, .{
3094 .offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, self.target)),
3095 .alignment = Type.anyerror.abiAlignment(self.target),
3096 });
3097 }
3347 try func.emitWValue(operand);
3348 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
3349 try func.addMemArg(.i32_load16_u, .{
3350 .offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, func.target)),
3351 .alignment = Type.anyerror.abiAlignment(func.target),
3352 });
3353 }
30983354
3099 // Compare the error value with '0'
3100 try self.addImm32(0);
3101 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3355 // Compare the error value with '0'
3356 try func.addImm32(0);
3357 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
31023358
3103 const is_err_tmp = try self.allocLocal(Type.i32);
3104 try self.addLabel(.local_set, is_err_tmp.local);
3105 return is_err_tmp;
3359 const is_err_tmp = try func.allocLocal(Type.i32);
3360 try func.addLabel(.local_set, is_err_tmp.local.value);
3361 break :result is_err_tmp;
3362 };
3363 func.finishAir(inst, result, &.{un_op});
31063364}
31073365
3108fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {
3109 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3110 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3111 const operand = try self.resolveInst(ty_op.operand);
3112 const op_ty = self.air.typeOf(ty_op.operand);
3366fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3367 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3368 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
3369
3370 const operand = try func.resolveInst(ty_op.operand);
3371 const op_ty = func.air.typeOf(ty_op.operand);
31133372 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
31143373 const payload_ty = err_ty.errorUnionPayload();
31153374
3116 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
3375 const result = result: {
3376 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result WValue{ .none = {} };
31173377
3118 const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target));
3119 if (op_is_ptr or isByRef(payload_ty, self.target)) {
3120 return self.buildPointerOffset(operand, pl_offset, .new);
3121 }
3378 const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, func.target));
3379 if (op_is_ptr or isByRef(payload_ty, func.target)) {
3380 break :result try func.buildPointerOffset(operand, pl_offset, .new);
3381 }
31223382
3123 const payload = try self.load(operand, payload_ty, pl_offset);
3124 return payload.toLocal(self, payload_ty);
3383 const payload = try func.load(operand, payload_ty, pl_offset);
3384 break :result try payload.toLocal(func, payload_ty);
3385 };
3386 func.finishAir(inst, result, &.{ty_op.operand});
31253387}
31263388
3127fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {
3128 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3389fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3390 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3391 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
31293392
3130 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3131 const operand = try self.resolveInst(ty_op.operand);
3132 const op_ty = self.air.typeOf(ty_op.operand);
3393 const operand = try func.resolveInst(ty_op.operand);
3394 const op_ty = func.air.typeOf(ty_op.operand);
31333395 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
31343396 const payload_ty = err_ty.errorUnionPayload();
31353397
3136 if (err_ty.errorUnionSet().errorSetIsEmpty()) {
3137 return WValue{ .imm32 = 0 };
3138 }
3398 const result = result: {
3399 if (err_ty.errorUnionSet().errorSetIsEmpty()) {
3400 break :result WValue{ .imm32 = 0 };
3401 }
31393402
3140 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
3141 return operand;
3142 }
3403 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
3404 break :result func.reuseOperand(ty_op.operand, operand);
3405 }
31433406
3144 const error_val = try self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));
3145 return error_val.toLocal(self, Type.anyerror);
3407 const error_val = try func.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, func.target)));
3408 break :result try error_val.toLocal(func, Type.anyerror);
3409 };
3410 func.finishAir(inst, result, &.{ty_op.operand});
31463411}
31473412
3148fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3149 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3413fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3414 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3415 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
31503416
3151 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3152 const operand = try self.resolveInst(ty_op.operand);
3153 const err_ty = self.air.typeOfIndex(inst);
3417 const operand = try func.resolveInst(ty_op.operand);
3418 const err_ty = func.air.typeOfIndex(inst);
31543419
3155 const pl_ty = self.air.typeOf(ty_op.operand);
3156 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3157 return operand;
3158 }
3159
3160 const err_union = try self.allocStack(err_ty);
3161 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
3162 try self.store(payload_ptr, operand, pl_ty, 0);
3420 const pl_ty = func.air.typeOf(ty_op.operand);
3421 const result = result: {
3422 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3423 break :result func.reuseOperand(ty_op.operand, operand);
3424 }
31633425
3164 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
3165 try self.emitWValue(err_union);
3166 try self.addImm32(0);
3167 const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
3168 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
3426 const err_union = try func.allocStack(err_ty);
3427 const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new);
3428 try func.store(payload_ptr, operand, pl_ty, 0);
31693429
3170 return err_union;
3430 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
3431 try func.emitWValue(err_union);
3432 try func.addImm32(0);
3433 const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, func.target));
3434 try func.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
3435 break :result err_union;
3436 };
3437 func.finishAir(inst, result, &.{ty_op.operand});
31713438}
31723439
3173fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3174 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3440fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3441 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3442 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
31753443
3176 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3177 const operand = try self.resolveInst(ty_op.operand);
3178 const err_ty = self.air.getRefType(ty_op.ty);
3444 const operand = try func.resolveInst(ty_op.operand);
3445 const err_ty = func.air.getRefType(ty_op.ty);
31793446 const pl_ty = err_ty.errorUnionPayload();
31803447
3181 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3182 return operand;
3183 }
3448 const result = result: {
3449 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3450 break :result func.reuseOperand(ty_op.operand, operand);
3451 }
31843452
3185 const err_union = try self.allocStack(err_ty);
3186 // store error value
3187 try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, self.target)));
3453 const err_union = try func.allocStack(err_ty);
3454 // store error value
3455 try func.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, func.target)));
31883456
3189 // write 'undefined' to the payload
3190 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
3191 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target));
3192 try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });
3457 // write 'undefined' to the payload
3458 const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new);
3459 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(func.target));
3460 try func.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });
31933461
3194 return err_union;
3462 break :result err_union;
3463 };
3464 func.finishAir(inst, result, &.{ty_op.operand});
31953465}
31963466
3197fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3198 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3467fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3468 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3469 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
31993470
3200 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3201 const ty = self.air.getRefType(ty_op.ty);
3202 const operand = try self.resolveInst(ty_op.operand);
3203 const operand_ty = self.air.typeOf(ty_op.operand);
3471 const ty = func.air.getRefType(ty_op.ty);
3472 const operand = try func.resolveInst(ty_op.operand);
3473 const operand_ty = func.air.typeOf(ty_op.operand);
32043474 if (ty.zigTypeTag() == .Vector or operand_ty.zigTypeTag() == .Vector) {
3205 return self.fail("todo Wasm intcast for vectors", .{});
3475 return func.fail("todo Wasm intcast for vectors", .{});
32063476 }
3207 if (ty.abiSize(self.target) > 16 or operand_ty.abiSize(self.target) > 16) {
3208 return self.fail("todo Wasm intcast for bitsize > 128", .{});
3477 if (ty.abiSize(func.target) > 16 or operand_ty.abiSize(func.target) > 16) {
3478 return func.fail("todo Wasm intcast for bitsize > 128", .{});
32093479 }
32103480
3211 return (try self.intcast(operand, operand_ty, ty)).toLocal(self, ty);
3481 const result = try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);
3482 func.finishAir(inst, result, &.{});
32123483}
32133484
32143485/// Upcasts or downcasts an integer based on the given and wanted types,
32153486/// and stores the result in a new operand.
32163487/// Asserts type's bitsize <= 128
32173488/// NOTE: May leave the result on the top of the stack.
3218fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
3219 const given_info = given.intInfo(self.target);
3220 const wanted_info = wanted.intInfo(self.target);
3489fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
3490 const given_info = given.intInfo(func.target);
3491 const wanted_info = wanted.intInfo(func.target);
32213492 assert(given_info.bits <= 128);
32223493 assert(wanted_info.bits <= 128);
32233494
......@@ -3226,431 +3497,463 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
32263497 if (op_bits == wanted_bits) return operand;
32273498
32283499 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {
3229 try self.emitWValue(operand);
3230 try self.addTag(.i32_wrap_i64);
3500 try func.emitWValue(operand);
3501 try func.addTag(.i32_wrap_i64);
32313502 } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) {
3232 try self.emitWValue(operand);
3233 try self.addTag(switch (wanted_info.signedness) {
3503 try func.emitWValue(operand);
3504 try func.addTag(switch (wanted_info.signedness) {
32343505 .signed => .i64_extend_i32_s,
32353506 .unsigned => .i64_extend_i32_u,
32363507 });
32373508 } else if (wanted_bits == 128) {
32383509 // for 128bit integers we store the integer in the virtual stack, rather than a local
3239 const stack_ptr = try self.allocStack(wanted);
3240 try self.emitWValue(stack_ptr);
3510 const stack_ptr = try func.allocStack(wanted);
3511 try func.emitWValue(stack_ptr);
32413512
32423513 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
32433514 // meaning less store operations are required.
32443515 const lhs = if (op_bits == 32) blk: {
3245 break :blk try self.intcast(operand, given, if (wanted.isSignedInt()) Type.i64 else Type.u64);
3516 break :blk try func.intcast(operand, given, if (wanted.isSignedInt()) Type.i64 else Type.u64);
32463517 } else operand;
32473518
32483519 // store msb first
3249 try self.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset());
3520 try func.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset());
32503521
32513522 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
32523523 if (wanted.isSignedInt()) {
3253 try self.emitWValue(stack_ptr);
3254 const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
3255 try self.store(.{ .stack = {} }, shr, Type.u64, 8 + stack_ptr.offset());
3524 try func.emitWValue(stack_ptr);
3525 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
3526 try func.store(.{ .stack = {} }, shr, Type.u64, 8 + stack_ptr.offset());
32563527 } else {
32573528 // Ensure memory of lsb is zero'd
3258 try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
3529 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
32593530 }
32603531 return stack_ptr;
3261 } else return self.load(operand, wanted, 0);
3532 } else return func.load(operand, wanted, 0);
32623533
32633534 return WValue{ .stack = {} };
32643535}
32653536
3266fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue {
3267 const un_op = self.air.instructions.items(.data)[inst].un_op;
3268 const operand = try self.resolveInst(un_op);
3537fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
3538 const un_op = func.air.instructions.items(.data)[inst].un_op;
3539 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
3540 const operand = try func.resolveInst(un_op);
32693541
3270 const op_ty = self.air.typeOf(un_op);
3542 const op_ty = func.air.typeOf(un_op);
32713543 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;
3272 const is_null = try self.isNull(operand, optional_ty, opcode);
3273 return is_null.toLocal(self, optional_ty);
3544 const is_null = try func.isNull(operand, optional_ty, opcode);
3545 const result = try is_null.toLocal(func, optional_ty);
3546 func.finishAir(inst, result, &.{un_op});
32743547}
32753548
32763549/// For a given type and operand, checks if it's considered `null`.
32773550/// NOTE: Leaves the result on the stack
3278fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
3279 try self.emitWValue(operand);
3551fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
3552 try func.emitWValue(operand);
32803553 if (!optional_ty.optionalReprIsPayload()) {
32813554 var buf: Type.Payload.ElemType = undefined;
32823555 const payload_ty = optional_ty.optionalChild(&buf);
32833556 // When payload is zero-bits, we can treat operand as a value, rather than
32843557 // a pointer to the stack value
32853558 if (payload_ty.hasRuntimeBitsIgnoreComptime()) {
3286 try self.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 });
3559 try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 });
32873560 }
32883561 }
32893562
32903563 // Compare the null value with '0'
3291 try self.addImm32(0);
3292 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3564 try func.addImm32(0);
3565 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
32933566
32943567 return WValue{ .stack = {} };
32953568}
32963569
3297fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3298 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3299 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3300 const operand = try self.resolveInst(ty_op.operand);
3301 const opt_ty = self.air.typeOf(ty_op.operand);
3302 const payload_ty = self.air.typeOfIndex(inst);
3303 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
3304 if (opt_ty.optionalReprIsPayload()) return operand;
3570fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3571 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3572 const opt_ty = func.air.typeOf(ty_op.operand);
3573 const payload_ty = func.air.typeOfIndex(inst);
3574 if (func.liveness.isUnused(inst) or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
3575 return func.finishAir(inst, .none, &.{ty_op.operand});
3576 }
33053577
3306 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
3578 const result = result: {
3579 const operand = try func.resolveInst(ty_op.operand);
3580 if (opt_ty.optionalReprIsPayload()) break :result func.reuseOperand(ty_op.operand, operand);
33073581
3308 if (isByRef(payload_ty, self.target)) {
3309 return self.buildPointerOffset(operand, offset, .new);
3310 }
3582 const offset = opt_ty.abiSize(func.target) - payload_ty.abiSize(func.target);
33113583
3312 const payload = try self.load(operand, payload_ty, @intCast(u32, offset));
3313 return payload.toLocal(self, payload_ty);
3314}
3584 if (isByRef(payload_ty, func.target)) {
3585 break :result try func.buildPointerOffset(operand, offset, .new);
3586 }
33153587
3316fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3317 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3588 const payload = try func.load(operand, payload_ty, @intCast(u32, offset));
3589 break :result try payload.toLocal(func, payload_ty);
3590 };
3591 func.finishAir(inst, result, &.{ty_op.operand});
3592}
33183593
3319 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3320 const operand = try self.resolveInst(ty_op.operand);
3321 const opt_ty = self.air.typeOf(ty_op.operand).childType();
3594fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3595 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3596 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
3597 const operand = try func.resolveInst(ty_op.operand);
3598 const opt_ty = func.air.typeOf(ty_op.operand).childType();
33223599
3323 var buf: Type.Payload.ElemType = undefined;
3324 const payload_ty = opt_ty.optionalChild(&buf);
3325 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) {
3326 return operand;
3327 }
3600 const result = result: {
3601 var buf: Type.Payload.ElemType = undefined;
3602 const payload_ty = opt_ty.optionalChild(&buf);
3603 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) {
3604 break :result func.reuseOperand(ty_op.operand, operand);
3605 }
33283606
3329 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
3330 return self.buildPointerOffset(operand, offset, .new);
3607 const offset = opt_ty.abiSize(func.target) - payload_ty.abiSize(func.target);
3608 break :result try func.buildPointerOffset(operand, offset, .new);
3609 };
3610 func.finishAir(inst, result, &.{ty_op.operand});
33313611}
33323612
3333fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3334 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3335 const operand = try self.resolveInst(ty_op.operand);
3336 const opt_ty = self.air.typeOf(ty_op.operand).childType();
3613fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3614 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3615 const operand = try func.resolveInst(ty_op.operand);
3616 const opt_ty = func.air.typeOf(ty_op.operand).childType();
33373617 var buf: Type.Payload.ElemType = undefined;
33383618 const payload_ty = opt_ty.optionalChild(&buf);
33393619 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3340 return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()});
3620 return func.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()});
33413621 }
33423622
33433623 if (opt_ty.optionalReprIsPayload()) {
3344 return operand;
3624 return func.finishAir(inst, operand, &.{ty_op.operand});
33453625 }
33463626
3347 const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {
3348 const module = self.bin_file.base.options.module.?;
3349 return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(module)});
3627 const offset = std.math.cast(u32, opt_ty.abiSize(func.target) - payload_ty.abiSize(func.target)) orelse {
3628 const module = func.bin_file.base.options.module.?;
3629 return func.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(module)});
33503630 };
33513631
3352 try self.emitWValue(operand);
3353 try self.addImm32(1);
3354 try self.addMemArg(.i32_store8, .{ .offset = operand.offset(), .alignment = 1 });
3632 try func.emitWValue(operand);
3633 try func.addImm32(1);
3634 try func.addMemArg(.i32_store8, .{ .offset = operand.offset(), .alignment = 1 });
33553635
3356 return self.buildPointerOffset(operand, offset, .new);
3636 const result = try func.buildPointerOffset(operand, offset, .new);
3637 return func.finishAir(inst, result, &.{ty_op.operand});
33573638}
33583639
3359fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3360 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3640fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3641 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3642 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
3643 const payload_ty = func.air.typeOf(ty_op.operand);
33613644
3362 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3363 const payload_ty = self.air.typeOf(ty_op.operand);
3364 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3365 const non_null_bit = try self.allocStack(Type.initTag(.u1));
3366 try self.emitWValue(non_null_bit);
3367 try self.addImm32(1);
3368 try self.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
3369 return non_null_bit;
3370 }
3645 const result = result: {
3646 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3647 const non_null_bit = try func.allocStack(Type.initTag(.u1));
3648 try func.emitWValue(non_null_bit);
3649 try func.addImm32(1);
3650 try func.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
3651 break :result non_null_bit;
3652 }
33713653
3372 const operand = try self.resolveInst(ty_op.operand);
3373 const op_ty = self.air.typeOfIndex(inst);
3374 if (op_ty.optionalReprIsPayload()) {
3375 return operand;
3376 }
3377 const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {
3378 const module = self.bin_file.base.options.module.?;
3379 return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)});
3380 };
3654 const operand = try func.resolveInst(ty_op.operand);
3655 const op_ty = func.air.typeOfIndex(inst);
3656 if (op_ty.optionalReprIsPayload()) {
3657 break :result func.reuseOperand(ty_op.operand, operand);
3658 }
3659 const offset = std.math.cast(u32, op_ty.abiSize(func.target) - payload_ty.abiSize(func.target)) orelse {
3660 const module = func.bin_file.base.options.module.?;
3661 return func.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)});
3662 };
33813663
3382 // Create optional type, set the non-null bit, and store the operand inside the optional type
3383 const result = try self.allocStack(op_ty);
3384 try self.emitWValue(result);
3385 try self.addImm32(1);
3386 try self.addMemArg(.i32_store8, .{ .offset = result.offset(), .alignment = 1 });
3664 // Create optional type, set the non-null bit, and store the operand inside the optional type
3665 const result_ptr = try func.allocStack(op_ty);
3666 try func.emitWValue(result_ptr);
3667 try func.addImm32(1);
3668 try func.addMemArg(.i32_store8, .{ .offset = result_ptr.offset(), .alignment = 1 });
33873669
3388 const payload_ptr = try self.buildPointerOffset(result, offset, .new);
3389 try self.store(payload_ptr, operand, payload_ty, 0);
3670 const payload_ptr = try func.buildPointerOffset(result_ptr, offset, .new);
3671 try func.store(payload_ptr, operand, payload_ty, 0);
3672 break :result result_ptr;
3673 };
33903674
3391 return result;
3675 func.finishAir(inst, result, &.{ty_op.operand});
33923676}
33933677
3394fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3395 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3678fn airSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3679 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3680 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
3681 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
33963682
3397 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3398 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3399 const lhs = try self.resolveInst(bin_op.lhs);
3400 const rhs = try self.resolveInst(bin_op.rhs);
3401 const slice_ty = self.air.typeOfIndex(inst);
3683 const lhs = try func.resolveInst(bin_op.lhs);
3684 const rhs = try func.resolveInst(bin_op.rhs);
3685 const slice_ty = func.air.typeOfIndex(inst);
34023686
3403 const slice = try self.allocStack(slice_ty);
3404 try self.store(slice, lhs, Type.usize, 0);
3405 try self.store(slice, rhs, Type.usize, self.ptrSize());
3687 const slice = try func.allocStack(slice_ty);
3688 try func.store(slice, lhs, Type.usize, 0);
3689 try func.store(slice, rhs, Type.usize, func.ptrSize());
34063690
3407 return slice;
3691 func.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs });
34083692}
34093693
3410fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3411 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3694fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3695 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3696 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
34123697
3413 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3414 const operand = try self.resolveInst(ty_op.operand);
3415
3416 const len = try self.load(operand, Type.usize, self.ptrSize());
3417 return len.toLocal(self, Type.usize);
3698 const operand = try func.resolveInst(ty_op.operand);
3699 const len = try func.load(operand, Type.usize, func.ptrSize());
3700 const result = try len.toLocal(func, Type.usize);
3701 func.finishAir(inst, result, &.{ty_op.operand});
34183702}
34193703
3420fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3421 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3704fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3705 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
3706 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
34223707
3423 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3424 const slice_ty = self.air.typeOf(bin_op.lhs);
3425 const slice = try self.resolveInst(bin_op.lhs);
3426 const index = try self.resolveInst(bin_op.rhs);
3708 const slice_ty = func.air.typeOf(bin_op.lhs);
3709 const slice = try func.resolveInst(bin_op.lhs);
3710 const index = try func.resolveInst(bin_op.rhs);
34273711 const elem_ty = slice_ty.childType();
3428 const elem_size = elem_ty.abiSize(self.target);
3712 const elem_size = elem_ty.abiSize(func.target);
34293713
34303714 // load pointer onto stack
3431 _ = try self.load(slice, Type.usize, 0);
3715 _ = try func.load(slice, Type.usize, 0);
34323716
34333717 // calculate index into slice
3434 try self.emitWValue(index);
3435 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3436 try self.addTag(.i32_mul);
3437 try self.addTag(.i32_add);
3718 try func.emitWValue(index);
3719 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3720 try func.addTag(.i32_mul);
3721 try func.addTag(.i32_add);
34383722
3439 const result = try self.allocLocal(elem_ty);
3440 try self.addLabel(.local_set, result.local);
3723 const result_ptr = try func.allocLocal(elem_ty);
3724 try func.addLabel(.local_set, result_ptr.local.value);
34413725
3442 if (isByRef(elem_ty, self.target)) {
3443 return result;
3444 }
3726 const result = if (!isByRef(elem_ty, func.target)) result: {
3727 const elem_val = try func.load(result_ptr, elem_ty, 0);
3728 break :result try elem_val.toLocal(func, elem_ty);
3729 } else result_ptr;
34453730
3446 const elem_val = try self.load(result, elem_ty, 0);
3447 return elem_val.toLocal(self, elem_ty);
3731 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
34483732}
34493733
3450fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3451 if (self.liveness.isUnused(inst)) return WValue.none;
3452 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3453 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3454 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
3455 const elem_size = elem_ty.abiSize(self.target);
3734fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3735 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3736 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
3737 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
34563738
3457 const slice = try self.resolveInst(bin_op.lhs);
3458 const index = try self.resolveInst(bin_op.rhs);
3739 const elem_ty = func.air.getRefType(ty_pl.ty).childType();
3740 const elem_size = elem_ty.abiSize(func.target);
34593741
3460 _ = try self.load(slice, Type.usize, 0);
3742 const slice = try func.resolveInst(bin_op.lhs);
3743 const index = try func.resolveInst(bin_op.rhs);
3744
3745 _ = try func.load(slice, Type.usize, 0);
34613746
34623747 // calculate index into slice
3463 try self.emitWValue(index);
3464 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3465 try self.addTag(.i32_mul);
3466 try self.addTag(.i32_add);
3748 try func.emitWValue(index);
3749 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3750 try func.addTag(.i32_mul);
3751 try func.addTag(.i32_add);
34673752
3468 const result = try self.allocLocal(Type.i32);
3469 try self.addLabel(.local_set, result.local);
3470 return result;
3753 const result = try func.allocLocal(Type.i32);
3754 try func.addLabel(.local_set, result.local.value);
3755 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
34713756}
34723757
3473fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3474 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3475 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3476 const operand = try self.resolveInst(ty_op.operand);
3477 const ptr = try self.load(operand, Type.usize, 0);
3478 return ptr.toLocal(self, Type.usize);
3758fn airSlicePtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3759 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3760 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
3761 const operand = try func.resolveInst(ty_op.operand);
3762 const ptr = try func.load(operand, Type.usize, 0);
3763 const result = try ptr.toLocal(func, Type.usize);
3764 func.finishAir(inst, result, &.{ty_op.operand});
34793765}
34803766
3481fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3482 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3483 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3484 const operand = try self.resolveInst(ty_op.operand);
3485 const wanted_ty = self.air.getRefType(ty_op.ty);
3486 const op_ty = self.air.typeOf(ty_op.operand);
3767fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3768 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3769 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
34873770
3488 const int_info = op_ty.intInfo(self.target);
3771 const operand = try func.resolveInst(ty_op.operand);
3772 const wanted_ty = func.air.getRefType(ty_op.ty);
3773 const op_ty = func.air.typeOf(ty_op.operand);
3774
3775 const int_info = op_ty.intInfo(func.target);
34893776 if (toWasmBits(int_info.bits) == null) {
3490 return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});
3777 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});
34913778 }
34923779
3493 var result = try self.intcast(operand, op_ty, wanted_ty);
3494 const wanted_bits = wanted_ty.intInfo(self.target).bits;
3780 var result = try func.intcast(operand, op_ty, wanted_ty);
3781 const wanted_bits = wanted_ty.intInfo(func.target).bits;
34953782 const wasm_bits = toWasmBits(wanted_bits).?;
34963783 if (wasm_bits != wanted_bits) {
3497 result = try self.wrapOperand(result, wanted_ty);
3784 result = try func.wrapOperand(result, wanted_ty);
34983785 }
3499 return result.toLocal(self, wanted_ty);
3786
3787 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
35003788}
35013789
3502fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3503 const un_op = self.air.instructions.items(.data)[inst].un_op;
3504 return self.resolveInst(un_op);
3790fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3791 const un_op = func.air.instructions.items(.data)[inst].un_op;
3792 const result = if (func.liveness.isUnused(inst))
3793 WValue{ .none = {} }
3794 else result: {
3795 const operand = try func.resolveInst(un_op);
3796 break :result func.reuseOperand(un_op, operand);
3797 };
3798
3799 func.finishAir(inst, result, &.{un_op});
35053800}
35063801
3507fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3508 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3509 const operand = try self.resolveInst(ty_op.operand);
3510 const array_ty = self.air.typeOf(ty_op.operand).childType();
3511 const slice_ty = self.air.getRefType(ty_op.ty);
3802fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3803 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3804 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
3805
3806 const operand = try func.resolveInst(ty_op.operand);
3807 const array_ty = func.air.typeOf(ty_op.operand).childType();
3808 const slice_ty = func.air.getRefType(ty_op.ty);
35123809
35133810 // create a slice on the stack
3514 const slice_local = try self.allocStack(slice_ty);
3811 const slice_local = try func.allocStack(slice_ty);
35153812
35163813 // store the array ptr in the slice
35173814 if (array_ty.hasRuntimeBitsIgnoreComptime()) {
3518 try self.store(slice_local, operand, Type.usize, 0);
3815 try func.store(slice_local, operand, Type.usize, 0);
35193816 }
35203817
35213818 // store the length of the array in the slice
35223819 const len = WValue{ .imm32 = @intCast(u32, array_ty.arrayLen()) };
3523 try self.store(slice_local, len, Type.usize, self.ptrSize());
3820 try func.store(slice_local, len, Type.usize, func.ptrSize());
35243821
3525 return slice_local;
3822 func.finishAir(inst, slice_local, &.{ty_op.operand});
35263823}
35273824
3528fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3529 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3530 const un_op = self.air.instructions.items(.data)[inst].un_op;
3531 const operand = try self.resolveInst(un_op);
3825fn airPtrToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3826 const un_op = func.air.instructions.items(.data)[inst].un_op;
3827 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
3828 const operand = try func.resolveInst(un_op);
35323829
3533 switch (operand) {
3830 const result = switch (operand) {
35343831 // for stack offset, return a pointer to this offset.
3535 .stack_offset => return self.buildPointerOffset(operand, 0, .new),
3536 else => return operand,
3537 }
3832 .stack_offset => try func.buildPointerOffset(operand, 0, .new),
3833 else => func.reuseOperand(un_op, operand),
3834 };
3835 func.finishAir(inst, result, &.{un_op});
35383836}
35393837
3540fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3541 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3838fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3839 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
3840 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
35423841
3543 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3544 const ptr_ty = self.air.typeOf(bin_op.lhs);
3545 const ptr = try self.resolveInst(bin_op.lhs);
3546 const index = try self.resolveInst(bin_op.rhs);
3842 const ptr_ty = func.air.typeOf(bin_op.lhs);
3843 const ptr = try func.resolveInst(bin_op.lhs);
3844 const index = try func.resolveInst(bin_op.rhs);
35473845 const elem_ty = ptr_ty.childType();
3548 const elem_size = elem_ty.abiSize(self.target);
3846 const elem_size = elem_ty.abiSize(func.target);
35493847
35503848 // load pointer onto the stack
35513849 if (ptr_ty.isSlice()) {
3552 _ = try self.load(ptr, Type.usize, 0);
3850 _ = try func.load(ptr, Type.usize, 0);
35533851 } else {
3554 try self.lowerToStack(ptr);
3852 try func.lowerToStack(ptr);
35553853 }
35563854
35573855 // calculate index into slice
3558 try self.emitWValue(index);
3559 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3560 try self.addTag(.i32_mul);
3561 try self.addTag(.i32_add);
3562
3563 var result = try self.allocLocal(elem_ty);
3564 try self.addLabel(.local_set, result.local);
3565 if (isByRef(elem_ty, self.target)) {
3566 return result;
3567 }
3568 defer result.free(self); // only free if it's not returned like above
3856 try func.emitWValue(index);
3857 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3858 try func.addTag(.i32_mul);
3859 try func.addTag(.i32_add);
3860
3861 const elem_result = val: {
3862 var result = try func.allocLocal(elem_ty);
3863 try func.addLabel(.local_set, result.local.value);
3864 if (isByRef(elem_ty, func.target)) {
3865 break :val result;
3866 }
3867 defer result.free(func); // only free if it's not returned like above
35693868
3570 const elem_val = try self.load(result, elem_ty, 0);
3571 return elem_val.toLocal(self, elem_ty);
3869 const elem_val = try func.load(result, elem_ty, 0);
3870 break :val try elem_val.toLocal(func, elem_ty);
3871 };
3872 func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
35723873}
35733874
3574fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3575 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3576 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3577 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3578 const ptr_ty = self.air.typeOf(bin_op.lhs);
3579 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
3580 const elem_size = elem_ty.abiSize(self.target);
3875fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3876 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3877 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
3878 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3879
3880 const ptr_ty = func.air.typeOf(bin_op.lhs);
3881 const elem_ty = func.air.getRefType(ty_pl.ty).childType();
3882 const elem_size = elem_ty.abiSize(func.target);
35813883
3582 const ptr = try self.resolveInst(bin_op.lhs);
3583 const index = try self.resolveInst(bin_op.rhs);
3884 const ptr = try func.resolveInst(bin_op.lhs);
3885 const index = try func.resolveInst(bin_op.rhs);
35843886
35853887 // load pointer onto the stack
35863888 if (ptr_ty.isSlice()) {
3587 _ = try self.load(ptr, Type.usize, 0);
3889 _ = try func.load(ptr, Type.usize, 0);
35883890 } else {
3589 try self.lowerToStack(ptr);
3891 try func.lowerToStack(ptr);
35903892 }
35913893
35923894 // calculate index into ptr
3593 try self.emitWValue(index);
3594 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3595 try self.addTag(.i32_mul);
3596 try self.addTag(.i32_add);
3895 try func.emitWValue(index);
3896 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3897 try func.addTag(.i32_mul);
3898 try func.addTag(.i32_add);
35973899
3598 const result = try self.allocLocal(Type.i32);
3599 try self.addLabel(.local_set, result.local);
3600 return result;
3900 const result = try func.allocLocal(Type.i32);
3901 try func.addLabel(.local_set, result.local.value);
3902 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
36013903}
36023904
3603fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
3604 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3605 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3606 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3607 const ptr = try self.resolveInst(bin_op.lhs);
3608 const offset = try self.resolveInst(bin_op.rhs);
3609 const ptr_ty = self.air.typeOf(bin_op.lhs);
3905fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
3906 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
3907 const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data;
3908 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
3909
3910 const ptr = try func.resolveInst(bin_op.lhs);
3911 const offset = try func.resolveInst(bin_op.rhs);
3912 const ptr_ty = func.air.typeOf(bin_op.lhs);
36103913 const pointee_ty = switch (ptr_ty.ptrSize()) {
36113914 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
36123915 else => ptr_ty.childType(),
36133916 };
36143917
3615 const valtype = typeToValtype(Type.usize, self.target);
3918 const valtype = typeToValtype(Type.usize, func.target);
36163919 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
36173920 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
36183921
3619 try self.lowerToStack(ptr);
3620 try self.emitWValue(offset);
3621 try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target))));
3622 try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));
3623 try self.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode));
3922 try func.lowerToStack(ptr);
3923 try func.emitWValue(offset);
3924 try func.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(func.target))));
3925 try func.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));
3926 try func.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode));
36243927
3625 const result = try self.allocLocal(Type.usize);
3626 try self.addLabel(.local_set, result.local);
3627 return result;
3928 const result = try func.allocLocal(Type.usize);
3929 try func.addLabel(.local_set, result.local.value);
3930 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
36283931}
36293932
3630fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3631 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3632 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
3933fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3934 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
3935 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
36333936
3634 const ptr = try self.resolveInst(pl_op.operand);
3635 const value = try self.resolveInst(bin_op.lhs);
3636 const len = try self.resolveInst(bin_op.rhs);
3637 try self.memset(ptr, len, value);
3937 const ptr = try func.resolveInst(pl_op.operand);
3938 const value = try func.resolveInst(bin_op.lhs);
3939 const len = try func.resolveInst(bin_op.rhs);
3940 try func.memset(ptr, len, value);
36383941
3639 return WValue{ .none = {} };
3942 func.finishAir(inst, .none, &.{pl_op.operand});
36403943}
36413944
36423945/// Sets a region of memory at `ptr` to the value of `value`
36433946/// When the user has enabled the bulk_memory feature, we lower
36443947/// this to wasm's memset instruction. When the feature is not present,
36453948/// we implement it manually.
3646fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void {
3949fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!void {
36473950 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
36483951 // If not, we lower it ourselves
3649 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {
3650 try self.lowerToStack(ptr);
3651 try self.emitWValue(value);
3652 try self.emitWValue(len);
3653 try self.addExtended(.memory_fill);
3952 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) {
3953 try func.lowerToStack(ptr);
3954 try func.emitWValue(value);
3955 try func.emitWValue(len);
3956 try func.addExtended(.memory_fill);
36543957 return;
36553958 }
36563959
......@@ -3667,14 +3970,14 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
36673970 var offset: u32 = 0;
36683971 const base = ptr.offset();
36693972 while (offset < length) : (offset += 1) {
3670 try self.emitWValue(ptr);
3671 try self.emitWValue(value);
3672 switch (self.arch()) {
3973 try func.emitWValue(ptr);
3974 try func.emitWValue(value);
3975 switch (func.arch()) {
36733976 .wasm32 => {
3674 try self.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 });
3977 try func.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 });
36753978 },
36763979 .wasm64 => {
3677 try self.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 });
3980 try func.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 });
36783981 },
36793982 else => unreachable,
36803983 }
......@@ -3683,376 +3986,378 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
36833986 else => {
36843987 // TODO: We should probably lower this to a call to compiler_rt
36853988 // But for now, we implement it manually
3686 const offset = try self.ensureAllocLocal(Type.usize); // local for counter
3989 const offset = try func.ensureAllocLocal(Type.usize); // local for counter
36873990 // outer block to jump to when loop is done
3688 try self.startBlock(.block, wasm.block_empty);
3689 try self.startBlock(.loop, wasm.block_empty);
3690 try self.emitWValue(offset);
3691 try self.emitWValue(len);
3692 switch (self.arch()) {
3693 .wasm32 => try self.addTag(.i32_eq),
3694 .wasm64 => try self.addTag(.i64_eq),
3991 try func.startBlock(.block, wasm.block_empty);
3992 try func.startBlock(.loop, wasm.block_empty);
3993 try func.emitWValue(offset);
3994 try func.emitWValue(len);
3995 switch (func.arch()) {
3996 .wasm32 => try func.addTag(.i32_eq),
3997 .wasm64 => try func.addTag(.i64_eq),
36953998 else => unreachable,
36963999 }
3697 try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
3698 try self.emitWValue(ptr);
3699 try self.emitWValue(offset);
3700 switch (self.arch()) {
3701 .wasm32 => try self.addTag(.i32_add),
3702 .wasm64 => try self.addTag(.i64_add),
4000 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
4001 try func.emitWValue(ptr);
4002 try func.emitWValue(offset);
4003 switch (func.arch()) {
4004 .wasm32 => try func.addTag(.i32_add),
4005 .wasm64 => try func.addTag(.i64_add),
37034006 else => unreachable,
37044007 }
3705 try self.emitWValue(value);
3706 const mem_store_op: Mir.Inst.Tag = switch (self.arch()) {
4008 try func.emitWValue(value);
4009 const mem_store_op: Mir.Inst.Tag = switch (func.arch()) {
37074010 .wasm32 => .i32_store8,
37084011 .wasm64 => .i64_store8,
37094012 else => unreachable,
37104013 };
3711 try self.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 });
3712 try self.emitWValue(offset);
3713 try self.addImm32(1);
3714 switch (self.arch()) {
3715 .wasm32 => try self.addTag(.i32_add),
3716 .wasm64 => try self.addTag(.i64_add),
4014 try func.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 });
4015 try func.emitWValue(offset);
4016 try func.addImm32(1);
4017 switch (func.arch()) {
4018 .wasm32 => try func.addTag(.i32_add),
4019 .wasm64 => try func.addTag(.i64_add),
37174020 else => unreachable,
37184021 }
3719 try self.addLabel(.local_set, offset.local);
3720 try self.addLabel(.br, 0); // jump to start of loop
3721 try self.endBlock();
3722 try self.endBlock();
4022 try func.addLabel(.local_set, offset.local.value);
4023 try func.addLabel(.br, 0); // jump to start of loop
4024 try func.endBlock();
4025 try func.endBlock();
37234026 },
37244027 }
37254028}
37264029
3727fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3728 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4030fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4031 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
4032 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
37294033
3730 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3731 const array_ty = self.air.typeOf(bin_op.lhs);
3732 const array = try self.resolveInst(bin_op.lhs);
3733 const index = try self.resolveInst(bin_op.rhs);
4034 const array_ty = func.air.typeOf(bin_op.lhs);
4035 const array = try func.resolveInst(bin_op.lhs);
4036 const index = try func.resolveInst(bin_op.rhs);
37344037 const elem_ty = array_ty.childType();
3735 const elem_size = elem_ty.abiSize(self.target);
4038 const elem_size = elem_ty.abiSize(func.target);
37364039
3737 try self.lowerToStack(array);
3738 try self.emitWValue(index);
3739 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3740 try self.addTag(.i32_mul);
3741 try self.addTag(.i32_add);
4040 try func.lowerToStack(array);
4041 try func.emitWValue(index);
4042 try func.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
4043 try func.addTag(.i32_mul);
4044 try func.addTag(.i32_add);
37424045
3743 var result = try self.allocLocal(Type.usize);
3744 try self.addLabel(.local_set, result.local);
4046 const elem_result = val: {
4047 var result = try func.allocLocal(Type.usize);
4048 try func.addLabel(.local_set, result.local.value);
37454049
3746 if (isByRef(elem_ty, self.target)) {
3747 return result;
3748 }
3749 defer result.free(self); // only free if no longer needed and not returned like above
4050 if (isByRef(elem_ty, func.target)) {
4051 break :val result;
4052 }
4053 defer result.free(func); // only free if no longer needed and not returned like above
4054
4055 const elem_val = try func.load(result, elem_ty, 0);
4056 break :val try elem_val.toLocal(func, elem_ty);
4057 };
37504058
3751 const elem_val = try self.load(result, elem_ty, 0);
3752 return elem_val.toLocal(self, elem_ty);
4059 func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
37534060}
37544061
3755fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3756 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4062fn airFloatToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4063 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4064 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
37574065
3758 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3759 const operand = try self.resolveInst(ty_op.operand);
3760 const dest_ty = self.air.typeOfIndex(inst);
3761 const op_ty = self.air.typeOf(ty_op.operand);
4066 const operand = try func.resolveInst(ty_op.operand);
4067 const dest_ty = func.air.typeOfIndex(inst);
4068 const op_ty = func.air.typeOf(ty_op.operand);
37624069
3763 if (op_ty.abiSize(self.target) > 8) {
3764 return self.fail("TODO: floatToInt for integers/floats with bitsize larger than 64 bits", .{});
4070 if (op_ty.abiSize(func.target) > 8) {
4071 return func.fail("TODO: floatToInt for integers/floats with bitsize larger than 64 bits", .{});
37654072 }
37664073
3767 try self.emitWValue(operand);
4074 try func.emitWValue(operand);
37684075 const op = buildOpcode(.{
37694076 .op = .trunc,
3770 .valtype1 = typeToValtype(dest_ty, self.target),
3771 .valtype2 = typeToValtype(op_ty, self.target),
4077 .valtype1 = typeToValtype(dest_ty, func.target),
4078 .valtype2 = typeToValtype(op_ty, func.target),
37724079 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,
37734080 });
3774 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
3775 const wrapped = try self.wrapOperand(.{ .stack = {} }, dest_ty);
3776 return wrapped.toLocal(self, dest_ty);
4081 try func.addTag(Mir.Inst.Tag.fromOpcode(op));
4082 const wrapped = try func.wrapOperand(.{ .stack = {} }, dest_ty);
4083 const result = try wrapped.toLocal(func, dest_ty);
4084 func.finishAir(inst, result, &.{ty_op.operand});
37774085}
37784086
3779fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3780 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4087fn airIntToFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4088 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4089 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
37814090
3782 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3783 const operand = try self.resolveInst(ty_op.operand);
3784 const dest_ty = self.air.typeOfIndex(inst);
3785 const op_ty = self.air.typeOf(ty_op.operand);
4091 const operand = try func.resolveInst(ty_op.operand);
4092 const dest_ty = func.air.typeOfIndex(inst);
4093 const op_ty = func.air.typeOf(ty_op.operand);
37864094
3787 if (op_ty.abiSize(self.target) > 8) {
3788 return self.fail("TODO: intToFloat for integers/floats with bitsize larger than 64 bits", .{});
4095 if (op_ty.abiSize(func.target) > 8) {
4096 return func.fail("TODO: intToFloat for integers/floats with bitsize larger than 64 bits", .{});
37894097 }
37904098
3791 try self.emitWValue(operand);
4099 try func.emitWValue(operand);
37924100 const op = buildOpcode(.{
37934101 .op = .convert,
3794 .valtype1 = typeToValtype(dest_ty, self.target),
3795 .valtype2 = typeToValtype(op_ty, self.target),
4102 .valtype1 = typeToValtype(dest_ty, func.target),
4103 .valtype2 = typeToValtype(op_ty, func.target),
37964104 .signedness = if (op_ty.isSignedInt()) .signed else .unsigned,
37974105 });
3798 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
4106 try func.addTag(Mir.Inst.Tag.fromOpcode(op));
37994107
3800 const result = try self.allocLocal(dest_ty);
3801 try self.addLabel(.local_set, result.local);
3802 return result;
4108 const result = try func.allocLocal(dest_ty);
4109 try func.addLabel(.local_set, result.local.value);
4110 func.finishAir(inst, result, &.{ty_op.operand});
38034111}
38044112
3805fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3806 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3807
3808 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3809 const operand = try self.resolveInst(ty_op.operand);
4113fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4114 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4115 const operand = try func.resolveInst(ty_op.operand);
38104116
38114117 _ = operand;
3812 return self.fail("TODO: Implement wasm airSplat", .{});
4118 return func.fail("TODO: Implement wasm airSplat", .{});
38134119}
38144120
3815fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3816 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3817
3818 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3819 const operand = try self.resolveInst(pl_op.operand);
4121fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4122 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4123 const operand = try func.resolveInst(pl_op.operand);
38204124
38214125 _ = operand;
3822 return self.fail("TODO: Implement wasm airSelect", .{});
4126 return func.fail("TODO: Implement wasm airSelect", .{});
38234127}
38244128
3825fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3826 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3827
3828 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3829 const operand = try self.resolveInst(ty_op.operand);
4129fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4130 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4131 const operand = try func.resolveInst(ty_op.operand);
38304132
38314133 _ = operand;
3832 return self.fail("TODO: Implement wasm airShuffle", .{});
4134 return func.fail("TODO: Implement wasm airShuffle", .{});
38334135}
38344136
3835fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3836 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3837
3838 const reduce = self.air.instructions.items(.data)[inst].reduce;
3839 const operand = try self.resolveInst(reduce.operand);
4137fn airReduce(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4138 const reduce = func.air.instructions.items(.data)[inst].reduce;
4139 const operand = try func.resolveInst(reduce.operand);
38404140
38414141 _ = operand;
3842 return self.fail("TODO: Implement wasm airReduce", .{});
4142 return func.fail("TODO: Implement wasm airReduce", .{});
38434143}
38444144
3845fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3846 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3847
3848 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3849 const result_ty = self.air.typeOfIndex(inst);
4145fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4146 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4147 const result_ty = func.air.typeOfIndex(inst);
38504148 const len = @intCast(usize, result_ty.arrayLen());
3851 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
3852
3853 switch (result_ty.zigTypeTag()) {
3854 .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
3855 .Array => {
3856 const result = try self.allocStack(result_ty);
3857 const elem_ty = result_ty.childType();
3858 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3859
3860 // When the element type is by reference, we must copy the entire
3861 // value. It is therefore safer to move the offset pointer and store
3862 // each value individually, instead of using store offsets.
3863 if (isByRef(elem_ty, self.target)) {
3864 // copy stack pointer into a temporary local, which is
3865 // moved for each element to store each value in the right position.
3866 const offset = try self.buildPointerOffset(result, 0, .new);
4149 const elements = @ptrCast([]const Air.Inst.Ref, func.air.extra[ty_pl.payload..][0..len]);
4150
4151 const result: WValue = result_value: {
4152 if (func.liveness.isUnused(inst)) break :result_value WValue.none;
4153 switch (result_ty.zigTypeTag()) {
4154 .Array => {
4155 const result = try func.allocStack(result_ty);
4156 const elem_ty = result_ty.childType();
4157 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
4158
4159 // When the element type is by reference, we must copy the entire
4160 // value. It is therefore safer to move the offset pointer and store
4161 // each value individually, instead of using store offsets.
4162 if (isByRef(elem_ty, func.target)) {
4163 // copy stack pointer into a temporary local, which is
4164 // moved for each element to store each value in the right position.
4165 const offset = try func.buildPointerOffset(result, 0, .new);
4166 for (elements) |elem, elem_index| {
4167 const elem_val = try func.resolveInst(elem);
4168 try func.store(offset, elem_val, elem_ty, 0);
4169
4170 if (elem_index < elements.len - 1) {
4171 _ = try func.buildPointerOffset(offset, elem_size, .modify);
4172 }
4173 }
4174 } else {
4175 var offset: u32 = 0;
4176 for (elements) |elem| {
4177 const elem_val = try func.resolveInst(elem);
4178 try func.store(result, elem_val, elem_ty, offset);
4179 offset += elem_size;
4180 }
4181 }
4182 break :result_value result;
4183 },
4184 .Struct => {
4185 const result = try func.allocStack(result_ty);
4186 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset
38674187 for (elements) |elem, elem_index| {
3868 const elem_val = try self.resolveInst(elem);
3869 try self.store(offset, elem_val, elem_ty, 0);
4188 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
4189
4190 const elem_ty = result_ty.structFieldType(elem_index);
4191 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
4192 const value = try func.resolveInst(elem);
4193 try func.store(offset, value, elem_ty, 0);
38704194
38714195 if (elem_index < elements.len - 1) {
3872 _ = try self.buildPointerOffset(offset, elem_size, .modify);
4196 _ = try func.buildPointerOffset(offset, elem_size, .modify);
38734197 }
38744198 }
3875 } else {
3876 var offset: u32 = 0;
3877 for (elements) |elem| {
3878 const elem_val = try self.resolveInst(elem);
3879 try self.store(result, elem_val, elem_ty, offset);
3880 offset += elem_size;
3881 }
3882 }
3883 return result;
3884 },
3885 .Struct => {
3886 const result = try self.allocStack(result_ty);
3887 const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset
3888 for (elements) |elem, elem_index| {
3889 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
3890
3891 const elem_ty = result_ty.structFieldType(elem_index);
3892 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3893 const value = try self.resolveInst(elem);
3894 try self.store(offset, value, elem_ty, 0);
3895
3896 if (elem_index < elements.len - 1) {
3897 _ = try self.buildPointerOffset(offset, elem_size, .modify);
3898 }
3899 }
39004199
3901 return result;
3902 },
3903 else => unreachable,
3904 }
4200 break :result_value result;
4201 },
4202 .Vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
4203 else => unreachable,
4204 }
4205 };
4206 func.finishAir(inst, result, &.{});
39054207}
39064208
3907fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3908 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4209fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4210 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4211 const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data;
4212 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.init});
39094213
3910 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3911 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
3912 const union_ty = self.air.typeOfIndex(inst);
3913 const layout = union_ty.unionGetLayout(self.target);
3914 if (layout.payload_size == 0) {
3915 if (layout.tag_size == 0) {
3916 return WValue{ .none = {} };
4214 const result = result: {
4215 const union_ty = func.air.typeOfIndex(inst);
4216 const layout = union_ty.unionGetLayout(func.target);
4217 if (layout.payload_size == 0) {
4218 if (layout.tag_size == 0) {
4219 break :result WValue{ .none = {} };
4220 }
4221 assert(!isByRef(union_ty, func.target));
4222 break :result WValue{ .imm32 = extra.field_index };
39174223 }
3918 assert(!isByRef(union_ty, self.target));
3919 return WValue{ .imm32 = extra.field_index };
3920 }
3921 assert(isByRef(union_ty, self.target));
4224 assert(isByRef(union_ty, func.target));
39224225
3923 const result_ptr = try self.allocStack(union_ty);
3924 const payload = try self.resolveInst(extra.init);
3925 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
3926 assert(union_obj.haveFieldTypes());
3927 const field = union_obj.fields.values()[extra.field_index];
4226 const result_ptr = try func.allocStack(union_ty);
4227 const payload = try func.resolveInst(extra.init);
4228 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
4229 assert(union_obj.haveFieldTypes());
4230 const field = union_obj.fields.values()[extra.field_index];
39284231
3929 if (layout.tag_align >= layout.payload_align) {
3930 const payload_ptr = try self.buildPointerOffset(result_ptr, layout.tag_size, .new);
3931 try self.store(payload_ptr, payload, field.ty, 0);
3932 } else {
3933 try self.store(result_ptr, payload, field.ty, 0);
3934 }
4232 if (layout.tag_align >= layout.payload_align) {
4233 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);
4234 try func.store(payload_ptr, payload, field.ty, 0);
4235 } else {
4236 try func.store(result_ptr, payload, field.ty, 0);
4237 }
4238 break :result result_ptr;
4239 };
39354240
3936 return result_ptr;
4241 func.finishAir(inst, result, &.{extra.init});
39374242}
39384243
3939fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3940 const prefetch = self.air.instructions.items(.data)[inst].prefetch;
3941 _ = prefetch;
3942 return WValue{ .none = {} };
4244fn airPrefetch(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4245 const prefetch = func.air.instructions.items(.data)[inst].prefetch;
4246 func.finishAir(inst, .none, &.{prefetch.ptr});
39434247}
39444248
3945fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) !WValue {
3946 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4249fn airWasmMemorySize(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4250 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4251 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{pl_op.operand});
39474252
3948 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3949
3950 const result = try self.allocLocal(self.air.typeOfIndex(inst));
3951 try self.addLabel(.memory_size, pl_op.payload);
3952 try self.addLabel(.local_set, result.local);
3953 return result;
4253 const result = try func.allocLocal(func.air.typeOfIndex(inst));
4254 try func.addLabel(.memory_size, pl_op.payload);
4255 try func.addLabel(.local_set, result.local.value);
4256 func.finishAir(inst, result, &.{pl_op.operand});
39544257}
39554258
3956fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue {
3957 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3958 const operand = try self.resolveInst(pl_op.operand);
4259fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void {
4260 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4261 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{pl_op.operand});
39594262
3960 const result = try self.allocLocal(self.air.typeOfIndex(inst));
3961 try self.emitWValue(operand);
3962 try self.addLabel(.memory_grow, pl_op.payload);
3963 try self.addLabel(.local_set, result.local);
3964 return result;
4263 const operand = try func.resolveInst(pl_op.operand);
4264 const result = try func.allocLocal(func.air.typeOfIndex(inst));
4265 try func.emitWValue(operand);
4266 try func.addLabel(.memory_grow, pl_op.payload);
4267 try func.addLabel(.local_set, result.local.value);
4268 func.finishAir(inst, result, &.{pl_op.operand});
39654269}
39664270
3967fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
4271fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
39684272 assert(operand_ty.hasRuntimeBitsIgnoreComptime());
39694273 assert(op == .eq or op == .neq);
39704274 var buf: Type.Payload.ElemType = undefined;
39714275 const payload_ty = operand_ty.optionalChild(&buf);
3972 const offset = @intCast(u32, operand_ty.abiSize(self.target) - payload_ty.abiSize(self.target));
4276 const offset = @intCast(u32, operand_ty.abiSize(func.target) - payload_ty.abiSize(func.target));
39734277
39744278 // We store the final result in here that will be validated
39754279 // if the optional is truly equal.
3976 var result = try self.ensureAllocLocal(Type.initTag(.i32));
3977 defer result.free(self);
3978
3979 try self.startBlock(.block, wasm.block_empty);
3980 _ = try self.isNull(lhs, operand_ty, .i32_eq);
3981 _ = try self.isNull(rhs, operand_ty, .i32_eq);
3982 try self.addTag(.i32_ne); // inverse so we can exit early
3983 try self.addLabel(.br_if, 0);
3984
3985 _ = try self.load(lhs, payload_ty, offset);
3986 _ = try self.load(rhs, payload_ty, offset);
3987 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, self.target) });
3988 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3989 try self.addLabel(.br_if, 0);
3990
3991 try self.addImm32(1);
3992 try self.addLabel(.local_set, result.local);
3993 try self.endBlock();
3994
3995 try self.emitWValue(result);
3996 try self.addImm32(0);
3997 try self.addTag(if (op == .eq) .i32_ne else .i32_eq);
4280 var result = try func.ensureAllocLocal(Type.initTag(.i32));
4281 defer result.free(func);
4282
4283 try func.startBlock(.block, wasm.block_empty);
4284 _ = try func.isNull(lhs, operand_ty, .i32_eq);
4285 _ = try func.isNull(rhs, operand_ty, .i32_eq);
4286 try func.addTag(.i32_ne); // inverse so we can exit early
4287 try func.addLabel(.br_if, 0);
4288
4289 _ = try func.load(lhs, payload_ty, offset);
4290 _ = try func.load(rhs, payload_ty, offset);
4291 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, func.target) });
4292 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
4293 try func.addLabel(.br_if, 0);
4294
4295 try func.addImm32(1);
4296 try func.addLabel(.local_set, result.local.value);
4297 try func.endBlock();
4298
4299 try func.emitWValue(result);
4300 try func.addImm32(0);
4301 try func.addTag(if (op == .eq) .i32_ne else .i32_eq);
39984302 return WValue{ .stack = {} };
39994303}
40004304
40014305/// Compares big integers by checking both its high bits and low bits.
40024306/// NOTE: Leaves the result of the comparison on top of the stack.
40034307/// TODO: Lower this to compiler_rt call when bitsize > 128
4004fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
4005 assert(operand_ty.abiSize(self.target) >= 16);
4308fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
4309 assert(operand_ty.abiSize(func.target) >= 16);
40064310 assert(!(lhs != .stack and rhs == .stack));
4007 if (operand_ty.intInfo(self.target).bits > 128) {
4008 return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});
4311 if (operand_ty.intInfo(func.target).bits > 128) {
4312 return func.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(func.target).bits});
40094313 }
40104314
4011 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
4012 defer lhs_high_bit.free(self);
4013 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
4014 defer rhs_high_bit.free(self);
4315 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
4316 defer lhs_high_bit.free(func);
4317 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
4318 defer rhs_high_bit.free(func);
40154319
40164320 switch (op) {
40174321 .eq, .neq => {
4018 const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
4019 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
4020 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
4021 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
4022 const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or");
4322 const xor_high = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
4323 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
4324 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
4325 const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
4326 const or_result = try func.binOp(xor_high, xor_low, Type.u64, .@"or");
40234327
40244328 switch (op) {
4025 .eq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),
4026 .neq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq),
4329 .eq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),
4330 .neq => return func.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq),
40274331 else => unreachable,
40284332 }
40294333 },
40304334 else => {
40314335 const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64;
40324336 // leave those value on top of the stack for '.select'
4033 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
4034 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
4035 _ = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op);
4036 _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op);
4037 _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);
4038 try self.addTag(.select);
4337 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
4338 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
4339 _ = try func.cmp(lhs_low_bit, rhs_low_bit, ty, op);
4340 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, op);
4341 _ = try func.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);
4342 try func.addTag(.select);
40394343 },
40404344 }
40414345
40424346 return WValue{ .stack = {} };
40434347}
40444348
4045fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4046 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4047 const un_ty = self.air.typeOf(bin_op.lhs).childType();
4048 const tag_ty = self.air.typeOf(bin_op.rhs);
4049 const layout = un_ty.unionGetLayout(self.target);
4050 if (layout.tag_size == 0) return WValue{ .none = {} };
4051 const union_ptr = try self.resolveInst(bin_op.lhs);
4052 const new_tag = try self.resolveInst(bin_op.rhs);
4349fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4350 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
4351 const un_ty = func.air.typeOf(bin_op.lhs).childType();
4352 const tag_ty = func.air.typeOf(bin_op.rhs);
4353 const layout = un_ty.unionGetLayout(func.target);
4354 if (layout.tag_size == 0) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4355
4356 const union_ptr = try func.resolveInst(bin_op.lhs);
4357 const new_tag = try func.resolveInst(bin_op.rhs);
40534358 if (layout.payload_size == 0) {
4054 try self.store(union_ptr, new_tag, tag_ty, 0);
4055 return WValue{ .none = {} };
4359 try func.store(union_ptr, new_tag, tag_ty, 0);
4360 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
40564361 }
40574362
40584363 // when the tag alignment is smaller than the payload, the field will be stored
......@@ -4060,53 +4365,54 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
40604365 const offset = if (layout.tag_align < layout.payload_align) blk: {
40614366 break :blk @intCast(u32, layout.payload_size);
40624367 } else @as(u32, 0);
4063 try self.store(union_ptr, new_tag, tag_ty, offset);
4064 return WValue{ .none = {} };
4368 try func.store(union_ptr, new_tag, tag_ty, offset);
4369 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
40654370}
40664371
4067fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4068 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4372fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4373 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4374 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
40694375
4070 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4071 const un_ty = self.air.typeOf(ty_op.operand);
4072 const tag_ty = self.air.typeOfIndex(inst);
4073 const layout = un_ty.unionGetLayout(self.target);
4074 if (layout.tag_size == 0) return WValue{ .none = {} };
4075 const operand = try self.resolveInst(ty_op.operand);
4376 const un_ty = func.air.typeOf(ty_op.operand);
4377 const tag_ty = func.air.typeOfIndex(inst);
4378 const layout = un_ty.unionGetLayout(func.target);
4379 if (layout.tag_size == 0) return func.finishAir(inst, .none, &.{ty_op.operand});
40764380
4381 const operand = try func.resolveInst(ty_op.operand);
40774382 // when the tag alignment is smaller than the payload, the field will be stored
40784383 // after the payload.
40794384 const offset = if (layout.tag_align < layout.payload_align) blk: {
40804385 break :blk @intCast(u32, layout.payload_size);
40814386 } else @as(u32, 0);
4082 const tag = try self.load(operand, tag_ty, offset);
4083 return tag.toLocal(self, tag_ty);
4387 const tag = try func.load(operand, tag_ty, offset);
4388 const result = try tag.toLocal(func, tag_ty);
4389 func.finishAir(inst, result, &.{ty_op.operand});
40844390}
40854391
4086fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4087 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4392fn airFpext(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4393 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4394 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
40884395
4089 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4090 const dest_ty = self.air.typeOfIndex(inst);
4091 const operand = try self.resolveInst(ty_op.operand);
4092
4093 const extended = try self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty);
4094 return extended.toLocal(self, dest_ty);
4396 const dest_ty = func.air.typeOfIndex(inst);
4397 const operand = try func.resolveInst(ty_op.operand);
4398 const extended = try func.fpext(operand, func.air.typeOf(ty_op.operand), dest_ty);
4399 const result = try extended.toLocal(func, dest_ty);
4400 func.finishAir(inst, result, &.{ty_op.operand});
40954401}
40964402
40974403/// Extends a float from a given `Type` to a larger wanted `Type`
40984404/// NOTE: Leaves the result on the stack
4099fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4100 const given_bits = given.floatBits(self.target);
4101 const wanted_bits = wanted.floatBits(self.target);
4405fn fpext(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4406 const given_bits = given.floatBits(func.target);
4407 const wanted_bits = wanted.floatBits(func.target);
41024408
41034409 if (wanted_bits == 64 and given_bits == 32) {
4104 try self.emitWValue(operand);
4105 try self.addTag(.f64_promote_f32);
4410 try func.emitWValue(operand);
4411 try func.addTag(.f64_promote_f32);
41064412 return WValue{ .stack = {} };
41074413 } else if (given_bits == 16) {
41084414 // call __extendhfsf2(f16) f32
4109 const f32_result = try self.callIntrinsic(
4415 const f32_result = try func.callIntrinsic(
41104416 "__extendhfsf2",
41114417 &.{Type.f16},
41124418 Type.f32,
......@@ -4117,156 +4423,162 @@ fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WVa
41174423 return f32_result;
41184424 }
41194425 if (wanted_bits == 64) {
4120 try self.addTag(.f64_promote_f32);
4426 try func.addTag(.f64_promote_f32);
41214427 return WValue{ .stack = {} };
41224428 }
4123 return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits});
4429 return func.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits});
41244430 } else {
41254431 // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2
4126 return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits});
4432 return func.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits});
41274433 }
41284434}
41294435
4130fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4131 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4436fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4437 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4438 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
41324439
4133 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4134 const dest_ty = self.air.typeOfIndex(inst);
4135 const operand = try self.resolveInst(ty_op.operand);
4136 const trunc = try self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty);
4137 return trunc.toLocal(self, dest_ty);
4440 const dest_ty = func.air.typeOfIndex(inst);
4441 const operand = try func.resolveInst(ty_op.operand);
4442 const trunc = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);
4443 const result = try trunc.toLocal(func, dest_ty);
4444 func.finishAir(inst, result, &.{ty_op.operand});
41384445}
41394446
41404447/// Truncates a float from a given `Type` to its wanted `Type`
41414448/// NOTE: The result value remains on the stack
4142fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4143 const given_bits = given.floatBits(self.target);
4144 const wanted_bits = wanted.floatBits(self.target);
4449fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4450 const given_bits = given.floatBits(func.target);
4451 const wanted_bits = wanted.floatBits(func.target);
41454452
41464453 if (wanted_bits == 32 and given_bits == 64) {
4147 try self.emitWValue(operand);
4148 try self.addTag(.f32_demote_f64);
4454 try func.emitWValue(operand);
4455 try func.addTag(.f32_demote_f64);
41494456 return WValue{ .stack = {} };
41504457 } else if (wanted_bits == 16) {
41514458 const op: WValue = if (given_bits == 64) blk: {
4152 try self.emitWValue(operand);
4153 try self.addTag(.f32_demote_f64);
4459 try func.emitWValue(operand);
4460 try func.addTag(.f32_demote_f64);
41544461 break :blk WValue{ .stack = {} };
41554462 } else operand;
41564463
41574464 // call __truncsfhf2(f32) f16
4158 return self.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op});
4465 return func.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op});
41594466 } else {
41604467 // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2
4161 return self.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{wanted_bits});
4468 return func.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{wanted_bits});
41624469 }
41634470}
41644471
4165fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4166 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4167 const err_set_ty = self.air.typeOf(ty_op.operand).childType();
4472fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4473 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4474 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
4475
4476 const err_set_ty = func.air.typeOf(ty_op.operand).childType();
41684477 const payload_ty = err_set_ty.errorUnionPayload();
4169 const operand = try self.resolveInst(ty_op.operand);
4478 const operand = try func.resolveInst(ty_op.operand);
41704479
41714480 // set error-tag to '0' to annotate error union is non-error
4172 try self.store(
4481 try func.store(
41734482 operand,
41744483 .{ .imm32 = 0 },
41754484 Type.anyerror,
4176 @intCast(u32, errUnionErrorOffset(payload_ty, self.target)),
4485 @intCast(u32, errUnionErrorOffset(payload_ty, func.target)),
41774486 );
41784487
4179 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4488 const result = result: {
4489 if (func.liveness.isUnused(inst)) break :result WValue{ .none = {} };
41804490
4181 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4182 return operand;
4183 }
4491 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4492 break :result func.reuseOperand(ty_op.operand, operand);
4493 }
41844494
4185 return self.buildPointerOffset(operand, @intCast(u32, errUnionPayloadOffset(payload_ty, self.target)), .new);
4495 break :result try func.buildPointerOffset(operand, @intCast(u32, errUnionPayloadOffset(payload_ty, func.target)), .new);
4496 };
4497 func.finishAir(inst, result, &.{ty_op.operand});
41864498}
41874499
4188fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4189 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4500fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4501 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4502 const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
4503 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr});
41904504
4191 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4192 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
4193 const field_ptr = try self.resolveInst(extra.field_ptr);
4505 const field_ptr = try func.resolveInst(extra.field_ptr);
4506 const struct_ty = func.air.getRefType(ty_pl.ty).childType();
4507 const field_offset = struct_ty.structFieldOffset(extra.field_index, func.target);
41944508
4195 const struct_ty = self.air.getRefType(ty_pl.ty).childType();
4196 const field_offset = struct_ty.structFieldOffset(extra.field_index, self.target);
4509 const result = if (field_offset != 0) result: {
4510 const base = try func.buildPointerOffset(field_ptr, 0, .new);
4511 try func.addLabel(.local_get, base.local.value);
4512 try func.addImm32(@bitCast(i32, @intCast(u32, field_offset)));
4513 try func.addTag(.i32_sub);
4514 try func.addLabel(.local_set, base.local.value);
4515 break :result base;
4516 } else func.reuseOperand(extra.field_ptr, field_ptr);
41974517
4198 if (field_offset == 0) {
4199 return field_ptr;
4200 }
4201
4202 const base = try self.buildPointerOffset(field_ptr, 0, .new);
4203 try self.addLabel(.local_get, base.local);
4204 try self.addImm32(@bitCast(i32, @intCast(u32, field_offset)));
4205 try self.addTag(.i32_sub);
4206 try self.addLabel(.local_set, base.local);
4207 return base;
4518 func.finishAir(inst, result, &.{extra.field_ptr});
42084519}
42094520
4210fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4211 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4212 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
4213 const dst = try self.resolveInst(pl_op.operand);
4214 const src = try self.resolveInst(bin_op.lhs);
4215 const len = try self.resolveInst(bin_op.rhs);
4216 try self.memcpy(dst, src, len);
4217 return WValue{ .none = {} };
4521fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4522 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4523 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
4524 const dst = try func.resolveInst(pl_op.operand);
4525 const src = try func.resolveInst(bin_op.lhs);
4526 const len = try func.resolveInst(bin_op.rhs);
4527 try func.memcpy(dst, src, len);
4528
4529 func.finishAir(inst, .none, &.{pl_op.operand});
42184530}
42194531
4220fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4221 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4222 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4223 const operand = try self.resolveInst(ty_op.operand);
4224 const op_ty = self.air.typeOf(ty_op.operand);
4225 const result_ty = self.air.typeOfIndex(inst);
4532fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4533 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4534 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
4535
4536 const operand = try func.resolveInst(ty_op.operand);
4537 const op_ty = func.air.typeOf(ty_op.operand);
4538 const result_ty = func.air.typeOfIndex(inst);
42264539
42274540 if (op_ty.zigTypeTag() == .Vector) {
4228 return self.fail("TODO: Implement @popCount for vectors", .{});
4541 return func.fail("TODO: Implement @popCount for vectors", .{});
42294542 }
42304543
4231 const int_info = op_ty.intInfo(self.target);
4544 const int_info = op_ty.intInfo(func.target);
42324545 const bits = int_info.bits;
42334546 const wasm_bits = toWasmBits(bits) orelse {
4234 return self.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});
4547 return func.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits});
42354548 };
42364549
42374550 switch (wasm_bits) {
42384551 128 => {
4239 _ = try self.load(operand, Type.u64, 0);
4240 try self.addTag(.i64_popcnt);
4241 _ = try self.load(operand, Type.u64, 8);
4242 try self.addTag(.i64_popcnt);
4243 try self.addTag(.i64_add);
4244 try self.addTag(.i32_wrap_i64);
4552 _ = try func.load(operand, Type.u64, 0);
4553 try func.addTag(.i64_popcnt);
4554 _ = try func.load(operand, Type.u64, 8);
4555 try func.addTag(.i64_popcnt);
4556 try func.addTag(.i64_add);
4557 try func.addTag(.i32_wrap_i64);
42454558 },
42464559 else => {
4247 try self.emitWValue(operand);
4560 try func.emitWValue(operand);
42484561 switch (wasm_bits) {
4249 32 => try self.addTag(.i32_popcnt),
4562 32 => try func.addTag(.i32_popcnt),
42504563 64 => {
4251 try self.addTag(.i64_popcnt);
4252 try self.addTag(.i32_wrap_i64);
4564 try func.addTag(.i64_popcnt);
4565 try func.addTag(.i32_wrap_i64);
42534566 },
42544567 else => unreachable,
42554568 }
42564569 },
42574570 }
42584571
4259 const result = try self.allocLocal(result_ty);
4260 try self.addLabel(.local_set, result.local);
4261 return result;
4572 const result = try func.allocLocal(result_ty);
4573 try func.addLabel(.local_set, result.local.value);
4574 func.finishAir(inst, result, &.{ty_op.operand});
42624575}
42634576
4264fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4265 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4266
4267 const un_op = self.air.instructions.items(.data)[inst].un_op;
4268 const operand = try self.resolveInst(un_op);
4577fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4578 const un_op = func.air.instructions.items(.data)[inst].un_op;
4579 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
42694580
4581 const operand = try func.resolveInst(un_op);
42704582 // First retrieve the symbol index to the error name table
42714583 // that will be used to emit a relocation for the pointer
42724584 // to the error name table.
......@@ -4278,60 +4590,63 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
42784590 //
42794591 // As the names are global and the slice elements are constant, we do not have
42804592 // to make a copy of the ptr+value but can point towards them directly.
4281 const error_table_symbol = try self.bin_file.getErrorTableSymbol();
4593 const error_table_symbol = try func.bin_file.getErrorTableSymbol();
42824594 const name_ty = Type.initTag(.const_slice_u8_sentinel_0);
4283 const abi_size = name_ty.abiSize(self.target);
4595 const abi_size = name_ty.abiSize(func.target);
42844596
42854597 const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation
4286 try self.emitWValue(error_name_value);
4287 try self.emitWValue(operand);
4288 switch (self.arch()) {
4598 try func.emitWValue(error_name_value);
4599 try func.emitWValue(operand);
4600 switch (func.arch()) {
42894601 .wasm32 => {
4290 try self.addImm32(@bitCast(i32, @intCast(u32, abi_size)));
4291 try self.addTag(.i32_mul);
4292 try self.addTag(.i32_add);
4602 try func.addImm32(@bitCast(i32, @intCast(u32, abi_size)));
4603 try func.addTag(.i32_mul);
4604 try func.addTag(.i32_add);
42934605 },
42944606 .wasm64 => {
4295 try self.addImm64(abi_size);
4296 try self.addTag(.i64_mul);
4297 try self.addTag(.i64_add);
4607 try func.addImm64(abi_size);
4608 try func.addTag(.i64_mul);
4609 try func.addTag(.i64_add);
42984610 },
42994611 else => unreachable,
43004612 }
43014613
4302 const result_ptr = try self.allocLocal(Type.usize);
4303 try self.addLabel(.local_set, result_ptr.local);
4304 return result_ptr;
4614 const result_ptr = try func.allocLocal(Type.usize);
4615 try func.addLabel(.local_set, result_ptr.local.value);
4616 func.finishAir(inst, result_ptr, &.{un_op});
43054617}
43064618
4307fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerError!WValue {
4308 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4309
4310 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4311 const slice_ptr = try self.resolveInst(ty_op.operand);
4312 return self.buildPointerOffset(slice_ptr, offset, .new);
4619fn airPtrSliceFieldPtr(func: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerError!void {
4620 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4621 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
4622 const slice_ptr = try func.resolveInst(ty_op.operand);
4623 const result = try func.buildPointerOffset(slice_ptr, offset, .new);
4624 func.finishAir(inst, result, &.{ty_op.operand});
43134625}
43144626
4315fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
4627fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
43164628 assert(op == .add or op == .sub);
4317 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4318 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4319 const lhs_op = try self.resolveInst(extra.lhs);
4320 const rhs_op = try self.resolveInst(extra.rhs);
4321 const lhs_ty = self.air.typeOf(extra.lhs);
4629 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4630 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
4631 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
4632
4633 const lhs_op = try func.resolveInst(extra.lhs);
4634 const rhs_op = try func.resolveInst(extra.rhs);
4635 const lhs_ty = func.air.typeOf(extra.lhs);
43224636
43234637 if (lhs_ty.zigTypeTag() == .Vector) {
4324 return self.fail("TODO: Implement overflow arithmetic for vectors", .{});
4638 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
43254639 }
43264640
4327 const int_info = lhs_ty.intInfo(self.target);
4641 const int_info = lhs_ty.intInfo(func.target);
43284642 const is_signed = int_info.signedness == .signed;
43294643 const wasm_bits = toWasmBits(int_info.bits) orelse {
4330 return self.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});
4644 return func.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});
43314645 };
43324646
43334647 if (wasm_bits == 128) {
4334 return self.airAddSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, self.air.typeOfIndex(inst), op);
4648 const result = try func.addSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, func.air.typeOfIndex(inst), op);
4649 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
43354650 }
43364651
43374652 const zero = switch (wasm_bits) {
......@@ -4343,185 +4658,189 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
43434658 // for signed integers, we first apply signed shifts by the difference in bits
43444659 // to get the signed value, as we store it internally as 2's complement.
43454660 var lhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4346 break :blk try (try self.signAbsValue(lhs_op, lhs_ty)).toLocal(self, lhs_ty);
4661 break :blk try (try func.signAbsValue(lhs_op, lhs_ty)).toLocal(func, lhs_ty);
43474662 } else lhs_op;
43484663 var rhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4349 break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty);
4664 break :blk try (try func.signAbsValue(rhs_op, lhs_ty)).toLocal(func, lhs_ty);
43504665 } else rhs_op;
43514666
4352 var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty);
4353 defer bin_op.free(self);
4667 // in this case, we performed a signAbsValue which created a temporary local
4668 // so let's free this so it can be re-used instead.
4669 // In the other case we do not want to free it, because that would free the
4670 // resolved instructions which may be referenced by other instructions.
4671 defer if (wasm_bits != int_info.bits and is_signed) {
4672 lhs.free(func);
4673 rhs.free(func);
4674 };
4675
4676 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
4677 defer bin_op.free(func);
43544678 var result = if (wasm_bits != int_info.bits) blk: {
4355 break :blk try (try self.wrapOperand(bin_op, lhs_ty)).toLocal(self, lhs_ty);
4679 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);
43564680 } else bin_op;
4357 defer result.free(self); // no-op when wasm_bits == int_info.bits
4681 defer result.free(func); // no-op when wasm_bits == int_info.bits
43584682
43594683 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
43604684 const overflow_bit: WValue = if (is_signed) blk: {
43614685 if (wasm_bits == int_info.bits) {
4362 const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op);
4363 const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt);
4364 break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor);
4686 const cmp_zero = try func.cmp(rhs, zero, lhs_ty, cmp_op);
4687 const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt);
4688 break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor);
43654689 }
4366 const abs = try self.signAbsValue(bin_op, lhs_ty);
4367 break :blk try self.cmp(abs, bin_op, lhs_ty, .neq);
4690 const abs = try func.signAbsValue(bin_op, lhs_ty);
4691 break :blk try func.cmp(abs, bin_op, lhs_ty, .neq);
43684692 } else if (wasm_bits == int_info.bits)
4369 try self.cmp(bin_op, lhs, lhs_ty, cmp_op)
4693 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)
43704694 else
4371 try self.cmp(bin_op, result, lhs_ty, .neq);
4372 var overflow_local = try overflow_bit.toLocal(self, Type.u32);
4373 defer overflow_local.free(self);
4374
4375 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4376 try self.store(result_ptr, result, lhs_ty, 0);
4377 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4378 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
4695 try func.cmp(bin_op, result, lhs_ty, .neq);
4696 var overflow_local = try overflow_bit.toLocal(func, Type.u32);
4697 defer overflow_local.free(func);
43794698
4380 // in this case, we performed a signAbsValue which created a temporary local
4381 // so let's free this so it can be re-used instead.
4382 // In the other case we do not want to free it, because that would free the
4383 // resolved instructions which may be referenced by other instructions.
4384 if (wasm_bits != int_info.bits and is_signed) {
4385 lhs.free(self);
4386 rhs.free(self);
4387 }
4699 const result_ptr = try func.allocStack(func.air.typeOfIndex(inst));
4700 try func.store(result_ptr, result, lhs_ty, 0);
4701 const offset = @intCast(u32, lhs_ty.abiSize(func.target));
4702 try func.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
43884703
4389 return result_ptr;
4704 func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
43904705}
43914706
4392fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue {
4707fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue {
43934708 assert(op == .add or op == .sub);
4394 const int_info = ty.intInfo(self.target);
4709 const int_info = ty.intInfo(func.target);
43954710 const is_signed = int_info.signedness == .signed;
43964711 if (int_info.bits != 128) {
4397 return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});
4712 return func.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});
43984713 }
43994714
4400 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
4401 defer lhs_high_bit.free(self);
4402 var lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64);
4403 defer lhs_low_bit.free(self);
4404 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
4405 defer rhs_high_bit.free(self);
4406 var rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64);
4407 defer rhs_low_bit.free(self);
4715 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
4716 defer lhs_high_bit.free(func);
4717 var lhs_low_bit = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
4718 defer lhs_low_bit.free(func);
4719 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
4720 defer rhs_high_bit.free(func);
4721 var rhs_low_bit = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
4722 defer rhs_low_bit.free(func);
44084723
4409 var low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
4410 defer low_op_res.free(self);
4411 var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
4412 defer high_op_res.free(self);
4724 var low_op_res = try (try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(func, Type.u64);
4725 defer low_op_res.free(func);
4726 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);
4727 defer high_op_res.free(func);
44134728
44144729 var lt = if (op == .add) blk: {
4415 break :blk try (try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32);
4730 break :blk try (try func.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(func, Type.u32);
44164731 } else if (op == .sub) blk: {
4417 break :blk try (try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32);
4732 break :blk try (try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(func, Type.u32);
44184733 } else unreachable;
4419 defer lt.free(self);
4420 var tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64);
4421 defer tmp.free(self);
4422 var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
4423 defer tmp_op.free(self);
4734 defer lt.free(func);
4735 var tmp = try (try func.intcast(lt, Type.u32, Type.u64)).toLocal(func, Type.u64);
4736 defer tmp.free(func);
4737 var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);
4738 defer tmp_op.free(func);
44244739
44254740 const overflow_bit = if (is_signed) blk: {
4426 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
4741 const xor_low = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
44274742 const to_wrap = if (op == .add) wrap: {
4428 break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
4743 break :wrap try func.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
44294744 } else xor_low;
4430 const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
4431 const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and");
4432 break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed
4745 const xor_op = try func.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
4746 const wrap = try func.binOp(to_wrap, xor_op, Type.u64, .@"and");
4747 break :blk try func.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed
44334748 } else blk: {
44344749 const first_arg = if (op == .sub) arg: {
4435 break :arg try self.cmp(high_op_res, lhs_high_bit, Type.u64, .gt);
4750 break :arg try func.cmp(high_op_res, lhs_high_bit, Type.u64, .gt);
44364751 } else lt;
44374752
4438 try self.emitWValue(first_arg);
4439 _ = try self.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt);
4440 _ = try self.cmp(tmp_op, lhs_low_bit, Type.u64, .eq);
4441 try self.addTag(.select);
4753 try func.emitWValue(first_arg);
4754 _ = try func.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt);
4755 _ = try func.cmp(tmp_op, lhs_low_bit, Type.u64, .eq);
4756 try func.addTag(.select);
44424757
44434758 break :blk WValue{ .stack = {} };
44444759 };
4445 var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));
4446 defer overflow_local.free(self);
4760 var overflow_local = try overflow_bit.toLocal(func, Type.initTag(.u1));
4761 defer overflow_local.free(func);
44474762
4448 const result_ptr = try self.allocStack(result_ty);
4449 try self.store(result_ptr, high_op_res, Type.u64, 0);
4450 try self.store(result_ptr, tmp_op, Type.u64, 8);
4451 try self.store(result_ptr, overflow_local, Type.initTag(.u1), 16);
4763 const result_ptr = try func.allocStack(result_ty);
4764 try func.store(result_ptr, high_op_res, Type.u64, 0);
4765 try func.store(result_ptr, tmp_op, Type.u64, 8);
4766 try func.store(result_ptr, overflow_local, Type.initTag(.u1), 16);
44524767
44534768 return result_ptr;
44544769}
44554770
4456fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4457 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4458 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4459 const lhs = try self.resolveInst(extra.lhs);
4460 const rhs = try self.resolveInst(extra.rhs);
4461 const lhs_ty = self.air.typeOf(extra.lhs);
4771fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4772 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4773 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
4774 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
4775
4776 const lhs = try func.resolveInst(extra.lhs);
4777 const rhs = try func.resolveInst(extra.rhs);
4778 const lhs_ty = func.air.typeOf(extra.lhs);
44624779
44634780 if (lhs_ty.zigTypeTag() == .Vector) {
4464 return self.fail("TODO: Implement overflow arithmetic for vectors", .{});
4781 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
44654782 }
44664783
4467 const int_info = lhs_ty.intInfo(self.target);
4784 const int_info = lhs_ty.intInfo(func.target);
44684785 const is_signed = int_info.signedness == .signed;
44694786 const wasm_bits = toWasmBits(int_info.bits) orelse {
4470 return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
4787 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
44714788 };
44724789
4473 var shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty);
4474 defer shl.free(self);
4790 var shl = try (try func.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(func, lhs_ty);
4791 defer shl.free(func);
44754792 var result = if (wasm_bits != int_info.bits) blk: {
4476 break :blk try (try self.wrapOperand(shl, lhs_ty)).toLocal(self, lhs_ty);
4793 break :blk try (try func.wrapOperand(shl, lhs_ty)).toLocal(func, lhs_ty);
44774794 } else shl;
4478 defer result.free(self); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)
4795 defer result.free(func); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)
44794796
44804797 const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: {
44814798 // emit lhs to stack to we can keep 'wrapped' on the stack also
4482 try self.emitWValue(lhs);
4483 const abs = try self.signAbsValue(shl, lhs_ty);
4484 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);
4485 break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq);
4799 try func.emitWValue(lhs);
4800 const abs = try func.signAbsValue(shl, lhs_ty);
4801 const wrapped = try func.wrapBinOp(abs, rhs, lhs_ty, .shr);
4802 break :blk try func.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq);
44864803 } else blk: {
4487 try self.emitWValue(lhs);
4488 const shr = try self.binOp(result, rhs, lhs_ty, .shr);
4489 break :blk try self.cmp(.{ .stack = {} }, shr, lhs_ty, .neq);
4804 try func.emitWValue(lhs);
4805 const shr = try func.binOp(result, rhs, lhs_ty, .shr);
4806 break :blk try func.cmp(.{ .stack = {} }, shr, lhs_ty, .neq);
44904807 };
4491 var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));
4492 defer overflow_local.free(self);
4808 var overflow_local = try overflow_bit.toLocal(func, Type.initTag(.u1));
4809 defer overflow_local.free(func);
44934810
4494 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4495 try self.store(result_ptr, result, lhs_ty, 0);
4496 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4497 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
4811 const result_ptr = try func.allocStack(func.air.typeOfIndex(inst));
4812 try func.store(result_ptr, result, lhs_ty, 0);
4813 const offset = @intCast(u32, lhs_ty.abiSize(func.target));
4814 try func.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
44984815
4499 return result_ptr;
4816 func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
45004817}
45014818
4502fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4503 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4504 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4505 const lhs = try self.resolveInst(extra.lhs);
4506 const rhs = try self.resolveInst(extra.rhs);
4507 const lhs_ty = self.air.typeOf(extra.lhs);
4819fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4820 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
4821 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
4822 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
4823
4824 const lhs = try func.resolveInst(extra.lhs);
4825 const rhs = try func.resolveInst(extra.rhs);
4826 const lhs_ty = func.air.typeOf(extra.lhs);
45084827
45094828 if (lhs_ty.zigTypeTag() == .Vector) {
4510 return self.fail("TODO: Implement overflow arithmetic for vectors", .{});
4829 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
45114830 }
45124831
45134832 // We store the bit if it's overflowed or not in this. As it's zero-initialized
45144833 // we only need to update it if an overflow (or underflow) occurred.
4515 var overflow_bit = try self.ensureAllocLocal(Type.initTag(.u1));
4516 defer overflow_bit.free(self);
4834 var overflow_bit = try func.ensureAllocLocal(Type.initTag(.u1));
4835 defer overflow_bit.free(func);
45174836
4518 const int_info = lhs_ty.intInfo(self.target);
4837 const int_info = lhs_ty.intInfo(func.target);
45194838 const wasm_bits = toWasmBits(int_info.bits) orelse {
4520 return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits});
4839 return func.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits});
45214840 };
45224841
45234842 if (wasm_bits > 32) {
4524 return self.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
4843 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
45254844 }
45264845
45274846 const zero = switch (wasm_bits) {
......@@ -4533,184 +4852,190 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
45334852 // for 32 bit integers we upcast it to a 64bit integer
45344853 const bin_op = if (int_info.bits == 32) blk: {
45354854 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
4536 const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty);
4537 const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty);
4538 const bin_op = try (try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(self, new_ty);
4855 const lhs_upcast = try func.intcast(lhs, lhs_ty, new_ty);
4856 const rhs_upcast = try func.intcast(rhs, lhs_ty, new_ty);
4857 const bin_op = try (try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(func, new_ty);
45394858 if (int_info.signedness == .unsigned) {
4540 const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4541 const wrap = try self.intcast(shr, new_ty, lhs_ty);
4542 _ = try self.cmp(wrap, zero, lhs_ty, .neq);
4543 try self.addLabel(.local_set, overflow_bit.local);
4544 break :blk try self.intcast(bin_op, new_ty, lhs_ty);
4859 const shr = try func.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4860 const wrap = try func.intcast(shr, new_ty, lhs_ty);
4861 _ = try func.cmp(wrap, zero, lhs_ty, .neq);
4862 try func.addLabel(.local_set, overflow_bit.local.value);
4863 break :blk try func.intcast(bin_op, new_ty, lhs_ty);
45454864 } else {
4546 const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty);
4547 var shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty);
4548 defer shr.free(self);
4549
4550 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4551 const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty);
4552 _ = try self.cmp(down_shr_res, shr, lhs_ty, .neq);
4553 try self.addLabel(.local_set, overflow_bit.local);
4865 const down_cast = try (try func.intcast(bin_op, new_ty, lhs_ty)).toLocal(func, lhs_ty);
4866 var shr = try (try func.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(func, lhs_ty);
4867 defer shr.free(func);
4868
4869 const shr_res = try func.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4870 const down_shr_res = try func.intcast(shr_res, new_ty, lhs_ty);
4871 _ = try func.cmp(down_shr_res, shr, lhs_ty, .neq);
4872 try func.addLabel(.local_set, overflow_bit.local.value);
45544873 break :blk down_cast;
45554874 }
45564875 } else if (int_info.signedness == .signed) blk: {
4557 const lhs_abs = try self.signAbsValue(lhs, lhs_ty);
4558 const rhs_abs = try self.signAbsValue(rhs, lhs_ty);
4559 const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty);
4560 const mul_abs = try self.signAbsValue(bin_op, lhs_ty);
4561 _ = try self.cmp(mul_abs, bin_op, lhs_ty, .neq);
4562 try self.addLabel(.local_set, overflow_bit.local);
4563 break :blk try self.wrapOperand(bin_op, lhs_ty);
4876 const lhs_abs = try func.signAbsValue(lhs, lhs_ty);
4877 const rhs_abs = try func.signAbsValue(rhs, lhs_ty);
4878 const bin_op = try (try func.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(func, lhs_ty);
4879 const mul_abs = try func.signAbsValue(bin_op, lhs_ty);
4880 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
4881 try func.addLabel(.local_set, overflow_bit.local.value);
4882 break :blk try func.wrapOperand(bin_op, lhs_ty);
45644883 } else blk: {
4565 var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty);
4566 defer bin_op.free(self);
4884 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);
4885 defer bin_op.free(func);
45674886 const shift_imm = if (wasm_bits == 32)
45684887 WValue{ .imm32 = int_info.bits }
45694888 else
45704889 WValue{ .imm64 = int_info.bits };
4571 const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr);
4572 _ = try self.cmp(shr, zero, lhs_ty, .neq);
4573 try self.addLabel(.local_set, overflow_bit.local);
4574 break :blk try self.wrapOperand(bin_op, lhs_ty);
4890 const shr = try func.binOp(bin_op, shift_imm, lhs_ty, .shr);
4891 _ = try func.cmp(shr, zero, lhs_ty, .neq);
4892 try func.addLabel(.local_set, overflow_bit.local.value);
4893 break :blk try func.wrapOperand(bin_op, lhs_ty);
45754894 };
4576 var bin_op_local = try bin_op.toLocal(self, lhs_ty);
4577 defer bin_op_local.free(self);
4895 var bin_op_local = try bin_op.toLocal(func, lhs_ty);
4896 defer bin_op_local.free(func);
45784897
4579 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4580 try self.store(result_ptr, bin_op_local, lhs_ty, 0);
4581 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4582 try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset);
4898 const result_ptr = try func.allocStack(func.air.typeOfIndex(inst));
4899 try func.store(result_ptr, bin_op_local, lhs_ty, 0);
4900 const offset = @intCast(u32, lhs_ty.abiSize(func.target));
4901 try func.store(result_ptr, overflow_bit, Type.initTag(.u1), offset);
45834902
4584 return result_ptr;
4903 func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
45854904}
45864905
4587fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerError!WValue {
4588 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4589 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4590 const ty = self.air.typeOfIndex(inst);
4906fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {
4907 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
4908 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4909
4910 const ty = func.air.typeOfIndex(inst);
45914911 if (ty.zigTypeTag() == .Vector) {
4592 return self.fail("TODO: `@maximum` and `@minimum` for vectors", .{});
4912 return func.fail("TODO: `@maximum` and `@minimum` for vectors", .{});
45934913 }
45944914
4595 if (ty.abiSize(self.target) > 16) {
4596 return self.fail("TODO: `@maximum` and `@minimum` for types larger than 16 bytes", .{});
4915 if (ty.abiSize(func.target) > 16) {
4916 return func.fail("TODO: `@maximum` and `@minimum` for types larger than 16 bytes", .{});
45974917 }
45984918
4599 const lhs = try self.resolveInst(bin_op.lhs);
4600 const rhs = try self.resolveInst(bin_op.rhs);
4919 const lhs = try func.resolveInst(bin_op.lhs);
4920 const rhs = try func.resolveInst(bin_op.rhs);
46014921
46024922 // operands to select from
4603 try self.lowerToStack(lhs);
4604 try self.lowerToStack(rhs);
4605 _ = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);
4923 try func.lowerToStack(lhs);
4924 try func.lowerToStack(rhs);
4925 _ = try func.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);
46064926
46074927 // based on the result from comparison, return operand 0 or 1.
4608 try self.addTag(.select);
4928 try func.addTag(.select);
46094929
46104930 // store result in local
4611 const result_ty = if (isByRef(ty, self.target)) Type.u32 else ty;
4612 const result = try self.allocLocal(result_ty);
4613 try self.addLabel(.local_set, result.local);
4614 return result;
4931 const result_ty = if (isByRef(ty, func.target)) Type.u32 else ty;
4932 const result = try func.allocLocal(result_ty);
4933 try func.addLabel(.local_set, result.local.value);
4934 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
46154935}
46164936
4617fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4618 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4619 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4620 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
4621 const ty = self.air.typeOfIndex(inst);
4937fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4938 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
4939 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
4940 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4941
4942 const ty = func.air.typeOfIndex(inst);
46224943 if (ty.zigTypeTag() == .Vector) {
4623 return self.fail("TODO: `@mulAdd` for vectors", .{});
4944 return func.fail("TODO: `@mulAdd` for vectors", .{});
46244945 }
46254946
4626 const addend = try self.resolveInst(pl_op.operand);
4627 const lhs = try self.resolveInst(bin_op.lhs);
4628 const rhs = try self.resolveInst(bin_op.rhs);
4947 const addend = try func.resolveInst(pl_op.operand);
4948 const lhs = try func.resolveInst(bin_op.lhs);
4949 const rhs = try func.resolveInst(bin_op.rhs);
46294950
4630 if (ty.floatBits(self.target) == 16) {
4631 const rhs_ext = try self.fpext(rhs, ty, Type.f32);
4632 const lhs_ext = try self.fpext(lhs, ty, Type.f32);
4633 const addend_ext = try self.fpext(addend, ty, Type.f32);
4951 const result = if (ty.floatBits(func.target) == 16) fl_result: {
4952 const rhs_ext = try func.fpext(rhs, ty, Type.f32);
4953 const lhs_ext = try func.fpext(lhs, ty, Type.f32);
4954 const addend_ext = try func.fpext(addend, ty, Type.f32);
46344955 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`
4635 var result = try self.callIntrinsic(
4956 var result = try func.callIntrinsic(
46364957 "fmaf",
46374958 &.{ Type.f32, Type.f32, Type.f32 },
46384959 Type.f32,
46394960 &.{ rhs_ext, lhs_ext, addend_ext },
46404961 );
4641 return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty);
4642 }
4962 break :fl_result try (try func.fptrunc(result, Type.f32, ty)).toLocal(func, ty);
4963 } else result: {
4964 const mul_result = try func.binOp(lhs, rhs, ty, .mul);
4965 break :result try (try func.binOp(mul_result, addend, ty, .add)).toLocal(func, ty);
4966 };
46434967
4644 const mul_result = try self.binOp(lhs, rhs, ty, .mul);
4645 return (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty);
4968 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
46464969}
46474970
4648fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4649 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4650 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4651 const ty = self.air.typeOf(ty_op.operand);
4652 const result_ty = self.air.typeOfIndex(inst);
4971fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4972 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
4973 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
4974
4975 const ty = func.air.typeOf(ty_op.operand);
4976 const result_ty = func.air.typeOfIndex(inst);
46534977 if (ty.zigTypeTag() == .Vector) {
4654 return self.fail("TODO: `@clz` for vectors", .{});
4978 return func.fail("TODO: `@clz` for vectors", .{});
46554979 }
46564980
4657 const operand = try self.resolveInst(ty_op.operand);
4658 const int_info = ty.intInfo(self.target);
4981 const operand = try func.resolveInst(ty_op.operand);
4982 const int_info = ty.intInfo(func.target);
46594983 const wasm_bits = toWasmBits(int_info.bits) orelse {
4660 return self.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});
4984 return func.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});
46614985 };
46624986
46634987 switch (wasm_bits) {
46644988 32 => {
4665 try self.emitWValue(operand);
4666 try self.addTag(.i32_clz);
4989 try func.emitWValue(operand);
4990 try func.addTag(.i32_clz);
46674991 },
46684992 64 => {
4669 try self.emitWValue(operand);
4670 try self.addTag(.i64_clz);
4671 try self.addTag(.i32_wrap_i64);
4993 try func.emitWValue(operand);
4994 try func.addTag(.i64_clz);
4995 try func.addTag(.i32_wrap_i64);
46724996 },
46734997 128 => {
4674 var lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64);
4675 defer lsb.free(self);
4676
4677 try self.emitWValue(lsb);
4678 try self.addTag(.i64_clz);
4679 _ = try self.load(operand, Type.u64, 0);
4680 try self.addTag(.i64_clz);
4681 try self.emitWValue(.{ .imm64 = 64 });
4682 try self.addTag(.i64_add);
4683 _ = try self.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
4684 try self.addTag(.select);
4685 try self.addTag(.i32_wrap_i64);
4998 var lsb = try (try func.load(operand, Type.u64, 8)).toLocal(func, Type.u64);
4999 defer lsb.free(func);
5000
5001 try func.emitWValue(lsb);
5002 try func.addTag(.i64_clz);
5003 _ = try func.load(operand, Type.u64, 0);
5004 try func.addTag(.i64_clz);
5005 try func.emitWValue(.{ .imm64 = 64 });
5006 try func.addTag(.i64_add);
5007 _ = try func.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
5008 try func.addTag(.select);
5009 try func.addTag(.i32_wrap_i64);
46865010 },
46875011 else => unreachable,
46885012 }
46895013
46905014 if (wasm_bits != int_info.bits) {
4691 try self.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });
4692 try self.addTag(.i32_sub);
5015 try func.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });
5016 try func.addTag(.i32_sub);
46935017 }
46945018
4695 const result = try self.allocLocal(result_ty);
4696 try self.addLabel(.local_set, result.local);
4697 return result;
5019 const result = try func.allocLocal(result_ty);
5020 try func.addLabel(.local_set, result.local.value);
5021 func.finishAir(inst, result, &.{ty_op.operand});
46985022}
46995023
4700fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4701 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
4702 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4703 const ty = self.air.typeOf(ty_op.operand);
4704 const result_ty = self.air.typeOfIndex(inst);
5024fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5025 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5026 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
5027
5028 const ty = func.air.typeOf(ty_op.operand);
5029 const result_ty = func.air.typeOfIndex(inst);
47055030
47065031 if (ty.zigTypeTag() == .Vector) {
4707 return self.fail("TODO: `@ctz` for vectors", .{});
5032 return func.fail("TODO: `@ctz` for vectors", .{});
47085033 }
47095034
4710 const operand = try self.resolveInst(ty_op.operand);
4711 const int_info = ty.intInfo(self.target);
5035 const operand = try func.resolveInst(ty_op.operand);
5036 const int_info = ty.intInfo(func.target);
47125037 const wasm_bits = toWasmBits(int_info.bits) orelse {
4713 return self.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});
5038 return func.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});
47145039 };
47155040
47165041 switch (wasm_bits) {
......@@ -4718,67 +5043,67 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
47185043 if (wasm_bits != int_info.bits) {
47195044 const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits);
47205045 // leave value on the stack
4721 _ = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or");
4722 } else try self.emitWValue(operand);
4723 try self.addTag(.i32_ctz);
5046 _ = try func.binOp(operand, .{ .imm32 = val }, ty, .@"or");
5047 } else try func.emitWValue(operand);
5048 try func.addTag(.i32_ctz);
47245049 },
47255050 64 => {
47265051 if (wasm_bits != int_info.bits) {
47275052 const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits);
47285053 // leave value on the stack
4729 _ = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or");
4730 } else try self.emitWValue(operand);
4731 try self.addTag(.i64_ctz);
4732 try self.addTag(.i32_wrap_i64);
5054 _ = try func.binOp(operand, .{ .imm64 = val }, ty, .@"or");
5055 } else try func.emitWValue(operand);
5056 try func.addTag(.i64_ctz);
5057 try func.addTag(.i32_wrap_i64);
47335058 },
47345059 128 => {
4735 var msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64);
4736 defer msb.free(self);
5060 var msb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);
5061 defer msb.free(func);
47375062
4738 try self.emitWValue(msb);
4739 try self.addTag(.i64_ctz);
4740 _ = try self.load(operand, Type.u64, 8);
5063 try func.emitWValue(msb);
5064 try func.addTag(.i64_ctz);
5065 _ = try func.load(operand, Type.u64, 8);
47415066 if (wasm_bits != int_info.bits) {
4742 try self.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64));
4743 try self.addTag(.i64_or);
5067 try func.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64));
5068 try func.addTag(.i64_or);
47445069 }
4745 try self.addTag(.i64_ctz);
4746 try self.addImm64(64);
5070 try func.addTag(.i64_ctz);
5071 try func.addImm64(64);
47475072 if (wasm_bits != int_info.bits) {
4748 try self.addTag(.i64_or);
5073 try func.addTag(.i64_or);
47495074 } else {
4750 try self.addTag(.i64_add);
5075 try func.addTag(.i64_add);
47515076 }
4752 _ = try self.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
4753 try self.addTag(.select);
4754 try self.addTag(.i32_wrap_i64);
5077 _ = try func.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
5078 try func.addTag(.select);
5079 try func.addTag(.i32_wrap_i64);
47555080 },
47565081 else => unreachable,
47575082 }
47585083
4759 const result = try self.allocLocal(result_ty);
4760 try self.addLabel(.local_set, result.local);
4761 return result;
5084 const result = try func.allocLocal(result_ty);
5085 try func.addLabel(.local_set, result.local.value);
5086 func.finishAir(inst, result, &.{ty_op.operand});
47625087}
47635088
4764fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {
4765 if (self.debug_output != .dwarf) return WValue{ .none = {} };
5089fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
5090 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
47665091
4767 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4768 const ty = self.air.typeOf(pl_op.operand);
4769 const operand = try self.resolveInst(pl_op.operand);
5092 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
5093 const ty = func.air.typeOf(pl_op.operand);
5094 const operand = try func.resolveInst(pl_op.operand);
47705095 const op_ty = if (is_ptr) ty.childType() else ty;
47715096
47725097 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, op_ty.fmtDebug(), operand });
47735098
4774 const name = self.air.nullTerminatedString(pl_op.payload);
5099 const name = func.air.nullTerminatedString(pl_op.payload);
47755100 log.debug(" var name = ({s})", .{name});
47765101
4777 const dbg_info = &self.debug_output.dwarf.dbg_info;
5102 const dbg_info = &func.debug_output.dwarf.dbg_info;
47785103 try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable));
47795104 switch (operand) {
47805105 .local => |local| {
4781 const leb_size = link.File.Wasm.getULEB128Size(local);
5106 const leb_size = link.File.Wasm.getULEB128Size(local.value);
47825107 try dbg_info.ensureUnusedCapacity(2 + leb_size);
47835108 // wasm locals are encoded as follow:
47845109 // DW_OP_WASM_location wasm-op
......@@ -4790,58 +5115,60 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue {
47905115 std.dwarf.OP.WASM_location,
47915116 std.dwarf.OP.WASM_local,
47925117 });
4793 leb.writeULEB128(dbg_info.writer(), local) catch unreachable;
5118 leb.writeULEB128(dbg_info.writer(), local.value) catch unreachable;
47945119 },
47955120 else => {}, // TODO
47965121 }
47975122
47985123 try dbg_info.ensureUnusedCapacity(5 + name.len + 1);
4799 try self.addDbgInfoTypeReloc(op_ty);
5124 try func.addDbgInfoTypeReloc(op_ty);
48005125 dbg_info.appendSliceAssumeCapacity(name);
48015126 dbg_info.appendAssumeCapacity(0);
4802 return WValue{ .none = {} };
5127 func.finishAir(inst, .none, &.{});
48035128}
48045129
4805fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {
4806 if (self.debug_output != .dwarf) return WValue{ .none = {} };
5130fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) !void {
5131 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
48075132
4808 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
4809 try self.addInst(.{ .tag = .dbg_line, .data = .{
4810 .payload = try self.addExtra(Mir.DbgLineColumn{
5133 const dbg_stmt = func.air.instructions.items(.data)[inst].dbg_stmt;
5134 try func.addInst(.{ .tag = .dbg_line, .data = .{
5135 .payload = try func.addExtra(Mir.DbgLineColumn{
48115136 .line = dbg_stmt.line,
48125137 .column = dbg_stmt.column,
48135138 }),
48145139 } });
4815 return WValue{ .none = {} };
5140 func.finishAir(inst, .none, &.{});
48165141}
48175142
4818fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4819 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4820 const err_union = try self.resolveInst(pl_op.operand);
4821 const extra = self.air.extraData(Air.Try, pl_op.payload);
4822 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4823 const err_union_ty = self.air.typeOf(pl_op.operand);
4824 return lowerTry(self, err_union, body, err_union_ty, false);
5143fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5144 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
5145 const err_union = try func.resolveInst(pl_op.operand);
5146 const extra = func.air.extraData(Air.Try, pl_op.payload);
5147 const body = func.air.extra[extra.end..][0..extra.data.body_len];
5148 const err_union_ty = func.air.typeOf(pl_op.operand);
5149 const result = try lowerTry(func, err_union, body, err_union_ty, false);
5150 func.finishAir(inst, result, &.{pl_op.operand});
48255151}
48265152
4827fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4828 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4829 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
4830 const err_union_ptr = try self.resolveInst(extra.data.ptr);
4831 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4832 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
4833 return lowerTry(self, err_union_ptr, body, err_union_ty, true);
5153fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5154 const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
5155 const extra = func.air.extraData(Air.TryPtr, ty_pl.payload);
5156 const err_union_ptr = try func.resolveInst(extra.data.ptr);
5157 const body = func.air.extra[extra.end..][0..extra.data.body_len];
5158 const err_union_ty = func.air.typeOf(extra.data.ptr).childType();
5159 const result = try lowerTry(func, err_union_ptr, body, err_union_ty, true);
5160 func.finishAir(inst, result, &.{extra.data.ptr});
48345161}
48355162
48365163fn lowerTry(
4837 self: *Self,
5164 func: *CodeGen,
48385165 err_union: WValue,
48395166 body: []const Air.Inst.Index,
48405167 err_union_ty: Type,
48415168 operand_is_ptr: bool,
48425169) InnerError!WValue {
48435170 if (operand_is_ptr) {
4844 return self.fail("TODO: lowerTry for pointers", .{});
5171 return func.fail("TODO: lowerTry for pointers", .{});
48455172 }
48465173
48475174 const pl_ty = err_union_ty.errorUnionPayload();
......@@ -4849,21 +5176,21 @@ fn lowerTry(
48495176
48505177 if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) {
48515178 // Block we can jump out of when error is not set
4852 try self.startBlock(.block, wasm.block_empty);
5179 try func.startBlock(.block, wasm.block_empty);
48535180
48545181 // check if the error tag is set for the error union.
4855 try self.emitWValue(err_union);
5182 try func.emitWValue(err_union);
48565183 if (pl_has_bits) {
4857 const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
4858 try self.addMemArg(.i32_load16_u, .{
5184 const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, func.target));
5185 try func.addMemArg(.i32_load16_u, .{
48595186 .offset = err_union.offset() + err_offset,
4860 .alignment = Type.anyerror.abiAlignment(self.target),
5187 .alignment = Type.anyerror.abiAlignment(func.target),
48615188 });
48625189 }
4863 try self.addTag(.i32_eqz);
4864 try self.addLabel(.br_if, 0); // jump out of block when error is '0'
4865 try self.genBody(body);
4866 try self.endBlock();
5190 try func.addTag(.i32_eqz);
5191 try func.addLabel(.br_if, 0); // jump out of block when error is '0'
5192 try func.genBody(body);
5193 try func.endBlock();
48675194 }
48685195
48695196 // if we reach here it means error was not set, and we want the payload
......@@ -4871,118 +5198,121 @@ fn lowerTry(
48715198 return WValue{ .none = {} };
48725199 }
48735200
4874 const pl_offset = @intCast(u32, errUnionPayloadOffset(pl_ty, self.target));
4875 if (isByRef(pl_ty, self.target)) {
4876 return buildPointerOffset(self, err_union, pl_offset, .new);
5201 const pl_offset = @intCast(u32, errUnionPayloadOffset(pl_ty, func.target));
5202 if (isByRef(pl_ty, func.target)) {
5203 return buildPointerOffset(func, err_union, pl_offset, .new);
48775204 }
4878 const payload = try self.load(err_union, pl_ty, pl_offset);
4879 return payload.toLocal(self, pl_ty);
5205 const payload = try func.load(err_union, pl_ty, pl_offset);
5206 return payload.toLocal(func, pl_ty);
48805207}
48815208
4882fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4883 if (self.liveness.isUnused(inst)) {
4884 return WValue{ .none = {} };
4885 }
5209fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5210 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
5211 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand});
48865212
4887 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4888 const ty = self.air.typeOfIndex(inst);
4889 const operand = try self.resolveInst(ty_op.operand);
5213 const ty = func.air.typeOfIndex(inst);
5214 const operand = try func.resolveInst(ty_op.operand);
48905215
48915216 if (ty.zigTypeTag() == .Vector) {
4892 return self.fail("TODO: @byteSwap for vectors", .{});
5217 return func.fail("TODO: @byteSwap for vectors", .{});
48935218 }
4894 const int_info = ty.intInfo(self.target);
5219 const int_info = ty.intInfo(func.target);
48955220
48965221 // bytes are no-op
48975222 if (int_info.bits == 8) {
4898 return operand;
4899 }
4900
4901 switch (int_info.bits) {
4902 16 => {
4903 const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4904 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and");
4905 const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4906 const res = if (int_info.signedness == .signed) blk: {
4907 break :blk try self.wrapOperand(shr_res, Type.u8);
4908 } else shr_res;
4909 return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);
4910 },
4911 24 => {
4912 var msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16);
4913 defer msb.free(self);
4914
4915 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);
4916 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");
4917 const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr);
4918
4919 const res = if (int_info.signedness == .signed) blk: {
4920 break :blk try self.wrapOperand(shr_res, Type.u8);
4921 } else shr_res;
4922 const lhs_tmp = try self.binOp(lhs, res, ty, .@"or");
4923 const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr);
4924 const rhs_wrap = try self.wrapOperand(msb, Type.u8);
4925 const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl);
4926
4927 const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);
4928 const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or");
4929 return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty);
4930 },
4931 32 => {
4932 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
4933 var lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty);
4934 defer lhs.free(self);
4935 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
4936 var rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);
4937 defer rhs.free(self);
4938 var tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty);
4939 defer tmp_or.free(self);
4940
4941 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);
4942 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);
4943 const res = if (int_info.signedness == .signed) blk: {
4944 break :blk try self.wrapOperand(shr, Type.u16);
4945 } else shr;
4946 return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty);
4947 },
4948 else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
4949 }
5223 return func.finishAir(inst, func.reuseOperand(ty_op.operand, operand), &.{ty_op.operand});
5224 }
5225
5226 const result = result: {
5227 switch (int_info.bits) {
5228 16 => {
5229 const shl_res = try func.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
5230 const lhs = try func.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and");
5231 const shr_res = try func.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
5232 const res = if (int_info.signedness == .signed) blk: {
5233 break :blk try func.wrapOperand(shr_res, Type.u8);
5234 } else shr_res;
5235 break :result try (try func.binOp(lhs, res, ty, .@"or")).toLocal(func, ty);
5236 },
5237 24 => {
5238 var msb = try (try func.wrapOperand(operand, Type.u16)).toLocal(func, Type.u16);
5239 defer msb.free(func);
5240
5241 const shl_res = try func.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);
5242 const lhs = try func.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");
5243 const shr_res = try func.binOp(msb, .{ .imm32 = 8 }, ty, .shr);
5244
5245 const res = if (int_info.signedness == .signed) blk: {
5246 break :blk try func.wrapOperand(shr_res, Type.u8);
5247 } else shr_res;
5248 const lhs_tmp = try func.binOp(lhs, res, ty, .@"or");
5249 const lhs_result = try func.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr);
5250 const rhs_wrap = try func.wrapOperand(msb, Type.u8);
5251 const rhs_result = try func.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl);
5252
5253 const lsb = try func.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);
5254 const tmp = try func.binOp(lhs_result, rhs_result, ty, .@"or");
5255 break :result try (try func.binOp(tmp, lsb, ty, .@"or")).toLocal(func, ty);
5256 },
5257 32 => {
5258 const shl_tmp = try func.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
5259 var lhs = try (try func.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(func, ty);
5260 defer lhs.free(func);
5261 const shr_tmp = try func.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
5262 var rhs = try (try func.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(func, ty);
5263 defer rhs.free(func);
5264 var tmp_or = try (try func.binOp(lhs, rhs, ty, .@"or")).toLocal(func, ty);
5265 defer tmp_or.free(func);
5266
5267 const shl = try func.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);
5268 const shr = try func.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);
5269 const res = if (int_info.signedness == .signed) blk: {
5270 break :blk try func.wrapOperand(shr, Type.u16);
5271 } else shr;
5272 break :result try (try func.binOp(shl, res, ty, .@"or")).toLocal(func, ty);
5273 },
5274 else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
5275 }
5276 };
5277 func.finishAir(inst, result, &.{ty_op.operand});
49505278}
49515279
4952fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4953 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5280fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5281 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5282 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
49545283
4955 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4956 const ty = self.air.typeOfIndex(inst);
4957 const lhs = try self.resolveInst(bin_op.lhs);
4958 const rhs = try self.resolveInst(bin_op.rhs);
5284 const ty = func.air.typeOfIndex(inst);
5285 const lhs = try func.resolveInst(bin_op.lhs);
5286 const rhs = try func.resolveInst(bin_op.rhs);
49595287
4960 if (ty.isSignedInt()) {
4961 return self.divSigned(lhs, rhs, ty);
4962 }
4963 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
5288 const result = if (ty.isSignedInt())
5289 try func.divSigned(lhs, rhs, ty)
5290 else
5291 try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
5292 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
49645293}
49655294
4966fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4967 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5295fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5296 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5297 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
49685298
4969 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4970 const ty = self.air.typeOfIndex(inst);
4971 const lhs = try self.resolveInst(bin_op.lhs);
4972 const rhs = try self.resolveInst(bin_op.rhs);
5299 const ty = func.air.typeOfIndex(inst);
5300 const lhs = try func.resolveInst(bin_op.lhs);
5301 const rhs = try func.resolveInst(bin_op.rhs);
49735302
49745303 if (ty.isUnsignedInt()) {
4975 return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
5304 const result = try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
5305 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
49765306 } else if (ty.isSignedInt()) {
4977 const int_bits = ty.intInfo(self.target).bits;
5307 const int_bits = ty.intInfo(func.target).bits;
49785308 const wasm_bits = toWasmBits(int_bits) orelse {
4979 return self.fail("TODO: `@divFloor` for signed integers larger than '{d}' bits", .{int_bits});
5309 return func.fail("TODO: `@divFloor` for signed integers larger than '{d}' bits", .{int_bits});
49805310 };
49815311 const lhs_res = if (wasm_bits != int_bits) blk: {
4982 break :blk try (try self.signAbsValue(lhs, ty)).toLocal(self, ty);
5312 break :blk try (try func.signAbsValue(lhs, ty)).toLocal(func, ty);
49835313 } else lhs;
49845314 const rhs_res = if (wasm_bits != int_bits) blk: {
4985 break :blk try (try self.signAbsValue(rhs, ty)).toLocal(self, ty);
5315 break :blk try (try func.signAbsValue(rhs, ty)).toLocal(func, ty);
49865316 } else rhs;
49875317
49885318 const zero = switch (wasm_bits) {
......@@ -4991,118 +5321,118 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
49915321 else => unreachable,
49925322 };
49935323
4994 const div_result = try self.allocLocal(ty);
5324 const div_result = try func.allocLocal(ty);
49955325 // leave on stack
4996 _ = try self.binOp(lhs_res, rhs_res, ty, .div);
4997 try self.addLabel(.local_tee, div_result.local);
4998 _ = try self.cmp(lhs_res, zero, ty, .lt);
4999 _ = try self.cmp(rhs_res, zero, ty, .lt);
5326 _ = try func.binOp(lhs_res, rhs_res, ty, .div);
5327 try func.addLabel(.local_tee, div_result.local.value);
5328 _ = try func.cmp(lhs_res, zero, ty, .lt);
5329 _ = try func.cmp(rhs_res, zero, ty, .lt);
50005330 switch (wasm_bits) {
50015331 32 => {
5002 try self.addTag(.i32_xor);
5003 try self.addTag(.i32_sub);
5332 try func.addTag(.i32_xor);
5333 try func.addTag(.i32_sub);
50045334 },
50055335 64 => {
5006 try self.addTag(.i64_xor);
5007 try self.addTag(.i64_sub);
5336 try func.addTag(.i64_xor);
5337 try func.addTag(.i64_sub);
50085338 },
50095339 else => unreachable,
50105340 }
5011 try self.emitWValue(div_result);
5341 try func.emitWValue(div_result);
50125342 // leave value on the stack
5013 _ = try self.binOp(lhs_res, rhs_res, ty, .rem);
5014 try self.addTag(.select);
5343 _ = try func.binOp(lhs_res, rhs_res, ty, .rem);
5344 try func.addTag(.select);
50155345 } else {
5016 const float_bits = ty.floatBits(self.target);
5346 const float_bits = ty.floatBits(func.target);
50175347 if (float_bits > 64) {
5018 return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits});
5348 return func.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits});
50195349 }
50205350 const is_f16 = float_bits == 16;
50215351
50225352 const lhs_operand = if (is_f16) blk: {
5023 break :blk try self.fpext(lhs, Type.f16, Type.f32);
5353 break :blk try func.fpext(lhs, Type.f16, Type.f32);
50245354 } else lhs;
50255355 const rhs_operand = if (is_f16) blk: {
5026 break :blk try self.fpext(rhs, Type.f16, Type.f32);
5356 break :blk try func.fpext(rhs, Type.f16, Type.f32);
50275357 } else rhs;
50285358
5029 try self.emitWValue(lhs_operand);
5030 try self.emitWValue(rhs_operand);
5359 try func.emitWValue(lhs_operand);
5360 try func.emitWValue(rhs_operand);
50315361
50325362 switch (float_bits) {
50335363 16, 32 => {
5034 try self.addTag(.f32_div);
5035 try self.addTag(.f32_floor);
5364 try func.addTag(.f32_div);
5365 try func.addTag(.f32_floor);
50365366 },
50375367 64 => {
5038 try self.addTag(.f64_div);
5039 try self.addTag(.f64_floor);
5368 try func.addTag(.f64_div);
5369 try func.addTag(.f64_floor);
50405370 },
50415371 else => unreachable,
50425372 }
50435373
50445374 if (is_f16) {
5045 _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
5375 _ = try func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
50465376 }
50475377 }
50485378
5049 const result = try self.allocLocal(ty);
5050 try self.addLabel(.local_set, result.local);
5051 return result;
5379 const result = try func.allocLocal(ty);
5380 try func.addLabel(.local_set, result.local.value);
5381 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
50525382}
50535383
5054fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue {
5055 const int_bits = ty.intInfo(self.target).bits;
5384fn divSigned(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue {
5385 const int_bits = ty.intInfo(func.target).bits;
50565386 const wasm_bits = toWasmBits(int_bits) orelse {
5057 return self.fail("TODO: Implement signed division for integers with bitsize '{d}'", .{int_bits});
5387 return func.fail("TODO: Implement signed division for integers with bitsize '{d}'", .{int_bits});
50585388 };
50595389
50605390 if (wasm_bits == 128) {
5061 return self.fail("TODO: Implement signed division for 128-bit integerrs", .{});
5391 return func.fail("TODO: Implement signed division for 128-bit integerrs", .{});
50625392 }
50635393
50645394 if (wasm_bits != int_bits) {
50655395 // Leave both values on the stack
5066 _ = try self.signAbsValue(lhs, ty);
5067 _ = try self.signAbsValue(rhs, ty);
5396 _ = try func.signAbsValue(lhs, ty);
5397 _ = try func.signAbsValue(rhs, ty);
50685398 } else {
5069 try self.emitWValue(lhs);
5070 try self.emitWValue(rhs);
5399 try func.emitWValue(lhs);
5400 try func.emitWValue(rhs);
50715401 }
5072 try self.addTag(.i32_div_s);
5402 try func.addTag(.i32_div_s);
50735403
5074 const result = try self.allocLocal(ty);
5075 try self.addLabel(.local_set, result.local);
5404 const result = try func.allocLocal(ty);
5405 try func.addLabel(.local_set, result.local.value);
50765406 return result;
50775407}
50785408
50795409/// Retrieves the absolute value of a signed integer
50805410/// NOTE: Leaves the result value on the stack.
5081fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
5082 const int_bits = ty.intInfo(self.target).bits;
5411fn signAbsValue(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
5412 const int_bits = ty.intInfo(func.target).bits;
50835413 const wasm_bits = toWasmBits(int_bits) orelse {
5084 return self.fail("TODO: signAbsValue for signed integers larger than '{d}' bits", .{int_bits});
5414 return func.fail("TODO: signAbsValue for signed integers larger than '{d}' bits", .{int_bits});
50855415 };
50865416
50875417 const shift_val = switch (wasm_bits) {
50885418 32 => WValue{ .imm32 = wasm_bits - int_bits },
50895419 64 => WValue{ .imm64 = wasm_bits - int_bits },
5090 else => return self.fail("TODO: signAbsValue for i128", .{}),
5420 else => return func.fail("TODO: signAbsValue for i128", .{}),
50915421 };
50925422
5093 try self.emitWValue(operand);
5423 try func.emitWValue(operand);
50945424 switch (wasm_bits) {
50955425 32 => {
5096 try self.emitWValue(shift_val);
5097 try self.addTag(.i32_shl);
5098 try self.emitWValue(shift_val);
5099 try self.addTag(.i32_shr_s);
5426 try func.emitWValue(shift_val);
5427 try func.addTag(.i32_shl);
5428 try func.emitWValue(shift_val);
5429 try func.addTag(.i32_shr_s);
51005430 },
51015431 64 => {
5102 try self.emitWValue(shift_val);
5103 try self.addTag(.i64_shl);
5104 try self.emitWValue(shift_val);
5105 try self.addTag(.i64_shr_s);
5432 try func.emitWValue(shift_val);
5433 try func.addTag(.i64_shl);
5434 try func.emitWValue(shift_val);
5435 try func.addTag(.i64_shr_s);
51065436 },
51075437 else => unreachable,
51085438 }
......@@ -5110,61 +5440,62 @@ fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
51105440 return WValue{ .stack = {} };
51115441}
51125442
5113fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
5114 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5443fn airCeilFloorTrunc(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
5444 const un_op = func.air.instructions.items(.data)[inst].un_op;
5445 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
51155446
5116 const un_op = self.air.instructions.items(.data)[inst].un_op;
5117 const ty = self.air.typeOfIndex(inst);
5118 const float_bits = ty.floatBits(self.target);
5447 const ty = func.air.typeOfIndex(inst);
5448 const float_bits = ty.floatBits(func.target);
51195449 const is_f16 = float_bits == 16;
51205450
51215451 if (ty.zigTypeTag() == .Vector) {
5122 return self.fail("TODO: Implement `@ceil` for vectors", .{});
5452 return func.fail("TODO: Implement `@ceil` for vectors", .{});
51235453 }
51245454 if (float_bits > 64) {
5125 return self.fail("TODO: implement `@ceil`, `@trunc`, `@floor` for floats larger than 64bits", .{});
5455 return func.fail("TODO: implement `@ceil`, `@trunc`, `@floor` for floats larger than 64bits", .{});
51265456 }
51275457
5128 const operand = try self.resolveInst(un_op);
5458 const operand = try func.resolveInst(un_op);
51295459 const op_to_lower = if (is_f16) blk: {
5130 break :blk try self.fpext(operand, Type.f16, Type.f32);
5460 break :blk try func.fpext(operand, Type.f16, Type.f32);
51315461 } else operand;
5132 try self.emitWValue(op_to_lower);
5133 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) });
5134 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
5462 try func.emitWValue(op_to_lower);
5463 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, func.target) });
5464 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
51355465
51365466 if (is_f16) {
5137 _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
5467 _ = try func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
51385468 }
51395469
5140 const result = try self.allocLocal(ty);
5141 try self.addLabel(.local_set, result.local);
5142 return result;
5470 const result = try func.allocLocal(ty);
5471 try func.addLabel(.local_set, result.local.value);
5472 func.finishAir(inst, result, &.{un_op});
51435473}
51445474
5145fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
5475fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
51465476 assert(op == .add or op == .sub);
5147 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5477 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5478 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
51485479
5149 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5150 const ty = self.air.typeOfIndex(inst);
5151 const lhs = try self.resolveInst(bin_op.lhs);
5152 const rhs = try self.resolveInst(bin_op.rhs);
5480 const ty = func.air.typeOfIndex(inst);
5481 const lhs = try func.resolveInst(bin_op.lhs);
5482 const rhs = try func.resolveInst(bin_op.rhs);
51535483
5154 const int_info = ty.intInfo(self.target);
5484 const int_info = ty.intInfo(func.target);
51555485 const is_signed = int_info.signedness == .signed;
51565486
51575487 if (int_info.bits > 64) {
5158 return self.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits});
5488 return func.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits});
51595489 }
51605490
51615491 if (is_signed) {
5162 return signedSat(self, lhs, rhs, ty, op);
5492 const result = try signedSat(func, lhs, rhs, ty, op);
5493 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
51635494 }
51645495
51655496 const wasm_bits = toWasmBits(int_info.bits).?;
5166 var bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);
5167 defer bin_result.free(self);
5497 var bin_result = try (try func.binOp(lhs, rhs, ty, op)).toLocal(func, ty);
5498 defer bin_result.free(func);
51685499 if (wasm_bits != int_info.bits and op == .add) {
51695500 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);
51705501 const imm_val = switch (wasm_bits) {
......@@ -5173,35 +5504,35 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
51735504 else => unreachable,
51745505 };
51755506
5176 try self.emitWValue(bin_result);
5177 try self.emitWValue(imm_val);
5178 _ = try self.cmp(bin_result, imm_val, ty, .lt);
5507 try func.emitWValue(bin_result);
5508 try func.emitWValue(imm_val);
5509 _ = try func.cmp(bin_result, imm_val, ty, .lt);
51795510 } else {
51805511 switch (wasm_bits) {
5181 32 => try self.addImm32(if (op == .add) @as(i32, -1) else 0),
5182 64 => try self.addImm64(if (op == .add) @bitCast(u64, @as(i64, -1)) else 0),
5512 32 => try func.addImm32(if (op == .add) @as(i32, -1) else 0),
5513 64 => try func.addImm64(if (op == .add) @bitCast(u64, @as(i64, -1)) else 0),
51835514 else => unreachable,
51845515 }
5185 try self.emitWValue(bin_result);
5186 _ = try self.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt);
5516 try func.emitWValue(bin_result);
5517 _ = try func.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt);
51875518 }
51885519
5189 try self.addTag(.select);
5190 const result = try self.allocLocal(ty);
5191 try self.addLabel(.local_set, result.local);
5192 return result;
5520 try func.addTag(.select);
5521 const result = try func.allocLocal(ty);
5522 try func.addLabel(.local_set, result.local.value);
5523 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
51935524}
51945525
5195fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {
5196 const int_info = ty.intInfo(self.target);
5526fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {
5527 const int_info = ty.intInfo(func.target);
51975528 const wasm_bits = toWasmBits(int_info.bits).?;
51985529 const is_wasm_bits = wasm_bits == int_info.bits;
51995530
52005531 var lhs = if (!is_wasm_bits) lhs: {
5201 break :lhs try (try self.signAbsValue(lhs_operand, ty)).toLocal(self, ty);
5532 break :lhs try (try func.signAbsValue(lhs_operand, ty)).toLocal(func, ty);
52025533 } else lhs_operand;
52035534 var rhs = if (!is_wasm_bits) rhs: {
5204 break :rhs try (try self.signAbsValue(rhs_operand, ty)).toLocal(self, ty);
5535 break :rhs try (try func.signAbsValue(rhs_operand, ty)).toLocal(func, ty);
52055536 } else rhs_operand;
52065537
52075538 const max_val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits - 1)) - 1);
......@@ -5217,94 +5548,94 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
52175548 else => unreachable,
52185549 };
52195550
5220 var bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);
5551 var bin_result = try (try func.binOp(lhs, rhs, ty, op)).toLocal(func, ty);
52215552 if (!is_wasm_bits) {
5222 defer bin_result.free(self); // not returned in this branch
5223 defer lhs.free(self); // uses temporary local for absvalue
5224 defer rhs.free(self); // uses temporary local for absvalue
5225 try self.emitWValue(bin_result);
5226 try self.emitWValue(max_wvalue);
5227 _ = try self.cmp(bin_result, max_wvalue, ty, .lt);
5228 try self.addTag(.select);
5229 try self.addLabel(.local_set, bin_result.local); // re-use local
5230
5231 try self.emitWValue(bin_result);
5232 try self.emitWValue(min_wvalue);
5233 _ = try self.cmp(bin_result, min_wvalue, ty, .gt);
5234 try self.addTag(.select);
5235 try self.addLabel(.local_set, bin_result.local); // re-use local
5236 return (try self.wrapOperand(bin_result, ty)).toLocal(self, ty);
5553 defer bin_result.free(func); // not returned in this branch
5554 defer lhs.free(func); // uses temporary local for absvalue
5555 defer rhs.free(func); // uses temporary local for absvalue
5556 try func.emitWValue(bin_result);
5557 try func.emitWValue(max_wvalue);
5558 _ = try func.cmp(bin_result, max_wvalue, ty, .lt);
5559 try func.addTag(.select);
5560 try func.addLabel(.local_set, bin_result.local.value); // re-use local
5561
5562 try func.emitWValue(bin_result);
5563 try func.emitWValue(min_wvalue);
5564 _ = try func.cmp(bin_result, min_wvalue, ty, .gt);
5565 try func.addTag(.select);
5566 try func.addLabel(.local_set, bin_result.local.value); // re-use local
5567 return (try func.wrapOperand(bin_result, ty)).toLocal(func, ty);
52375568 } else {
52385569 const zero = switch (wasm_bits) {
52395570 32 => WValue{ .imm32 = 0 },
52405571 64 => WValue{ .imm64 = 0 },
52415572 else => unreachable,
52425573 };
5243 try self.emitWValue(max_wvalue);
5244 try self.emitWValue(min_wvalue);
5245 _ = try self.cmp(bin_result, zero, ty, .lt);
5246 try self.addTag(.select);
5247 try self.emitWValue(bin_result);
5574 try func.emitWValue(max_wvalue);
5575 try func.emitWValue(min_wvalue);
5576 _ = try func.cmp(bin_result, zero, ty, .lt);
5577 try func.addTag(.select);
5578 try func.emitWValue(bin_result);
52485579 // leave on stack
5249 const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
5250 const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt);
5251 _ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
5252 try self.addTag(.select);
5253 try self.addLabel(.local_set, bin_result.local); // re-use local
5580 const cmp_zero_result = try func.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
5581 const cmp_bin_result = try func.cmp(bin_result, lhs, ty, .lt);
5582 _ = try func.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
5583 try func.addTag(.select);
5584 try func.addLabel(.local_set, bin_result.local.value); // re-use local
52545585 return bin_result;
52555586 }
52565587}
52575588
5258fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
5259 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
5589fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5590 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
5591 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
52605592
5261 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5262 const ty = self.air.typeOfIndex(inst);
5263 const int_info = ty.intInfo(self.target);
5593 const ty = func.air.typeOfIndex(inst);
5594 const int_info = ty.intInfo(func.target);
52645595 const is_signed = int_info.signedness == .signed;
52655596 if (int_info.bits > 64) {
5266 return self.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits});
5597 return func.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits});
52675598 }
52685599
5269 const lhs = try self.resolveInst(bin_op.lhs);
5270 const rhs = try self.resolveInst(bin_op.rhs);
5600 const lhs = try func.resolveInst(bin_op.lhs);
5601 const rhs = try func.resolveInst(bin_op.rhs);
52715602 const wasm_bits = toWasmBits(int_info.bits).?;
5272 const result = try self.allocLocal(ty);
5603 const result = try func.allocLocal(ty);
52735604
5274 if (wasm_bits == int_info.bits) {
5275 var shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);
5276 defer shl.free(self);
5277 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5278 defer shr.free(self);
5605 if (wasm_bits == int_info.bits) outer_blk: {
5606 var shl = try (try func.binOp(lhs, rhs, ty, .shl)).toLocal(func, ty);
5607 defer shl.free(func);
5608 var shr = try (try func.binOp(shl, rhs, ty, .shr)).toLocal(func, ty);
5609 defer shr.free(func);
52795610
52805611 switch (wasm_bits) {
52815612 32 => blk: {
52825613 if (!is_signed) {
5283 try self.addImm32(-1);
5614 try func.addImm32(-1);
52845615 break :blk;
52855616 }
5286 try self.addImm32(std.math.minInt(i32));
5287 try self.addImm32(std.math.maxInt(i32));
5288 _ = try self.cmp(lhs, .{ .imm32 = 0 }, ty, .lt);
5289 try self.addTag(.select);
5617 try func.addImm32(std.math.minInt(i32));
5618 try func.addImm32(std.math.maxInt(i32));
5619 _ = try func.cmp(lhs, .{ .imm32 = 0 }, ty, .lt);
5620 try func.addTag(.select);
52905621 },
52915622 64 => blk: {
52925623 if (!is_signed) {
5293 try self.addImm64(@bitCast(u64, @as(i64, -1)));
5624 try func.addImm64(@bitCast(u64, @as(i64, -1)));
52945625 break :blk;
52955626 }
5296 try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64))));
5297 try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64))));
5298 _ = try self.cmp(lhs, .{ .imm64 = 0 }, ty, .lt);
5299 try self.addTag(.select);
5627 try func.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64))));
5628 try func.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64))));
5629 _ = try func.cmp(lhs, .{ .imm64 = 0 }, ty, .lt);
5630 try func.addTag(.select);
53005631 },
53015632 else => unreachable,
53025633 }
5303 try self.emitWValue(shl);
5304 _ = try self.cmp(lhs, shr, ty, .neq);
5305 try self.addTag(.select);
5306 try self.addLabel(.local_set, result.local);
5307 return result;
5634 try func.emitWValue(shl);
5635 _ = try func.cmp(lhs, shr, ty, .neq);
5636 try func.addTag(.select);
5637 try func.addLabel(.local_set, result.local.value);
5638 break :outer_blk;
53085639 } else {
53095640 const shift_size = wasm_bits - int_info.bits;
53105641 const shift_value = switch (wasm_bits) {
......@@ -5313,48 +5644,50 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
53135644 else => unreachable,
53145645 };
53155646
5316 var shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty);
5317 defer shl_res.free(self);
5318 var shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty);
5319 defer shl.free(self);
5320 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5321 defer shr.free(self);
5647 var shl_res = try (try func.binOp(lhs, shift_value, ty, .shl)).toLocal(func, ty);
5648 defer shl_res.free(func);
5649 var shl = try (try func.binOp(shl_res, rhs, ty, .shl)).toLocal(func, ty);
5650 defer shl.free(func);
5651 var shr = try (try func.binOp(shl, rhs, ty, .shr)).toLocal(func, ty);
5652 defer shr.free(func);
53225653
53235654 switch (wasm_bits) {
53245655 32 => blk: {
53255656 if (!is_signed) {
5326 try self.addImm32(-1);
5657 try func.addImm32(-1);
53275658 break :blk;
53285659 }
53295660
5330 try self.addImm32(std.math.minInt(i32));
5331 try self.addImm32(std.math.maxInt(i32));
5332 _ = try self.cmp(shl_res, .{ .imm32 = 0 }, ty, .lt);
5333 try self.addTag(.select);
5661 try func.addImm32(std.math.minInt(i32));
5662 try func.addImm32(std.math.maxInt(i32));
5663 _ = try func.cmp(shl_res, .{ .imm32 = 0 }, ty, .lt);
5664 try func.addTag(.select);
53345665 },
53355666 64 => blk: {
53365667 if (!is_signed) {
5337 try self.addImm64(@bitCast(u64, @as(i64, -1)));
5668 try func.addImm64(@bitCast(u64, @as(i64, -1)));
53385669 break :blk;
53395670 }
53405671
5341 try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64))));
5342 try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64))));
5343 _ = try self.cmp(shl_res, .{ .imm64 = 0 }, ty, .lt);
5344 try self.addTag(.select);
5672 try func.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64))));
5673 try func.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64))));
5674 _ = try func.cmp(shl_res, .{ .imm64 = 0 }, ty, .lt);
5675 try func.addTag(.select);
53455676 },
53465677 else => unreachable,
53475678 }
5348 try self.emitWValue(shl);
5349 _ = try self.cmp(shl_res, shr, ty, .neq);
5350 try self.addTag(.select);
5351 try self.addLabel(.local_set, result.local);
5352 var shift_result = try self.binOp(result, shift_value, ty, .shr);
5679 try func.emitWValue(shl);
5680 _ = try func.cmp(shl_res, shr, ty, .neq);
5681 try func.addTag(.select);
5682 try func.addLabel(.local_set, result.local.value);
5683 var shift_result = try func.binOp(result, shift_value, ty, .shr);
53535684 if (is_signed) {
5354 shift_result = try self.wrapOperand(shift_result, ty);
5685 shift_result = try func.wrapOperand(shift_result, ty);
53555686 }
5356 return shift_result.toLocal(self, ty);
5687 try func.addLabel(.local_set, result.local.value);
53575688 }
5689
5690 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
53585691}
53595692
53605693/// Calls a compiler-rt intrinsic by creating an undefined symbol,
......@@ -5364,29 +5697,29 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
53645697/// passed as the first parameter.
53655698/// May leave the return value on the stack.
53665699fn callIntrinsic(
5367 self: *Self,
5700 func: *CodeGen,
53685701 name: []const u8,
53695702 param_types: []const Type,
53705703 return_type: Type,
53715704 args: []const WValue,
53725705) InnerError!WValue {
53735706 assert(param_types.len == args.len);
5374 const symbol_index = self.bin_file.base.getGlobalSymbol(name) catch |err| {
5375 return self.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
5707 const symbol_index = func.bin_file.base.getGlobalSymbol(name) catch |err| {
5708 return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
53765709 };
53775710
53785711 // Always pass over C-ABI
5379 var func_type = try genFunctype(self.gpa, .C, param_types, return_type, self.target);
5380 defer func_type.deinit(self.gpa);
5381 const func_type_index = try self.bin_file.putOrGetFuncType(func_type);
5382 try self.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
5712 var func_type = try genFunctype(func.gpa, .C, param_types, return_type, func.target);
5713 defer func_type.deinit(func.gpa);
5714 const func_type_index = try func.bin_file.putOrGetFuncType(func_type);
5715 try func.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index);
53835716
5384 const want_sret_param = firstParamSRet(.C, return_type, self.target);
5717 const want_sret_param = firstParamSRet(.C, return_type, func.target);
53855718 // if we want return as first param, we allocate a pointer to stack,
53865719 // and emit it as our first argument
53875720 const sret = if (want_sret_param) blk: {
5388 const sret_local = try self.allocStack(return_type);
5389 try self.lowerToStack(sret_local);
5721 const sret_local = try func.allocStack(return_type);
5722 try func.lowerToStack(sret_local);
53905723 break :blk sret_local;
53915724 } else WValue{ .none = {} };
53925725
......@@ -5394,16 +5727,16 @@ fn callIntrinsic(
53945727 for (args) |arg, arg_i| {
53955728 assert(!(want_sret_param and arg == .stack));
53965729 assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime());
5397 try self.lowerArg(.C, param_types[arg_i], arg);
5730 try func.lowerArg(.C, param_types[arg_i], arg);
53985731 }
53995732
54005733 // Actually call our intrinsic
5401 try self.addLabel(.call, symbol_index);
5734 try func.addLabel(.call, symbol_index);
54025735
54035736 if (!return_type.hasRuntimeBitsIgnoreComptime()) {
54045737 return WValue.none;
54055738 } else if (return_type.isNoReturn()) {
5406 try self.addTag(.@"unreachable");
5739 try func.addTag(.@"unreachable");
54075740 return WValue.none;
54085741 } else if (want_sret_param) {
54095742 return sret;