authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-16 15:48:08+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-10-16 15:54:56+02:00
log0aa23fe8b7b8ae3b3b0a4716e1d92a8116b1377e
tree4281a32c74e1045c62df0fc95d75f0c08c09345d
parentff1cab037c1a770fba558b9d888a01a5b71190b8
signaturelock-open Commit is signed but in an unrecognized format.

wasm: rename 'self' to more explanatory name

'Self' isn't a very good name to describe what it does. This commit changes the type name into `CodeGen` and the parameter to `func` as we're generating code for a function. With this change, the backend's coding style is in line with the self-hosted Wasm-linker.

1 files changed, 2143 insertions(+), 2145 deletions(-)

src/arch/wasm/CodeGen.zig+2143-2145
......@@ -78,8 +78,8 @@ const WValue = union(enum) {
7878 /// bottom of the stack. For instances where `WValue` is not `stack_value`
7979 /// this will return 0, which allows us to simply call this function for all
8080 /// loads and stores without requiring checks everywhere.
81 fn offset(self: WValue) u32 {
82 switch (self) {
81 fn offset(value: WValue) u32 {
82 switch (value) {
8383 .stack_offset => |stack_offset| return stack_offset.value,
8484 else => return 0,
8585 }
......@@ -88,7 +88,7 @@ const WValue = union(enum) {
8888 /// Promotes a `WValue` to a local when given value is on top of the stack.
8989 /// When encountering a `local` or `stack_offset` this is essentially a no-op.
9090 /// All other tags are illegal.
91 fn toLocal(value: WValue, gen: *Self, ty: Type) InnerError!WValue {
91 fn toLocal(value: WValue, gen: *CodeGen, ty: Type) InnerError!WValue {
9292 switch (value) {
9393 .stack => {
9494 const new_local = try gen.allocLocal(ty);
......@@ -103,7 +103,7 @@ const WValue = union(enum) {
103103 /// Marks a local as no longer being referenced and essentially allows
104104 /// us to re-use it somewhere else within the function.
105105 /// The valtype of the local is deducted by using the index of the given `WValue`.
106 fn free(value: *WValue, gen: *Self) void {
106 fn free(value: *WValue, gen: *CodeGen) void {
107107 if (value.* != .local) return;
108108 const local_value = value.local.value;
109109 const reserved = gen.args.len + @boolToInt(gen.return_value != .none);
......@@ -584,7 +584,7 @@ pub const Result = union(enum) {
584584/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
585585pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);
586586
587const Self = @This();
587const CodeGen = @This();
588588
589589/// Reference to the function declaration the code
590590/// section belongs to
......@@ -686,37 +686,34 @@ const InnerError = error{
686686 Overflow,
687687};
688688
689pub fn deinit(self: *Self) void {
690 for (self.branches.items) |*branch| {
691 branch.deinit(self.gpa);
692 }
693 self.branches.deinit(self.gpa);
694 // self.values.deinit(self.gpa);
695 self.blocks.deinit(self.gpa);
696 self.locals.deinit(self.gpa);
697 self.mir_instructions.deinit(self.gpa);
698 self.mir_extra.deinit(self.gpa);
699 self.free_locals_i32.deinit(self.gpa);
700 self.free_locals_i64.deinit(self.gpa);
701 self.free_locals_f32.deinit(self.gpa);
702 self.free_locals_f64.deinit(self.gpa);
703 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;
704701}
705702
706703/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
707fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError {
704fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {
708705 const src = LazySrcLoc.nodeOffset(0);
709 const src_loc = src.toSrcLoc(self.decl);
710 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);
711708 return error.CodegenFail;
712709}
713710
714711/// Resolves the `WValue` for the given instruction `inst`
715712/// When the given instruction has a `Value`, it returns a constant instead
716fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
717 var branch_index = self.branches.items.len;
713fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue {
714 var branch_index = func.branches.items.len;
718715 while (branch_index > 0) : (branch_index -= 1) {
719 const branch = self.branches.items[branch_index - 1];
716 const branch = func.branches.items[branch_index - 1];
720717 if (branch.values.get(ref)) |value| {
721718 return value;
722719 }
......@@ -726,11 +723,11 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
726723 // means we must generate it from a constant.
727724 // We always store constants in the most outer branch as they must never
728725 // be removed. The most outer branch is always at index 0.
729 const gop = try self.branches.items[0].values.getOrPut(self.gpa, ref);
726 const gop = try func.branches.items[0].values.getOrPut(func.gpa, ref);
730727 assert(!gop.found_existing);
731728
732 const val = self.air.value(ref).?;
733 const ty = self.air.typeOf(ref);
729 const val = func.air.value(ref).?;
730 const ty = func.air.typeOf(ref);
734731 if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt() and !ty.isError()) {
735732 gop.value_ptr.* = WValue{ .none = {} };
736733 return gop.value_ptr.*;
......@@ -742,34 +739,34 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
742739 //
743740 // In the other cases, we will simply lower the constant to a value that fits
744741 // into a single local (such as a pointer, integer, bool, etc).
745 const result = if (isByRef(ty, self.target)) blk: {
746 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);
747744 break :blk WValue{ .memory = sym_index };
748 } else try self.lowerConstant(val, ty);
745 } else try func.lowerConstant(val, ty);
749746
750747 gop.value_ptr.* = result;
751748 return result;
752749}
753750
754fn finishAir(self: *Self, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) void {
751fn finishAir(func: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) void {
755752 assert(operands.len <= Liveness.bpi - 1);
756 var tomb_bits = self.liveness.getTombBits(inst);
753 var tomb_bits = func.liveness.getTombBits(inst);
757754 for (operands) |operand| {
758755 const dies = @truncate(u1, tomb_bits) != 0;
759756 tomb_bits >>= 1;
760757 if (!dies) continue;
761 processDeath(self, operand);
758 processDeath(func, operand);
762759 }
763760
764761 // results of `none` can never be referenced.
765762 if (result != .none) {
766763 assert(result != .stack); // it's illegal to store a stack value as we cannot track its position
767 const branch = self.currentBranch();
764 const branch = func.currentBranch();
768765 branch.values.putAssumeCapacityNoClobber(Air.indexToRef(inst), result);
769766 }
770767
771768 if (builtin.mode == .Debug) {
772 self.air_bookkeeping += 1;
769 func.air_bookkeeping += 1;
773770 }
774771}
775772
......@@ -781,12 +778,12 @@ const Branch = struct {
781778 }
782779};
783780
784inline fn currentBranch(self: *Self) *Branch {
785 return &self.branches.items[self.branches.items.len - 1];
781inline fn currentBranch(func: *CodeGen) *Branch {
782 return &func.branches.items[func.branches.items.len - 1];
786783}
787784
788785const BigTomb = struct {
789 gen: *Self,
786 gen: *CodeGen,
790787 inst: Air.Inst.Index,
791788 lbt: Liveness.BigTomb,
792789
......@@ -809,85 +806,85 @@ const BigTomb = struct {
809806 }
810807};
811808
812fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
813 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, operand_count + 1);
809fn iterateBigTomb(func: *CodeGen, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
810 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, operand_count + 1);
814811 return BigTomb{
815 .gen = self,
812 .gen = func,
816813 .inst = inst,
817 .lbt = self.liveness.iterateBigTomb(inst),
814 .lbt = func.liveness.iterateBigTomb(inst),
818815 };
819816}
820817
821fn processDeath(self: *Self, ref: Air.Inst.Ref) void {
818fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {
822819 const inst = Air.refToIndex(ref) orelse return;
823 if (self.air.instructions.items(.tag)[inst] == .constant) return;
820 if (func.air.instructions.items(.tag)[inst] == .constant) return;
824821 // Branches are currently only allowed to free locals allocated
825822 // within their own branch.
826823 // TODO: Upon branch consolidation free any locals if needed.
827 const value = self.currentBranch().values.getPtr(ref) orelse return;
824 const value = func.currentBranch().values.getPtr(ref) orelse return;
828825 if (value.* != .local) return;
829826 log.debug("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)});
830827 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
831828 if (value.local.references == 0) {
832 value.free(self);
829 value.free(func);
833830 }
834831}
835832
836833/// Appends a MIR instruction and returns its index within the list of instructions
837fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void {
838 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);
839836}
840837
841fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
842 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 = {} } });
843840}
844841
845fn addExtended(self: *Self, opcode: wasm.PrefixedOpcode) error{OutOfMemory}!void {
846 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 = {} } });
847844}
848845
849fn addLabel(self: *Self, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
850 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 } });
851848}
852849
853fn addImm32(self: *Self, imm: i32) error{OutOfMemory}!void {
854 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 } });
855852}
856853
857854/// Accepts an unsigned 64bit integer rather than a signed integer to
858855/// prevent us from having to bitcast multiple times as most values
859856/// within codegen are represented as unsigned rather than signed.
860fn addImm64(self: *Self, imm: u64) error{OutOfMemory}!void {
861 const extra_index = try self.addExtra(Mir.Imm64.fromU64(imm));
862 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 } });
863860}
864861
865fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void {
866 const extra_index = try self.addExtra(Mir.Float64.fromFloat64(float));
867 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 } });
868865}
869866
870867/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.
871fn addMemArg(self: *Self, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
872 const extra_index = try self.addExtra(mem_arg);
873 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 } });
874871}
875872
876873/// Appends entries to `mir_extra` based on the type of `extra`.
877874/// Returns the index into `mir_extra`
878fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
875fn addExtra(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
879876 const fields = std.meta.fields(@TypeOf(extra));
880 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
881 return self.addExtraAssumeCapacity(extra);
877 try func.mir_extra.ensureUnusedCapacity(func.gpa, fields.len);
878 return func.addExtraAssumeCapacity(extra);
882879}
883880
884881/// Appends entries to `mir_extra` based on the type of `extra`.
885882/// Returns the index into `mir_extra`
886fn addExtraAssumeCapacity(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
883fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32 {
887884 const fields = std.meta.fields(@TypeOf(extra));
888 const result = @intCast(u32, self.mir_extra.items.len);
885 const result = @intCast(u32, func.mir_extra.items.len);
889886 inline for (fields) |field| {
890 self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
887 func.mir_extra.appendAssumeCapacity(switch (field.field_type) {
891888 u32 => @field(extra, field.name),
892889 else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)),
893890 });
......@@ -932,32 +929,32 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
932929}
933930
934931/// Writes the bytecode depending on the given `WValue` in `val`
935fn emitWValue(self: *Self, value: WValue) InnerError!void {
932fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
936933 switch (value) {
937934 .none, .stack => {}, // no-op
938 .local => |idx| try self.addLabel(.local_get, idx.value),
939 .imm32 => |val| try self.addImm32(@bitCast(i32, val)),
940 .imm64 => |val| try self.addImm64(val),
941 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
942 .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),
943940 .memory => |ptr| {
944 const extra_index = try self.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
945 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 } });
946943 },
947944 .memory_offset => |mem_off| {
948 const extra_index = try self.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
949 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 } });
950947 },
951 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
952 .stack_offset => try self.addLabel(.local_get, self.bottom_stack_value.local.value), // 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
953950 }
954951}
955952
956953/// If given a local or stack-offset, increases the reference count by 1.
957954/// The old `WValue` found at instruction `ref` is then replaced by the
958955/// modified `WValue` and returned. When given a non-local or non-stack-offset,
959/// returns the given `operand` itself instead.
960fn reuseOperand(self: *Self, ref: Air.Inst.Ref, operand: WValue) WValue {
956/// returns the given `operand` itfunc instead.
957fn reuseOperand(func: *CodeGen, ref: Air.Inst.Ref, operand: WValue) WValue {
961958 if (operand != .local and operand != .stack_offset) return operand;
962959 var new_value = operand;
963960 switch (new_value) {
......@@ -965,17 +962,17 @@ fn reuseOperand(self: *Self, ref: Air.Inst.Ref, operand: WValue) WValue {
965962 .stack_offset => |*stack_offset| stack_offset.references += 1,
966963 else => unreachable,
967964 }
968 const old_value = self.getResolvedInst(ref);
965 const old_value = func.getResolvedInst(ref);
969966 old_value.* = new_value;
970967 return new_value;
971968}
972969
973970/// From a reference, returns its resolved `WValue`.
974971/// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet.
975fn getResolvedInst(self: *Self, ref: Air.Inst.Ref) *WValue {
976 var index = self.branches.items.len;
972fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue {
973 var index = func.branches.items.len;
977974 while (index > 0) : (index -= 1) {
978 const branch = self.branches.items[index - 1];
975 const branch = func.branches.items[index - 1];
979976 if (branch.values.getPtr(ref)) |value| {
980977 return value;
981978 }
......@@ -985,37 +982,37 @@ fn getResolvedInst(self: *Self, ref: Air.Inst.Ref) *WValue {
985982
986983/// Creates one locals for a given `Type`.
987984/// Returns a corresponding `Wvalue` with `local` as active tag
988fn allocLocal(self: *Self, ty: Type) InnerError!WValue {
989 const valtype = typeToValtype(ty, self.target);
985fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
986 const valtype = typeToValtype(ty, func.target);
990987 switch (valtype) {
991 .i32 => if (self.free_locals_i32.popOrNull()) |index| {
988 .i32 => if (func.free_locals_i32.popOrNull()) |index| {
992989 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
993990 return WValue{ .local = .{ .value = index, .references = 1 } };
994991 },
995 .i64 => if (self.free_locals_i64.popOrNull()) |index| {
992 .i64 => if (func.free_locals_i64.popOrNull()) |index| {
996993 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
997994 return WValue{ .local = .{ .value = index, .references = 1 } };
998995 },
999 .f32 => if (self.free_locals_f32.popOrNull()) |index| {
996 .f32 => if (func.free_locals_f32.popOrNull()) |index| {
1000997 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1001998 return WValue{ .local = .{ .value = index, .references = 1 } };
1002999 },
1003 .f64 => if (self.free_locals_f64.popOrNull()) |index| {
1000 .f64 => if (func.free_locals_f64.popOrNull()) |index| {
10041001 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
10051002 return WValue{ .local = .{ .value = index, .references = 1 } };
10061003 },
10071004 }
10081005 log.debug("new local of type {}\n", .{valtype});
10091006 // no local was free to be re-used, so allocate a new local instead
1010 return self.ensureAllocLocal(ty);
1007 return func.ensureAllocLocal(ty);
10111008}
10121009
10131010/// Ensures a new local will be created. This is useful when it's useful
10141011/// to use a zero-initialized local.
1015fn ensureAllocLocal(self: *Self, ty: Type) InnerError!WValue {
1016 try self.locals.append(self.gpa, genValtype(ty, self.target));
1017 const initial_index = self.local_index;
1018 self.local_index += 1;
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;
10191016 return WValue{ .local = .{ .value = initial_index, .references = 1 } };
10201017}
10211018
......@@ -1082,7 +1079,7 @@ pub fn generate(
10821079 debug_output: codegen.DebugInfoOutput,
10831080) codegen.GenerateSymbolError!codegen.FnResult {
10841081 _ = src_loc;
1085 var code_gen: Self = .{
1082 var code_gen: CodeGen = .{
10861083 .gpa = bin_file.allocator,
10871084 .air = air,
10881085 .liveness = liveness,
......@@ -1107,88 +1104,89 @@ pub fn generate(
11071104 return codegen.FnResult{ .appended = {} };
11081105}
11091106
1110fn genFunc(self: *Self) InnerError!void {
1111 const fn_info = self.decl.ty.fnInfo();
1112 var func_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target);
1113 defer func_type.deinit(self.gpa);
1114 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);
11151112
1116 var cc_result = try self.resolveCallingConventionValues(self.decl.ty);
1117 defer cc_result.deinit(self.gpa);
1113 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);
1114 defer cc_result.deinit(func.gpa);
11181115
1119 self.args = cc_result.args;
1120 self.return_value = cc_result.return_value;
1116 func.args = cc_result.args;
1117 func.return_value = cc_result.return_value;
11211118
1122 try self.addTag(.dbg_prologue_end);
1119 try func.addTag(.dbg_prologue_end);
11231120
1124 try self.branches.append(self.gpa, .{});
1121 try func.branches.append(func.gpa, .{});
11251122 // Generate MIR for function body
1126 try self.genBody(self.air.getMainBody());
1123 try func.genBody(func.air.getMainBody());
11271124
11281125 // clean up outer branch
1129 _ = self.branches.pop();
1126 var outer_branch = func.branches.pop();
1127 outer_branch.deinit(func.gpa);
11301128
11311129 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
11321130 // we emit an unreachable instruction to tell the stack validator that part will never be reached.
1133 if (func_type.returns.len != 0 and self.air.instructions.len > 0) {
1134 const inst = @intCast(u32, self.air.instructions.len - 1);
1135 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);
11361134 if (!last_inst_ty.hasRuntimeBitsIgnoreComptime() or last_inst_ty.isNoReturn()) {
1137 try self.addTag(.@"unreachable");
1135 try func.addTag(.@"unreachable");
11381136 }
11391137 }
11401138 // End of function body
1141 try self.addTag(.end);
1139 try func.addTag(.end);
11421140
1143 try self.addTag(.dbg_epilogue_begin);
1141 try func.addTag(.dbg_epilogue_begin);
11441142
11451143 // check if we have to initialize and allocate anything into the stack frame.
11461144 // If so, create enough stack space and insert the instructions at the front of the list.
1147 if (self.stack_size > 0) {
1148 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);
11491147 defer prologue.deinit();
11501148
11511149 // load stack pointer
11521150 try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });
11531151 // store stack pointer so we can restore it when we return from the function
1154 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.initial_stack_value.local.value } });
1152 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });
11551153 // get the total stack size
1156 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);
11571155 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, aligned_stack) } });
11581156 // substract it from the current stack pointer
11591157 try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } });
11601158 // Get negative stack aligment
1161 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 } });
11621160 // Bitwise-and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment
11631161 try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } });
11641162 // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets
1165 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.bottom_stack_value.local.value } });
1163 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
11661164 // Store the current stack pointer value into the global stack pointer so other function calls will
11671165 // start from this value instead and not overwrite the current stack.
11681166 try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } });
11691167
11701168 // reserve space and insert all prologue instructions at the front of the instruction list
11711169 // We insert them in reserve order as there is no insertSlice in multiArrayList.
1172 try self.mir_instructions.ensureUnusedCapacity(self.gpa, prologue.items.len);
1170 try func.mir_instructions.ensureUnusedCapacity(func.gpa, prologue.items.len);
11731171 for (prologue.items) |_, index| {
11741172 const inst = prologue.items[prologue.items.len - 1 - index];
1175 self.mir_instructions.insertAssumeCapacity(0, inst);
1173 func.mir_instructions.insertAssumeCapacity(0, inst);
11761174 }
11771175 }
11781176
11791177 var mir: Mir = .{
1180 .instructions = self.mir_instructions.toOwnedSlice(),
1181 .extra = self.mir_extra.toOwnedSlice(self.gpa),
1178 .instructions = func.mir_instructions.toOwnedSlice(),
1179 .extra = func.mir_extra.toOwnedSlice(func.gpa),
11821180 };
1183 defer mir.deinit(self.gpa);
1181 defer mir.deinit(func.gpa);
11841182
11851183 var emit: Emit = .{
11861184 .mir = mir,
1187 .bin_file = &self.bin_file.base,
1188 .code = self.code,
1189 .locals = self.locals.items,
1190 .decl = self.decl,
1191 .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,
11921190 .prev_di_line = 0,
11931191 .prev_di_column = 0,
11941192 .prev_di_offset = 0,
......@@ -1196,7 +1194,7 @@ fn genFunc(self: *Self) InnerError!void {
11961194
11971195 emit.emitMir() catch |err| switch (err) {
11981196 error.EmitFail => {
1199 self.err_msg = emit.error_msg.?;
1197 func.err_msg = emit.error_msg.?;
12001198 return error.CodegenFail;
12011199 },
12021200 else => |e| return e,
......@@ -1207,16 +1205,16 @@ const CallWValues = struct {
12071205 args: []WValue,
12081206 return_value: WValue,
12091207
1210 fn deinit(self: *CallWValues, gpa: Allocator) void {
1211 gpa.free(self.args);
1212 self.* = undefined;
1208 fn deinit(values: *CallWValues, gpa: Allocator) void {
1209 gpa.free(values.args);
1210 values.* = undefined;
12131211 }
12141212};
12151213
1216fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValues {
1214fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues {
12171215 const cc = fn_ty.fnCallingConvention();
1218 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1219 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);
12201218 fn_ty.fnParamTypes(param_types);
12211219 var result: CallWValues = .{
12221220 .args = &.{},
......@@ -1224,17 +1222,17 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
12241222 };
12251223 if (cc == .Naked) return result;
12261224
1227 var args = std.ArrayList(WValue).init(self.gpa);
1225 var args = std.ArrayList(WValue).init(func.gpa);
12281226 defer args.deinit();
12291227
12301228 // Check if we store the result as a pointer to the stack rather than
12311229 // by value
12321230 const fn_info = fn_ty.fnInfo();
1233 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1231 if (firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
12341232 // the sret arg will be passed as first argument, therefore we
12351233 // set the `return_value` before allocating locals for regular args.
1236 result.return_value = .{ .local = .{ .value = self.local_index, .references = 1 } };
1237 self.local_index += 1;
1234 result.return_value = .{ .local = .{ .value = func.local_index, .references = 1 } };
1235 func.local_index += 1;
12381236 }
12391237
12401238 switch (cc) {
......@@ -1244,21 +1242,21 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
12441242 continue;
12451243 }
12461244
1247 try args.append(.{ .local = .{ .value = self.local_index, .references = 1 } });
1248 self.local_index += 1;
1245 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1246 func.local_index += 1;
12491247 }
12501248 },
12511249 .C => {
12521250 for (param_types) |ty| {
1253 const ty_classes = abi.classifyType(ty, self.target);
1251 const ty_classes = abi.classifyType(ty, func.target);
12541252 for (ty_classes) |class| {
12551253 if (class == .none) continue;
1256 try args.append(.{ .local = .{ .value = self.local_index, .references = 1 } });
1257 self.local_index += 1;
1254 try args.append(.{ .local = .{ .value = func.local_index, .references = 1 } });
1255 func.local_index += 1;
12581256 }
12591257 }
12601258 },
1261 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)}),
12621260 }
12631261 result.args = args.toOwnedSlice();
12641262 return result;
......@@ -1279,14 +1277,14 @@ fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, target:
12791277
12801278/// For a given `Type`, add debug information to .debug_info at the current position.
12811279/// The actual bytes will be written to the position after relocation.
1282fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
1283 switch (self.debug_output) {
1280fn addDbgInfoTypeReloc(func: *CodeGen, ty: Type) !void {
1281 switch (func.debug_output) {
12841282 .dwarf => |dwarf| {
12851283 assert(ty.hasRuntimeBitsIgnoreComptime());
12861284 const dbg_info = &dwarf.dbg_info;
12871285 const index = dbg_info.items.len;
12881286 try dbg_info.resize(index + 4);
1289 const atom = &self.decl.link.wasm.dbg_info_atom;
1287 const atom = &func.decl.link.wasm.dbg_info_atom;
12901288 try dwarf.addTypeRelocGlobal(atom, ty, @intCast(u32, index));
12911289 },
12921290 .plan9 => unreachable,
......@@ -1296,96 +1294,96 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
12961294
12971295/// Lowers a Zig type and its value based on a given calling convention to ensure
12981296/// it matches the ABI.
1299fn 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 {
13001298 if (cc != .C) {
1301 return self.lowerToStack(value);
1299 return func.lowerToStack(value);
13021300 }
13031301
1304 const ty_classes = abi.classifyType(ty, self.target);
1302 const ty_classes = abi.classifyType(ty, func.target);
13051303 assert(ty_classes[0] != .none);
13061304 switch (ty.zigTypeTag()) {
13071305 .Struct, .Union => {
13081306 if (ty_classes[0] == .indirect) {
1309 return self.lowerToStack(value);
1307 return func.lowerToStack(value);
13101308 }
13111309 assert(ty_classes[0] == .direct);
1312 const scalar_type = abi.scalarType(ty, self.target);
1313 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);
13141312 const opcode = buildOpcode(.{
13151313 .op = .load,
13161314 .width = @intCast(u8, abi_size),
13171315 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
1318 .valtype1 = typeToValtype(scalar_type, self.target),
1316 .valtype1 = typeToValtype(scalar_type, func.target),
13191317 });
1320 try self.emitWValue(value);
1321 try self.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
1318 try func.emitWValue(value);
1319 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
13221320 .offset = value.offset(),
1323 .alignment = scalar_type.abiAlignment(self.target),
1321 .alignment = scalar_type.abiAlignment(func.target),
13241322 });
13251323 },
13261324 .Int, .Float => {
13271325 if (ty_classes[1] == .none) {
1328 return self.lowerToStack(value);
1326 return func.lowerToStack(value);
13291327 }
13301328 assert(ty_classes[0] == .direct and ty_classes[1] == .direct);
1331 assert(ty.abiSize(self.target) == 16);
1329 assert(ty.abiSize(func.target) == 16);
13321330 // in this case we have an integer or float that must be lowered as 2 i64's.
1333 try self.emitWValue(value);
1334 try self.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 });
1335 try self.emitWValue(value);
1336 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 });
13371335 },
1338 else => return self.lowerToStack(value),
1336 else => return func.lowerToStack(value),
13391337 }
13401338}
13411339
13421340/// Lowers a `WValue` to the stack. This means when the `value` results in
13431341/// `.stack_offset` we calculate the pointer of this offset and use that.
13441342/// The value is left on the stack, and not stored in any temporary.
1345fn lowerToStack(self: *Self, value: WValue) !void {
1343fn lowerToStack(func: *CodeGen, value: WValue) !void {
13461344 switch (value) {
13471345 .stack_offset => |offset| {
1348 try self.emitWValue(value);
1346 try func.emitWValue(value);
13491347 if (offset.value > 0) {
1350 switch (self.arch()) {
1348 switch (func.arch()) {
13511349 .wasm32 => {
1352 try self.addImm32(@bitCast(i32, offset.value));
1353 try self.addTag(.i32_add);
1350 try func.addImm32(@bitCast(i32, offset.value));
1351 try func.addTag(.i32_add);
13541352 },
13551353 .wasm64 => {
1356 try self.addImm64(offset.value);
1357 try self.addTag(.i64_add);
1354 try func.addImm64(offset.value);
1355 try func.addTag(.i64_add);
13581356 },
13591357 else => unreachable,
13601358 }
13611359 }
13621360 },
1363 else => try self.emitWValue(value),
1361 else => try func.emitWValue(value),
13641362 }
13651363}
13661364
13671365/// Creates a local for the initial stack value
13681366/// Asserts `initial_stack_value` is `.none`
1369fn initializeStack(self: *Self) !void {
1370 assert(self.initial_stack_value == .none);
1367fn initializeStack(func: *CodeGen) !void {
1368 assert(func.initial_stack_value == .none);
13711369 // Reserve a local to store the current stack pointer
13721370 // We can later use this local to set the stack pointer back to the value
13731371 // we have stored here.
1374 self.initial_stack_value = try self.ensureAllocLocal(Type.usize);
1372 func.initial_stack_value = try func.ensureAllocLocal(Type.usize);
13751373 // Also reserve a local to store the bottom stack value
1376 self.bottom_stack_value = try self.ensureAllocLocal(Type.usize);
1374 func.bottom_stack_value = try func.ensureAllocLocal(Type.usize);
13771375}
13781376
13791377/// Reads the stack pointer from `Context.initial_stack_value` and writes it
13801378/// to the global stack pointer variable
1381fn restoreStackPointer(self: *Self) !void {
1379fn restoreStackPointer(func: *CodeGen) !void {
13821380 // only restore the pointer if it was initialized
1383 if (self.initial_stack_value == .none) return;
1381 if (func.initial_stack_value == .none) return;
13841382 // Get the original stack pointer's value
1385 try self.emitWValue(self.initial_stack_value);
1383 try func.emitWValue(func.initial_stack_value);
13861384
13871385 // save its value in the global stack pointer
1388 try self.addLabel(.global_set, 0);
1386 try func.addLabel(.global_set, 0);
13891387}
13901388
13911389/// From a given type, will create space on the virtual stack to store the value of such type.
......@@ -1394,26 +1392,26 @@ fn restoreStackPointer(self: *Self) !void {
13941392/// moveStack unless a local was already created to store the pointer.
13951393///
13961394/// Asserts Type has codegenbits
1397fn allocStack(self: *Self, ty: Type) !WValue {
1395fn allocStack(func: *CodeGen, ty: Type) !WValue {
13981396 assert(ty.hasRuntimeBitsIgnoreComptime());
1399 if (self.initial_stack_value == .none) {
1400 try self.initializeStack();
1397 if (func.initial_stack_value == .none) {
1398 try func.initializeStack();
14011399 }
14021400
1403 const abi_size = std.math.cast(u32, ty.abiSize(self.target)) orelse {
1404 const module = self.bin_file.base.options.module.?;
1405 return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1406 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),
14071405 });
14081406 };
1409 const abi_align = ty.abiAlignment(self.target);
1407 const abi_align = ty.abiAlignment(func.target);
14101408
1411 if (abi_align > self.stack_alignment) {
1412 self.stack_alignment = abi_align;
1409 if (abi_align > func.stack_alignment) {
1410 func.stack_alignment = abi_align;
14131411 }
14141412
1415 const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align);
1416 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;
14171415
14181416 return WValue{ .stack_offset = .{ .value = offset, .references = 1 } };
14191417}
......@@ -1422,31 +1420,31 @@ fn allocStack(self: *Self, ty: Type) !WValue {
14221420/// the value of its type will live.
14231421/// This is different from allocStack where this will use the pointer's alignment
14241422/// if it is set, to ensure the stack alignment will be set correctly.
1425fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {
1426 const ptr_ty = self.air.typeOfIndex(inst);
1423fn allocStackPtr(func: *CodeGen, inst: Air.Inst.Index) !WValue {
1424 const ptr_ty = func.air.typeOfIndex(inst);
14271425 const pointee_ty = ptr_ty.childType();
14281426
1429 if (self.initial_stack_value == .none) {
1430 try self.initializeStack();
1427 if (func.initial_stack_value == .none) {
1428 try func.initializeStack();
14311429 }
14321430
14331431 if (!pointee_ty.hasRuntimeBitsIgnoreComptime()) {
1434 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.
14351433 }
14361434
1437 const abi_alignment = ptr_ty.ptrAlignment(self.target);
1438 const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) orelse {
1439 const module = self.bin_file.base.options.module.?;
1440 return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
1441 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),
14421440 });
14431441 };
1444 if (abi_alignment > self.stack_alignment) {
1445 self.stack_alignment = abi_alignment;
1442 if (abi_alignment > func.stack_alignment) {
1443 func.stack_alignment = abi_alignment;
14461444 }
14471445
1448 const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment);
1449 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;
14501448
14511449 return WValue{ .stack_offset = .{ .value = offset, .references = 1 } };
14521450}
......@@ -1460,14 +1458,14 @@ fn toWasmBits(bits: u16) ?u16 {
14601458
14611459/// Performs a copy of bytes for a given type. Copying all bytes
14621460/// from rhs to lhs.
1463fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
1461fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
14641462 // When bulk_memory is enabled, we lower it to wasm's memcpy instruction.
14651463 // If not, we lower it ourselves manually
1466 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {
1467 try self.lowerToStack(dst);
1468 try self.lowerToStack(src);
1469 try self.emitWValue(len);
1470 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);
14711469 return;
14721470 }
14731471
......@@ -1485,17 +1483,17 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
14851483 const rhs_base = src.offset();
14861484 while (offset < length) : (offset += 1) {
14871485 // get dst's address to store the result
1488 try self.emitWValue(dst);
1486 try func.emitWValue(dst);
14891487 // load byte from src's address
1490 try self.emitWValue(src);
1491 switch (self.arch()) {
1488 try func.emitWValue(src);
1489 switch (func.arch()) {
14921490 .wasm32 => {
1493 try self.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1494 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 });
14951493 },
14961494 .wasm64 => {
1497 try self.addMemArg(.i64_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 });
1498 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 });
14991497 },
15001498 else => unreachable,
15011499 }
......@@ -1504,50 +1502,50 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
15041502 else => {
15051503 // TODO: We should probably lower this to a call to compiler_rt
15061504 // But for now, we implement it manually
1507 var offset = try self.ensureAllocLocal(Type.usize); // local for counter
1508 defer offset.free(self);
1505 var offset = try func.ensureAllocLocal(Type.usize); // local for counter
1506 defer offset.free(func);
15091507
15101508 // outer block to jump to when loop is done
1511 try self.startBlock(.block, wasm.block_empty);
1512 try self.startBlock(.loop, wasm.block_empty);
1509 try func.startBlock(.block, wasm.block_empty);
1510 try func.startBlock(.loop, wasm.block_empty);
15131511
15141512 // loop condition (offset == length -> break)
15151513 {
1516 try self.emitWValue(offset);
1517 try self.emitWValue(len);
1518 switch (self.arch()) {
1519 .wasm32 => try self.addTag(.i32_eq),
1520 .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),
15211519 else => unreachable,
15221520 }
1523 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)
15241522 }
15251523
15261524 // get dst ptr
15271525 {
1528 try self.emitWValue(dst);
1529 try self.emitWValue(offset);
1530 switch (self.arch()) {
1531 .wasm32 => try self.addTag(.i32_add),
1532 .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),
15331531 else => unreachable,
15341532 }
15351533 }
15361534
15371535 // get src value and also store in dst
15381536 {
1539 try self.emitWValue(src);
1540 try self.emitWValue(offset);
1541 switch (self.arch()) {
1537 try func.emitWValue(src);
1538 try func.emitWValue(offset);
1539 switch (func.arch()) {
15421540 .wasm32 => {
1543 try self.addTag(.i32_add);
1544 try self.addMemArg(.i32_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1545 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 });
15461544 },
15471545 .wasm64 => {
1548 try self.addTag(.i64_add);
1549 try self.addMemArg(.i64_load8_u, .{ .offset = src.offset(), .alignment = 1 });
1550 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 });
15511549 },
15521550 else => unreachable,
15531551 }
......@@ -1555,33 +1553,33 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void {
15551553
15561554 // increment loop counter
15571555 {
1558 try self.emitWValue(offset);
1559 switch (self.arch()) {
1556 try func.emitWValue(offset);
1557 switch (func.arch()) {
15601558 .wasm32 => {
1561 try self.addImm32(1);
1562 try self.addTag(.i32_add);
1559 try func.addImm32(1);
1560 try func.addTag(.i32_add);
15631561 },
15641562 .wasm64 => {
1565 try self.addImm64(1);
1566 try self.addTag(.i64_add);
1563 try func.addImm64(1);
1564 try func.addTag(.i64_add);
15671565 },
15681566 else => unreachable,
15691567 }
1570 try self.addLabel(.local_set, offset.local.value);
1571 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
15721570 }
1573 try self.endBlock(); // close off loop block
1574 try self.endBlock(); // close off outer block
1571 try func.endBlock(); // close off loop block
1572 try func.endBlock(); // close off outer block
15751573 },
15761574 }
15771575}
15781576
1579fn ptrSize(self: *const Self) u16 {
1580 return @divExact(self.target.cpu.arch.ptrBitWidth(), 8);
1577fn ptrSize(func: *const CodeGen) u16 {
1578 return @divExact(func.target.cpu.arch.ptrBitWidth(), 8);
15811579}
15821580
1583fn arch(self: *const Self) std.Target.Cpu.Arch {
1584 return self.target.cpu.arch;
1581fn arch(func: *const CodeGen) std.Target.Cpu.Arch {
1582 return func.target.cpu.arch;
15851583}
15861584
15871585/// For a given `Type`, will return true when the type will be passed
......@@ -1639,191 +1637,191 @@ fn isByRef(ty: Type, target: std.Target) bool {
16391637/// This can be used to get a pointer to a struct field, error payload, etc.
16401638/// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new
16411639/// local value to store the pointer. This allows for local re-use and improves binary size.
1642fn 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 {
16431641 // do not perform arithmetic when offset is 0.
16441642 if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value;
16451643 const result_ptr: WValue = switch (action) {
1646 .new => try self.ensureAllocLocal(Type.usize),
1644 .new => try func.ensureAllocLocal(Type.usize),
16471645 .modify => ptr_value,
16481646 };
1649 try self.emitWValue(ptr_value);
1647 try func.emitWValue(ptr_value);
16501648 if (offset + ptr_value.offset() > 0) {
1651 switch (self.arch()) {
1649 switch (func.arch()) {
16521650 .wasm32 => {
1653 try self.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset())));
1654 try self.addTag(.i32_add);
1651 try func.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset())));
1652 try func.addTag(.i32_add);
16551653 },
16561654 .wasm64 => {
1657 try self.addImm64(offset + ptr_value.offset());
1658 try self.addTag(.i64_add);
1655 try func.addImm64(offset + ptr_value.offset());
1656 try func.addTag(.i64_add);
16591657 },
16601658 else => unreachable,
16611659 }
16621660 }
1663 try self.addLabel(.local_set, result_ptr.local.value);
1661 try func.addLabel(.local_set, result_ptr.local.value);
16641662 return result_ptr;
16651663}
16661664
1667fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void {
1668 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);
16691667 return switch (air_tags[inst]) {
16701668 .constant => unreachable,
16711669 .const_ty => unreachable,
16721670
1673 .add => self.airBinOp(inst, .add),
1674 .add_sat => self.airSatBinOp(inst, .add),
1675 .addwrap => self.airWrapBinOp(inst, .add),
1676 .sub => self.airBinOp(inst, .sub),
1677 .sub_sat => self.airSatBinOp(inst, .sub),
1678 .subwrap => self.airWrapBinOp(inst, .sub),
1679 .mul => self.airBinOp(inst, .mul),
1680 .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),
16811679 .div_float,
16821680 .div_exact,
16831681 .div_trunc,
1684 => self.airDiv(inst),
1685 .div_floor => self.airDivFloor(inst),
1686 .ceil => self.airCeilFloorTrunc(inst, .ceil),
1687 .floor => self.airCeilFloorTrunc(inst, .floor),
1688 .trunc_float => self.airCeilFloorTrunc(inst, .trunc),
1689 .bit_and => self.airBinOp(inst, .@"and"),
1690 .bit_or => self.airBinOp(inst, .@"or"),
1691 .bool_and => self.airBinOp(inst, .@"and"),
1692 .bool_or => self.airBinOp(inst, .@"or"),
1693 .rem => self.airBinOp(inst, .rem),
1694 .shl => self.airWrapBinOp(inst, .shl),
1695 .shl_exact => self.airBinOp(inst, .shl),
1696 .shl_sat => self.airShlSat(inst),
1697 .shr, .shr_exact => self.airBinOp(inst, .shr),
1698 .xor => self.airBinOp(inst, .xor),
1699 .max => self.airMaxMin(inst, .max),
1700 .min => self.airMaxMin(inst, .min),
1701 .mul_add => self.airMulAdd(inst),
1702
1703 .add_with_overflow => self.airAddSubWithOverflow(inst, .add),
1704 .sub_with_overflow => self.airAddSubWithOverflow(inst, .sub),
1705 .shl_with_overflow => self.airShlWithOverflow(inst),
1706 .mul_with_overflow => self.airMulWithOverflow(inst),
1707
1708 .clz => self.airClz(inst),
1709 .ctz => self.airCtz(inst),
1710
1711 .cmp_eq => self.airCmp(inst, .eq),
1712 .cmp_gte => self.airCmp(inst, .gte),
1713 .cmp_gt => self.airCmp(inst, .gt),
1714 .cmp_lte => self.airCmp(inst, .lte),
1715 .cmp_lt => self.airCmp(inst, .lt),
1716 .cmp_neq => self.airCmp(inst, .neq),
1717
1718 .cmp_vector => self.airCmpVector(inst),
1719 .cmp_lt_errors_len => self.airCmpLtErrorsLen(inst),
1720
1721 .array_elem_val => self.airArrayElemVal(inst),
1722 .array_to_slice => self.airArrayToSlice(inst),
1723 .alloc => self.airAlloc(inst),
1724 .arg => self.airArg(inst),
1725 .bitcast => self.airBitcast(inst),
1726 .block => self.airBlock(inst),
1727 .breakpoint => self.airBreakpoint(inst),
1728 .br => self.airBr(inst),
1729 .bool_to_int => self.airBoolToInt(inst),
1730 .cond_br => self.airCondBr(inst),
1731 .intcast => self.airIntcast(inst),
1732 .fptrunc => self.airFptrunc(inst),
1733 .fpext => self.airFpext(inst),
1734 .float_to_int => self.airFloatToInt(inst),
1735 .int_to_float => self.airIntToFloat(inst),
1736 .get_union_tag => self.airGetUnionTag(inst),
1737
1738 .@"try" => self.airTry(inst),
1739 .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),
17401738
17411739 // TODO
17421740 .dbg_inline_begin,
17431741 .dbg_inline_end,
17441742 .dbg_block_begin,
17451743 .dbg_block_end,
1746 => self.finishAir(inst, .none, &.{}),
1747
1748 .dbg_var_ptr => self.airDbgVar(inst, true),
1749 .dbg_var_val => self.airDbgVar(inst, false),
1750
1751 .dbg_stmt => self.airDbgStmt(inst),
1752
1753 .call => self.airCall(inst, .auto),
1754 .call_always_tail => self.airCall(inst, .always_tail),
1755 .call_never_tail => self.airCall(inst, .never_tail),
1756 .call_never_inline => self.airCall(inst, .never_inline),
1757
1758 .is_err => self.airIsErr(inst, .i32_ne),
1759 .is_non_err => self.airIsErr(inst, .i32_eq),
1760
1761 .is_null => self.airIsNull(inst, .i32_eq, .value),
1762 .is_non_null => self.airIsNull(inst, .i32_ne, .value),
1763 .is_null_ptr => self.airIsNull(inst, .i32_eq, .ptr),
1764 .is_non_null_ptr => self.airIsNull(inst, .i32_ne, .ptr),
1765
1766 .load => self.airLoad(inst),
1767 .loop => self.airLoop(inst),
1768 .memset => self.airMemset(inst),
1769 .not => self.airNot(inst),
1770 .optional_payload => self.airOptionalPayload(inst),
1771 .optional_payload_ptr => self.airOptionalPayloadPtr(inst),
1772 .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst),
1773 .ptr_add => self.airPtrBinOp(inst, .add),
1774 .ptr_sub => self.airPtrBinOp(inst, .sub),
1775 .ptr_elem_ptr => self.airPtrElemPtr(inst),
1776 .ptr_elem_val => self.airPtrElemVal(inst),
1777 .ptrtoint => self.airPtrToInt(inst),
1778 .ret => self.airRet(inst),
1779 .ret_ptr => self.airRetPtr(inst),
1780 .ret_load => self.airRetLoad(inst),
1781 .splat => self.airSplat(inst),
1782 .select => self.airSelect(inst),
1783 .shuffle => self.airShuffle(inst),
1784 .reduce => self.airReduce(inst),
1785 .aggregate_init => self.airAggregateInit(inst),
1786 .union_init => self.airUnionInit(inst),
1787 .prefetch => self.airPrefetch(inst),
1788 .popcount => self.airPopcount(inst),
1789 .byte_swap => self.airByteSwap(inst),
1790
1791 .slice => self.airSlice(inst),
1792 .slice_len => self.airSliceLen(inst),
1793 .slice_elem_val => self.airSliceElemVal(inst),
1794 .slice_elem_ptr => self.airSliceElemPtr(inst),
1795 .slice_ptr => self.airSlicePtr(inst),
1796 .ptr_slice_len_ptr => self.airPtrSliceFieldPtr(inst, self.ptrSize()),
1797 .ptr_slice_ptr_ptr => self.airPtrSliceFieldPtr(inst, 0),
1798 .store => self.airStore(inst),
1799
1800 .set_union_tag => self.airSetUnionTag(inst),
1801 .struct_field_ptr => self.airStructFieldPtr(inst),
1802 .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0),
1803 .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1),
1804 .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2),
1805 .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3),
1806 .struct_field_val => self.airStructFieldVal(inst),
1807 .field_parent_ptr => self.airFieldParentPtr(inst),
1808
1809 .switch_br => self.airSwitchBr(inst),
1810 .trunc => self.airTrunc(inst),
1811 .unreach => self.airUnreachable(inst),
1812
1813 .wrap_optional => self.airWrapOptional(inst),
1814 .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst, false),
1815 .unwrap_errunion_payload_ptr => self.airUnwrapErrUnionPayload(inst, true),
1816 .unwrap_errunion_err => self.airUnwrapErrUnionError(inst, false),
1817 .unwrap_errunion_err_ptr => self.airUnwrapErrUnionError(inst, true),
1818 .wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
1819 .wrap_errunion_err => self.airWrapErrUnionErr(inst),
1820 .errunion_payload_ptr_set => self.airErrUnionPayloadPtrSet(inst),
1821 .error_name => self.airErrorName(inst),
1822
1823 .wasm_memory_size => self.airWasmMemorySize(inst),
1824 .wasm_memory_grow => self.airWasmMemoryGrow(inst),
1825
1826 .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),
18271825
18281826 .mul_sat,
18291827 .mod,
......@@ -1862,7 +1860,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void {
18621860 .is_named_enum_value,
18631861 .error_set_has_value,
18641862 .addrspace_cast,
1865 => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
1863 => |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
18661864
18671865 .add_optimized,
18681866 .addwrap_optimized,
......@@ -1886,116 +1884,116 @@ fn genInst(self: *Self, inst: Air.Inst.Index) InnerError!void {
18861884 .cmp_vector_optimized,
18871885 .reduce_optimized,
18881886 .float_to_int_optimized,
1889 => return self.fail("TODO implement optimized float mode", .{}),
1887 => return func.fail("TODO implement optimized float mode", .{}),
18901888 };
18911889}
18921890
1893fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1891fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
18941892 for (body) |inst| {
1895 const old_bookkeeping_value = self.air_bookkeeping;
1893 const old_bookkeeping_value = func.air_bookkeeping;
18961894 // TODO: Determine why we need to pre-allocate an extra 4 possible values here.
1897 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, Liveness.bpi + 4);
1898 try self.genInst(inst);
1895 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, Liveness.bpi + 4);
1896 try func.genInst(inst);
18991897
1900 if (builtin.mode == .Debug and self.air_bookkeeping < old_bookkeeping_value + 1) {
1898 if (builtin.mode == .Debug and func.air_bookkeeping < old_bookkeeping_value + 1) {
19011899 std.debug.panic("Missing call to `finishAir` in AIR instruction %{d} ('{}')", .{
19021900 inst,
1903 self.air.instructions.items(.tag)[inst],
1901 func.air.instructions.items(.tag)[inst],
19041902 });
19051903 }
19061904 }
19071905}
19081906
1909fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!void {
1910 const un_op = self.air.instructions.items(.data)[inst].un_op;
1911 const operand = try self.resolveInst(un_op);
1912 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();
19131911 const ret_ty = fn_info.return_type;
19141912
19151913 // result must be stored in the stack and we return a pointer
19161914 // to the stack instead
1917 if (self.return_value != .none) {
1918 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);
19191917 } else if (fn_info.cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime()) {
19201918 switch (ret_ty.zigTypeTag()) {
19211919 // Aggregate types can be lowered as a singular value
19221920 .Struct, .Union => {
1923 const scalar_type = abi.scalarType(ret_ty, self.target);
1924 try self.emitWValue(operand);
1921 const scalar_type = abi.scalarType(ret_ty, func.target);
1922 try func.emitWValue(operand);
19251923 const opcode = buildOpcode(.{
19261924 .op = .load,
1927 .width = @intCast(u8, scalar_type.abiSize(self.target) * 8),
1925 .width = @intCast(u8, scalar_type.abiSize(func.target) * 8),
19281926 .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned,
1929 .valtype1 = typeToValtype(scalar_type, self.target),
1927 .valtype1 = typeToValtype(scalar_type, func.target),
19301928 });
1931 try self.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
1929 try func.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{
19321930 .offset = operand.offset(),
1933 .alignment = scalar_type.abiAlignment(self.target),
1931 .alignment = scalar_type.abiAlignment(func.target),
19341932 });
19351933 },
1936 else => try self.emitWValue(operand),
1934 else => try func.emitWValue(operand),
19371935 }
19381936 } else {
19391937 if (!ret_ty.hasRuntimeBitsIgnoreComptime() and ret_ty.isError()) {
1940 try self.addImm32(0);
1938 try func.addImm32(0);
19411939 } else {
1942 try self.emitWValue(operand);
1940 try func.emitWValue(operand);
19431941 }
19441942 }
1945 try self.restoreStackPointer();
1946 try self.addTag(.@"return");
1943 try func.restoreStackPointer();
1944 try func.addTag(.@"return");
19471945
1948 self.finishAir(inst, .none, &.{un_op});
1946 func.finishAir(inst, .none, &.{un_op});
19491947}
19501948
1951fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
1952 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();
19531951
19541952 var result = result: {
19551953 if (!child_type.isFnOrHasRuntimeBitsIgnoreComptime()) {
1956 break :result try self.allocStack(Type.usize); // create pointer to void
1954 break :result try func.allocStack(Type.usize); // create pointer to void
19571955 }
19581956
1959 const fn_info = self.decl.ty.fnInfo();
1960 if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) {
1961 break :result self.return_value;
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;
19621960 }
19631961
1964 break :result try self.allocStackPtr(inst);
1962 break :result try func.allocStackPtr(inst);
19651963 };
19661964
1967 self.finishAir(inst, result, &.{});
1965 func.finishAir(inst, result, &.{});
19681966}
19691967
1970fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
1971 const un_op = self.air.instructions.items(.data)[inst].un_op;
1972 const operand = try self.resolveInst(un_op);
1973 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();
19741972 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {
19751973 if (ret_ty.isError()) {
1976 try self.addImm32(0);
1974 try func.addImm32(0);
19771975 } else {
1978 return self.finishAir(inst, .none, &.{});
1976 return func.finishAir(inst, .none, &.{});
19791977 }
19801978 }
19811979
1982 const fn_info = self.decl.ty.fnInfo();
1983 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)) {
19841982 // leave on the stack
1985 _ = try self.load(operand, ret_ty, 0);
1983 _ = try func.load(operand, ret_ty, 0);
19861984 }
19871985
1988 try self.restoreStackPointer();
1989 try self.addTag(.@"return");
1990 return self.finishAir(inst, .none, &.{});
1986 try func.restoreStackPointer();
1987 try func.addTag(.@"return");
1988 return func.finishAir(inst, .none, &.{});
19911989}
19921990
1993fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) InnerError!void {
1994 if (modifier == .always_tail) return self.fail("TODO implement tail calls for wasm", .{});
1995 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1996 const extra = self.air.extraData(Air.Call, pl_op.payload);
1997 const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
1998 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);
19991997
20001998 const fn_ty = switch (ty.zigTypeTag()) {
20011999 .Fn => ty,
......@@ -2004,21 +2002,21 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
20042002 };
20052003 const ret_ty = fn_ty.fnReturnType();
20062004 const fn_info = fn_ty.fnInfo();
2007 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);
20082006
20092007 const callee: ?*Decl = blk: {
2010 const func_val = self.air.value(pl_op.operand) orelse break :blk null;
2011 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.?;
20122010
2013 if (func_val.castTag(.function)) |func| {
2014 break :blk module.declPtr(func.data.owner_decl);
2011 if (func_val.castTag(.function)) |function| {
2012 break :blk module.declPtr(function.data.owner_decl);
20152013 } else if (func_val.castTag(.extern_fn)) |extern_fn| {
20162014 const ext_decl = module.declPtr(extern_fn.data.owner_decl);
20172015 const ext_info = ext_decl.ty.fnInfo();
2018 var func_type = try genFunctype(self.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, self.target);
2019 defer func_type.deinit(self.gpa);
2020 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
2021 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(
20222020 mem.sliceTo(ext_decl.name, 0),
20232021 ext_decl.link.wasm.sym_index,
20242022 ext_decl.getExternFn().?.lib_name,
......@@ -2028,151 +2026,151 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
20282026 } else if (func_val.castTag(.decl_ref)) |decl_ref| {
20292027 break :blk module.declPtr(decl_ref.data);
20302028 }
2031 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()});
20322030 };
20332031
20342032 const sret = if (first_param_sret) blk: {
2035 const sret_local = try self.allocStack(ret_ty);
2036 try self.lowerToStack(sret_local);
2033 const sret_local = try func.allocStack(ret_ty);
2034 try func.lowerToStack(sret_local);
20372035 break :blk sret_local;
20382036 } else WValue{ .none = {} };
20392037
20402038 for (args) |arg| {
2041 const arg_val = try self.resolveInst(arg);
2039 const arg_val = try func.resolveInst(arg);
20422040
2043 const arg_ty = self.air.typeOf(arg);
2041 const arg_ty = func.air.typeOf(arg);
20442042 if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue;
20452043
2046 try self.lowerArg(fn_ty.fnInfo().cc, arg_ty, arg_val);
2044 try func.lowerArg(fn_ty.fnInfo().cc, arg_ty, arg_val);
20472045 }
20482046
20492047 if (callee) |direct| {
2050 try self.addLabel(.call, direct.link.wasm.sym_index);
2048 try func.addLabel(.call, direct.link.wasm.sym_index);
20512049 } else {
20522050 // in this case we call a function pointer
20532051 // so load its value onto the stack
20542052 std.debug.assert(ty.zigTypeTag() == .Pointer);
2055 const operand = try self.resolveInst(pl_op.operand);
2056 try self.emitWValue(operand);
2053 const operand = try func.resolveInst(pl_op.operand);
2054 try func.emitWValue(operand);
20572055
2058 var fn_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target);
2059 defer fn_type.deinit(self.gpa);
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);
20602058
2061 const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);
2062 try self.addLabel(.call_indirect, fn_type_index);
2059 const fn_type_index = try func.bin_file.putOrGetFuncType(fn_type);
2060 try func.addLabel(.call_indirect, fn_type_index);
20632061 }
20642062
20652063 const result_value = result_value: {
2066 if (self.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
2064 if (func.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
20672065 break :result_value WValue{ .none = {} };
20682066 } else if (ret_ty.isNoReturn()) {
2069 try self.addTag(.@"unreachable");
2067 try func.addTag(.@"unreachable");
20702068 break :result_value WValue{ .none = {} };
20712069 } else if (first_param_sret) {
20722070 break :result_value sret;
20732071 // TODO: Make this less fragile and optimize
20742072 } else if (fn_ty.fnInfo().cc == .C and ret_ty.zigTypeTag() == .Struct or ret_ty.zigTypeTag() == .Union) {
2075 const result_local = try self.allocLocal(ret_ty);
2076 try self.addLabel(.local_set, result_local.local.value);
2077 const scalar_type = abi.scalarType(ret_ty, self.target);
2078 const result = try self.allocStack(scalar_type);
2079 try self.store(result, result_local, scalar_type, 0);
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);
20802078 break :result_value result;
20812079 } else {
2082 const result_local = try self.allocLocal(ret_ty);
2083 try self.addLabel(.local_set, result_local.local.value);
2080 const result_local = try func.allocLocal(ret_ty);
2081 try func.addLabel(.local_set, result_local.local.value);
20842082 break :result_value result_local;
20852083 }
20862084 };
20872085
2088 var bt = try self.iterateBigTomb(inst, 1 + args.len);
2086 var bt = try func.iterateBigTomb(inst, 1 + args.len);
20892087 bt.feed(pl_op.operand);
20902088 for (args) |arg| bt.feed(arg);
20912089 return bt.finishAir(result_value);
20922090}
20932091
2094fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!void {
2095 const value = try self.allocStackPtr(inst);
2096 self.finishAir(inst, value, &.{});
2092fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2093 const value = try func.allocStackPtr(inst);
2094 func.finishAir(inst, value, &.{});
20972095}
20982096
2099fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!void {
2100 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;
21012099
2102 const lhs = try self.resolveInst(bin_op.lhs);
2103 const rhs = try self.resolveInst(bin_op.rhs);
2104 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();
21052103
2106 try self.store(lhs, rhs, ty, 0);
2107 self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2104 try func.store(lhs, rhs, ty, 0);
2105 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
21082106}
21092107
2110fn 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 {
21112109 assert(!(lhs != .stack and rhs == .stack));
21122110 switch (ty.zigTypeTag()) {
21132111 .ErrorUnion => {
21142112 const pl_ty = ty.errorUnionPayload();
21152113 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
2116 return self.store(lhs, rhs, Type.anyerror, 0);
2114 return func.store(lhs, rhs, Type.anyerror, 0);
21172115 }
21182116
2119 const len = @intCast(u32, ty.abiSize(self.target));
2120 return self.memcpy(lhs, rhs, .{ .imm32 = len });
2117 const len = @intCast(u32, ty.abiSize(func.target));
2118 return func.memcpy(lhs, rhs, .{ .imm32 = len });
21212119 },
21222120 .Optional => {
21232121 if (ty.isPtrLikeOptional()) {
2124 return self.store(lhs, rhs, Type.usize, 0);
2122 return func.store(lhs, rhs, Type.usize, 0);
21252123 }
21262124 var buf: Type.Payload.ElemType = undefined;
21272125 const pl_ty = ty.optionalChild(&buf);
21282126 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
2129 return self.store(lhs, rhs, Type.u8, 0);
2127 return func.store(lhs, rhs, Type.u8, 0);
21302128 }
21312129 if (pl_ty.zigTypeTag() == .ErrorSet) {
2132 return self.store(lhs, rhs, Type.anyerror, 0);
2130 return func.store(lhs, rhs, Type.anyerror, 0);
21332131 }
21342132
2135 const len = @intCast(u32, ty.abiSize(self.target));
2136 return self.memcpy(lhs, rhs, .{ .imm32 = len });
2133 const len = @intCast(u32, ty.abiSize(func.target));
2134 return func.memcpy(lhs, rhs, .{ .imm32 = len });
21372135 },
21382136 .Struct, .Array, .Union, .Vector => {
2139 const len = @intCast(u32, ty.abiSize(self.target));
2140 return self.memcpy(lhs, rhs, .{ .imm32 = len });
2137 const len = @intCast(u32, ty.abiSize(func.target));
2138 return func.memcpy(lhs, rhs, .{ .imm32 = len });
21412139 },
21422140 .Pointer => {
21432141 if (ty.isSlice()) {
21442142 // store pointer first
21452143 // lower it to the stack so we do not have to store rhs into a local first
2146 try self.emitWValue(lhs);
2147 const ptr_local = try self.load(rhs, Type.usize, 0);
2148 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());
21492147
21502148 // retrieve length from rhs, and store that alongside lhs as well
2151 try self.emitWValue(lhs);
2152 const len_local = try self.load(rhs, Type.usize, self.ptrSize());
2153 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());
21542152 return;
21552153 }
21562154 },
2157 .Int => if (ty.intInfo(self.target).bits > 64) {
2158 try self.emitWValue(lhs);
2159 const lsb = try self.load(rhs, Type.u64, 0);
2160 try self.store(.{ .stack = {} }, lsb, Type.u64, 0 + lhs.offset());
2161
2162 try self.emitWValue(lhs);
2163 const msb = try self.load(rhs, Type.u64, 8);
2164 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());
21652163 return;
21662164 },
21672165 else => {},
21682166 }
2169 try self.emitWValue(lhs);
2167 try func.emitWValue(lhs);
21702168 // In this case we're actually interested in storing the stack position
21712169 // into lhs, so we calculate that and emit that instead
2172 try self.lowerToStack(rhs);
2170 try func.lowerToStack(rhs);
21732171
2174 const valtype = typeToValtype(ty, self.target);
2175 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));
21762174
21772175 const opcode = buildOpcode(.{
21782176 .valtype1 = valtype,
......@@ -2181,64 +2179,64 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
21812179 });
21822180
21832181 // store rhs value at stack pointer's location in memory
2184 try self.addMemArg(
2182 try func.addMemArg(
21852183 Mir.Inst.Tag.fromOpcode(opcode),
2186 .{ .offset = offset + lhs.offset(), .alignment = ty.abiAlignment(self.target) },
2184 .{ .offset = offset + lhs.offset(), .alignment = ty.abiAlignment(func.target) },
21872185 );
21882186}
21892187
2190fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!void {
2191 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2192 const operand = try self.resolveInst(ty_op.operand);
2193 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);
21942192
2195 if (!ty.hasRuntimeBitsIgnoreComptime()) return self.finishAir(inst, .none, &.{ty_op.operand});
2193 if (!ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{ty_op.operand});
21962194
21972195 const result = result: {
2198 if (isByRef(ty, self.target)) {
2199 const new_local = try self.allocStack(ty);
2200 try self.store(new_local, operand, ty, 0);
2196 if (isByRef(ty, func.target)) {
2197 const new_local = try func.allocStack(ty);
2198 try func.store(new_local, operand, ty, 0);
22012199 break :result new_local;
22022200 }
22032201
2204 const stack_loaded = try self.load(operand, ty, 0);
2205 break :result try stack_loaded.toLocal(self, ty);
2202 const stack_loaded = try func.load(operand, ty, 0);
2203 break :result try stack_loaded.toLocal(func, ty);
22062204 };
2207 self.finishAir(inst, result, &.{ty_op.operand});
2205 func.finishAir(inst, result, &.{ty_op.operand});
22082206}
22092207
22102208/// Loads an operand from the linear memory section.
22112209/// NOTE: Leaves the value on the stack.
2212fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
2210fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
22132211 // load local's value from memory by its stack position
2214 try self.emitWValue(operand);
2212 try func.emitWValue(operand);
22152213
2216 const abi_size = @intCast(u8, ty.abiSize(self.target));
2214 const abi_size = @intCast(u8, ty.abiSize(func.target));
22172215 const opcode = buildOpcode(.{
2218 .valtype1 = typeToValtype(ty, self.target),
2216 .valtype1 = typeToValtype(ty, func.target),
22192217 .width = abi_size * 8,
22202218 .op = .load,
22212219 .signedness = .unsigned,
22222220 });
22232221
2224 try self.addMemArg(
2222 try func.addMemArg(
22252223 Mir.Inst.Tag.fromOpcode(opcode),
2226 .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(self.target) },
2224 .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(func.target) },
22272225 );
22282226
22292227 return WValue{ .stack = {} };
22302228}
22312229
2232fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
2233 const arg_index = self.arg_index;
2234 const arg = self.args[arg_index];
2235 const cc = self.decl.ty.fnInfo().cc;
2236 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);
22372235 if (cc == .C) {
2238 const arg_classes = abi.classifyType(arg_ty, self.target);
2236 const arg_classes = abi.classifyType(arg_ty, func.target);
22392237 for (arg_classes) |class| {
22402238 if (class != .none) {
2241 self.arg_index += 1;
2239 func.arg_index += 1;
22422240 }
22432241 }
22442242
......@@ -2246,24 +2244,24 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
22462244 // we combine them into a single stack value
22472245 if (arg_classes[0] == .direct and arg_classes[1] == .direct) {
22482246 if (arg_ty.zigTypeTag() != .Int) {
2249 return self.fail(
2247 return func.fail(
22502248 "TODO: Implement C-ABI argument for type '{}'",
2251 .{arg_ty.fmt(self.bin_file.base.options.module.?)},
2249 .{arg_ty.fmt(func.bin_file.base.options.module.?)},
22522250 );
22532251 }
2254 const result = try self.allocStack(arg_ty);
2255 try self.store(result, arg, Type.u64, 0);
2256 try self.store(result, self.args[arg_index + 1], Type.u64, 8);
2257 return self.finishAir(inst, arg, &.{});
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, &.{});
22582256 }
22592257 } else {
2260 self.arg_index += 1;
2258 func.arg_index += 1;
22612259 }
22622260
2263 switch (self.debug_output) {
2261 switch (func.debug_output) {
22642262 .dwarf => |dwarf| {
22652263 // TODO: Get the original arg index rather than wasm arg index
2266 const name = self.mod_fn.getParamName(self.bin_file.base.options.module.?, arg_index);
2264 const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, arg_index);
22672265 const leb_size = link.File.Wasm.getULEB128Size(arg.local.value);
22682266 const dbg_info = &dwarf.dbg_info;
22692267 try dbg_info.ensureUnusedCapacity(3 + leb_size + 5 + name.len + 1);
......@@ -2279,196 +2277,196 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
22792277 std.dwarf.OP.WASM_local,
22802278 });
22812279 leb.writeULEB128(dbg_info.writer(), arg.local.value) catch unreachable;
2282 try self.addDbgInfoTypeReloc(arg_ty);
2280 try func.addDbgInfoTypeReloc(arg_ty);
22832281 dbg_info.appendSliceAssumeCapacity(name);
22842282 dbg_info.appendAssumeCapacity(0);
22852283 },
22862284 else => {},
22872285 }
22882286
2289 self.finishAir(inst, arg, &.{});
2287 func.finishAir(inst, arg, &.{});
22902288}
22912289
2292fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
2293 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2294 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
2295 const lhs = try self.resolveInst(bin_op.lhs);
2296 const rhs = try self.resolveInst(bin_op.rhs);
2297 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);
22982296
2299 const stack_value = try self.binOp(lhs, rhs, ty, op);
2300 self.finishAir(inst, try stack_value.toLocal(self, ty), &.{ bin_op.lhs, bin_op.rhs });
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 });
23012299}
23022300
23032301/// Performs a binary operation on the given `WValue`'s
23042302/// NOTE: THis leaves the value on top of the stack.
2305fn 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 {
23062304 assert(!(lhs != .stack and rhs == .stack));
2307 if (isByRef(ty, self.target)) {
2305 if (isByRef(ty, func.target)) {
23082306 if (ty.zigTypeTag() == .Int) {
2309 return self.binOpBigInt(lhs, rhs, ty, op);
2307 return func.binOpBigInt(lhs, rhs, ty, op);
23102308 } else {
2311 return self.fail(
2309 return func.fail(
23122310 "TODO: Implement binary operation for type: {}",
2313 .{ty.fmt(self.bin_file.base.options.module.?)},
2311 .{ty.fmt(func.bin_file.base.options.module.?)},
23142312 );
23152313 }
23162314 }
23172315
2318 if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) {
2319 return self.binOpFloat16(lhs, rhs, op);
2316 if (ty.isAnyFloat() and ty.floatBits(func.target) == 16) {
2317 return func.binOpFloat16(lhs, rhs, op);
23202318 }
23212319
23222320 const opcode: wasm.Opcode = buildOpcode(.{
23232321 .op = op,
2324 .valtype1 = typeToValtype(ty, self.target),
2322 .valtype1 = typeToValtype(ty, func.target),
23252323 .signedness = if (ty.isSignedInt()) .signed else .unsigned,
23262324 });
2327 try self.emitWValue(lhs);
2328 try self.emitWValue(rhs);
2325 try func.emitWValue(lhs);
2326 try func.emitWValue(rhs);
23292327
2330 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2328 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
23312329
23322330 return WValue{ .stack = {} };
23332331}
23342332
23352333/// Performs a binary operation for 16-bit floats.
23362334/// NOTE: Leaves the result value on the stack
2337fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue {
2335fn binOpFloat16(func: *CodeGen, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue {
23382336 const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned });
2339 _ = try self.fpext(lhs, Type.f16, Type.f32);
2340 _ = try self.fpext(rhs, Type.f16, Type.f32);
2341 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));
23422340
2343 return self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
2341 return func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
23442342}
23452343
2346fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2347 if (ty.intInfo(self.target).bits > 128) {
2348 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", .{});
23492347 }
23502348
23512349 if (op != .add and op != .sub) {
2352 return self.fail("TODO: Implement binary operation for big integers", .{});
2350 return func.fail("TODO: Implement binary operation for big integers", .{});
23532351 }
23542352
2355 const result = try self.allocStack(ty);
2356 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
2357 defer lhs_high_bit.free(self);
2358 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
2359 defer rhs_high_bit.free(self);
2360 var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
2361 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);
23622360
2363 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
2364 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
2365 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);
23662364
23672365 const lt = if (op == .add) blk: {
2368 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);
23692367 } else if (op == .sub) blk: {
2370 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);
23712369 } else unreachable;
2372 const tmp = try self.intcast(lt, Type.u32, Type.u64);
2373 var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
2374 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);
23752373
2376 try self.store(result, high_op_res, Type.u64, 0);
2377 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);
23782376 return result;
23792377}
23802378
2381fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
2382 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2383 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, 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 });
23842382
2385 const lhs = try self.resolveInst(bin_op.lhs);
2386 const rhs = try self.resolveInst(bin_op.rhs);
2387 const ty = self.air.typeOf(bin_op.lhs);
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);
23882386
23892387 if (ty.zigTypeTag() == .Vector) {
2390 return self.fail("TODO: Implement wrapping arithmetic for vectors", .{});
2388 return func.fail("TODO: Implement wrapping arithmetic for vectors", .{});
23912389 }
23922390
2393 const result = try (try self.wrapBinOp(lhs, rhs, ty, op)).toLocal(self, ty);
2394 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
23952393}
23962394
23972395/// Performs a wrapping binary operation.
23982396/// Asserts rhs is not a stack value when lhs also isn't.
23992397/// NOTE: Leaves the result on the stack when its Type is <= 64 bits
2400fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2401 const bin_local = try self.binOp(lhs, rhs, ty, op);
2402 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);
24032401}
24042402
24052403/// Wraps an operand based on a given type's bitsize.
24062404/// Asserts `Type` is <= 128 bits.
24072405/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.
2408fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
2409 assert(ty.abiSize(self.target) <= 16);
2410 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;
24112409 const wasm_bits = toWasmBits(bitsize) orelse {
2412 return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
2410 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
24132411 };
24142412
24152413 if (wasm_bits == bitsize) return operand;
24162414
24172415 if (wasm_bits == 128) {
24182416 assert(operand != .stack);
2419 const lsb = try self.load(operand, Type.u64, 8);
2417 const lsb = try func.load(operand, Type.u64, 8);
24202418
2421 const result_ptr = try self.allocStack(ty);
2422 try self.emitWValue(result_ptr);
2423 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());
24242422 const result = (@as(u64, 1) << @intCast(u6, 64 - (wasm_bits - bitsize))) - 1;
2425 try self.emitWValue(result_ptr);
2426 _ = try self.load(operand, Type.u64, 0);
2427 try self.addImm64(result);
2428 try self.addTag(.i64_and);
2429 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 });
24302428 return result_ptr;
24312429 }
24322430
24332431 const result = (@as(u64, 1) << @intCast(u6, bitsize)) - 1;
2434 try self.emitWValue(operand);
2432 try func.emitWValue(operand);
24352433 if (bitsize <= 32) {
2436 try self.addImm32(@bitCast(i32, @intCast(u32, result)));
2437 try self.addTag(.i32_and);
2434 try func.addImm32(@bitCast(i32, @intCast(u32, result)));
2435 try func.addTag(.i32_and);
24382436 } else if (bitsize <= 64) {
2439 try self.addImm64(result);
2440 try self.addTag(.i64_and);
2437 try func.addImm64(result);
2438 try func.addTag(.i64_and);
24412439 } else unreachable;
24422440
24432441 return WValue{ .stack = {} };
24442442}
24452443
2446fn 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 {
24472445 switch (ptr_val.tag()) {
24482446 .decl_ref_mut => {
24492447 const decl_index = ptr_val.castTag(.decl_ref_mut).?.data.decl_index;
2450 return self.lowerParentPtrDecl(ptr_val, decl_index);
2448 return func.lowerParentPtrDecl(ptr_val, decl_index);
24512449 },
24522450 .decl_ref => {
24532451 const decl_index = ptr_val.castTag(.decl_ref).?.data;
2454 return self.lowerParentPtrDecl(ptr_val, decl_index);
2452 return func.lowerParentPtrDecl(ptr_val, decl_index);
24552453 },
24562454 .variable => {
24572455 const decl_index = ptr_val.castTag(.variable).?.data.owner_decl;
2458 return self.lowerParentPtrDecl(ptr_val, decl_index);
2456 return func.lowerParentPtrDecl(ptr_val, decl_index);
24592457 },
24602458 .field_ptr => {
24612459 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
24622460 const parent_ty = field_ptr.container_ty;
2463 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);
24642462
24652463 const offset = switch (parent_ty.zigTypeTag()) {
24662464 .Struct => blk: {
2467 const offset = parent_ty.structFieldOffset(field_ptr.field_index, self.target);
2465 const offset = parent_ty.structFieldOffset(field_ptr.field_index, func.target);
24682466 break :blk offset;
24692467 },
24702468 .Union => blk: {
2471 const layout: Module.Union.Layout = parent_ty.unionGetLayout(self.target);
2469 const layout: Module.Union.Layout = parent_ty.unionGetLayout(func.target);
24722470 if (layout.payload_size == 0) break :blk 0;
24732471 if (layout.payload_align > layout.tag_align) break :blk 0;
24742472
......@@ -2479,7 +2477,7 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
24792477 .Pointer => switch (parent_ty.ptrSize()) {
24802478 .Slice => switch (field_ptr.field_index) {
24812479 0 => 0,
2482 1 => self.ptrSize(),
2480 1 => func.ptrSize(),
24832481 else => unreachable,
24842482 },
24852483 else => unreachable,
......@@ -2506,8 +2504,8 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
25062504 .elem_ptr => {
25072505 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
25082506 const index = elem_ptr.index;
2509 const offset = index * ptr_child_ty.abiSize(self.target);
2510 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);
25112509
25122510 return WValue{ .memory_offset = .{
25132511 .pointer = array_ptr.memory,
......@@ -2516,27 +2514,27 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
25162514 },
25172515 .opt_payload_ptr => {
25182516 const payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
2519 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);
25202518 var buf: Type.Payload.ElemType = undefined;
25212519 const payload_ty = payload_ptr.container_ty.optionalChild(&buf);
25222520 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.optionalReprIsPayload()) {
25232521 return parent_ptr;
25242522 }
25252523
2526 const abi_size = payload_ptr.container_ty.abiSize(self.target);
2527 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);
25282526
25292527 return WValue{ .memory_offset = .{
25302528 .pointer = parent_ptr.memory,
25312529 .offset = @intCast(u32, offset),
25322530 } };
25332531 },
2534 else => |tag| return self.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
2532 else => |tag| return func.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
25352533 }
25362534}
25372535
2538fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl_index: Module.Decl.Index) InnerError!WValue {
2539 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.?;
25402538 const decl = module.declPtr(decl_index);
25412539 module.markDeclAlive(decl);
25422540 var ptr_ty_payload: Type.Payload.ElemType = .{
......@@ -2544,15 +2542,15 @@ fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl_index: Module.Decl.Index
25442542 .data = decl.ty,
25452543 };
25462544 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2547 return self.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index);
2545 return func.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index);
25482546}
25492547
2550fn 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 {
25512549 if (tv.ty.isSlice()) {
2552 return WValue{ .memory = try self.bin_file.lowerUnnamedConst(tv, decl_index) };
2550 return WValue{ .memory = try func.bin_file.lowerUnnamedConst(tv, decl_index) };
25532551 }
25542552
2555 const module = self.bin_file.base.options.module.?;
2553 const module = func.bin_file.base.options.module.?;
25562554 const decl = module.declPtr(decl_index);
25572555 if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
25582556 return WValue{ .imm32 = 0xaaaaaaaa };
......@@ -2562,7 +2560,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index)
25622560
25632561 const target_sym_index = decl.link.wasm.sym_index;
25642562 if (decl.ty.zigTypeTag() == .Fn) {
2565 try self.bin_file.addTableFunction(target_sym_index);
2563 try func.bin_file.addTableFunction(target_sym_index);
25662564 return WValue{ .function_index = target_sym_index };
25672565 } else return WValue{ .memory = target_sym_index };
25682566}
......@@ -2583,21 +2581,21 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(
25832581 return @intCast(WantedT, result);
25842582}
25852583
2586fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
2587 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);
25882586 if (val.castTag(.decl_ref)) |decl_ref| {
25892587 const decl_index = decl_ref.data;
2590 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
2588 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
25912589 }
25922590 if (val.castTag(.decl_ref_mut)) |decl_ref_mut| {
25932591 const decl_index = decl_ref_mut.data.decl_index;
2594 return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
2592 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
25952593 }
2596 const target = self.target;
2594 const target = func.target;
25972595 switch (ty.zigTypeTag()) {
25982596 .Void => return WValue{ .none = {} },
25992597 .Int => {
2600 const int_info = ty.intInfo(self.target);
2598 const int_info = ty.intInfo(func.target);
26012599 switch (int_info.signedness) {
26022600 .signed => switch (int_info.bits) {
26032601 0...32 => return WValue{ .imm32 = @intCast(u32, toTwosComplement(
......@@ -2618,7 +2616,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
26182616 }
26192617 },
26202618 .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
2621 .Float => switch (ty.floatBits(self.target)) {
2619 .Float => switch (ty.floatBits(func.target)) {
26222620 16 => return WValue{ .imm32 = @bitCast(u16, val.toFloat(f16)) },
26232621 32 => return WValue{ .float32 = val.toFloat(f32) },
26242622 64 => return WValue{ .float64 = val.toFloat(f64) },
......@@ -2626,11 +2624,11 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
26262624 },
26272625 .Pointer => switch (val.tag()) {
26282626 .field_ptr, .elem_ptr, .opt_payload_ptr => {
2629 return self.lowerParentPtr(val, ty.childType());
2627 return func.lowerParentPtr(val, ty.childType());
26302628 },
26312629 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
26322630 .zero, .null_value => return WValue{ .imm32 = 0 },
2633 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()}),
26342632 },
26352633 .Enum => {
26362634 if (val.castTag(.enum_field_index)) |field_index| {
......@@ -2640,7 +2638,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
26402638 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
26412639 if (enum_full.values.count() != 0) {
26422640 const tag_val = enum_full.values.keys()[field_index.data];
2643 return self.lowerConstant(tag_val, enum_full.tag_ty);
2641 return func.lowerConstant(tag_val, enum_full.tag_ty);
26442642 } else {
26452643 return WValue{ .imm32 = field_index.data };
26462644 }
......@@ -2649,19 +2647,19 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
26492647 const index = field_index.data;
26502648 const enum_data = ty.castTag(.enum_numbered).?.data;
26512649 const enum_val = enum_data.values.keys()[index];
2652 return self.lowerConstant(enum_val, enum_data.tag_ty);
2650 return func.lowerConstant(enum_val, enum_data.tag_ty);
26532651 },
2654 else => return self.fail("TODO: lowerConstant for enum tag: {}", .{ty.tag()}),
2652 else => return func.fail("TODO: lowerConstant for enum tag: {}", .{ty.tag()}),
26552653 }
26562654 } else {
26572655 var int_tag_buffer: Type.Payload.Bits = undefined;
26582656 const int_tag_ty = ty.intTagType(&int_tag_buffer);
2659 return self.lowerConstant(val, int_tag_ty);
2657 return func.lowerConstant(val, int_tag_ty);
26602658 }
26612659 },
26622660 .ErrorSet => switch (val.tag()) {
26632661 .@"error" => {
2664 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().?);
26652663 return WValue{ .imm32 = kv.value };
26662664 },
26672665 else => return WValue{ .imm32 = 0 },
......@@ -2670,41 +2668,41 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
26702668 const error_type = ty.errorUnionSet();
26712669 const is_pl = val.errorUnionIsPayload();
26722670 const err_val = if (!is_pl) val else Value.initTag(.zero);
2673 return self.lowerConstant(err_val, error_type);
2671 return func.lowerConstant(err_val, error_type);
26742672 },
26752673 .Optional => if (ty.optionalReprIsPayload()) {
26762674 var buf: Type.Payload.ElemType = undefined;
26772675 const pl_ty = ty.optionalChild(&buf);
26782676 if (val.castTag(.opt_payload)) |payload| {
2679 return self.lowerConstant(payload.data, pl_ty);
2677 return func.lowerConstant(payload.data, pl_ty);
26802678 } else if (val.isNull()) {
26812679 return WValue{ .imm32 = 0 };
26822680 } else {
2683 return self.lowerConstant(val, pl_ty);
2681 return func.lowerConstant(val, pl_ty);
26842682 }
26852683 } else {
26862684 const is_pl = val.tag() == .opt_payload;
26872685 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
26882686 },
2689 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}),
26902688 }
26912689}
26922690
2693fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
2691fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue {
26942692 switch (ty.zigTypeTag()) {
26952693 .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa },
2696 .Int => switch (ty.intInfo(self.target).bits) {
2694 .Int => switch (ty.intInfo(func.target).bits) {
26972695 0...32 => return WValue{ .imm32 = 0xaaaaaaaa },
26982696 33...64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
26992697 else => unreachable,
27002698 },
2701 .Float => switch (ty.floatBits(self.target)) {
2699 .Float => switch (ty.floatBits(func.target)) {
27022700 16 => return WValue{ .imm32 = 0xaaaaaaaa },
27032701 32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) },
27042702 64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) },
27052703 else => unreachable,
27062704 },
2707 .Pointer => switch (self.arch()) {
2705 .Pointer => switch (func.arch()) {
27082706 .wasm32 => return WValue{ .imm32 = 0xaaaaaaaa },
27092707 .wasm64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
27102708 else => unreachable,
......@@ -2713,22 +2711,22 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
27132711 var buf: Type.Payload.ElemType = undefined;
27142712 const pl_ty = ty.optionalChild(&buf);
27152713 if (ty.optionalReprIsPayload()) {
2716 return self.emitUndefined(pl_ty);
2714 return func.emitUndefined(pl_ty);
27172715 }
27182716 return WValue{ .imm32 = 0xaaaaaaaa };
27192717 },
27202718 .ErrorUnion => {
27212719 return WValue{ .imm32 = 0xaaaaaaaa };
27222720 },
2723 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()}),
27242722 }
27252723}
27262724
27272725/// Returns a `Value` as a signed 32 bit value.
27282726/// It's illegal to provide a value with a type that cannot be represented
27292727/// as an integer value.
2730fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
2731 const target = self.target;
2728fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 {
2729 const target = func.target;
27322730 switch (ty.zigTypeTag()) {
27332731 .Enum => {
27342732 if (val.castTag(.enum_field_index)) |field_index| {
......@@ -2738,28 +2736,28 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
27382736 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
27392737 if (enum_full.values.count() != 0) {
27402738 const tag_val = enum_full.values.keys()[field_index.data];
2741 return self.valueAsI32(tag_val, enum_full.tag_ty);
2739 return func.valueAsI32(tag_val, enum_full.tag_ty);
27422740 } else return @bitCast(i32, field_index.data);
27432741 },
27442742 .enum_numbered => {
27452743 const index = field_index.data;
27462744 const enum_data = ty.castTag(.enum_numbered).?.data;
2747 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);
27482746 },
27492747 else => unreachable,
27502748 }
27512749 } else {
27522750 var int_tag_buffer: Type.Payload.Bits = undefined;
27532751 const int_tag_ty = ty.intTagType(&int_tag_buffer);
2754 return self.valueAsI32(val, int_tag_ty);
2752 return func.valueAsI32(val, int_tag_ty);
27552753 }
27562754 },
2757 .Int => switch (ty.intInfo(self.target).signedness) {
2755 .Int => switch (ty.intInfo(func.target).signedness) {
27582756 .signed => return @truncate(i32, val.toSignedInt()),
27592757 .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt(target))),
27602758 },
27612759 .ErrorSet => {
2762 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
27632761 return @bitCast(i32, kv.value);
27642762 },
27652763 .Bool => return @intCast(i32, val.toSignedInt()),
......@@ -2768,139 +2766,139 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
27682766 }
27692767}
27702768
2771fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!void {
2772 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2773 const block_ty = self.air.getRefType(ty_pl.ty);
2774 const wasm_block_ty = genBlockType(block_ty, self.target);
2775 const extra = self.air.extraData(Air.Block, ty_pl.payload);
2776 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];
27772775
27782776 // if wasm_block_ty is non-empty, we create a register to store the temporary value
27792777 const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {
2780 const ty: Type = if (isByRef(block_ty, self.target)) Type.u32 else block_ty;
2781 break :blk try self.ensureAllocLocal(ty); // make sure it's a clean local as it may never get overwritten
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
27822780 } else WValue.none;
27832781
2784 try self.startBlock(.block, wasm.block_empty);
2782 try func.startBlock(.block, wasm.block_empty);
27852783 // Here we set the current block idx, so breaks know the depth to jump
27862784 // to when breaking out.
2787 try self.blocks.putNoClobber(self.gpa, inst, .{
2788 .label = self.block_depth,
2785 try func.blocks.putNoClobber(func.gpa, inst, .{
2786 .label = func.block_depth,
27892787 .value = block_result,
27902788 });
2791 try self.genBody(body);
2792 try self.endBlock();
2789 try func.genBody(body);
2790 try func.endBlock();
27932791
2794 self.finishAir(inst, block_result, &.{});
2792 func.finishAir(inst, block_result, &.{});
27952793}
27962794
27972795/// appends a new wasm block to the code section and increases the `block_depth` by 1
2798fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8) !void {
2799 self.block_depth += 1;
2800 try self.addInst(.{
2796fn startBlock(func: *CodeGen, block_tag: wasm.Opcode, valtype: u8) !void {
2797 func.block_depth += 1;
2798 try func.addInst(.{
28012799 .tag = Mir.Inst.Tag.fromOpcode(block_tag),
28022800 .data = .{ .block_type = valtype },
28032801 });
28042802}
28052803
28062804/// Ends the current wasm block and decreases the `block_depth` by 1
2807fn endBlock(self: *Self) !void {
2808 try self.addTag(.end);
2809 self.block_depth -= 1;
2805fn endBlock(func: *CodeGen) !void {
2806 try func.addTag(.end);
2807 func.block_depth -= 1;
28102808}
28112809
2812fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!void {
2813 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2814 const loop = self.air.extraData(Air.Block, ty_pl.payload);
2815 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];
28162814
28172815 // result type of loop is always 'noreturn', meaning we can always
28182816 // emit the wasm type 'block_empty'.
2819 try self.startBlock(.loop, wasm.block_empty);
2820 try self.genBody(body);
2817 try func.startBlock(.loop, wasm.block_empty);
2818 try func.genBody(body);
28212819
28222820 // breaking to the index of a loop block will continue the loop instead
2823 try self.addLabel(.br, 0);
2824 try self.endBlock();
2821 try func.addLabel(.br, 0);
2822 try func.endBlock();
28252823
2826 self.finishAir(inst, .none, &.{});
2824 func.finishAir(inst, .none, &.{});
28272825}
28282826
2829fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
2830 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2831 const condition = try self.resolveInst(pl_op.operand);
2832 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
2833 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2834 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
2835 const liveness_condbr = self.liveness.getCondBr(inst);
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);
28362834
28372835 // result type is always noreturn, so use `block_empty` as type.
2838 try self.startBlock(.block, wasm.block_empty);
2836 try func.startBlock(.block, wasm.block_empty);
28392837 // emit the conditional value
2840 try self.emitWValue(condition);
2838 try func.emitWValue(condition);
28412839
28422840 // we inserted the block in front of the condition
28432841 // so now check if condition matches. If not, break outside this block
28442842 // and continue with the then codepath
2845 try self.addLabel(.br_if, 0);
2843 try func.addLabel(.br_if, 0);
28462844
2847 try self.branches.ensureUnusedCapacity(self.gpa, 2);
2845 try func.branches.ensureUnusedCapacity(func.gpa, 2);
28482846
2849 self.branches.appendAssumeCapacity(.{});
2850 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
2847 func.branches.appendAssumeCapacity(.{});
2848 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
28512849 for (liveness_condbr.else_deaths) |death| {
2852 self.processDeath(Air.indexToRef(death));
2850 func.processDeath(Air.indexToRef(death));
28532851 }
2854 try self.genBody(else_body);
2855 try self.endBlock();
2856 var else_stack = self.branches.pop();
2857 defer else_stack.deinit(self.gpa);
2852 try func.genBody(else_body);
2853 try func.endBlock();
2854 var else_stack = func.branches.pop();
2855 defer else_stack.deinit(func.gpa);
28582856
28592857 // Outer block that matches the condition
2860 self.branches.appendAssumeCapacity(.{});
2861 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
2858 func.branches.appendAssumeCapacity(.{});
2859 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
28622860 for (liveness_condbr.then_deaths) |death| {
2863 self.processDeath(Air.indexToRef(death));
2861 func.processDeath(Air.indexToRef(death));
28642862 }
2865 try self.genBody(then_body);
2866 var then_stack = self.branches.pop();
2867 defer then_stack.deinit(self.gpa);
2863 try func.genBody(then_body);
2864 var then_stack = func.branches.pop();
2865 defer then_stack.deinit(func.gpa);
28682866
2869 try self.mergeBranch(&else_stack);
2870 try self.mergeBranch(&then_stack);
2867 try func.mergeBranch(&else_stack);
2868 try func.mergeBranch(&then_stack);
28712869
2872 self.finishAir(inst, .none, &.{});
2870 func.finishAir(inst, .none, &.{});
28732871}
28742872
2875fn mergeBranch(self: *Self, branch: *const Branch) !void {
2876 const parent = self.currentBranch();
2873fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
2874 const parent = func.currentBranch();
28772875
28782876 const target_slice = branch.values.entries.slice();
28792877 const target_keys = target_slice.items(.key);
28802878 const target_values = target_slice.items(.value);
28812879
2882 try parent.values.ensureUnusedCapacity(self.gpa, branch.values.count());
2880 try parent.values.ensureUnusedCapacity(func.gpa, branch.values.count());
28832881 for (target_keys) |key, index| {
28842882 // TODO: process deaths from branches
28852883 parent.values.putAssumeCapacity(key, target_values[index]);
28862884 }
28872885}
28882886
2889fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
2890 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2891 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
28922890
2893 const lhs = try self.resolveInst(bin_op.lhs);
2894 const rhs = try self.resolveInst(bin_op.rhs);
2895 const operand_ty = self.air.typeOf(bin_op.lhs);
2896 const result = try (try self.cmp(lhs, rhs, operand_ty, op)).toLocal(self, Type.u32); // comparison result is always 32 bits
2897 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
28982896}
28992897
29002898/// Compares two operands.
29012899/// Asserts rhs is not a stack value when the lhs isn't a stack value either
29022900/// NOTE: This leaves the result on top of the stack, rather than a new local.
2903fn 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 {
29042902 assert(!(lhs != .stack and rhs == .stack));
29052903 if (ty.zigTypeTag() == .Optional and !ty.optionalReprIsPayload()) {
29062904 var buf: Type.Payload.ElemType = undefined;
......@@ -2909,28 +2907,28 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper
29092907 // When we hit this case, we must check the value of optionals
29102908 // that are not pointers. This means first checking against non-null for
29112909 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
2912 return self.cmpOptionals(lhs, rhs, ty, op);
2910 return func.cmpOptionals(lhs, rhs, ty, op);
29132911 }
2914 } else if (isByRef(ty, self.target)) {
2915 return self.cmpBigInt(lhs, rhs, ty, op);
2916 } else if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) {
2917 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);
29182916 }
29192917
29202918 // ensure that when we compare pointers, we emit
29212919 // the true pointer of a stack value, rather than the stack pointer.
2922 try self.lowerToStack(lhs);
2923 try self.lowerToStack(rhs);
2920 try func.lowerToStack(lhs);
2921 try func.lowerToStack(rhs);
29242922
29252923 const signedness: std.builtin.Signedness = blk: {
29262924 // by default we tell the operand type is unsigned (i.e. bools and enum values)
29272925 if (ty.zigTypeTag() != .Int) break :blk .unsigned;
29282926
29292927 // incase of an actual integer, we emit the correct signedness
2930 break :blk ty.intInfo(self.target).signedness;
2928 break :blk ty.intInfo(func.target).signedness;
29312929 };
29322930 const opcode: wasm.Opcode = buildOpcode(.{
2933 .valtype1 = typeToValtype(ty, self.target),
2931 .valtype1 = typeToValtype(ty, func.target),
29342932 .op = switch (op) {
29352933 .lt => .lt,
29362934 .lte => .le,
......@@ -2941,14 +2939,14 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper
29412939 },
29422940 .signedness = signedness,
29432941 });
2944 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
2942 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
29452943
29462944 return WValue{ .stack = {} };
29472945}
29482946
29492947/// Compares 16-bit floats
29502948/// NOTE: The result value remains on top of the stack.
2951fn 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 {
29522950 const opcode: wasm.Opcode = buildOpcode(.{
29532951 .op = switch (op) {
29542952 .lt => .lt,
......@@ -2961,200 +2959,200 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato
29612959 .valtype1 = .f32,
29622960 .signedness = .unsigned,
29632961 });
2964 _ = try self.fpext(lhs, Type.f16, Type.f32);
2965 _ = try self.fpext(rhs, Type.f16, Type.f32);
2966 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));
29672965
29682966 return WValue{ .stack = {} };
29692967}
29702968
2971fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!void {
2969fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
29722970 _ = inst;
2973 return self.fail("TODO implement airCmpVector for wasm", .{});
2971 return func.fail("TODO implement airCmpVector for wasm", .{});
29742972}
29752973
2976fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
2977 const un_op = self.air.instructions.items(.data)[inst].un_op;
2978 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);
29792977
29802978 _ = operand;
2981 return self.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
2979 return func.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
29822980}
29832981
2984fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
2985 const br = self.air.instructions.items(.data)[inst].br;
2986 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).?;
29872985
29882986 // if operand has codegen bits we should break with a value
2989 if (self.air.typeOf(br.operand).hasRuntimeBitsIgnoreComptime()) {
2990 const operand = try self.resolveInst(br.operand);
2991 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);
29922990
29932991 if (block.value != .none) {
2994 try self.addLabel(.local_set, block.value.local.value);
2992 try func.addLabel(.local_set, block.value.local.value);
29952993 }
29962994 }
29972995
29982996 // We map every block to its block index.
29992997 // We then determine how far we have to jump to it by subtracting it from current block depth
3000 const idx: u32 = self.block_depth - block.label;
3001 try self.addLabel(.br, idx);
2998 const idx: u32 = func.block_depth - block.label;
2999 try func.addLabel(.br, idx);
30023000
3003 self.finishAir(inst, .none, &.{br.operand});
3001 func.finishAir(inst, .none, &.{br.operand});
30043002}
30053003
3006fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!void {
3007 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3008 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
30093007
3010 const operand = try self.resolveInst(ty_op.operand);
3011 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);
30123010
30133011 const result = result: {
30143012 if (operand_ty.zigTypeTag() == .Bool) {
3015 try self.emitWValue(operand);
3016 try self.addTag(.i32_eqz);
3017 const not_tmp = try self.allocLocal(operand_ty);
3018 try self.addLabel(.local_set, not_tmp.local.value);
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);
30193017 break :result not_tmp;
30203018 } else {
3021 const operand_bits = operand_ty.intInfo(self.target).bits;
3019 const operand_bits = operand_ty.intInfo(func.target).bits;
30223020 const wasm_bits = toWasmBits(operand_bits) orelse {
3023 return self.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});
3021 return func.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});
30243022 };
30253023
30263024 switch (wasm_bits) {
30273025 32 => {
3028 const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);
3029 break :result try (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
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);
30303028 },
30313029 64 => {
3032 const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);
3033 break :result try (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty);
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);
30343032 },
30353033 128 => {
3036 const result_ptr = try self.allocStack(operand_ty);
3037 try self.emitWValue(result_ptr);
3038 const msb = try self.load(operand, Type.u64, 0);
3039 const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
3040 try self.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset());
3041
3042 try self.emitWValue(result_ptr);
3043 const lsb = try self.load(operand, Type.u64, 8);
3044 const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
3045 try self.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset());
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());
30463044 break :result result_ptr;
30473045 },
30483046 else => unreachable,
30493047 }
30503048 }
30513049 };
3052 self.finishAir(inst, result, &.{ty_op.operand});
3050 func.finishAir(inst, result, &.{ty_op.operand});
30533051}
30543052
3055fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!void {
3056 // 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
30573055 // for wasm
3058 self.finishAir(inst, .none, &.{});
3056 func.finishAir(inst, .none, &.{});
30593057}
30603058
3061fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!void {
3062 try self.addTag(.@"unreachable");
3063 self.finishAir(inst, .none, &.{});
3059fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3060 try func.addTag(.@"unreachable");
3061 func.finishAir(inst, .none, &.{});
30643062}
30653063
3066fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!void {
3067 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3068 const result = if (!self.liveness.isUnused(inst)) result: {
3069 const operand = try self.resolveInst(ty_op.operand);
3070 break :result self.reuseOperand(ty_op.operand, 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);
30713069 } else WValue{ .none = {} };
3072 self.finishAir(inst, result, &.{});
3070 func.finishAir(inst, result, &.{});
30733071}
30743072
3075fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3076 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3077 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
3078 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{extra.data.struct_operand});
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});
30793077
3080 const struct_ptr = try self.resolveInst(extra.data.struct_operand);
3081 const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
3082 const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) orelse {
3083 const module = self.bin_file.base.options.module.?;
3084 return self.fail("Field type '{}' too big to fit into stack frame", .{
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", .{
30853083 struct_ty.structFieldType(extra.data.field_index).fmt(module),
30863084 });
30873085 };
3088 const result = try self.structFieldPtr(struct_ptr, offset);
3089 self.finishAir(inst, result, &.{extra.data.struct_operand});
3086 const result = try func.structFieldPtr(struct_ptr, offset);
3087 func.finishAir(inst, result, &.{extra.data.struct_operand});
30903088}
30913089
3092fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!void {
3093 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3094 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3095 const struct_ptr = try self.resolveInst(ty_op.operand);
3096 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();
30973095 const field_ty = struct_ty.structFieldType(index);
3098 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) orelse {
3099 const module = self.bin_file.base.options.module.?;
3100 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", .{
31013099 field_ty.fmt(module),
31023100 });
31033101 };
3104 const result = try self.structFieldPtr(struct_ptr, offset);
3105 self.finishAir(inst, result, &.{ty_op.operand});
3102 const result = try func.structFieldPtr(struct_ptr, offset);
3103 func.finishAir(inst, result, &.{ty_op.operand});
31063104}
31073105
3108fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue {
3106fn structFieldPtr(func: *CodeGen, struct_ptr: WValue, offset: u32) InnerError!WValue {
31093107 switch (struct_ptr) {
31103108 .stack_offset => |stack_offset| {
31113109 return WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
31123110 },
3113 else => return self.buildPointerOffset(struct_ptr, offset, .new),
3111 else => return func.buildPointerOffset(struct_ptr, offset, .new),
31143112 }
31153113}
31163114
3117fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3118 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3119 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
3120 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{struct_field.struct_operand});
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});
31213119
3122 const struct_ty = self.air.typeOf(struct_field.struct_operand);
3123 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);
31243122 const field_index = struct_field.field_index;
31253123 const field_ty = struct_ty.structFieldType(field_index);
3126 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return self.finishAir(inst, .none, &.{struct_field.struct_operand});
3124 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{struct_field.struct_operand});
31273125
3128 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) orelse {
3129 const module = self.bin_file.base.options.module.?;
3130 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
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)});
31313129 };
31323130
31333131 const result = result: {
3134 if (isByRef(field_ty, self.target)) {
3132 if (isByRef(field_ty, func.target)) {
31353133 switch (operand) {
31363134 .stack_offset => |stack_offset| {
31373135 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
31383136 },
3139 else => break :result try self.buildPointerOffset(operand, offset, .new),
3137 else => break :result try func.buildPointerOffset(operand, offset, .new),
31403138 }
31413139 }
31423140
3143 const field = try self.load(operand, field_ty, offset);
3144 break :result try field.toLocal(self, field_ty);
3141 const field = try func.load(operand, field_ty, offset);
3142 break :result try field.toLocal(func, field_ty);
31453143 };
3146 self.finishAir(inst, result, &.{struct_field.struct_operand});
3144 func.finishAir(inst, result, &.{struct_field.struct_operand});
31473145}
31483146
3149fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3147fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
31503148 // result type is always 'noreturn'
31513149 const blocktype = wasm.block_empty;
3152 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3153 const target = try self.resolveInst(pl_op.operand);
3154 const target_ty = self.air.typeOf(pl_op.operand);
3155 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
3156 const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.data.cases_len + 1);
3157 defer self.gpa.free(liveness.deaths);
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);
31583156
31593157 var extra_index: usize = switch_br.end;
31603158 var case_i: u32 = 0;
......@@ -3164,24 +3162,24 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
31643162 var case_list = try std.ArrayList(struct {
31653163 values: []const CaseValue,
31663164 body: []const Air.Inst.Index,
3167 }).initCapacity(self.gpa, switch_br.data.cases_len);
3165 }).initCapacity(func.gpa, switch_br.data.cases_len);
31683166 defer for (case_list.items) |case| {
3169 self.gpa.free(case.values);
3167 func.gpa.free(case.values);
31703168 } else case_list.deinit();
31713169
31723170 var lowest_maybe: ?i32 = null;
31733171 var highest_maybe: ?i32 = null;
31743172 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
3175 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
3176 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
3177 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];
31783176 extra_index = case.end + items.len + case_body.len;
3179 const values = try self.gpa.alloc(CaseValue, items.len);
3180 errdefer self.gpa.free(values);
3177 const values = try func.gpa.alloc(CaseValue, items.len);
3178 errdefer func.gpa.free(values);
31813179
31823180 for (items) |ref, i| {
3183 const item_val = self.air.value(ref).?;
3184 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);
31853183 if (lowest_maybe == null or int_val < lowest_maybe.?) {
31863184 lowest_maybe = int_val;
31873185 }
......@@ -3192,7 +3190,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
31923190 }
31933191
31943192 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });
3195 try self.startBlock(.block, blocktype);
3193 try func.startBlock(.block, blocktype);
31963194 }
31973195
31983196 // When highest and lowest are null, we have no cases and can use a jump table
......@@ -3203,12 +3201,12 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
32033201 // When the target is an integer size larger than u32, we have no way to use the value
32043202 // as an index, therefore we also use an if/else-chain for those cases.
32053203 // TODO: Benchmark this to find a proper value, LLVM seems to draw the line at '40~45'.
3206 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;
32073205
3208 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];
32093207 const has_else_body = else_body.len != 0;
32103208 if (has_else_body) {
3211 try self.startBlock(.block, blocktype);
3209 try func.startBlock(.block, blocktype);
32123210 }
32133211
32143212 if (!is_sparse) {
......@@ -3216,25 +3214,25 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
32163214 // The value 'target' represents the index into the table.
32173215 // Each index in the table represents a label to the branch
32183216 // to jump to.
3219 try self.startBlock(.block, blocktype);
3220 try self.emitWValue(target);
3217 try func.startBlock(.block, blocktype);
3218 try func.emitWValue(target);
32213219 if (lowest < 0) {
32223220 // since br_table works using indexes, starting from '0', we must ensure all values
32233221 // we put inside, are atleast 0.
3224 try self.addImm32(lowest * -1);
3225 try self.addTag(.i32_add);
3222 try func.addImm32(lowest * -1);
3223 try func.addTag(.i32_add);
32263224 } else if (lowest > 0) {
32273225 // make the index start from 0 by substracting the lowest value
3228 try self.addImm32(lowest);
3229 try self.addTag(.i32_sub);
3226 try func.addImm32(lowest);
3227 try func.addTag(.i32_sub);
32303228 }
32313229
32323230 // Account for default branch so always add '1'
32333231 const depth = @intCast(u32, highest - lowest + @boolToInt(has_else_body)) + 1;
32343232 const jump_table: Mir.JumpTable = .{ .length = depth };
3235 const table_extra_index = try self.addExtra(jump_table);
3236 try self.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
3237 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);
32383236 var value = lowest;
32393237 while (value <= highest) : (value += 1) {
32403238 // idx represents the branch we jump to
......@@ -3250,11 +3248,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
32503248 // by using a jump table for this instead of if-else chains.
32513249 break :blk if (has_else_body or target_ty.zigTypeTag() == .ErrorSet) case_i else unreachable;
32523250 };
3253 self.mir_extra.appendAssumeCapacity(idx);
3251 func.mir_extra.appendAssumeCapacity(idx);
32543252 } else if (has_else_body) {
3255 self.mir_extra.appendAssumeCapacity(case_i); // default branch
3253 func.mir_extra.appendAssumeCapacity(case_i); // default branch
32563254 }
3257 try self.endBlock();
3255 try func.endBlock();
32583256 }
32593257
32603258 const signedness: std.builtin.Signedness = blk: {
......@@ -3262,79 +3260,79 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!void {
32623260 if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
32633261
32643262 // incase of an actual integer, we emit the correct signedness
3265 break :blk target_ty.intInfo(self.target).signedness;
3263 break :blk target_ty.intInfo(func.target).signedness;
32663264 };
32673265
3268 try self.branches.ensureUnusedCapacity(self.gpa, case_list.items.len + @boolToInt(has_else_body));
3266 try func.branches.ensureUnusedCapacity(func.gpa, case_list.items.len + @boolToInt(has_else_body));
32693267 for (case_list.items) |case, index| {
32703268 // when sparse, we use if/else-chain, so emit conditional checks
32713269 if (is_sparse) {
32723270 // for single value prong we can emit a simple if
32733271 if (case.values.len == 1) {
3274 try self.emitWValue(target);
3275 const val = try self.lowerConstant(case.values[0].value, target_ty);
3276 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);
32773275 const opcode = buildOpcode(.{
3278 .valtype1 = typeToValtype(target_ty, self.target),
3276 .valtype1 = typeToValtype(target_ty, func.target),
32793277 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
32803278 .signedness = signedness,
32813279 });
3282 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3283 try self.addLabel(.br_if, 0);
3280 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3281 try func.addLabel(.br_if, 0);
32843282 } else {
32853283 // in multi-value prongs we must check if any prongs match the target value.
3286 try self.startBlock(.block, blocktype);
3284 try func.startBlock(.block, blocktype);
32873285 for (case.values) |value| {
3288 try self.emitWValue(target);
3289 const val = try self.lowerConstant(value.value, target_ty);
3290 try self.emitWValue(val);
3286 try func.emitWValue(target);
3287 const val = try func.lowerConstant(value.value, target_ty);
3288 try func.emitWValue(val);
32913289 const opcode = buildOpcode(.{
3292 .valtype1 = typeToValtype(target_ty, self.target),
3290 .valtype1 = typeToValtype(target_ty, func.target),
32933291 .op = .eq,
32943292 .signedness = signedness,
32953293 });
3296 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3297 try self.addLabel(.br_if, 0);
3294 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3295 try func.addLabel(.br_if, 0);
32983296 }
32993297 // value did not match any of the prong values
3300 try self.addLabel(.br, 1);
3301 try self.endBlock();
3298 try func.addLabel(.br, 1);
3299 try func.endBlock();
33023300 }
33033301 }
3304 self.branches.appendAssumeCapacity(.{});
3302 func.branches.appendAssumeCapacity(.{});
33053303
3306 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, liveness.deaths[index].len);
3304 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len);
33073305 for (liveness.deaths[index]) |operand| {
3308 self.processDeath(Air.indexToRef(operand));
3306 func.processDeath(Air.indexToRef(operand));
33093307 }
3310 try self.genBody(case.body);
3311 try self.endBlock();
3312 var case_branch = self.branches.pop();
3313 defer case_branch.deinit(self.gpa);
3314 try self.mergeBranch(&case_branch);
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);
33153313 }
33163314
33173315 if (has_else_body) {
3318 self.branches.appendAssumeCapacity(.{});
3316 func.branches.appendAssumeCapacity(.{});
33193317 const else_deaths = liveness.deaths.len - 1;
3320 try self.currentBranch().values.ensureUnusedCapacity(self.gpa, liveness.deaths[else_deaths].len);
3318 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len);
33213319 for (liveness.deaths[else_deaths]) |operand| {
3322 self.processDeath(Air.indexToRef(operand));
3320 func.processDeath(Air.indexToRef(operand));
33233321 }
3324 try self.genBody(else_body);
3325 try self.endBlock();
3326 var else_branch = self.branches.pop();
3327 defer else_branch.deinit(self.gpa);
3328 try self.mergeBranch(&else_branch);
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);
33293327 }
3330 self.finishAir(inst, .none, &.{});
3328 func.finishAir(inst, .none, &.{});
33313329}
33323330
3333fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void {
3334 const un_op = self.air.instructions.items(.data)[inst].un_op;
3335 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
3336 const operand = try self.resolveInst(un_op);
3337 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);
33383336 const pl_ty = err_union_ty.errorUnionPayload();
33393337
33403338 const result = result: {
......@@ -3346,54 +3344,54 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!v
33463344 }
33473345 }
33483346
3349 try self.emitWValue(operand);
3347 try func.emitWValue(operand);
33503348 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
3351 try self.addMemArg(.i32_load16_u, .{
3352 .offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, self.target)),
3353 .alignment = Type.anyerror.abiAlignment(self.target),
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),
33543352 });
33553353 }
33563354
33573355 // Compare the error value with '0'
3358 try self.addImm32(0);
3359 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3356 try func.addImm32(0);
3357 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
33603358
3361 const is_err_tmp = try self.allocLocal(Type.i32);
3362 try self.addLabel(.local_set, is_err_tmp.local.value);
3359 const is_err_tmp = try func.allocLocal(Type.i32);
3360 try func.addLabel(.local_set, is_err_tmp.local.value);
33633361 break :result is_err_tmp;
33643362 };
3365 self.finishAir(inst, result, &.{un_op});
3363 func.finishAir(inst, result, &.{un_op});
33663364}
33673365
3368fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3369 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3370 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{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});
33713369
3372 const operand = try self.resolveInst(ty_op.operand);
3373 const op_ty = self.air.typeOf(ty_op.operand);
3370 const operand = try func.resolveInst(ty_op.operand);
3371 const op_ty = func.air.typeOf(ty_op.operand);
33743372 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
33753373 const payload_ty = err_ty.errorUnionPayload();
33763374
33773375 const result = result: {
33783376 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result WValue{ .none = {} };
33793377
3380 const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target));
3381 if (op_is_ptr or isByRef(payload_ty, self.target)) {
3382 break :result try self.buildPointerOffset(operand, pl_offset, .new);
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);
33833381 }
33843382
3385 const payload = try self.load(operand, payload_ty, pl_offset);
3386 break :result try 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);
33873385 };
3388 self.finishAir(inst, result, &.{ty_op.operand});
3386 func.finishAir(inst, result, &.{ty_op.operand});
33893387}
33903388
3391fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void {
3392 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3393 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
33943392
3395 const operand = try self.resolveInst(ty_op.operand);
3396 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);
33973395 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
33983396 const payload_ty = err_ty.errorUnionPayload();
33993397
......@@ -3403,94 +3401,94 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In
34033401 }
34043402
34053403 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
3406 break :result self.reuseOperand(ty_op.operand, operand);
3404 break :result func.reuseOperand(ty_op.operand, operand);
34073405 }
34083406
3409 const error_val = try self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));
3410 break :result try 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);
34113409 };
3412 self.finishAir(inst, result, &.{ty_op.operand});
3410 func.finishAir(inst, result, &.{ty_op.operand});
34133411}
34143412
3415fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3416 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3417 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
34183416
3419 const operand = try self.resolveInst(ty_op.operand);
3420 const err_ty = self.air.typeOfIndex(inst);
3417 const operand = try func.resolveInst(ty_op.operand);
3418 const err_ty = func.air.typeOfIndex(inst);
34213419
3422 const pl_ty = self.air.typeOf(ty_op.operand);
3420 const pl_ty = func.air.typeOf(ty_op.operand);
34233421 const result = result: {
34243422 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3425 break :result self.reuseOperand(ty_op.operand, operand);
3423 break :result func.reuseOperand(ty_op.operand, operand);
34263424 }
34273425
3428 const err_union = try self.allocStack(err_ty);
3429 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
3430 try self.store(payload_ptr, operand, pl_ty, 0);
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);
34313429
34323430 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
3433 try self.emitWValue(err_union);
3434 try self.addImm32(0);
3435 const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
3436 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
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 });
34373435 break :result err_union;
34383436 };
3439 self.finishAir(inst, result, &.{ty_op.operand});
3437 func.finishAir(inst, result, &.{ty_op.operand});
34403438}
34413439
3442fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3443 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3444 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
34453443
3446 const operand = try self.resolveInst(ty_op.operand);
3447 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);
34483446 const pl_ty = err_ty.errorUnionPayload();
34493447
34503448 const result = result: {
34513449 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3452 break :result self.reuseOperand(ty_op.operand, operand);
3450 break :result func.reuseOperand(ty_op.operand, operand);
34533451 }
34543452
3455 const err_union = try self.allocStack(err_ty);
3453 const err_union = try func.allocStack(err_ty);
34563454 // store error value
3457 try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, self.target)));
3455 try func.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, func.target)));
34583456
34593457 // write 'undefined' to the payload
3460 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
3461 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target));
3462 try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });
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 });
34633461
34643462 break :result err_union;
34653463 };
3466 self.finishAir(inst, result, &.{ty_op.operand});
3464 func.finishAir(inst, result, &.{ty_op.operand});
34673465}
34683466
3469fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!void {
3470 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3471 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
34723470
3473 const ty = self.air.getRefType(ty_op.ty);
3474 const operand = try self.resolveInst(ty_op.operand);
3475 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);
34763474 if (ty.zigTypeTag() == .Vector or operand_ty.zigTypeTag() == .Vector) {
3477 return self.fail("todo Wasm intcast for vectors", .{});
3475 return func.fail("todo Wasm intcast for vectors", .{});
34783476 }
3479 if (ty.abiSize(self.target) > 16 or operand_ty.abiSize(self.target) > 16) {
3480 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", .{});
34813479 }
34823480
3483 const result = try (try self.intcast(operand, operand_ty, ty)).toLocal(self, ty);
3484 self.finishAir(inst, result, &.{});
3481 const result = try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);
3482 func.finishAir(inst, result, &.{});
34853483}
34863484
34873485/// Upcasts or downcasts an integer based on the given and wanted types,
34883486/// and stores the result in a new operand.
34893487/// Asserts type's bitsize <= 128
34903488/// NOTE: May leave the result on the top of the stack.
3491fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
3492 const given_info = given.intInfo(self.target);
3493 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);
34943492 assert(given_info.bits <= 128);
34953493 assert(wanted_info.bits <= 128);
34963494
......@@ -3499,463 +3497,463 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
34993497 if (op_bits == wanted_bits) return operand;
35003498
35013499 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {
3502 try self.emitWValue(operand);
3503 try self.addTag(.i32_wrap_i64);
3500 try func.emitWValue(operand);
3501 try func.addTag(.i32_wrap_i64);
35043502 } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) {
3505 try self.emitWValue(operand);
3506 try self.addTag(switch (wanted_info.signedness) {
3503 try func.emitWValue(operand);
3504 try func.addTag(switch (wanted_info.signedness) {
35073505 .signed => .i64_extend_i32_s,
35083506 .unsigned => .i64_extend_i32_u,
35093507 });
35103508 } else if (wanted_bits == 128) {
35113509 // for 128bit integers we store the integer in the virtual stack, rather than a local
3512 const stack_ptr = try self.allocStack(wanted);
3513 try self.emitWValue(stack_ptr);
3510 const stack_ptr = try func.allocStack(wanted);
3511 try func.emitWValue(stack_ptr);
35143512
35153513 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
35163514 // meaning less store operations are required.
35173515 const lhs = if (op_bits == 32) blk: {
3518 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);
35193517 } else operand;
35203518
35213519 // store msb first
3522 try self.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset());
3520 try func.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset());
35233521
35243522 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
35253523 if (wanted.isSignedInt()) {
3526 try self.emitWValue(stack_ptr);
3527 const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
3528 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());
35293527 } else {
35303528 // Ensure memory of lsb is zero'd
3531 try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
3529 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
35323530 }
35333531 return stack_ptr;
3534 } else return self.load(operand, wanted, 0);
3532 } else return func.load(operand, wanted, 0);
35353533
35363534 return WValue{ .stack = {} };
35373535}
35383536
3539fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
3540 const un_op = self.air.instructions.items(.data)[inst].un_op;
3541 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
3542 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);
35433541
3544 const op_ty = self.air.typeOf(un_op);
3542 const op_ty = func.air.typeOf(un_op);
35453543 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;
3546 const is_null = try self.isNull(operand, optional_ty, opcode);
3547 const result = try is_null.toLocal(self, optional_ty);
3548 self.finishAir(inst, result, &.{un_op});
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});
35493547}
35503548
35513549/// For a given type and operand, checks if it's considered `null`.
35523550/// NOTE: Leaves the result on the stack
3553fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
3554 try self.emitWValue(operand);
3551fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
3552 try func.emitWValue(operand);
35553553 if (!optional_ty.optionalReprIsPayload()) {
35563554 var buf: Type.Payload.ElemType = undefined;
35573555 const payload_ty = optional_ty.optionalChild(&buf);
35583556 // When payload is zero-bits, we can treat operand as a value, rather than
35593557 // a pointer to the stack value
35603558 if (payload_ty.hasRuntimeBitsIgnoreComptime()) {
3561 try self.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 });
3559 try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 });
35623560 }
35633561 }
35643562
35653563 // Compare the null value with '0'
3566 try self.addImm32(0);
3567 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3564 try func.addImm32(0);
3565 try func.addTag(Mir.Inst.Tag.fromOpcode(opcode));
35683566
35693567 return WValue{ .stack = {} };
35703568}
35713569
3572fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!void {
3573 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3574 const opt_ty = self.air.typeOf(ty_op.operand);
3575 const payload_ty = self.air.typeOfIndex(inst);
3576 if (self.liveness.isUnused(inst) or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
3577 return self.finishAir(inst, .none, &.{ty_op.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});
35783576 }
35793577
35803578 const result = result: {
3581 const operand = try self.resolveInst(ty_op.operand);
3582 if (opt_ty.optionalReprIsPayload()) break :result self.reuseOperand(ty_op.operand, operand);
3579 const operand = try func.resolveInst(ty_op.operand);
3580 if (opt_ty.optionalReprIsPayload()) break :result func.reuseOperand(ty_op.operand, operand);
35833581
3584 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
3582 const offset = opt_ty.abiSize(func.target) - payload_ty.abiSize(func.target);
35853583
3586 if (isByRef(payload_ty, self.target)) {
3587 break :result try self.buildPointerOffset(operand, offset, .new);
3584 if (isByRef(payload_ty, func.target)) {
3585 break :result try func.buildPointerOffset(operand, offset, .new);
35883586 }
35893587
3590 const payload = try self.load(operand, payload_ty, @intCast(u32, offset));
3591 break :result try payload.toLocal(self, payload_ty);
3588 const payload = try func.load(operand, payload_ty, @intCast(u32, offset));
3589 break :result try payload.toLocal(func, payload_ty);
35923590 };
3593 self.finishAir(inst, result, &.{ty_op.operand});
3591 func.finishAir(inst, result, &.{ty_op.operand});
35943592}
35953593
3596fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3597 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3598 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3599 const operand = try self.resolveInst(ty_op.operand);
3600 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();
36013599
36023600 const result = result: {
36033601 var buf: Type.Payload.ElemType = undefined;
36043602 const payload_ty = opt_ty.optionalChild(&buf);
36053603 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) {
3606 break :result self.reuseOperand(ty_op.operand, operand);
3604 break :result func.reuseOperand(ty_op.operand, operand);
36073605 }
36083606
3609 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
3610 break :result try 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);
36113609 };
3612 self.finishAir(inst, result, &.{ty_op.operand});
3610 func.finishAir(inst, result, &.{ty_op.operand});
36133611}
36143612
3615fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
3616 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3617 const operand = try self.resolveInst(ty_op.operand);
3618 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();
36193617 var buf: Type.Payload.ElemType = undefined;
36203618 const payload_ty = opt_ty.optionalChild(&buf);
36213619 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3622 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()});
36233621 }
36243622
36253623 if (opt_ty.optionalReprIsPayload()) {
3626 return self.finishAir(inst, operand, &.{ty_op.operand});
3624 return func.finishAir(inst, operand, &.{ty_op.operand});
36273625 }
36283626
3629 const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {
3630 const module = self.bin_file.base.options.module.?;
3631 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)});
36323630 };
36333631
3634 try self.emitWValue(operand);
3635 try self.addImm32(1);
3636 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 });
36373635
3638 const result = try self.buildPointerOffset(operand, offset, .new);
3639 return self.finishAir(inst, result, &.{ty_op.operand});
3636 const result = try func.buildPointerOffset(operand, offset, .new);
3637 return func.finishAir(inst, result, &.{ty_op.operand});
36403638}
36413639
3642fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!void {
3643 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3644 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3645 const payload_ty = self.air.typeOf(ty_op.operand);
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);
36463644
36473645 const result = result: {
36483646 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3649 const non_null_bit = try self.allocStack(Type.initTag(.u1));
3650 try self.emitWValue(non_null_bit);
3651 try self.addImm32(1);
3652 try self.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 });
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 });
36533651 break :result non_null_bit;
36543652 }
36553653
3656 const operand = try self.resolveInst(ty_op.operand);
3657 const op_ty = self.air.typeOfIndex(inst);
3654 const operand = try func.resolveInst(ty_op.operand);
3655 const op_ty = func.air.typeOfIndex(inst);
36583656 if (op_ty.optionalReprIsPayload()) {
3659 break :result self.reuseOperand(ty_op.operand, operand);
3657 break :result func.reuseOperand(ty_op.operand, operand);
36603658 }
3661 const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse {
3662 const module = self.bin_file.base.options.module.?;
3663 return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)});
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)});
36643662 };
36653663
36663664 // Create optional type, set the non-null bit, and store the operand inside the optional type
3667 const result_ptr = try self.allocStack(op_ty);
3668 try self.emitWValue(result_ptr);
3669 try self.addImm32(1);
3670 try self.addMemArg(.i32_store8, .{ .offset = result_ptr.offset(), .alignment = 1 });
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 });
36713669
3672 const payload_ptr = try self.buildPointerOffset(result_ptr, offset, .new);
3673 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);
36743672 break :result result_ptr;
36753673 };
36763674
3677 self.finishAir(inst, result, &.{ty_op.operand});
3675 func.finishAir(inst, result, &.{ty_op.operand});
36783676}
36793677
3680fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
3681 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3682 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3683 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
36843682
3685 const lhs = try self.resolveInst(bin_op.lhs);
3686 const rhs = try self.resolveInst(bin_op.rhs);
3687 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);
36883686
3689 const slice = try self.allocStack(slice_ty);
3690 try self.store(slice, lhs, Type.usize, 0);
3691 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());
36923690
3693 self.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs });
3691 func.finishAir(inst, slice, &.{ bin_op.lhs, bin_op.rhs });
36943692}
36953693
3696fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!void {
3697 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3698 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
36993697
3700 const operand = try self.resolveInst(ty_op.operand);
3701 const len = try self.load(operand, Type.usize, self.ptrSize());
3702 const result = try len.toLocal(self, Type.usize);
3703 self.finishAir(inst, result, &.{ty_op.operand});
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});
37043702}
37053703
3706fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3707 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3708 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
37093707
3710 const slice_ty = self.air.typeOf(bin_op.lhs);
3711 const slice = try self.resolveInst(bin_op.lhs);
3712 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);
37133711 const elem_ty = slice_ty.childType();
3714 const elem_size = elem_ty.abiSize(self.target);
3712 const elem_size = elem_ty.abiSize(func.target);
37153713
37163714 // load pointer onto stack
3717 _ = try self.load(slice, Type.usize, 0);
3715 _ = try func.load(slice, Type.usize, 0);
37183716
37193717 // calculate index into slice
3720 try self.emitWValue(index);
3721 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3722 try self.addTag(.i32_mul);
3723 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);
37243722
3725 const result_ptr = try self.allocLocal(elem_ty);
3726 try self.addLabel(.local_set, result_ptr.local.value);
3723 const result_ptr = try func.allocLocal(elem_ty);
3724 try func.addLabel(.local_set, result_ptr.local.value);
37273725
3728 const result = if (!isByRef(elem_ty, self.target)) result: {
3729 const elem_val = try self.load(result_ptr, elem_ty, 0);
3730 break :result try elem_val.toLocal(self, elem_ty);
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);
37313729 } else result_ptr;
37323730
3733 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
3731 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
37343732}
37353733
3736fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3737 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3738 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3739 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
37403738
3741 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
3742 const elem_size = elem_ty.abiSize(self.target);
3739 const elem_ty = func.air.getRefType(ty_pl.ty).childType();
3740 const elem_size = elem_ty.abiSize(func.target);
37433741
3744 const slice = try self.resolveInst(bin_op.lhs);
3745 const index = try self.resolveInst(bin_op.rhs);
3742 const slice = try func.resolveInst(bin_op.lhs);
3743 const index = try func.resolveInst(bin_op.rhs);
37463744
3747 _ = try self.load(slice, Type.usize, 0);
3745 _ = try func.load(slice, Type.usize, 0);
37483746
37493747 // calculate index into slice
3750 try self.emitWValue(index);
3751 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3752 try self.addTag(.i32_mul);
3753 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);
37543752
3755 const result = try self.allocLocal(Type.i32);
3756 try self.addLabel(.local_set, result.local.value);
3757 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
37583756}
37593757
3760fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3761 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3762 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
3763 const operand = try self.resolveInst(ty_op.operand);
3764 const ptr = try self.load(operand, Type.usize, 0);
3765 const result = try ptr.toLocal(self, Type.usize);
3766 self.finishAir(inst, result, &.{ty_op.operand});
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});
37673765}
37683766
3769fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
3770 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3771 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{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});
37723770
3773 const operand = try self.resolveInst(ty_op.operand);
3774 const wanted_ty = self.air.getRefType(ty_op.ty);
3775 const op_ty = self.air.typeOf(ty_op.operand);
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);
37763774
3777 const int_info = op_ty.intInfo(self.target);
3775 const int_info = op_ty.intInfo(func.target);
37783776 if (toWasmBits(int_info.bits) == null) {
3779 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});
37803778 }
37813779
3782 var result = try self.intcast(operand, op_ty, wanted_ty);
3783 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;
37843782 const wasm_bits = toWasmBits(wanted_bits).?;
37853783 if (wasm_bits != wanted_bits) {
3786 result = try self.wrapOperand(result, wanted_ty);
3784 result = try func.wrapOperand(result, wanted_ty);
37873785 }
37883786
3789 self.finishAir(inst, try result.toLocal(self, wanted_ty), &.{ty_op.operand});
3787 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
37903788}
37913789
3792fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
3793 const un_op = self.air.instructions.items(.data)[inst].un_op;
3794 const result = if (self.liveness.isUnused(inst))
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))
37953793 WValue{ .none = {} }
37963794 else result: {
3797 const operand = try self.resolveInst(un_op);
3798 break :result self.reuseOperand(un_op, operand);
3795 const operand = try func.resolveInst(un_op);
3796 break :result func.reuseOperand(un_op, operand);
37993797 };
38003798
3801 self.finishAir(inst, result, &.{un_op});
3799 func.finishAir(inst, result, &.{un_op});
38023800}
38033801
3804fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!void {
3805 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3806 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
38073805
3808 const operand = try self.resolveInst(ty_op.operand);
3809 const array_ty = self.air.typeOf(ty_op.operand).childType();
3810 const slice_ty = self.air.getRefType(ty_op.ty);
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);
38113809
38123810 // create a slice on the stack
3813 const slice_local = try self.allocStack(slice_ty);
3811 const slice_local = try func.allocStack(slice_ty);
38143812
38153813 // store the array ptr in the slice
38163814 if (array_ty.hasRuntimeBitsIgnoreComptime()) {
3817 try self.store(slice_local, operand, Type.usize, 0);
3815 try func.store(slice_local, operand, Type.usize, 0);
38183816 }
38193817
38203818 // store the length of the array in the slice
38213819 const len = WValue{ .imm32 = @intCast(u32, array_ty.arrayLen()) };
3822 try self.store(slice_local, len, Type.usize, self.ptrSize());
3820 try func.store(slice_local, len, Type.usize, func.ptrSize());
38233821
3824 self.finishAir(inst, slice_local, &.{ty_op.operand});
3822 func.finishAir(inst, slice_local, &.{ty_op.operand});
38253823}
38263824
3827fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
3828 const un_op = self.air.instructions.items(.data)[inst].un_op;
3829 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
3830 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);
38313829
38323830 const result = switch (operand) {
38333831 // for stack offset, return a pointer to this offset.
3834 .stack_offset => try self.buildPointerOffset(operand, 0, .new),
3835 else => self.reuseOperand(un_op, operand),
3832 .stack_offset => try func.buildPointerOffset(operand, 0, .new),
3833 else => func.reuseOperand(un_op, operand),
38363834 };
3837 self.finishAir(inst, result, &.{un_op});
3835 func.finishAir(inst, result, &.{un_op});
38383836}
38393837
3840fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
3841 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3842 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
38433841
3844 const ptr_ty = self.air.typeOf(bin_op.lhs);
3845 const ptr = try self.resolveInst(bin_op.lhs);
3846 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);
38473845 const elem_ty = ptr_ty.childType();
3848 const elem_size = elem_ty.abiSize(self.target);
3846 const elem_size = elem_ty.abiSize(func.target);
38493847
38503848 // load pointer onto the stack
38513849 if (ptr_ty.isSlice()) {
3852 _ = try self.load(ptr, Type.usize, 0);
3850 _ = try func.load(ptr, Type.usize, 0);
38533851 } else {
3854 try self.lowerToStack(ptr);
3852 try func.lowerToStack(ptr);
38553853 }
38563854
38573855 // calculate index into slice
3858 try self.emitWValue(index);
3859 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3860 try self.addTag(.i32_mul);
3861 try self.addTag(.i32_add);
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);
38623860
38633861 const elem_result = val: {
3864 var result = try self.allocLocal(elem_ty);
3865 try self.addLabel(.local_set, result.local.value);
3866 if (isByRef(elem_ty, self.target)) {
3862 var result = try func.allocLocal(elem_ty);
3863 try func.addLabel(.local_set, result.local.value);
3864 if (isByRef(elem_ty, func.target)) {
38673865 break :val result;
38683866 }
3869 defer result.free(self); // only free if it's not returned like above
3867 defer result.free(func); // only free if it's not returned like above
38703868
3871 const elem_val = try self.load(result, elem_ty, 0);
3872 break :val try 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);
38733871 };
3874 self.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
3872 func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
38753873}
38763874
3877fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
3878 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3879 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3880 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
38813879
3882 const ptr_ty = self.air.typeOf(bin_op.lhs);
3883 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
3884 const elem_size = elem_ty.abiSize(self.target);
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);
38853883
3886 const ptr = try self.resolveInst(bin_op.lhs);
3887 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);
38883886
38893887 // load pointer onto the stack
38903888 if (ptr_ty.isSlice()) {
3891 _ = try self.load(ptr, Type.usize, 0);
3889 _ = try func.load(ptr, Type.usize, 0);
38923890 } else {
3893 try self.lowerToStack(ptr);
3891 try func.lowerToStack(ptr);
38943892 }
38953893
38963894 // calculate index into ptr
3897 try self.emitWValue(index);
3898 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
3899 try self.addTag(.i32_mul);
3900 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);
39013899
3902 const result = try self.allocLocal(Type.i32);
3903 try self.addLabel(.local_set, result.local.value);
3904 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
39053903}
39063904
3907fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
3908 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3909 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
3910 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
39113909
3912 const ptr = try self.resolveInst(bin_op.lhs);
3913 const offset = try self.resolveInst(bin_op.rhs);
3914 const ptr_ty = self.air.typeOf(bin_op.lhs);
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);
39153913 const pointee_ty = switch (ptr_ty.ptrSize()) {
39163914 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
39173915 else => ptr_ty.childType(),
39183916 };
39193917
3920 const valtype = typeToValtype(Type.usize, self.target);
3918 const valtype = typeToValtype(Type.usize, func.target);
39213919 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
39223920 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
39233921
3924 try self.lowerToStack(ptr);
3925 try self.emitWValue(offset);
3926 try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target))));
3927 try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));
3928 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));
39293927
3930 const result = try self.allocLocal(Type.usize);
3931 try self.addLabel(.local_set, result.local.value);
3932 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
39333931}
39343932
3935fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!void {
3936 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3937 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;
39383936
3939 const ptr = try self.resolveInst(pl_op.operand);
3940 const value = try self.resolveInst(bin_op.lhs);
3941 const len = try self.resolveInst(bin_op.rhs);
3942 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);
39433941
3944 self.finishAir(inst, .none, &.{pl_op.operand});
3942 func.finishAir(inst, .none, &.{pl_op.operand});
39453943}
39463944
39473945/// Sets a region of memory at `ptr` to the value of `value`
39483946/// When the user has enabled the bulk_memory feature, we lower
39493947/// this to wasm's memset instruction. When the feature is not present,
39503948/// we implement it manually.
3951fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void {
3949fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!void {
39523950 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
39533951 // If not, we lower it ourselves
3954 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {
3955 try self.lowerToStack(ptr);
3956 try self.emitWValue(value);
3957 try self.emitWValue(len);
3958 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);
39593957 return;
39603958 }
39613959
......@@ -3972,14 +3970,14 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
39723970 var offset: u32 = 0;
39733971 const base = ptr.offset();
39743972 while (offset < length) : (offset += 1) {
3975 try self.emitWValue(ptr);
3976 try self.emitWValue(value);
3977 switch (self.arch()) {
3973 try func.emitWValue(ptr);
3974 try func.emitWValue(value);
3975 switch (func.arch()) {
39783976 .wasm32 => {
3979 try self.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 });
3977 try func.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 });
39803978 },
39813979 .wasm64 => {
3982 try self.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 });
3980 try func.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 });
39833981 },
39843982 else => unreachable,
39853983 }
......@@ -3988,378 +3986,378 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
39883986 else => {
39893987 // TODO: We should probably lower this to a call to compiler_rt
39903988 // But for now, we implement it manually
3991 const offset = try self.ensureAllocLocal(Type.usize); // local for counter
3989 const offset = try func.ensureAllocLocal(Type.usize); // local for counter
39923990 // outer block to jump to when loop is done
3993 try self.startBlock(.block, wasm.block_empty);
3994 try self.startBlock(.loop, wasm.block_empty);
3995 try self.emitWValue(offset);
3996 try self.emitWValue(len);
3997 switch (self.arch()) {
3998 .wasm32 => try self.addTag(.i32_eq),
3999 .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),
40003998 else => unreachable,
40013999 }
4002 try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
4003 try self.emitWValue(ptr);
4004 try self.emitWValue(offset);
4005 switch (self.arch()) {
4006 .wasm32 => try self.addTag(.i32_add),
4007 .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),
40084006 else => unreachable,
40094007 }
4010 try self.emitWValue(value);
4011 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()) {
40124010 .wasm32 => .i32_store8,
40134011 .wasm64 => .i64_store8,
40144012 else => unreachable,
40154013 };
4016 try self.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 });
4017 try self.emitWValue(offset);
4018 try self.addImm32(1);
4019 switch (self.arch()) {
4020 .wasm32 => try self.addTag(.i32_add),
4021 .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),
40224020 else => unreachable,
40234021 }
4024 try self.addLabel(.local_set, offset.local.value);
4025 try self.addLabel(.br, 0); // jump to start of loop
4026 try self.endBlock();
4027 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();
40284026 },
40294027 }
40304028}
40314029
4032fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!void {
4033 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4034 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
40354033
4036 const array_ty = self.air.typeOf(bin_op.lhs);
4037 const array = try self.resolveInst(bin_op.lhs);
4038 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);
40394037 const elem_ty = array_ty.childType();
4040 const elem_size = elem_ty.abiSize(self.target);
4038 const elem_size = elem_ty.abiSize(func.target);
40414039
4042 try self.lowerToStack(array);
4043 try self.emitWValue(index);
4044 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
4045 try self.addTag(.i32_mul);
4046 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);
40474045
40484046 const elem_result = val: {
4049 var result = try self.allocLocal(Type.usize);
4050 try self.addLabel(.local_set, result.local.value);
4047 var result = try func.allocLocal(Type.usize);
4048 try func.addLabel(.local_set, result.local.value);
40514049
4052 if (isByRef(elem_ty, self.target)) {
4050 if (isByRef(elem_ty, func.target)) {
40534051 break :val result;
40544052 }
4055 defer result.free(self); // only free if no longer needed and not returned like above
4053 defer result.free(func); // only free if no longer needed and not returned like above
40564054
4057 const elem_val = try self.load(result, elem_ty, 0);
4058 break :val try elem_val.toLocal(self, elem_ty);
4055 const elem_val = try func.load(result, elem_ty, 0);
4056 break :val try elem_val.toLocal(func, elem_ty);
40594057 };
40604058
4061 self.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
4059 func.finishAir(inst, elem_result, &.{ bin_op.lhs, bin_op.rhs });
40624060}
40634061
4064fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!void {
4065 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4066 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
40674065
4068 const operand = try self.resolveInst(ty_op.operand);
4069 const dest_ty = self.air.typeOfIndex(inst);
4070 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);
40714069
4072 if (op_ty.abiSize(self.target) > 8) {
4073 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", .{});
40744072 }
40754073
4076 try self.emitWValue(operand);
4074 try func.emitWValue(operand);
40774075 const op = buildOpcode(.{
40784076 .op = .trunc,
4079 .valtype1 = typeToValtype(dest_ty, self.target),
4080 .valtype2 = typeToValtype(op_ty, self.target),
4077 .valtype1 = typeToValtype(dest_ty, func.target),
4078 .valtype2 = typeToValtype(op_ty, func.target),
40814079 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,
40824080 });
4083 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
4084 const wrapped = try self.wrapOperand(.{ .stack = {} }, dest_ty);
4085 const result = try wrapped.toLocal(self, dest_ty);
4086 self.finishAir(inst, result, &.{ty_op.operand});
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});
40874085}
40884086
4089fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!void {
4090 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4091 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
40924090
4093 const operand = try self.resolveInst(ty_op.operand);
4094 const dest_ty = self.air.typeOfIndex(inst);
4095 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);
40964094
4097 if (op_ty.abiSize(self.target) > 8) {
4098 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", .{});
40994097 }
41004098
4101 try self.emitWValue(operand);
4099 try func.emitWValue(operand);
41024100 const op = buildOpcode(.{
41034101 .op = .convert,
4104 .valtype1 = typeToValtype(dest_ty, self.target),
4105 .valtype2 = typeToValtype(op_ty, self.target),
4102 .valtype1 = typeToValtype(dest_ty, func.target),
4103 .valtype2 = typeToValtype(op_ty, func.target),
41064104 .signedness = if (op_ty.isSignedInt()) .signed else .unsigned,
41074105 });
4108 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
4106 try func.addTag(Mir.Inst.Tag.fromOpcode(op));
41094107
4110 const result = try self.allocLocal(dest_ty);
4111 try self.addLabel(.local_set, result.local.value);
4112 self.finishAir(inst, result, &.{ty_op.operand});
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});
41134111}
41144112
4115fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!void {
4116 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4117 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);
41184116
41194117 _ = operand;
4120 return self.fail("TODO: Implement wasm airSplat", .{});
4118 return func.fail("TODO: Implement wasm airSplat", .{});
41214119}
41224120
4123fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!void {
4124 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4125 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);
41264124
41274125 _ = operand;
4128 return self.fail("TODO: Implement wasm airSelect", .{});
4126 return func.fail("TODO: Implement wasm airSelect", .{});
41294127}
41304128
4131fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!void {
4132 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4133 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);
41344132
41354133 _ = operand;
4136 return self.fail("TODO: Implement wasm airShuffle", .{});
4134 return func.fail("TODO: Implement wasm airShuffle", .{});
41374135}
41384136
4139fn airReduce(self: *Self, inst: Air.Inst.Index) InnerError!void {
4140 const reduce = self.air.instructions.items(.data)[inst].reduce;
4141 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);
41424140
41434141 _ = operand;
4144 return self.fail("TODO: Implement wasm airReduce", .{});
4142 return func.fail("TODO: Implement wasm airReduce", .{});
41454143}
41464144
4147fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
4148 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4149 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);
41504148 const len = @intCast(usize, result_ty.arrayLen());
4151 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
4149 const elements = @ptrCast([]const Air.Inst.Ref, func.air.extra[ty_pl.payload..][0..len]);
41524150
41534151 const result: WValue = result_value: {
4154 if (self.liveness.isUnused(inst)) break :result_value WValue.none;
4152 if (func.liveness.isUnused(inst)) break :result_value WValue.none;
41554153 switch (result_ty.zigTypeTag()) {
41564154 .Array => {
4157 const result = try self.allocStack(result_ty);
4155 const result = try func.allocStack(result_ty);
41584156 const elem_ty = result_ty.childType();
4159 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
4157 const elem_size = @intCast(u32, elem_ty.abiSize(func.target));
41604158
41614159 // When the element type is by reference, we must copy the entire
41624160 // value. It is therefore safer to move the offset pointer and store
41634161 // each value individually, instead of using store offsets.
4164 if (isByRef(elem_ty, self.target)) {
4162 if (isByRef(elem_ty, func.target)) {
41654163 // copy stack pointer into a temporary local, which is
41664164 // moved for each element to store each value in the right position.
4167 const offset = try self.buildPointerOffset(result, 0, .new);
4165 const offset = try func.buildPointerOffset(result, 0, .new);
41684166 for (elements) |elem, elem_index| {
4169 const elem_val = try self.resolveInst(elem);
4170 try self.store(offset, elem_val, elem_ty, 0);
4167 const elem_val = try func.resolveInst(elem);
4168 try func.store(offset, elem_val, elem_ty, 0);
41714169
41724170 if (elem_index < elements.len - 1) {
4173 _ = try self.buildPointerOffset(offset, elem_size, .modify);
4171 _ = try func.buildPointerOffset(offset, elem_size, .modify);
41744172 }
41754173 }
41764174 } else {
41774175 var offset: u32 = 0;
41784176 for (elements) |elem| {
4179 const elem_val = try self.resolveInst(elem);
4180 try self.store(result, elem_val, elem_ty, offset);
4177 const elem_val = try func.resolveInst(elem);
4178 try func.store(result, elem_val, elem_ty, offset);
41814179 offset += elem_size;
41824180 }
41834181 }
41844182 break :result_value result;
41854183 },
41864184 .Struct => {
4187 const result = try self.allocStack(result_ty);
4188 const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset
4185 const result = try func.allocStack(result_ty);
4186 const offset = try func.buildPointerOffset(result, 0, .new); // pointer to offset
41894187 for (elements) |elem, elem_index| {
41904188 if (result_ty.structFieldValueComptime(elem_index) != null) continue;
41914189
41924190 const elem_ty = result_ty.structFieldType(elem_index);
4193 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
4194 const value = try self.resolveInst(elem);
4195 try self.store(offset, value, elem_ty, 0);
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);
41964194
41974195 if (elem_index < elements.len - 1) {
4198 _ = try self.buildPointerOffset(offset, elem_size, .modify);
4196 _ = try func.buildPointerOffset(offset, elem_size, .modify);
41994197 }
42004198 }
42014199
42024200 break :result_value result;
42034201 },
4204 .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
4202 .Vector => return func.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
42054203 else => unreachable,
42064204 }
42074205 };
4208 self.finishAir(inst, result, &.{});
4206 func.finishAir(inst, result, &.{});
42094207}
42104208
4211fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!void {
4212 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4213 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
4214 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{extra.init});
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});
42154213
42164214 const result = result: {
4217 const union_ty = self.air.typeOfIndex(inst);
4218 const layout = union_ty.unionGetLayout(self.target);
4215 const union_ty = func.air.typeOfIndex(inst);
4216 const layout = union_ty.unionGetLayout(func.target);
42194217 if (layout.payload_size == 0) {
42204218 if (layout.tag_size == 0) {
42214219 break :result WValue{ .none = {} };
42224220 }
4223 assert(!isByRef(union_ty, self.target));
4221 assert(!isByRef(union_ty, func.target));
42244222 break :result WValue{ .imm32 = extra.field_index };
42254223 }
4226 assert(isByRef(union_ty, self.target));
4224 assert(isByRef(union_ty, func.target));
42274225
4228 const result_ptr = try self.allocStack(union_ty);
4229 const payload = try self.resolveInst(extra.init);
4226 const result_ptr = try func.allocStack(union_ty);
4227 const payload = try func.resolveInst(extra.init);
42304228 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
42314229 assert(union_obj.haveFieldTypes());
42324230 const field = union_obj.fields.values()[extra.field_index];
42334231
42344232 if (layout.tag_align >= layout.payload_align) {
4235 const payload_ptr = try self.buildPointerOffset(result_ptr, layout.tag_size, .new);
4236 try self.store(payload_ptr, payload, field.ty, 0);
4233 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);
4234 try func.store(payload_ptr, payload, field.ty, 0);
42374235 } else {
4238 try self.store(result_ptr, payload, field.ty, 0);
4236 try func.store(result_ptr, payload, field.ty, 0);
42394237 }
42404238 break :result result_ptr;
42414239 };
42424240
4243 self.finishAir(inst, result, &.{extra.init});
4241 func.finishAir(inst, result, &.{extra.init});
42444242}
42454243
4246fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!void {
4247 const prefetch = self.air.instructions.items(.data)[inst].prefetch;
4248 self.finishAir(inst, .none, &.{prefetch.ptr});
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});
42494247}
42504248
4251fn airWasmMemorySize(self: *Self, inst: Air.Inst.Index) InnerError!void {
4252 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4253 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{pl_op.operand});
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});
42544252
4255 const result = try self.allocLocal(self.air.typeOfIndex(inst));
4256 try self.addLabel(.memory_size, pl_op.payload);
4257 try self.addLabel(.local_set, result.local.value);
4258 self.finishAir(inst, result, &.{pl_op.operand});
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});
42594257}
42604258
4261fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !void {
4262 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4263 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{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});
42644262
4265 const operand = try self.resolveInst(pl_op.operand);
4266 const result = try self.allocLocal(self.air.typeOfIndex(inst));
4267 try self.emitWValue(operand);
4268 try self.addLabel(.memory_grow, pl_op.payload);
4269 try self.addLabel(.local_set, result.local.value);
4270 self.finishAir(inst, result, &.{pl_op.operand});
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});
42714269}
42724270
4273fn 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 {
42744272 assert(operand_ty.hasRuntimeBitsIgnoreComptime());
42754273 assert(op == .eq or op == .neq);
42764274 var buf: Type.Payload.ElemType = undefined;
42774275 const payload_ty = operand_ty.optionalChild(&buf);
4278 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));
42794277
42804278 // We store the final result in here that will be validated
42814279 // if the optional is truly equal.
4282 var result = try self.ensureAllocLocal(Type.initTag(.i32));
4283 defer result.free(self);
4284
4285 try self.startBlock(.block, wasm.block_empty);
4286 _ = try self.isNull(lhs, operand_ty, .i32_eq);
4287 _ = try self.isNull(rhs, operand_ty, .i32_eq);
4288 try self.addTag(.i32_ne); // inverse so we can exit early
4289 try self.addLabel(.br_if, 0);
4290
4291 _ = try self.load(lhs, payload_ty, offset);
4292 _ = try self.load(rhs, payload_ty, offset);
4293 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, self.target) });
4294 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
4295 try self.addLabel(.br_if, 0);
4296
4297 try self.addImm32(1);
4298 try self.addLabel(.local_set, result.local.value);
4299 try self.endBlock();
4300
4301 try self.emitWValue(result);
4302 try self.addImm32(0);
4303 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);
43044302 return WValue{ .stack = {} };
43054303}
43064304
43074305/// Compares big integers by checking both its high bits and low bits.
43084306/// NOTE: Leaves the result of the comparison on top of the stack.
43094307/// TODO: Lower this to compiler_rt call when bitsize > 128
4310fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
4311 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);
43124310 assert(!(lhs != .stack and rhs == .stack));
4313 if (operand_ty.intInfo(self.target).bits > 128) {
4314 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});
43154313 }
43164314
4317 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
4318 defer lhs_high_bit.free(self);
4319 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
4320 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);
43214319
43224320 switch (op) {
43234321 .eq, .neq => {
4324 const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
4325 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
4326 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
4327 const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
4328 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");
43294327
43304328 switch (op) {
4331 .eq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),
4332 .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),
43334331 else => unreachable,
43344332 }
43354333 },
43364334 else => {
43374335 const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64;
43384336 // leave those value on top of the stack for '.select'
4339 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
4340 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
4341 _ = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op);
4342 _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op);
4343 _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);
4344 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);
43454343 },
43464344 }
43474345
43484346 return WValue{ .stack = {} };
43494347}
43504348
4351fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
4352 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4353 const un_ty = self.air.typeOf(bin_op.lhs).childType();
4354 const tag_ty = self.air.typeOf(bin_op.rhs);
4355 const layout = un_ty.unionGetLayout(self.target);
4356 if (layout.tag_size == 0) return self.finishAir(inst, .none, &.{ bin_op.lhs, 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 });
43574355
4358 const union_ptr = try self.resolveInst(bin_op.lhs);
4359 const new_tag = try self.resolveInst(bin_op.rhs);
4356 const union_ptr = try func.resolveInst(bin_op.lhs);
4357 const new_tag = try func.resolveInst(bin_op.rhs);
43604358 if (layout.payload_size == 0) {
4361 try self.store(union_ptr, new_tag, tag_ty, 0);
4362 return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4359 try func.store(union_ptr, new_tag, tag_ty, 0);
4360 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
43634361 }
43644362
43654363 // when the tag alignment is smaller than the payload, the field will be stored
......@@ -4367,54 +4365,54 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
43674365 const offset = if (layout.tag_align < layout.payload_align) blk: {
43684366 break :blk @intCast(u32, layout.payload_size);
43694367 } else @as(u32, 0);
4370 try self.store(union_ptr, new_tag, tag_ty, offset);
4371 self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4368 try func.store(union_ptr, new_tag, tag_ty, offset);
4369 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
43724370}
43734371
4374fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!void {
4375 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4376 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
43774375
4378 const un_ty = self.air.typeOf(ty_op.operand);
4379 const tag_ty = self.air.typeOfIndex(inst);
4380 const layout = un_ty.unionGetLayout(self.target);
4381 if (layout.tag_size == 0) return self.finishAir(inst, .none, &.{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});
43824380
4383 const operand = try self.resolveInst(ty_op.operand);
4381 const operand = try func.resolveInst(ty_op.operand);
43844382 // when the tag alignment is smaller than the payload, the field will be stored
43854383 // after the payload.
43864384 const offset = if (layout.tag_align < layout.payload_align) blk: {
43874385 break :blk @intCast(u32, layout.payload_size);
43884386 } else @as(u32, 0);
4389 const tag = try self.load(operand, tag_ty, offset);
4390 const result = try tag.toLocal(self, tag_ty);
4391 self.finishAir(inst, result, &.{ty_op.operand});
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});
43924390}
43934391
4394fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!void {
4395 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4396 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
43974395
4398 const dest_ty = self.air.typeOfIndex(inst);
4399 const operand = try self.resolveInst(ty_op.operand);
4400 const extended = try self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty);
4401 const result = try extended.toLocal(self, dest_ty);
4402 self.finishAir(inst, result, &.{ty_op.operand});
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});
44034401}
44044402
44054403/// Extends a float from a given `Type` to a larger wanted `Type`
44064404/// NOTE: Leaves the result on the stack
4407fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4408 const given_bits = given.floatBits(self.target);
4409 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);
44104408
44114409 if (wanted_bits == 64 and given_bits == 32) {
4412 try self.emitWValue(operand);
4413 try self.addTag(.f64_promote_f32);
4410 try func.emitWValue(operand);
4411 try func.addTag(.f64_promote_f32);
44144412 return WValue{ .stack = {} };
44154413 } else if (given_bits == 16) {
44164414 // call __extendhfsf2(f16) f32
4417 const f32_result = try self.callIntrinsic(
4415 const f32_result = try func.callIntrinsic(
44184416 "__extendhfsf2",
44194417 &.{Type.f16},
44204418 Type.f32,
......@@ -4425,162 +4423,162 @@ fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WVa
44254423 return f32_result;
44264424 }
44274425 if (wanted_bits == 64) {
4428 try self.addTag(.f64_promote_f32);
4426 try func.addTag(.f64_promote_f32);
44294427 return WValue{ .stack = {} };
44304428 }
4431 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});
44324430 } else {
44334431 // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2
4434 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});
44354433 }
44364434}
44374435
4438fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!void {
4439 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4440 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
44414439
4442 const dest_ty = self.air.typeOfIndex(inst);
4443 const operand = try self.resolveInst(ty_op.operand);
4444 const trunc = try self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty);
4445 const result = try trunc.toLocal(self, dest_ty);
4446 self.finishAir(inst, result, &.{ty_op.operand});
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});
44474445}
44484446
44494447/// Truncates a float from a given `Type` to its wanted `Type`
44504448/// NOTE: The result value remains on the stack
4451fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue {
4452 const given_bits = given.floatBits(self.target);
4453 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);
44544452
44554453 if (wanted_bits == 32 and given_bits == 64) {
4456 try self.emitWValue(operand);
4457 try self.addTag(.f32_demote_f64);
4454 try func.emitWValue(operand);
4455 try func.addTag(.f32_demote_f64);
44584456 return WValue{ .stack = {} };
44594457 } else if (wanted_bits == 16) {
44604458 const op: WValue = if (given_bits == 64) blk: {
4461 try self.emitWValue(operand);
4462 try self.addTag(.f32_demote_f64);
4459 try func.emitWValue(operand);
4460 try func.addTag(.f32_demote_f64);
44634461 break :blk WValue{ .stack = {} };
44644462 } else operand;
44654463
44664464 // call __truncsfhf2(f32) f16
4467 return self.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op});
4465 return func.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op});
44684466 } else {
44694467 // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2
4470 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});
44714469 }
44724470}
44734471
4474fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!void {
4475 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4476 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
44774475
4478 const err_set_ty = self.air.typeOf(ty_op.operand).childType();
4476 const err_set_ty = func.air.typeOf(ty_op.operand).childType();
44794477 const payload_ty = err_set_ty.errorUnionPayload();
4480 const operand = try self.resolveInst(ty_op.operand);
4478 const operand = try func.resolveInst(ty_op.operand);
44814479
44824480 // set error-tag to '0' to annotate error union is non-error
4483 try self.store(
4481 try func.store(
44844482 operand,
44854483 .{ .imm32 = 0 },
44864484 Type.anyerror,
4487 @intCast(u32, errUnionErrorOffset(payload_ty, self.target)),
4485 @intCast(u32, errUnionErrorOffset(payload_ty, func.target)),
44884486 );
44894487
44904488 const result = result: {
4491 if (self.liveness.isUnused(inst)) break :result WValue{ .none = {} };
4489 if (func.liveness.isUnused(inst)) break :result WValue{ .none = {} };
44924490
44934491 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4494 break :result self.reuseOperand(ty_op.operand, operand);
4492 break :result func.reuseOperand(ty_op.operand, operand);
44954493 }
44964494
4497 break :result try 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);
44984496 };
4499 self.finishAir(inst, result, &.{ty_op.operand});
4497 func.finishAir(inst, result, &.{ty_op.operand});
45004498}
45014499
4502fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
4503 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4504 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
4505 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{extra.field_ptr});
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});
45064504
4507 const field_ptr = try self.resolveInst(extra.field_ptr);
4508 const struct_ty = self.air.getRefType(ty_pl.ty).childType();
4509 const field_offset = struct_ty.structFieldOffset(extra.field_index, self.target);
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);
45104508
45114509 const result = if (field_offset != 0) result: {
4512 const base = try self.buildPointerOffset(field_ptr, 0, .new);
4513 try self.addLabel(.local_get, base.local.value);
4514 try self.addImm32(@bitCast(i32, @intCast(u32, field_offset)));
4515 try self.addTag(.i32_sub);
4516 try self.addLabel(.local_set, base.local.value);
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);
45174515 break :result base;
4518 } else self.reuseOperand(extra.field_ptr, field_ptr);
4516 } else func.reuseOperand(extra.field_ptr, field_ptr);
45194517
4520 self.finishAir(inst, result, &.{extra.field_ptr});
4518 func.finishAir(inst, result, &.{extra.field_ptr});
45214519}
45224520
4523fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void {
4524 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4525 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
4526 const dst = try self.resolveInst(pl_op.operand);
4527 const src = try self.resolveInst(bin_op.lhs);
4528 const len = try self.resolveInst(bin_op.rhs);
4529 try self.memcpy(dst, src, len);
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);
45304528
4531 self.finishAir(inst, .none, &.{pl_op.operand});
4529 func.finishAir(inst, .none, &.{pl_op.operand});
45324530}
45334531
4534fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!void {
4535 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4536 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
45374535
4538 const operand = try self.resolveInst(ty_op.operand);
4539 const op_ty = self.air.typeOf(ty_op.operand);
4540 const result_ty = self.air.typeOfIndex(inst);
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);
45414539
45424540 if (op_ty.zigTypeTag() == .Vector) {
4543 return self.fail("TODO: Implement @popCount for vectors", .{});
4541 return func.fail("TODO: Implement @popCount for vectors", .{});
45444542 }
45454543
4546 const int_info = op_ty.intInfo(self.target);
4544 const int_info = op_ty.intInfo(func.target);
45474545 const bits = int_info.bits;
45484546 const wasm_bits = toWasmBits(bits) orelse {
4549 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});
45504548 };
45514549
45524550 switch (wasm_bits) {
45534551 128 => {
4554 _ = try self.load(operand, Type.u64, 0);
4555 try self.addTag(.i64_popcnt);
4556 _ = try self.load(operand, Type.u64, 8);
4557 try self.addTag(.i64_popcnt);
4558 try self.addTag(.i64_add);
4559 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);
45604558 },
45614559 else => {
4562 try self.emitWValue(operand);
4560 try func.emitWValue(operand);
45634561 switch (wasm_bits) {
4564 32 => try self.addTag(.i32_popcnt),
4562 32 => try func.addTag(.i32_popcnt),
45654563 64 => {
4566 try self.addTag(.i64_popcnt);
4567 try self.addTag(.i32_wrap_i64);
4564 try func.addTag(.i64_popcnt);
4565 try func.addTag(.i32_wrap_i64);
45684566 },
45694567 else => unreachable,
45704568 }
45714569 },
45724570 }
45734571
4574 const result = try self.allocLocal(result_ty);
4575 try self.addLabel(.local_set, result.local.value);
4576 self.finishAir(inst, result, &.{ty_op.operand});
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});
45774575}
45784576
4579fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
4580 const un_op = self.air.instructions.items(.data)[inst].un_op;
4581 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{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});
45824580
4583 const operand = try self.resolveInst(un_op);
4581 const operand = try func.resolveInst(un_op);
45844582 // First retrieve the symbol index to the error name table
45854583 // that will be used to emit a relocation for the pointer
45864584 // to the error name table.
......@@ -4592,63 +4590,63 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!void {
45924590 //
45934591 // As the names are global and the slice elements are constant, we do not have
45944592 // to make a copy of the ptr+value but can point towards them directly.
4595 const error_table_symbol = try self.bin_file.getErrorTableSymbol();
4593 const error_table_symbol = try func.bin_file.getErrorTableSymbol();
45964594 const name_ty = Type.initTag(.const_slice_u8_sentinel_0);
4597 const abi_size = name_ty.abiSize(self.target);
4595 const abi_size = name_ty.abiSize(func.target);
45984596
45994597 const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation
4600 try self.emitWValue(error_name_value);
4601 try self.emitWValue(operand);
4602 switch (self.arch()) {
4598 try func.emitWValue(error_name_value);
4599 try func.emitWValue(operand);
4600 switch (func.arch()) {
46034601 .wasm32 => {
4604 try self.addImm32(@bitCast(i32, @intCast(u32, abi_size)));
4605 try self.addTag(.i32_mul);
4606 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);
46074605 },
46084606 .wasm64 => {
4609 try self.addImm64(abi_size);
4610 try self.addTag(.i64_mul);
4611 try self.addTag(.i64_add);
4607 try func.addImm64(abi_size);
4608 try func.addTag(.i64_mul);
4609 try func.addTag(.i64_add);
46124610 },
46134611 else => unreachable,
46144612 }
46154613
4616 const result_ptr = try self.allocLocal(Type.usize);
4617 try self.addLabel(.local_set, result_ptr.local.value);
4618 self.finishAir(inst, result_ptr, &.{un_op});
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});
46194617}
46204618
4621fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerError!void {
4622 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4623 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
4624 const slice_ptr = try self.resolveInst(ty_op.operand);
4625 const result = try self.buildPointerOffset(slice_ptr, offset, .new);
4626 self.finishAir(inst, result, &.{ty_op.operand});
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});
46274625}
46284626
4629fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
4627fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
46304628 assert(op == .add or op == .sub);
4631 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4632 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4633 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
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 });
46344632
4635 const lhs_op = try self.resolveInst(extra.lhs);
4636 const rhs_op = try self.resolveInst(extra.rhs);
4637 const lhs_ty = self.air.typeOf(extra.lhs);
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);
46384636
46394637 if (lhs_ty.zigTypeTag() == .Vector) {
4640 return self.fail("TODO: Implement overflow arithmetic for vectors", .{});
4638 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
46414639 }
46424640
4643 const int_info = lhs_ty.intInfo(self.target);
4641 const int_info = lhs_ty.intInfo(func.target);
46444642 const is_signed = int_info.signedness == .signed;
46454643 const wasm_bits = toWasmBits(int_info.bits) orelse {
4646 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});
46474645 };
46484646
46494647 if (wasm_bits == 128) {
4650 const result = try self.addSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, self.air.typeOfIndex(inst), op);
4651 return self.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
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 });
46524650 }
46534651
46544652 const zero = switch (wasm_bits) {
......@@ -4660,10 +4658,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!v
46604658 // for signed integers, we first apply signed shifts by the difference in bits
46614659 // to get the signed value, as we store it internally as 2's complement.
46624660 var lhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4663 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);
46644662 } else lhs_op;
46654663 var rhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4666 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);
46674665 } else rhs_op;
46684666
46694667 // in this case, we performed a signAbsValue which created a temporary local
......@@ -4671,178 +4669,178 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!v
46714669 // In the other case we do not want to free it, because that would free the
46724670 // resolved instructions which may be referenced by other instructions.
46734671 defer if (wasm_bits != int_info.bits and is_signed) {
4674 lhs.free(self);
4675 rhs.free(self);
4672 lhs.free(func);
4673 rhs.free(func);
46764674 };
46774675
4678 var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty);
4679 defer bin_op.free(self);
4676 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
4677 defer bin_op.free(func);
46804678 var result = if (wasm_bits != int_info.bits) blk: {
4681 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);
46824680 } else bin_op;
4683 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
46844682
46854683 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
46864684 const overflow_bit: WValue = if (is_signed) blk: {
46874685 if (wasm_bits == int_info.bits) {
4688 const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op);
4689 const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt);
4690 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);
46914689 }
4692 const abs = try self.signAbsValue(bin_op, lhs_ty);
4693 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);
46944692 } else if (wasm_bits == int_info.bits)
4695 try self.cmp(bin_op, lhs, lhs_ty, cmp_op)
4693 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)
46964694 else
4697 try self.cmp(bin_op, result, lhs_ty, .neq);
4698 var overflow_local = try overflow_bit.toLocal(self, Type.u32);
4699 defer overflow_local.free(self);
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);
47004698
4701 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4702 try self.store(result_ptr, result, lhs_ty, 0);
4703 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4704 try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset);
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);
47054703
4706 self.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
4704 func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
47074705}
47084706
4709fn addSubWithOverflowBigInt(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 {
47104708 assert(op == .add or op == .sub);
4711 const int_info = ty.intInfo(self.target);
4709 const int_info = ty.intInfo(func.target);
47124710 const is_signed = int_info.signedness == .signed;
47134711 if (int_info.bits != 128) {
4714 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});
47154713 }
47164714
4717 var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64);
4718 defer lhs_high_bit.free(self);
4719 var lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64);
4720 defer lhs_low_bit.free(self);
4721 var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64);
4722 defer rhs_high_bit.free(self);
4723 var rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64);
4724 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);
47254723
4726 var low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64);
4727 defer low_op_res.free(self);
4728 var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64);
4729 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);
47304728
47314729 var lt = if (op == .add) blk: {
4732 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);
47334731 } else if (op == .sub) blk: {
4734 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);
47354733 } else unreachable;
4736 defer lt.free(self);
4737 var tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64);
4738 defer tmp.free(self);
4739 var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64);
4740 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);
47414739
47424740 const overflow_bit = if (is_signed) blk: {
4743 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);
47444742 const to_wrap = if (op == .add) wrap: {
4745 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);
47464744 } else xor_low;
4747 const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
4748 const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and");
4749 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
47504748 } else blk: {
47514749 const first_arg = if (op == .sub) arg: {
4752 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);
47534751 } else lt;
47544752
4755 try self.emitWValue(first_arg);
4756 _ = try self.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt);
4757 _ = try self.cmp(tmp_op, lhs_low_bit, Type.u64, .eq);
4758 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);
47594757
47604758 break :blk WValue{ .stack = {} };
47614759 };
4762 var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));
4763 defer overflow_local.free(self);
4760 var overflow_local = try overflow_bit.toLocal(func, Type.initTag(.u1));
4761 defer overflow_local.free(func);
47644762
4765 const result_ptr = try self.allocStack(result_ty);
4766 try self.store(result_ptr, high_op_res, Type.u64, 0);
4767 try self.store(result_ptr, tmp_op, Type.u64, 8);
4768 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);
47694767
47704768 return result_ptr;
47714769}
47724770
4773fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
4774 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4775 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4776 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
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 });
47774775
4778 const lhs = try self.resolveInst(extra.lhs);
4779 const rhs = try self.resolveInst(extra.rhs);
4780 const lhs_ty = self.air.typeOf(extra.lhs);
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);
47814779
47824780 if (lhs_ty.zigTypeTag() == .Vector) {
4783 return self.fail("TODO: Implement overflow arithmetic for vectors", .{});
4781 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
47844782 }
47854783
4786 const int_info = lhs_ty.intInfo(self.target);
4784 const int_info = lhs_ty.intInfo(func.target);
47874785 const is_signed = int_info.signedness == .signed;
47884786 const wasm_bits = toWasmBits(int_info.bits) orelse {
4789 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});
47904788 };
47914789
4792 var shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty);
4793 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);
47944792 var result = if (wasm_bits != int_info.bits) blk: {
4795 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);
47964794 } else shl;
4797 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)
47984796
47994797 const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: {
48004798 // emit lhs to stack to we can keep 'wrapped' on the stack also
4801 try self.emitWValue(lhs);
4802 const abs = try self.signAbsValue(shl, lhs_ty);
4803 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);
4804 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);
48054803 } else blk: {
4806 try self.emitWValue(lhs);
4807 const shr = try self.binOp(result, rhs, lhs_ty, .shr);
4808 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);
48094807 };
4810 var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1));
4811 defer overflow_local.free(self);
4808 var overflow_local = try overflow_bit.toLocal(func, Type.initTag(.u1));
4809 defer overflow_local.free(func);
48124810
4813 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4814 try self.store(result_ptr, result, lhs_ty, 0);
4815 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4816 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);
48174815
4818 self.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
4816 func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
48194817}
48204818
4821fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
4822 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4823 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4824 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ extra.lhs, extra.rhs });
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 });
48254823
4826 const lhs = try self.resolveInst(extra.lhs);
4827 const rhs = try self.resolveInst(extra.rhs);
4828 const lhs_ty = self.air.typeOf(extra.lhs);
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);
48294827
48304828 if (lhs_ty.zigTypeTag() == .Vector) {
4831 return self.fail("TODO: Implement overflow arithmetic for vectors", .{});
4829 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
48324830 }
48334831
48344832 // We store the bit if it's overflowed or not in this. As it's zero-initialized
48354833 // we only need to update it if an overflow (or underflow) occurred.
4836 var overflow_bit = try self.ensureAllocLocal(Type.initTag(.u1));
4837 defer overflow_bit.free(self);
4834 var overflow_bit = try func.ensureAllocLocal(Type.initTag(.u1));
4835 defer overflow_bit.free(func);
48384836
4839 const int_info = lhs_ty.intInfo(self.target);
4837 const int_info = lhs_ty.intInfo(func.target);
48404838 const wasm_bits = toWasmBits(int_info.bits) orelse {
4841 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});
48424840 };
48434841
48444842 if (wasm_bits > 32) {
4845 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});
48464844 }
48474845
48484846 const zero = switch (wasm_bits) {
......@@ -4854,190 +4852,190 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!void {
48544852 // for 32 bit integers we upcast it to a 64bit integer
48554853 const bin_op = if (int_info.bits == 32) blk: {
48564854 const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64;
4857 const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty);
4858 const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty);
4859 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);
48604858 if (int_info.signedness == .unsigned) {
4861 const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4862 const wrap = try self.intcast(shr, new_ty, lhs_ty);
4863 _ = try self.cmp(wrap, zero, lhs_ty, .neq);
4864 try self.addLabel(.local_set, overflow_bit.local.value);
4865 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);
48664864 } else {
4867 const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty);
4868 var shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty);
4869 defer shr.free(self);
4870
4871 const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr);
4872 const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty);
4873 _ = try self.cmp(down_shr_res, shr, lhs_ty, .neq);
4874 try self.addLabel(.local_set, overflow_bit.local.value);
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);
48754873 break :blk down_cast;
48764874 }
48774875 } else if (int_info.signedness == .signed) blk: {
4878 const lhs_abs = try self.signAbsValue(lhs, lhs_ty);
4879 const rhs_abs = try self.signAbsValue(rhs, lhs_ty);
4880 const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty);
4881 const mul_abs = try self.signAbsValue(bin_op, lhs_ty);
4882 _ = try self.cmp(mul_abs, bin_op, lhs_ty, .neq);
4883 try self.addLabel(.local_set, overflow_bit.local.value);
4884 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);
48854883 } else blk: {
4886 var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty);
4887 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);
48884886 const shift_imm = if (wasm_bits == 32)
48894887 WValue{ .imm32 = int_info.bits }
48904888 else
48914889 WValue{ .imm64 = int_info.bits };
4892 const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr);
4893 _ = try self.cmp(shr, zero, lhs_ty, .neq);
4894 try self.addLabel(.local_set, overflow_bit.local.value);
4895 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);
48964894 };
4897 var bin_op_local = try bin_op.toLocal(self, lhs_ty);
4898 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);
48994897
4900 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4901 try self.store(result_ptr, bin_op_local, lhs_ty, 0);
4902 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4903 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);
49044902
4905 self.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
4903 func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
49064904}
49074905
4908fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {
4909 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
4910 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
49114909
4912 const ty = self.air.typeOfIndex(inst);
4910 const ty = func.air.typeOfIndex(inst);
49134911 if (ty.zigTypeTag() == .Vector) {
4914 return self.fail("TODO: `@maximum` and `@minimum` for vectors", .{});
4912 return func.fail("TODO: `@maximum` and `@minimum` for vectors", .{});
49154913 }
49164914
4917 if (ty.abiSize(self.target) > 16) {
4918 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", .{});
49194917 }
49204918
4921 const lhs = try self.resolveInst(bin_op.lhs);
4922 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);
49234921
49244922 // operands to select from
4925 try self.lowerToStack(lhs);
4926 try self.lowerToStack(rhs);
4927 _ = 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);
49284926
49294927 // based on the result from comparison, return operand 0 or 1.
4930 try self.addTag(.select);
4928 try func.addTag(.select);
49314929
49324930 // store result in local
4933 const result_ty = if (isByRef(ty, self.target)) Type.u32 else ty;
4934 const result = try self.allocLocal(result_ty);
4935 try self.addLabel(.local_set, result.local.value);
4936 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
49374935}
49384936
4939fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!void {
4940 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4941 const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data;
4942 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
49434941
4944 const ty = self.air.typeOfIndex(inst);
4942 const ty = func.air.typeOfIndex(inst);
49454943 if (ty.zigTypeTag() == .Vector) {
4946 return self.fail("TODO: `@mulAdd` for vectors", .{});
4944 return func.fail("TODO: `@mulAdd` for vectors", .{});
49474945 }
49484946
4949 const addend = try self.resolveInst(pl_op.operand);
4950 const lhs = try self.resolveInst(bin_op.lhs);
4951 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);
49524950
4953 const result = if (ty.floatBits(self.target) == 16) fl_result: {
4954 const rhs_ext = try self.fpext(rhs, ty, Type.f32);
4955 const lhs_ext = try self.fpext(lhs, ty, Type.f32);
4956 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);
49574955 // call to compiler-rt `fn fmaf(f32, f32, f32) f32`
4958 var result = try self.callIntrinsic(
4956 var result = try func.callIntrinsic(
49594957 "fmaf",
49604958 &.{ Type.f32, Type.f32, Type.f32 },
49614959 Type.f32,
49624960 &.{ rhs_ext, lhs_ext, addend_ext },
49634961 );
4964 break :fl_result try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty);
4962 break :fl_result try (try func.fptrunc(result, Type.f32, ty)).toLocal(func, ty);
49654963 } else result: {
4966 const mul_result = try self.binOp(lhs, rhs, ty, .mul);
4967 break :result try (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty);
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);
49684966 };
49694967
4970 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
4968 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
49714969}
49724970
4973fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!void {
4974 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4975 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
49764974
4977 const ty = self.air.typeOf(ty_op.operand);
4978 const result_ty = self.air.typeOfIndex(inst);
4975 const ty = func.air.typeOf(ty_op.operand);
4976 const result_ty = func.air.typeOfIndex(inst);
49794977 if (ty.zigTypeTag() == .Vector) {
4980 return self.fail("TODO: `@clz` for vectors", .{});
4978 return func.fail("TODO: `@clz` for vectors", .{});
49814979 }
49824980
4983 const operand = try self.resolveInst(ty_op.operand);
4984 const int_info = ty.intInfo(self.target);
4981 const operand = try func.resolveInst(ty_op.operand);
4982 const int_info = ty.intInfo(func.target);
49854983 const wasm_bits = toWasmBits(int_info.bits) orelse {
4986 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});
49874985 };
49884986
49894987 switch (wasm_bits) {
49904988 32 => {
4991 try self.emitWValue(operand);
4992 try self.addTag(.i32_clz);
4989 try func.emitWValue(operand);
4990 try func.addTag(.i32_clz);
49934991 },
49944992 64 => {
4995 try self.emitWValue(operand);
4996 try self.addTag(.i64_clz);
4997 try self.addTag(.i32_wrap_i64);
4993 try func.emitWValue(operand);
4994 try func.addTag(.i64_clz);
4995 try func.addTag(.i32_wrap_i64);
49984996 },
49994997 128 => {
5000 var lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64);
5001 defer lsb.free(self);
5002
5003 try self.emitWValue(lsb);
5004 try self.addTag(.i64_clz);
5005 _ = try self.load(operand, Type.u64, 0);
5006 try self.addTag(.i64_clz);
5007 try self.emitWValue(.{ .imm64 = 64 });
5008 try self.addTag(.i64_add);
5009 _ = try self.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq);
5010 try self.addTag(.select);
5011 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);
50125010 },
50135011 else => unreachable,
50145012 }
50155013
50165014 if (wasm_bits != int_info.bits) {
5017 try self.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });
5018 try self.addTag(.i32_sub);
5015 try func.emitWValue(.{ .imm32 = wasm_bits - int_info.bits });
5016 try func.addTag(.i32_sub);
50195017 }
50205018
5021 const result = try self.allocLocal(result_ty);
5022 try self.addLabel(.local_set, result.local.value);
5023 self.finishAir(inst, result, &.{ty_op.operand});
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});
50245022}
50255023
5026fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
5027 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5028 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
50295027
5030 const ty = self.air.typeOf(ty_op.operand);
5031 const result_ty = self.air.typeOfIndex(inst);
5028 const ty = func.air.typeOf(ty_op.operand);
5029 const result_ty = func.air.typeOfIndex(inst);
50325030
50335031 if (ty.zigTypeTag() == .Vector) {
5034 return self.fail("TODO: `@ctz` for vectors", .{});
5032 return func.fail("TODO: `@ctz` for vectors", .{});
50355033 }
50365034
5037 const operand = try self.resolveInst(ty_op.operand);
5038 const int_info = ty.intInfo(self.target);
5035 const operand = try func.resolveInst(ty_op.operand);
5036 const int_info = ty.intInfo(func.target);
50395037 const wasm_bits = toWasmBits(int_info.bits) orelse {
5040 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});
50415039 };
50425040
50435041 switch (wasm_bits) {
......@@ -5045,63 +5043,63 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!void {
50455043 if (wasm_bits != int_info.bits) {
50465044 const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits);
50475045 // leave value on the stack
5048 _ = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or");
5049 } else try self.emitWValue(operand);
5050 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);
50515049 },
50525050 64 => {
50535051 if (wasm_bits != int_info.bits) {
50545052 const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits);
50555053 // leave value on the stack
5056 _ = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or");
5057 } else try self.emitWValue(operand);
5058 try self.addTag(.i64_ctz);
5059 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);
50605058 },
50615059 128 => {
5062 var msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64);
5063 defer msb.free(self);
5060 var msb = try (try func.load(operand, Type.u64, 0)).toLocal(func, Type.u64);
5061 defer msb.free(func);
50645062
5065 try self.emitWValue(msb);
5066 try self.addTag(.i64_ctz);
5067 _ = 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);
50685066 if (wasm_bits != int_info.bits) {
5069 try self.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64));
5070 try self.addTag(.i64_or);
5067 try func.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64));
5068 try func.addTag(.i64_or);
50715069 }
5072 try self.addTag(.i64_ctz);
5073 try self.addImm64(64);
5070 try func.addTag(.i64_ctz);
5071 try func.addImm64(64);
50745072 if (wasm_bits != int_info.bits) {
5075 try self.addTag(.i64_or);
5073 try func.addTag(.i64_or);
50765074 } else {
5077 try self.addTag(.i64_add);
5075 try func.addTag(.i64_add);
50785076 }
5079 _ = try self.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq);
5080 try self.addTag(.select);
5081 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);
50825080 },
50835081 else => unreachable,
50845082 }
50855083
5086 const result = try self.allocLocal(result_ty);
5087 try self.addLabel(.local_set, result.local.value);
5088 self.finishAir(inst, result, &.{ty_op.operand});
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});
50895087}
50905088
5091fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !void {
5092 if (self.debug_output != .dwarf) return self.finishAir(inst, .none, &.{});
5089fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
5090 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
50935091
5094 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
5095 const ty = self.air.typeOf(pl_op.operand);
5096 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);
50975095 const op_ty = if (is_ptr) ty.childType() else ty;
50985096
50995097 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, op_ty.fmtDebug(), operand });
51005098
5101 const name = self.air.nullTerminatedString(pl_op.payload);
5099 const name = func.air.nullTerminatedString(pl_op.payload);
51025100 log.debug(" var name = ({s})", .{name});
51035101
5104 const dbg_info = &self.debug_output.dwarf.dbg_info;
5102 const dbg_info = &func.debug_output.dwarf.dbg_info;
51055103 try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable));
51065104 switch (operand) {
51075105 .local => |local| {
......@@ -5123,54 +5121,54 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !void {
51235121 }
51245122
51255123 try dbg_info.ensureUnusedCapacity(5 + name.len + 1);
5126 try self.addDbgInfoTypeReloc(op_ty);
5124 try func.addDbgInfoTypeReloc(op_ty);
51275125 dbg_info.appendSliceAssumeCapacity(name);
51285126 dbg_info.appendAssumeCapacity(0);
5129 self.finishAir(inst, .none, &.{});
5127 func.finishAir(inst, .none, &.{});
51305128}
51315129
5132fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
5133 if (self.debug_output != .dwarf) return self.finishAir(inst, .none, &.{});
5130fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) !void {
5131 if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
51345132
5135 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
5136 try self.addInst(.{ .tag = .dbg_line, .data = .{
5137 .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{
51385136 .line = dbg_stmt.line,
51395137 .column = dbg_stmt.column,
51405138 }),
51415139 } });
5142 self.finishAir(inst, .none, &.{});
5140 func.finishAir(inst, .none, &.{});
51435141}
51445142
5145fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!void {
5146 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
5147 const err_union = try self.resolveInst(pl_op.operand);
5148 const extra = self.air.extraData(Air.Try, pl_op.payload);
5149 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5150 const err_union_ty = self.air.typeOf(pl_op.operand);
5151 const result = try lowerTry(self, err_union, body, err_union_ty, false);
5152 self.finishAir(inst, result, &.{pl_op.operand});
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});
51535151}
51545152
5155fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!void {
5156 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5157 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
5158 const err_union_ptr = try self.resolveInst(extra.data.ptr);
5159 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5160 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
5161 const result = try lowerTry(self, err_union_ptr, body, err_union_ty, true);
5162 self.finishAir(inst, result, &.{extra.data.ptr});
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});
51635161}
51645162
51655163fn lowerTry(
5166 self: *Self,
5164 func: *CodeGen,
51675165 err_union: WValue,
51685166 body: []const Air.Inst.Index,
51695167 err_union_ty: Type,
51705168 operand_is_ptr: bool,
51715169) InnerError!WValue {
51725170 if (operand_is_ptr) {
5173 return self.fail("TODO: lowerTry for pointers", .{});
5171 return func.fail("TODO: lowerTry for pointers", .{});
51745172 }
51755173
51765174 const pl_ty = err_union_ty.errorUnionPayload();
......@@ -5178,21 +5176,21 @@ fn lowerTry(
51785176
51795177 if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) {
51805178 // Block we can jump out of when error is not set
5181 try self.startBlock(.block, wasm.block_empty);
5179 try func.startBlock(.block, wasm.block_empty);
51825180
51835181 // check if the error tag is set for the error union.
5184 try self.emitWValue(err_union);
5182 try func.emitWValue(err_union);
51855183 if (pl_has_bits) {
5186 const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
5187 try self.addMemArg(.i32_load16_u, .{
5184 const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, func.target));
5185 try func.addMemArg(.i32_load16_u, .{
51885186 .offset = err_union.offset() + err_offset,
5189 .alignment = Type.anyerror.abiAlignment(self.target),
5187 .alignment = Type.anyerror.abiAlignment(func.target),
51905188 });
51915189 }
5192 try self.addTag(.i32_eqz);
5193 try self.addLabel(.br_if, 0); // jump out of block when error is '0'
5194 try self.genBody(body);
5195 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();
51965194 }
51975195
51985196 // if we reach here it means error was not set, and we want the payload
......@@ -5200,121 +5198,121 @@ fn lowerTry(
52005198 return WValue{ .none = {} };
52015199 }
52025200
5203 const pl_offset = @intCast(u32, errUnionPayloadOffset(pl_ty, self.target));
5204 if (isByRef(pl_ty, self.target)) {
5205 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);
52065204 }
5207 const payload = try self.load(err_union, pl_ty, pl_offset);
5208 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);
52095207}
52105208
5211fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!void {
5212 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5213 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ty_op.operand});
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});
52145212
5215 const ty = self.air.typeOfIndex(inst);
5216 const operand = try self.resolveInst(ty_op.operand);
5213 const ty = func.air.typeOfIndex(inst);
5214 const operand = try func.resolveInst(ty_op.operand);
52175215
52185216 if (ty.zigTypeTag() == .Vector) {
5219 return self.fail("TODO: @byteSwap for vectors", .{});
5217 return func.fail("TODO: @byteSwap for vectors", .{});
52205218 }
5221 const int_info = ty.intInfo(self.target);
5219 const int_info = ty.intInfo(func.target);
52225220
52235221 // bytes are no-op
52245222 if (int_info.bits == 8) {
5225 return self.finishAir(inst, self.reuseOperand(ty_op.operand, operand), &.{ty_op.operand});
5223 return func.finishAir(inst, func.reuseOperand(ty_op.operand, operand), &.{ty_op.operand});
52265224 }
52275225
52285226 const result = result: {
52295227 switch (int_info.bits) {
52305228 16 => {
5231 const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
5232 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and");
5233 const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
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);
52345232 const res = if (int_info.signedness == .signed) blk: {
5235 break :blk try self.wrapOperand(shr_res, Type.u8);
5233 break :blk try func.wrapOperand(shr_res, Type.u8);
52365234 } else shr_res;
5237 break :result try (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty);
5235 break :result try (try func.binOp(lhs, res, ty, .@"or")).toLocal(func, ty);
52385236 },
52395237 24 => {
5240 var msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16);
5241 defer msb.free(self);
5238 var msb = try (try func.wrapOperand(operand, Type.u16)).toLocal(func, Type.u16);
5239 defer msb.free(func);
52425240
5243 const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl);
5244 const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and");
5245 const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr);
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);
52465244
52475245 const res = if (int_info.signedness == .signed) blk: {
5248 break :blk try self.wrapOperand(shr_res, Type.u8);
5246 break :blk try func.wrapOperand(shr_res, Type.u8);
52495247 } else shr_res;
5250 const lhs_tmp = try self.binOp(lhs, res, ty, .@"or");
5251 const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr);
5252 const rhs_wrap = try self.wrapOperand(msb, Type.u8);
5253 const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl);
5254
5255 const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr);
5256 const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or");
5257 break :result try (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty);
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);
52585256 },
52595257 32 => {
5260 const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl);
5261 var lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty);
5262 defer lhs.free(self);
5263 const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr);
5264 var rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty);
5265 defer rhs.free(self);
5266 var tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty);
5267 defer tmp_or.free(self);
5268
5269 const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl);
5270 const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr);
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);
52715269 const res = if (int_info.signedness == .signed) blk: {
5272 break :blk try self.wrapOperand(shr, Type.u16);
5270 break :blk try func.wrapOperand(shr, Type.u16);
52735271 } else shr;
5274 break :result try (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty);
5272 break :result try (try func.binOp(shl, res, ty, .@"or")).toLocal(func, ty);
52755273 },
5276 else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
5274 else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
52775275 }
52785276 };
5279 self.finishAir(inst, result, &.{ty_op.operand});
5277 func.finishAir(inst, result, &.{ty_op.operand});
52805278}
52815279
5282fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!void {
5283 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5284 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
52855283
5286 const ty = self.air.typeOfIndex(inst);
5287 const lhs = try self.resolveInst(bin_op.lhs);
5288 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);
52895287
52905288 const result = if (ty.isSignedInt())
5291 try self.divSigned(lhs, rhs, ty)
5289 try func.divSigned(lhs, rhs, ty)
52925290 else
5293 try (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
5294 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5291 try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
5292 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
52955293}
52965294
5297fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!void {
5298 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5299 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
53005298
5301 const ty = self.air.typeOfIndex(inst);
5302 const lhs = try self.resolveInst(bin_op.lhs);
5303 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);
53045302
53055303 if (ty.isUnsignedInt()) {
5306 const result = try (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty);
5307 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
53085306 } else if (ty.isSignedInt()) {
5309 const int_bits = ty.intInfo(self.target).bits;
5307 const int_bits = ty.intInfo(func.target).bits;
53105308 const wasm_bits = toWasmBits(int_bits) orelse {
5311 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});
53125310 };
53135311 const lhs_res = if (wasm_bits != int_bits) blk: {
5314 break :blk try (try self.signAbsValue(lhs, ty)).toLocal(self, ty);
5312 break :blk try (try func.signAbsValue(lhs, ty)).toLocal(func, ty);
53155313 } else lhs;
53165314 const rhs_res = if (wasm_bits != int_bits) blk: {
5317 break :blk try (try self.signAbsValue(rhs, ty)).toLocal(self, ty);
5315 break :blk try (try func.signAbsValue(rhs, ty)).toLocal(func, ty);
53185316 } else rhs;
53195317
53205318 const zero = switch (wasm_bits) {
......@@ -5323,118 +5321,118 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!void {
53235321 else => unreachable,
53245322 };
53255323
5326 const div_result = try self.allocLocal(ty);
5324 const div_result = try func.allocLocal(ty);
53275325 // leave on stack
5328 _ = try self.binOp(lhs_res, rhs_res, ty, .div);
5329 try self.addLabel(.local_tee, div_result.local.value);
5330 _ = try self.cmp(lhs_res, zero, ty, .lt);
5331 _ = 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);
53325330 switch (wasm_bits) {
53335331 32 => {
5334 try self.addTag(.i32_xor);
5335 try self.addTag(.i32_sub);
5332 try func.addTag(.i32_xor);
5333 try func.addTag(.i32_sub);
53365334 },
53375335 64 => {
5338 try self.addTag(.i64_xor);
5339 try self.addTag(.i64_sub);
5336 try func.addTag(.i64_xor);
5337 try func.addTag(.i64_sub);
53405338 },
53415339 else => unreachable,
53425340 }
5343 try self.emitWValue(div_result);
5341 try func.emitWValue(div_result);
53445342 // leave value on the stack
5345 _ = try self.binOp(lhs_res, rhs_res, ty, .rem);
5346 try self.addTag(.select);
5343 _ = try func.binOp(lhs_res, rhs_res, ty, .rem);
5344 try func.addTag(.select);
53475345 } else {
5348 const float_bits = ty.floatBits(self.target);
5346 const float_bits = ty.floatBits(func.target);
53495347 if (float_bits > 64) {
5350 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});
53515349 }
53525350 const is_f16 = float_bits == 16;
53535351
53545352 const lhs_operand = if (is_f16) blk: {
5355 break :blk try self.fpext(lhs, Type.f16, Type.f32);
5353 break :blk try func.fpext(lhs, Type.f16, Type.f32);
53565354 } else lhs;
53575355 const rhs_operand = if (is_f16) blk: {
5358 break :blk try self.fpext(rhs, Type.f16, Type.f32);
5356 break :blk try func.fpext(rhs, Type.f16, Type.f32);
53595357 } else rhs;
53605358
5361 try self.emitWValue(lhs_operand);
5362 try self.emitWValue(rhs_operand);
5359 try func.emitWValue(lhs_operand);
5360 try func.emitWValue(rhs_operand);
53635361
53645362 switch (float_bits) {
53655363 16, 32 => {
5366 try self.addTag(.f32_div);
5367 try self.addTag(.f32_floor);
5364 try func.addTag(.f32_div);
5365 try func.addTag(.f32_floor);
53685366 },
53695367 64 => {
5370 try self.addTag(.f64_div);
5371 try self.addTag(.f64_floor);
5368 try func.addTag(.f64_div);
5369 try func.addTag(.f64_floor);
53725370 },
53735371 else => unreachable,
53745372 }
53755373
53765374 if (is_f16) {
5377 _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
5375 _ = try func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
53785376 }
53795377 }
53805378
5381 const result = try self.allocLocal(ty);
5382 try self.addLabel(.local_set, result.local.value);
5383 self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
53845382}
53855383
5386fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue {
5387 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;
53885386 const wasm_bits = toWasmBits(int_bits) orelse {
5389 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});
53905388 };
53915389
53925390 if (wasm_bits == 128) {
5393 return self.fail("TODO: Implement signed division for 128-bit integerrs", .{});
5391 return func.fail("TODO: Implement signed division for 128-bit integerrs", .{});
53945392 }
53955393
53965394 if (wasm_bits != int_bits) {
53975395 // Leave both values on the stack
5398 _ = try self.signAbsValue(lhs, ty);
5399 _ = try self.signAbsValue(rhs, ty);
5396 _ = try func.signAbsValue(lhs, ty);
5397 _ = try func.signAbsValue(rhs, ty);
54005398 } else {
5401 try self.emitWValue(lhs);
5402 try self.emitWValue(rhs);
5399 try func.emitWValue(lhs);
5400 try func.emitWValue(rhs);
54035401 }
5404 try self.addTag(.i32_div_s);
5402 try func.addTag(.i32_div_s);
54055403
5406 const result = try self.allocLocal(ty);
5407 try self.addLabel(.local_set, result.local.value);
5404 const result = try func.allocLocal(ty);
5405 try func.addLabel(.local_set, result.local.value);
54085406 return result;
54095407}
54105408
54115409/// Retrieves the absolute value of a signed integer
54125410/// NOTE: Leaves the result value on the stack.
5413fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
5414 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;
54155413 const wasm_bits = toWasmBits(int_bits) orelse {
5416 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});
54175415 };
54185416
54195417 const shift_val = switch (wasm_bits) {
54205418 32 => WValue{ .imm32 = wasm_bits - int_bits },
54215419 64 => WValue{ .imm64 = wasm_bits - int_bits },
5422 else => return self.fail("TODO: signAbsValue for i128", .{}),
5420 else => return func.fail("TODO: signAbsValue for i128", .{}),
54235421 };
54245422
5425 try self.emitWValue(operand);
5423 try func.emitWValue(operand);
54265424 switch (wasm_bits) {
54275425 32 => {
5428 try self.emitWValue(shift_val);
5429 try self.addTag(.i32_shl);
5430 try self.emitWValue(shift_val);
5431 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);
54325430 },
54335431 64 => {
5434 try self.emitWValue(shift_val);
5435 try self.addTag(.i64_shl);
5436 try self.emitWValue(shift_val);
5437 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);
54385436 },
54395437 else => unreachable,
54405438 }
......@@ -5442,62 +5440,62 @@ fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue {
54425440 return WValue{ .stack = {} };
54435441}
54445442
5445fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
5446 const un_op = self.air.instructions.items(.data)[inst].un_op;
5447 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{un_op});
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});
54485446
5449 const ty = self.air.typeOfIndex(inst);
5450 const float_bits = ty.floatBits(self.target);
5447 const ty = func.air.typeOfIndex(inst);
5448 const float_bits = ty.floatBits(func.target);
54515449 const is_f16 = float_bits == 16;
54525450
54535451 if (ty.zigTypeTag() == .Vector) {
5454 return self.fail("TODO: Implement `@ceil` for vectors", .{});
5452 return func.fail("TODO: Implement `@ceil` for vectors", .{});
54555453 }
54565454 if (float_bits > 64) {
5457 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", .{});
54585456 }
54595457
5460 const operand = try self.resolveInst(un_op);
5458 const operand = try func.resolveInst(un_op);
54615459 const op_to_lower = if (is_f16) blk: {
5462 break :blk try self.fpext(operand, Type.f16, Type.f32);
5460 break :blk try func.fpext(operand, Type.f16, Type.f32);
54635461 } else operand;
5464 try self.emitWValue(op_to_lower);
5465 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) });
5466 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));
54675465
54685466 if (is_f16) {
5469 _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
5467 _ = try func.fptrunc(.{ .stack = {} }, Type.f32, Type.f16);
54705468 }
54715469
5472 const result = try self.allocLocal(ty);
5473 try self.addLabel(.local_set, result.local.value);
5474 self.finishAir(inst, result, &.{un_op});
5470 const result = try func.allocLocal(ty);
5471 try func.addLabel(.local_set, result.local.value);
5472 func.finishAir(inst, result, &.{un_op});
54755473}
54765474
5477fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
5475fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
54785476 assert(op == .add or op == .sub);
5479 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5480 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
54815479
5482 const ty = self.air.typeOfIndex(inst);
5483 const lhs = try self.resolveInst(bin_op.lhs);
5484 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);
54855483
5486 const int_info = ty.intInfo(self.target);
5484 const int_info = ty.intInfo(func.target);
54875485 const is_signed = int_info.signedness == .signed;
54885486
54895487 if (int_info.bits > 64) {
5490 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});
54915489 }
54925490
54935491 if (is_signed) {
5494 const result = try signedSat(self, lhs, rhs, ty, op);
5495 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5492 const result = try signedSat(func, lhs, rhs, ty, op);
5493 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
54965494 }
54975495
54985496 const wasm_bits = toWasmBits(int_info.bits).?;
5499 var bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty);
5500 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);
55015499 if (wasm_bits != int_info.bits and op == .add) {
55025500 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);
55035501 const imm_val = switch (wasm_bits) {
......@@ -5506,35 +5504,35 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!void {
55065504 else => unreachable,
55075505 };
55085506
5509 try self.emitWValue(bin_result);
5510 try self.emitWValue(imm_val);
5511 _ = 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);
55125510 } else {
55135511 switch (wasm_bits) {
5514 32 => try self.addImm32(if (op == .add) @as(i32, -1) else 0),
5515 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),
55165514 else => unreachable,
55175515 }
5518 try self.emitWValue(bin_result);
5519 _ = 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);
55205518 }
55215519
5522 try self.addTag(.select);
5523 const result = try self.allocLocal(ty);
5524 try self.addLabel(.local_set, result.local.value);
5525 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
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 });
55265524}
55275525
5528fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {
5529 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);
55305528 const wasm_bits = toWasmBits(int_info.bits).?;
55315529 const is_wasm_bits = wasm_bits == int_info.bits;
55325530
55335531 var lhs = if (!is_wasm_bits) lhs: {
5534 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);
55355533 } else lhs_operand;
55365534 var rhs = if (!is_wasm_bits) rhs: {
5537 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);
55385536 } else rhs_operand;
55395537
55405538 const max_val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits - 1)) - 1);
......@@ -5550,93 +5548,93 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op
55505548 else => unreachable,
55515549 };
55525550
5553 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);
55545552 if (!is_wasm_bits) {
5555 defer bin_result.free(self); // not returned in this branch
5556 defer lhs.free(self); // uses temporary local for absvalue
5557 defer rhs.free(self); // uses temporary local for absvalue
5558 try self.emitWValue(bin_result);
5559 try self.emitWValue(max_wvalue);
5560 _ = try self.cmp(bin_result, max_wvalue, ty, .lt);
5561 try self.addTag(.select);
5562 try self.addLabel(.local_set, bin_result.local.value); // re-use local
5563
5564 try self.emitWValue(bin_result);
5565 try self.emitWValue(min_wvalue);
5566 _ = try self.cmp(bin_result, min_wvalue, ty, .gt);
5567 try self.addTag(.select);
5568 try self.addLabel(.local_set, bin_result.local.value); // re-use local
5569 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);
55705568 } else {
55715569 const zero = switch (wasm_bits) {
55725570 32 => WValue{ .imm32 = 0 },
55735571 64 => WValue{ .imm64 = 0 },
55745572 else => unreachable,
55755573 };
5576 try self.emitWValue(max_wvalue);
5577 try self.emitWValue(min_wvalue);
5578 _ = try self.cmp(bin_result, zero, ty, .lt);
5579 try self.addTag(.select);
5580 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);
55815579 // leave on stack
5582 const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
5583 const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt);
5584 _ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor.
5585 try self.addTag(.select);
5586 try self.addLabel(.local_set, bin_result.local.value); // 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
55875585 return bin_result;
55885586 }
55895587}
55905588
5591fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
5592 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5593 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
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 });
55945592
5595 const ty = self.air.typeOfIndex(inst);
5596 const int_info = ty.intInfo(self.target);
5593 const ty = func.air.typeOfIndex(inst);
5594 const int_info = ty.intInfo(func.target);
55975595 const is_signed = int_info.signedness == .signed;
55985596 if (int_info.bits > 64) {
5599 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});
56005598 }
56015599
5602 const lhs = try self.resolveInst(bin_op.lhs);
5603 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);
56045602 const wasm_bits = toWasmBits(int_info.bits).?;
5605 const result = try self.allocLocal(ty);
5603 const result = try func.allocLocal(ty);
56065604
56075605 if (wasm_bits == int_info.bits) outer_blk: {
5608 var shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty);
5609 defer shl.free(self);
5610 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5611 defer shr.free(self);
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);
56125610
56135611 switch (wasm_bits) {
56145612 32 => blk: {
56155613 if (!is_signed) {
5616 try self.addImm32(-1);
5614 try func.addImm32(-1);
56175615 break :blk;
56185616 }
5619 try self.addImm32(std.math.minInt(i32));
5620 try self.addImm32(std.math.maxInt(i32));
5621 _ = try self.cmp(lhs, .{ .imm32 = 0 }, ty, .lt);
5622 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);
56235621 },
56245622 64 => blk: {
56255623 if (!is_signed) {
5626 try self.addImm64(@bitCast(u64, @as(i64, -1)));
5624 try func.addImm64(@bitCast(u64, @as(i64, -1)));
56275625 break :blk;
56285626 }
5629 try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64))));
5630 try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64))));
5631 _ = try self.cmp(lhs, .{ .imm64 = 0 }, ty, .lt);
5632 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);
56335631 },
56345632 else => unreachable,
56355633 }
5636 try self.emitWValue(shl);
5637 _ = try self.cmp(lhs, shr, ty, .neq);
5638 try self.addTag(.select);
5639 try self.addLabel(.local_set, result.local.value);
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);
56405638 break :outer_blk;
56415639 } else {
56425640 const shift_size = wasm_bits - int_info.bits;
......@@ -5646,50 +5644,50 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
56465644 else => unreachable,
56475645 };
56485646
5649 var shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty);
5650 defer shl_res.free(self);
5651 var shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty);
5652 defer shl.free(self);
5653 var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty);
5654 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);
56555653
56565654 switch (wasm_bits) {
56575655 32 => blk: {
56585656 if (!is_signed) {
5659 try self.addImm32(-1);
5657 try func.addImm32(-1);
56605658 break :blk;
56615659 }
56625660
5663 try self.addImm32(std.math.minInt(i32));
5664 try self.addImm32(std.math.maxInt(i32));
5665 _ = try self.cmp(shl_res, .{ .imm32 = 0 }, ty, .lt);
5666 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);
56675665 },
56685666 64 => blk: {
56695667 if (!is_signed) {
5670 try self.addImm64(@bitCast(u64, @as(i64, -1)));
5668 try func.addImm64(@bitCast(u64, @as(i64, -1)));
56715669 break :blk;
56725670 }
56735671
5674 try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64))));
5675 try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64))));
5676 _ = try self.cmp(shl_res, .{ .imm64 = 0 }, ty, .lt);
5677 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);
56785676 },
56795677 else => unreachable,
56805678 }
5681 try self.emitWValue(shl);
5682 _ = try self.cmp(shl_res, shr, ty, .neq);
5683 try self.addTag(.select);
5684 try self.addLabel(.local_set, result.local.value);
5685 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);
56865684 if (is_signed) {
5687 shift_result = try self.wrapOperand(shift_result, ty);
5685 shift_result = try func.wrapOperand(shift_result, ty);
56885686 }
5689 try self.addLabel(.local_set, result.local.value);
5687 try func.addLabel(.local_set, result.local.value);
56905688 }
56915689
5692 return self.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5690 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
56935691}
56945692
56955693/// Calls a compiler-rt intrinsic by creating an undefined symbol,
......@@ -5699,29 +5697,29 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!void {
56995697/// passed as the first parameter.
57005698/// May leave the return value on the stack.
57015699fn callIntrinsic(
5702 self: *Self,
5700 func: *CodeGen,
57035701 name: []const u8,
57045702 param_types: []const Type,
57055703 return_type: Type,
57065704 args: []const WValue,
57075705) InnerError!WValue {
57085706 assert(param_types.len == args.len);
5709 const symbol_index = self.bin_file.base.getGlobalSymbol(name) catch |err| {
5710 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)});
57115709 };
57125710
57135711 // Always pass over C-ABI
5714 var func_type = try genFunctype(self.gpa, .C, param_types, return_type, self.target);
5715 defer func_type.deinit(self.gpa);
5716 const func_type_index = try self.bin_file.putOrGetFuncType(func_type);
5717 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);
57185716
5719 const want_sret_param = firstParamSRet(.C, return_type, self.target);
5717 const want_sret_param = firstParamSRet(.C, return_type, func.target);
57205718 // if we want return as first param, we allocate a pointer to stack,
57215719 // and emit it as our first argument
57225720 const sret = if (want_sret_param) blk: {
5723 const sret_local = try self.allocStack(return_type);
5724 try self.lowerToStack(sret_local);
5721 const sret_local = try func.allocStack(return_type);
5722 try func.lowerToStack(sret_local);
57255723 break :blk sret_local;
57265724 } else WValue{ .none = {} };
57275725
......@@ -5729,16 +5727,16 @@ fn callIntrinsic(
57295727 for (args) |arg, arg_i| {
57305728 assert(!(want_sret_param and arg == .stack));
57315729 assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime());
5732 try self.lowerArg(.C, param_types[arg_i], arg);
5730 try func.lowerArg(.C, param_types[arg_i], arg);
57335731 }
57345732
57355733 // Actually call our intrinsic
5736 try self.addLabel(.call, symbol_index);
5734 try func.addLabel(.call, symbol_index);
57375735
57385736 if (!return_type.hasRuntimeBitsIgnoreComptime()) {
57395737 return WValue.none;
57405738 } else if (return_type.isNoReturn()) {
5741 try self.addTag(.@"unreachable");
5739 try func.addTag(.@"unreachable");
57425740 return WValue.none;
57435741 } else if (want_sret_param) {
57445742 return sret;