authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-10-21 09:17:56+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-21 09:17:56+02:00
log41575b1f55b0f18d65bfeb23dc04a5489ed47b65
tree48433d99e1d339f25b401ed5ad9614fa60ace9d5
parent0f00766661e533ac5caa88817648f2ada0ff62c5
parent67941926b25e1adfdc47d22f7223af12cf3f5b01
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13236 from joachimschmidt557/stage2-aarch64

stage2 AArch64: move to new allocRegs mechanism

4 files changed, 1503 insertions(+), 1092 deletions(-)

src/arch/aarch64/CodeGen.zig+1207-918
...@@ -157,40 +157,6 @@ const MCValue = union(enum) {...@@ -157,40 +157,6 @@ const MCValue = union(enum) {
157 condition_flags: Condition,157 condition_flags: Condition,
158 /// The value is a function argument passed via the stack.158 /// The value is a function argument passed via the stack.
159 stack_argument_offset: u32,159 stack_argument_offset: u32,
160
161 fn isMemory(mcv: MCValue) bool {
162 return switch (mcv) {
163 .memory, .stack_offset, .stack_argument_offset => true,
164 else => false,
165 };
166 }
167
168 fn isImmediate(mcv: MCValue) bool {
169 return switch (mcv) {
170 .immediate => true,
171 else => false,
172 };
173 }
174
175 fn isMutable(mcv: MCValue) bool {
176 return switch (mcv) {
177 .none => unreachable,
178 .unreach => unreachable,
179 .dead => unreachable,
180
181 .immediate,
182 .memory,
183 .condition_flags,
184 .ptr_stack_offset,
185 .undef,
186 .stack_argument_offset,
187 => false,
188
189 .register,
190 .stack_offset,
191 => true,
192 };
193 }
194};160};
195161
196const Branch = struct {162const Branch = struct {
...@@ -414,11 +380,9 @@ fn gen(self: *Self) !void {...@@ -414,11 +380,9 @@ fn gen(self: *Self) !void {
414 // to the stack.380 // to the stack.
415 const ptr_bits = self.target.cpu.arch.ptrBitWidth();381 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
416 const ptr_bytes = @divExact(ptr_bits, 8);382 const ptr_bytes = @divExact(ptr_bits, 8);
417 const ret_ptr_reg = registerAlias(.x0, ptr_bytes);383 const ret_ptr_reg = self.registerAlias(.x0, Type.usize);
418384
419 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, ptr_bytes) + ptr_bytes;385 const stack_offset = try self.allocMem(ptr_bytes, ptr_bytes, null);
420 self.next_stack_offset = stack_offset;
421 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
422386
423 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg });387 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg });
424 self.ret_mcv = MCValue{ .stack_offset = stack_offset };388 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
...@@ -879,17 +843,30 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {...@@ -879,17 +843,30 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
879 }843 }
880}844}
881845
882fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {846fn allocMem(
847 self: *Self,
848 abi_size: u32,
849 abi_align: u32,
850 maybe_inst: ?Air.Inst.Index,
851) !u32 {
852 assert(abi_size > 0);
853 assert(abi_align > 0);
854
883 if (abi_align > self.stack_align)855 if (abi_align > self.stack_align)
884 self.stack_align = abi_align;856 self.stack_align = abi_align;
857
885 // TODO find a free slot instead of always appending858 // TODO find a free slot instead of always appending
886 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;859 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
887 self.next_stack_offset = offset;860 self.next_stack_offset = offset;
888 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);861 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
889 try self.stack.putNoClobber(self.gpa, offset, .{862
890 .inst = inst,863 if (maybe_inst) |inst| {
891 .size = abi_size,864 try self.stack.putNoClobber(self.gpa, offset, .{
892 });865 .inst = inst,
866 .size = abi_size,
867 });
868 }
869
893 return offset;870 return offset;
894}871}
895872
...@@ -910,40 +887,41 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {...@@ -910,40 +887,41 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
910 };887 };
911 // TODO swap this for inst.ty.ptrAlign888 // TODO swap this for inst.ty.ptrAlign
912 const abi_align = elem_ty.abiAlignment(self.target.*);889 const abi_align = elem_ty.abiAlignment(self.target.*);
913 return self.allocMem(inst, abi_size, abi_align);890
891 return self.allocMem(abi_size, abi_align, inst);
914}892}
915893
916fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {894fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue {
917 const elem_ty = self.air.typeOfIndex(inst);
918 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {895 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {
919 const mod = self.bin_file.options.module.?;896 const mod = self.bin_file.options.module.?;
920 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});897 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
921 };898 };
922 const abi_align = elem_ty.abiAlignment(self.target.*);899 const abi_align = elem_ty.abiAlignment(self.target.*);
923 if (abi_align > self.stack_align)
924 self.stack_align = abi_align;
925900
926 if (reg_ok) {901 if (reg_ok) {
927 // Make sure the type can fit in a register before we try to allocate one.902 // Make sure the type can fit in a register before we try to allocate one.
928 if (abi_size <= 8) {903 if (abi_size <= 8) {
929 if (self.register_manager.tryAllocReg(inst, gp)) |reg| {904 if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| {
930 return MCValue{ .register = registerAlias(reg, abi_size) };905 return MCValue{ .register = self.registerAlias(reg, elem_ty) };
931 }906 }
932 }907 }
933 }908 }
934 const stack_offset = try self.allocMem(inst, abi_size, abi_align);909
910 const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst);
935 return MCValue{ .stack_offset = stack_offset };911 return MCValue{ .stack_offset = stack_offset };
936}912}
937913
938pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {914pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
939 const stack_mcv = try self.allocRegOrMem(inst, false);915 const stack_mcv = try self.allocRegOrMem(self.air.typeOfIndex(inst), false, inst);
940 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });916 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
917
941 const reg_mcv = self.getResolvedInstValue(inst);918 const reg_mcv = self.getResolvedInstValue(inst);
942 switch (reg_mcv) {919 switch (reg_mcv) {
943 .register => |r| assert(reg.id() == r.id()),920 .register => |r| assert(reg.id() == r.id()),
944 .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()),921 .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()),
945 else => unreachable, // not a register922 else => unreachable, // not a register
946 }923 }
924
947 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];925 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
948 try branch.inst_table.put(self.gpa, inst, stack_mcv);926 try branch.inst_table.put(self.gpa, inst, stack_mcv);
949 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);927 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
...@@ -953,10 +931,11 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -953,10 +931,11 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
953/// occupied931/// occupied
954fn spillCompareFlagsIfOccupied(self: *Self) !void {932fn spillCompareFlagsIfOccupied(self: *Self) !void {
955 if (self.condition_flags_inst) |inst_to_save| {933 if (self.condition_flags_inst) |inst_to_save| {
934 const ty = self.air.typeOfIndex(inst_to_save);
956 const mcv = self.getResolvedInstValue(inst_to_save);935 const mcv = self.getResolvedInstValue(inst_to_save);
957 const new_mcv = switch (mcv) {936 const new_mcv = switch (mcv) {
958 .condition_flags => try self.allocRegOrMem(inst_to_save, true),937 .condition_flags => try self.allocRegOrMem(ty, true, inst_to_save),
959 .register_with_overflow => try self.allocRegOrMem(inst_to_save, false),938 .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save),
960 else => unreachable, // mcv doesn't occupy the compare flags939 else => unreachable, // mcv doesn't occupy the compare flags
961 };940 };
962941
...@@ -982,7 +961,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {...@@ -982,7 +961,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
982/// This can have a side effect of spilling instructions to the stack to free up a register.961/// This can have a side effect of spilling instructions to the stack to free up a register.
983fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {962fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
984 const raw_reg = try self.register_manager.allocReg(null, gp);963 const raw_reg = try self.register_manager.allocReg(null, gp);
985 const reg = registerAlias(raw_reg, ty.abiSize(self.target.*));964 const reg = self.registerAlias(raw_reg, ty);
986 try self.genSetReg(ty, reg, mcv);965 try self.genSetReg(ty, reg, mcv);
987 return reg;966 return reg;
988}967}
...@@ -993,7 +972,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {...@@ -993,7 +972,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
993fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {972fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
994 const raw_reg = try self.register_manager.allocReg(reg_owner, gp);973 const raw_reg = try self.register_manager.allocReg(reg_owner, gp);
995 const ty = self.air.typeOfIndex(reg_owner);974 const ty = self.air.typeOfIndex(reg_owner);
996 const reg = registerAlias(raw_reg, ty.abiSize(self.target.*));975 const reg = self.registerAlias(raw_reg, ty);
997 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);976 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
998 return MCValue{ .register = reg };977 return MCValue{ .register = reg };
999}978}
...@@ -1031,7 +1010,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -1031,7 +1010,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1031 const operand_info = operand_ty.intInfo(self.target.*);1010 const operand_info = operand_ty.intInfo(self.target.*);
10321011
1033 const dest_ty = self.air.typeOfIndex(inst);1012 const dest_ty = self.air.typeOfIndex(inst);
1034 const dest_abi_size = dest_ty.abiSize(self.target.*);
1035 const dest_info = dest_ty.intInfo(self.target.*);1013 const dest_info = dest_ty.intInfo(self.target.*);
10361014
1037 const result: MCValue = result: {1015 const result: MCValue = result: {
...@@ -1042,19 +1020,19 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -1042,19 +1020,19 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1042 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);1020 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
10431021
1044 const truncated: MCValue = switch (operand_mcv) {1022 const truncated: MCValue = switch (operand_mcv) {
1045 .register => |r| MCValue{ .register = registerAlias(r, dest_abi_size) },1023 .register => |r| MCValue{ .register = self.registerAlias(r, dest_ty) },
1046 else => operand_mcv,1024 else => operand_mcv,
1047 };1025 };
10481026
1049 if (dest_info.bits > operand_info.bits) {1027 if (dest_info.bits > operand_info.bits) {
1050 const dest_mcv = try self.allocRegOrMem(inst, true);1028 const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst);
1051 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);1029 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);
1052 break :result dest_mcv;1030 break :result dest_mcv;
1053 } else {1031 } else {
1054 if (self.reuseOperand(inst, operand, 0, truncated)) {1032 if (self.reuseOperand(inst, operand, 0, truncated)) {
1055 break :result truncated;1033 break :result truncated;
1056 } else {1034 } else {
1057 const dest_mcv = try self.allocRegOrMem(inst, true);1035 const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst);
1058 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);1036 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);
1059 break :result dest_mcv;1037 break :result dest_mcv;
1060 }1038 }
...@@ -1117,7 +1095,7 @@ fn trunc(...@@ -1117,7 +1095,7 @@ fn trunc(
1117 else => operand_reg: {1095 else => operand_reg: {
1118 if (info_a.bits <= 64) {1096 if (info_a.bits <= 64) {
1119 const raw_reg = try self.copyToTmpRegister(operand_ty, operand);1097 const raw_reg = try self.copyToTmpRegister(operand_ty, operand);
1120 break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*));1098 break :operand_reg self.registerAlias(raw_reg, operand_ty);
1121 } else {1099 } else {
1122 return self.fail("TODO load least significant word into register", .{});1100 return self.fail("TODO load least significant word into register", .{});
1123 }1101 }
...@@ -1130,14 +1108,14 @@ fn trunc(...@@ -1130,14 +1108,14 @@ fn trunc(
1130 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1108 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
11311109
1132 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {1110 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1133 break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*));1111 break :blk self.registerAlias(operand_reg, dest_ty);
1134 } else {1112 } else {
1135 const raw_reg = try self.register_manager.allocReg(inst, gp);1113 const raw_reg = try self.register_manager.allocReg(inst, gp);
1136 break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));1114 break :blk self.registerAlias(raw_reg, dest_ty);
1137 }1115 }
1138 } else blk: {1116 } else blk: {
1139 const raw_reg = try self.register_manager.allocReg(null, gp);1117 const raw_reg = try self.register_manager.allocReg(null, gp);
1140 break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));1118 break :blk self.registerAlias(raw_reg, dest_ty);
1141 };1119 };
11421120
1143 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);1121 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
...@@ -1194,7 +1172,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -1194,7 +1172,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1194 }1172 }
11951173
1196 const raw_reg = try self.register_manager.allocReg(null, gp);1174 const raw_reg = try self.register_manager.allocReg(null, gp);
1197 break :blk raw_reg.to32();1175 break :blk self.registerAlias(raw_reg, operand_ty);
1198 };1176 };
11991177
1200 _ = try self.addInst(.{1178 _ = try self.addInst(.{
...@@ -1227,7 +1205,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -1227,7 +1205,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1227 }1205 }
12281206
1229 const raw_reg = try self.register_manager.allocReg(null, gp);1207 const raw_reg = try self.register_manager.allocReg(null, gp);
1230 break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*));1208 break :blk self.registerAlias(raw_reg, operand_ty);
1231 };1209 };
12321210
1233 _ = try self.addInst(.{1211 _ = try self.addInst(.{
...@@ -1279,7 +1257,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -1279,7 +1257,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1279 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1257 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1280 const ptr_bytes = @divExact(ptr_bits, 8);1258 const ptr_bytes = @divExact(ptr_bits, 8);
12811259
1282 const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2);1260 const stack_offset = try self.allocMem(ptr_bytes * 2, ptr_bytes * 2, inst);
1283 try self.genSetStack(ptr_ty, stack_offset, ptr);1261 try self.genSetStack(ptr_ty, stack_offset, ptr);
1284 try self.genSetStack(len_ty, stack_offset - ptr_bytes, len);1262 try self.genSetStack(len_ty, stack_offset - ptr_bytes, len);
1285 break :result MCValue{ .stack_offset = stack_offset };1263 break :result MCValue{ .stack_offset = stack_offset };
...@@ -1287,101 +1265,266 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -1287,101 +1265,266 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1287 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1265 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1288}1266}
12891267
1290/// Don't call this function directly. Use binOp instead.1268/// An argument to a Mir instruction which is read (and possibly also
1269/// written to) by the respective instruction
1270const ReadArg = struct {
1271 ty: Type,
1272 bind: Bind,
1273 class: RegisterManager.RegisterBitSet,
1274 reg: *Register,
1275
1276 const Bind = union(enum) {
1277 inst: Air.Inst.Ref,
1278 mcv: MCValue,
1279
1280 fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue {
1281 return switch (bind) {
1282 .inst => |inst| try function.resolveInst(inst),
1283 .mcv => |mcv| mcv,
1284 };
1285 }
1286
1287 fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u64 {
1288 switch (bind) {
1289 .inst => |inst| {
1290 // TODO resolve independently of inst_table
1291 const mcv = try function.resolveInst(inst);
1292 switch (mcv) {
1293 .immediate => |imm| return imm,
1294 else => return null,
1295 }
1296 },
1297 .mcv => |mcv| {
1298 switch (mcv) {
1299 .immediate => |imm| return imm,
1300 else => return null,
1301 }
1302 },
1303 }
1304 }
1305 };
1306};
1307
1308/// An argument to a Mir instruction which is written to (but not read
1309/// from) by the respective instruction
1310const WriteArg = struct {
1311 ty: Type,
1312 bind: Bind,
1313 class: RegisterManager.RegisterBitSet,
1314 reg: *Register,
1315
1316 const Bind = union(enum) {
1317 reg: Register,
1318 none: void,
1319 };
1320};
1321
1322/// Holds all data necessary for enabling the potential reuse of
1323/// operand registers as destinations
1324const ReuseMetadata = struct {
1325 corresponding_inst: Air.Inst.Index,
1326
1327 /// Maps every element index of read_args to the corresponding
1328 /// index in the Air instruction
1329 ///
1330 /// When the order of read_args corresponds exactly to the order
1331 /// of the inputs of the Air instruction, this would be e.g.
1332 /// &.{ 0, 1 }. However, when the order is not the same or some
1333 /// inputs to the Air instruction are omitted (e.g. when they can
1334 /// be represented as immediates to the Mir instruction),
1335 /// operand_mapping should reflect that fact.
1336 operand_mapping: []const Liveness.OperandInt,
1337};
1338
1339/// Allocate a set of registers for use as arguments for a Mir
1340/// instruction
1291///1341///
1292/// Calling this function signals an intention to generate a Mir1342/// If the Mir instruction these registers are allocated for
1293/// instruction of the form1343/// corresponds exactly to a single Air instruction, populate
1344/// reuse_metadata in order to enable potential reuse of an operand as
1345/// the destination (provided that that operand dies in this
1346/// instruction).
1294///1347///
1295/// op dest, lhs, rhs1348/// Reusing an operand register as destination is the only time two
1349/// arguments may share the same register. In all other cases,
1350/// allocRegs guarantees that a register will never be allocated to
1351/// more than one argument.
1296///1352///
1297/// Asserts that generating an instruction of that form is possible.1353/// Furthermore, allocReg guarantees that all arguments which are
1298fn binOpRegister(1354/// already bound to registers before calling allocRegs will not
1355/// change their register binding. This is done by locking these
1356/// registers.
1357fn allocRegs(
1299 self: *Self,1358 self: *Self,
1300 mir_tag: Mir.Inst.Tag,1359 read_args: []const ReadArg,
1301 lhs: MCValue,1360 write_args: []const WriteArg,
1302 rhs: MCValue,1361 reuse_metadata: ?ReuseMetadata,
1303 lhs_ty: Type,1362) InnerError!void {
1304 rhs_ty: Type,1363 // Air instructions have exactly one output
1305 metadata: ?BinOpMetadata,1364 assert(!(reuse_metadata != null and write_args.len != 1)); // see note above
1306) !MCValue {1365
1307 const lhs_is_register = lhs == .register;1366 // The operand mapping is a 1:1 mapping of read args to their
1308 const rhs_is_register = rhs == .register;1367 // corresponding operand index in the Air instruction
1368 assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above
1369
1370 const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len);
1371 defer self.gpa.free(locks);
1372 const read_locks = locks[0..read_args.len];
1373 const write_locks = locks[read_args.len..];
1374
1375 std.mem.set(?RegisterLock, locks, null);
1376 defer for (locks) |lock| {
1377 if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
1378 };
13091379
1310 if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*)));1380 // When we reuse a read_arg as a destination, the corresponding
1311 if (rhs_is_register) assert(rhs.register == registerAlias(rhs.register, rhs_ty.abiSize(self.target.*)));1381 // MCValue of the read_arg will be set to .dead. In that case, we
1382 // skip allocating this read_arg.
1383 var reused_read_arg: ?usize = null;
13121384
1313 const lhs_lock: ?RegisterLock = if (lhs_is_register)1385 // Lock all args which are already allocated to registers
1314 self.register_manager.lockReg(lhs.register)1386 for (read_args) |arg, i| {
1315 else1387 const mcv = try arg.bind.resolveToMcv(self);
1316 null;1388 if (mcv == .register) {
1317 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);1389 read_locks[i] = self.register_manager.lockReg(mcv.register);
1390 }
1391 }
13181392
1319 const rhs_lock: ?RegisterLock = if (rhs_is_register)1393 for (write_args) |arg, i| {
1320 self.register_manager.lockReg(rhs.register)1394 if (arg.bind == .reg) {
1321 else1395 write_locks[i] = self.register_manager.lockReg(arg.bind.reg);
1322 null;1396 }
1323 defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);1397 }
13241398
1325 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1399 // Allocate registers for all args which aren't allocated to
1400 // registers yet
1401 for (read_args) |arg, i| {
1402 const mcv = try arg.bind.resolveToMcv(self);
1403 if (mcv == .register) {
1404 const raw_reg = mcv.register;
1405 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1406 } else {
1407 const track_inst: ?Air.Inst.Index = switch (arg.bind) {
1408 .inst => |inst| Air.refToIndex(inst).?,
1409 else => null,
1410 };
1411 const raw_reg = try self.register_manager.allocReg(track_inst, gp);
1412 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1413 read_locks[i] = self.register_manager.lockReg(arg.reg.*);
1414 }
1415 }
13261416
1327 const lhs_reg = if (lhs_is_register) lhs.register else blk: {1417 if (reuse_metadata != null) {
1328 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {1418 const inst = reuse_metadata.?.corresponding_inst;
1329 break :inst Air.refToIndex(md.lhs).?;1419 const operand_mapping = reuse_metadata.?.operand_mapping;
1330 } else null;1420 const arg = write_args[0];
1421 if (arg.bind == .reg) {
1422 const raw_reg = arg.bind.reg;
1423 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1424 } else {
1425 reuse_operand: for (read_args) |read_arg, i| {
1426 if (read_arg.bind == .inst) {
1427 const operand = read_arg.bind.inst;
1428 const mcv = try self.resolveInst(operand);
1429 if (mcv == .register and
1430 std.meta.eql(arg.class, read_arg.class) and
1431 self.reuseOperand(inst, operand, operand_mapping[i], mcv))
1432 {
1433 const raw_reg = mcv.register;
1434 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1435 write_locks[0] = null;
1436 reused_read_arg = i;
1437 break :reuse_operand;
1438 }
1439 }
1440 } else {
1441 const raw_reg = try self.register_manager.allocReg(inst, arg.class);
1442 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1443 write_locks[0] = self.register_manager.lockReg(arg.reg.*);
1444 }
1445 }
1446 } else {
1447 for (write_args) |arg, i| {
1448 if (arg.bind == .reg) {
1449 const raw_reg = arg.bind.reg;
1450 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1451 } else {
1452 const raw_reg = try self.register_manager.allocReg(null, arg.class);
1453 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1454 write_locks[i] = self.register_manager.lockReg(arg.reg.*);
1455 }
1456 }
1457 }
13311458
1332 const raw_reg = try self.register_manager.allocReg(track_inst, gp);1459 // For all read_args which need to be moved from non-register to
1333 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));1460 // register, perform the move
1461 for (read_args) |arg, i| {
1462 if (reused_read_arg) |j| {
1463 // Check whether this read_arg was reused
1464 if (i == j) continue;
1465 }
13341466
1335 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });1467 const mcv = try arg.bind.resolveToMcv(self);
1468 if (mcv != .register) {
1469 if (arg.bind == .inst) {
1470 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1471 const inst = Air.refToIndex(arg.bind.inst).?;
13361472
1337 break :blk reg;1473 // Overwrite the MCValue associated with this inst
1338 };1474 branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* });
1339 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
1340 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
1341
1342 const rhs_reg = if (rhs_is_register)
1343 // lhs is almost always equal to rhs, except in shifts. In
1344 // order to guarantee that registers will have equal sizes, we
1345 // use the register alias of rhs corresponding to the size of
1346 // lhs.
1347 registerAlias(rhs.register, lhs_ty.abiSize(self.target.*))
1348 else blk: {
1349 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1350 break :inst Air.refToIndex(md.rhs).?;
1351 } else null;
13521475
1353 const raw_reg = try self.register_manager.allocReg(track_inst, gp);1476 // If the previous MCValue occupied some space we track, we
1477 // need to make sure it is marked as free now.
1478 switch (mcv) {
1479 .condition_flags => {
1480 assert(self.condition_flags_inst.? == inst);
1481 self.condition_flags_inst = null;
1482 },
1483 .register => |prev_reg| {
1484 assert(!self.register_manager.isRegFree(prev_reg));
1485 self.register_manager.freeReg(prev_reg);
1486 },
1487 else => {},
1488 }
1489 }
13541490
1355 // Here, we deliberately use lhs as lhs and rhs may differ in1491 try self.genSetReg(arg.ty, arg.reg.*, mcv);
1356 // the case of shifts. See comment above.1492 }
1357 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));1493 }
1494}
13581495
1359 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });1496/// Wrapper around allocRegs and addInst tailored for specific Mir
1497/// instructions which are binary operations acting on two registers
1498///
1499/// Returns the destination register
1500fn binOpRegister(
1501 self: *Self,
1502 mir_tag: Mir.Inst.Tag,
1503 lhs_bind: ReadArg.Bind,
1504 rhs_bind: ReadArg.Bind,
1505 lhs_ty: Type,
1506 rhs_ty: Type,
1507 maybe_inst: ?Air.Inst.Index,
1508) !MCValue {
1509 var lhs_reg: Register = undefined;
1510 var rhs_reg: Register = undefined;
1511 var dest_reg: Register = undefined;
13601512
1361 break :blk reg;1513 const read_args = [_]ReadArg{
1514 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1515 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1362 };1516 };
1363 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);1517 const write_args = [_]WriteArg{
1364 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);1518 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1365
1366 const dest_reg = switch (mir_tag) {
1367 .cmp_shifted_register => undefined, // cmp has no destination register
1368 else => if (metadata) |md| blk: {
1369 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {
1370 break :blk lhs_reg;
1371 } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
1372 break :blk rhs_reg;
1373 } else {
1374 const raw_reg = try self.register_manager.allocReg(md.inst, gp);
1375 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1376 }
1377 } else blk: {
1378 const raw_reg = try self.register_manager.allocReg(null, gp);
1379 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1380 },
1381 };1519 };
13821520 try self.allocRegs(
1383 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);1521 &read_args,
1384 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);1522 &write_args,
1523 if (maybe_inst) |inst| .{
1524 .corresponding_inst = inst,
1525 .operand_mapping = &.{ 0, 1 },
1526 } else null,
1527 );
13851528
1386 const mir_data: Mir.Inst.Data = switch (mir_tag) {1529 const mir_data: Mir.Inst.Data = switch (mir_tag) {
1387 .add_shifted_register,1530 .add_shifted_register,
...@@ -1395,12 +1538,6 @@ fn binOpRegister(...@@ -1395,12 +1538,6 @@ fn binOpRegister(
1395 .imm6 = 0,1538 .imm6 = 0,
1396 .shift = .lsl,1539 .shift = .lsl,
1397 } },1540 } },
1398 .cmp_shifted_register => .{ .rr_imm6_shift = .{
1399 .rn = lhs_reg,
1400 .rm = rhs_reg,
1401 .imm6 = 0,
1402 .shift = .lsl,
1403 } },
1404 .mul,1541 .mul,
1405 .lsl_register,1542 .lsl_register,
1406 .asr_register,1543 .asr_register,
...@@ -1415,7 +1552,7 @@ fn binOpRegister(...@@ -1415,7 +1552,7 @@ fn binOpRegister(
1415 .smull,1552 .smull,
1416 .umull,1553 .umull,
1417 => .{ .rrr = .{1554 => .{ .rrr = .{
1418 .rd = dest_reg.to64(),1555 .rd = dest_reg.toX(),
1419 .rn = lhs_reg,1556 .rn = lhs_reg,
1420 .rm = rhs_reg,1557 .rm = rhs_reg,
1421 } },1558 } },
...@@ -1440,77 +1577,38 @@ fn binOpRegister(...@@ -1440,77 +1577,38 @@ fn binOpRegister(
1440 return MCValue{ .register = dest_reg };1577 return MCValue{ .register = dest_reg };
1441}1578}
14421579
1443/// Don't call this function directly. Use binOp instead.1580/// Wrapper around allocRegs and addInst tailored for specific Mir
1444///1581/// instructions which are binary operations acting on a register and
1445/// Calling this function signals an intention to generate a Mir1582/// an immediate
1446/// instruction of the form
1447///1583///
1448/// op dest, lhs, #rhs_imm1584/// Returns the destination register
1449///
1450/// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to
1451/// rhs and vice versa. This parameter is only used when maybe_inst !=
1452/// null.
1453///
1454/// Asserts that generating an instruction of that form is possible.
1455fn binOpImmediate(1585fn binOpImmediate(
1456 self: *Self,1586 self: *Self,
1457 mir_tag: Mir.Inst.Tag,1587 mir_tag: Mir.Inst.Tag,
1458 lhs: MCValue,1588 lhs_bind: ReadArg.Bind,
1459 rhs: MCValue,1589 rhs_immediate: u64,
1460 lhs_ty: Type,1590 lhs_ty: Type,
1461 lhs_and_rhs_swapped: bool,1591 lhs_and_rhs_swapped: bool,
1462 metadata: ?BinOpMetadata,1592 maybe_inst: ?Air.Inst.Index,
1463) !MCValue {1593) !MCValue {
1464 const lhs_is_register = lhs == .register;1594 var lhs_reg: Register = undefined;
14651595 var dest_reg: Register = undefined;
1466 if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*)));
14671596
1468 const lhs_lock: ?RegisterLock = if (lhs_is_register)1597 const read_args = [_]ReadArg{
1469 self.register_manager.lockReg(lhs.register)1598 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1470 else
1471 null;
1472 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
1473
1474 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1475
1476 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1477 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1478 break :inst Air.refToIndex(
1479 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
1480 ).?;
1481 } else null;
1482
1483 const raw_reg = try self.register_manager.allocReg(track_inst, gp);
1484 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1485
1486 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
1487
1488 break :blk reg;
1489 };1599 };
1490 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);1600 const write_args = [_]WriteArg{
1491 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);1601 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1492
1493 const dest_reg = switch (mir_tag) {
1494 .cmp_immediate => undefined, // cmp has no destination register
1495 else => if (metadata) |md| blk: {
1496 if (lhs_is_register and self.reuseOperand(
1497 md.inst,
1498 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
1499 if (lhs_and_rhs_swapped) 1 else 0,
1500 lhs,
1501 )) {
1502 break :blk lhs_reg;
1503 } else {
1504 const raw_reg = try self.register_manager.allocReg(md.inst, gp);
1505 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1506 }
1507 } else blk: {
1508 const raw_reg = try self.register_manager.allocReg(null, gp);
1509 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1510 },
1511 };1602 };
15121603 const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0};
1513 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);1604 try self.allocRegs(
1605 &read_args,
1606 &write_args,
1607 if (maybe_inst) |inst| .{
1608 .corresponding_inst = inst,
1609 .operand_mapping = operand_mapping,
1610 } else null,
1611 );
15141612
1515 const mir_data: Mir.Inst.Data = switch (mir_tag) {1613 const mir_data: Mir.Inst.Data = switch (mir_tag) {
1516 .add_immediate,1614 .add_immediate,
...@@ -1520,7 +1618,7 @@ fn binOpImmediate(...@@ -1520,7 +1618,7 @@ fn binOpImmediate(
1520 => .{ .rr_imm12_sh = .{1618 => .{ .rr_imm12_sh = .{
1521 .rd = dest_reg,1619 .rd = dest_reg,
1522 .rn = lhs_reg,1620 .rn = lhs_reg,
1523 .imm12 = @intCast(u12, rhs.immediate),1621 .imm12 = @intCast(u12, rhs_immediate),
1524 } },1622 } },
1525 .lsl_immediate,1623 .lsl_immediate,
1526 .asr_immediate,1624 .asr_immediate,
...@@ -1528,11 +1626,7 @@ fn binOpImmediate(...@@ -1528,11 +1626,7 @@ fn binOpImmediate(
1528 => .{ .rr_shift = .{1626 => .{ .rr_shift = .{
1529 .rd = dest_reg,1627 .rd = dest_reg,
1530 .rn = lhs_reg,1628 .rn = lhs_reg,
1531 .shift = @intCast(u6, rhs.immediate),1629 .shift = @intCast(u6, rhs_immediate),
1532 } },
1533 .cmp_immediate => .{ .r_imm12_sh = .{
1534 .rn = lhs_reg,
1535 .imm12 = @intCast(u12, rhs.immediate),
1536 } },1630 } },
1537 else => unreachable,1631 else => unreachable,
1538 };1632 };
...@@ -1545,428 +1639,527 @@ fn binOpImmediate(...@@ -1545,428 +1639,527 @@ fn binOpImmediate(
1545 return MCValue{ .register = dest_reg };1639 return MCValue{ .register = dest_reg };
1546}1640}
15471641
1548const BinOpMetadata = struct {1642fn addSub(
1549 inst: Air.Inst.Index,
1550 lhs: Air.Inst.Ref,
1551 rhs: Air.Inst.Ref,
1552};
1553
1554/// For all your binary operation needs, this function will generate
1555/// the corresponding Mir instruction(s). Returns the location of the
1556/// result.
1557///
1558/// If the binary operation itself happens to be an Air instruction,
1559/// pass the corresponding index in the inst parameter. That helps
1560/// this function do stuff like reusing operands.
1561///
1562/// This function does not do any lowering to Mir itself, but instead
1563/// looks at the lhs and rhs and determines which kind of lowering
1564/// would be best suitable and then delegates the lowering to other
1565/// functions.
1566fn binOp(
1567 self: *Self,1643 self: *Self,
1568 tag: Air.Inst.Tag,1644 tag: Air.Inst.Tag,
1569 lhs: MCValue,1645 lhs_bind: ReadArg.Bind,
1570 rhs: MCValue,1646 rhs_bind: ReadArg.Bind,
1571 lhs_ty: Type,1647 lhs_ty: Type,
1572 rhs_ty: Type,1648 rhs_ty: Type,
1573 metadata: ?BinOpMetadata,1649 maybe_inst: ?Air.Inst.Index,
1574) InnerError!MCValue {1650) InnerError!MCValue {
1575 const mod = self.bin_file.options.module.?;1651 const mod = self.bin_file.options.module.?;
1576 switch (tag) {1652 switch (lhs_ty.zigTypeTag()) {
1577 .add,1653 .Float => return self.fail("TODO binary operations on floats", .{}),
1578 .sub,1654 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1579 .cmp_eq,1655 .Int => {
1580 => {1656 assert(lhs_ty.eql(rhs_ty, mod));
1581 switch (lhs_ty.zigTypeTag()) {1657 const int_info = lhs_ty.intInfo(self.target.*);
1582 .Float => return self.fail("TODO binary operations on floats", .{}),1658 if (int_info.bits <= 64) {
1583 .Vector => return self.fail("TODO binary operations on vectors", .{}),1659 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
1584 .Int => {1660 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
1585 assert(lhs_ty.eql(rhs_ty, mod));1661
1586 const int_info = lhs_ty.intInfo(self.target.*);1662 // Only say yes if the operation is
1587 if (int_info.bits <= 64) {1663 // commutative, i.e. we can swap both of the
1588 // Only say yes if the operation is1664 // operands
1589 // commutative, i.e. we can swap both of the1665 const lhs_immediate_ok = switch (tag) {
1590 // operands1666 .add => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
1591 const lhs_immediate_ok = switch (tag) {1667 .sub => false,
1592 .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),1668 else => unreachable,
1593 .sub, .cmp_eq => false,1669 };
1594 else => unreachable,1670 const rhs_immediate_ok = switch (tag) {
1595 };1671 .add,
1596 const rhs_immediate_ok = switch (tag) {1672 .sub,
1597 .add,1673 => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
1598 .sub,1674 else => unreachable,
1599 .cmp_eq,1675 };
1600 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
1601 else => unreachable,
1602 };
16031676
1604 const mir_tag_register: Mir.Inst.Tag = switch (tag) {1677 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1605 .add => .add_shifted_register,1678 .add => .add_shifted_register,
1606 .sub => .sub_shifted_register,1679 .sub => .sub_shifted_register,
1607 .cmp_eq => .cmp_shifted_register,1680 else => unreachable,
1608 else => unreachable,1681 };
1609 };1682 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
1610 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {1683 .add => .add_immediate,
1611 .add => .add_immediate,1684 .sub => .sub_immediate,
1612 .sub => .sub_immediate,1685 else => unreachable,
1613 .cmp_eq => .cmp_immediate,1686 };
1614 else => unreachable,
1615 };
16161687
1617 if (rhs_immediate_ok) {1688 if (rhs_immediate_ok) {
1618 return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata);1689 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
1619 } else if (lhs_immediate_ok) {1690 } else if (lhs_immediate_ok) {
1620 // swap lhs and rhs1691 // swap lhs and rhs
1621 return try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, metadata);1692 return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
1622 } else {1693 } else {
1623 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);1694 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1624 }1695 }
1625 } else {1696 } else {
1626 return self.fail("TODO binary operations on int with bits > 64", .{});1697 return self.fail("TODO binary operations on int with bits > 64", .{});
1627 }
1628 },
1629 else => unreachable,
1630 }1698 }
1631 },1699 },
1632 .mul => {1700 else => unreachable,
1633 switch (lhs_ty.zigTypeTag()) {1701 }
1634 .Vector => return self.fail("TODO binary operations on vectors", .{}),1702}
1635 .Int => {1703
1636 assert(lhs_ty.eql(rhs_ty, mod));1704fn mul(
1637 const int_info = lhs_ty.intInfo(self.target.*);1705 self: *Self,
1638 if (int_info.bits <= 64) {1706 lhs_bind: ReadArg.Bind,
1639 // TODO add optimisations for multiplication1707 rhs_bind: ReadArg.Bind,
1640 // with immediates, for example a * 2 can be1708 lhs_ty: Type,
1641 // lowered to a << 11709 rhs_ty: Type,
1642 return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata);1710 maybe_inst: ?Air.Inst.Index,
1643 } else {1711) InnerError!MCValue {
1644 return self.fail("TODO binary operations on int with bits > 64", .{});1712 const mod = self.bin_file.options.module.?;
1645 }1713 switch (lhs_ty.zigTypeTag()) {
1646 },1714 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1647 else => unreachable,1715 .Int => {
1716 assert(lhs_ty.eql(rhs_ty, mod));
1717 const int_info = lhs_ty.intInfo(self.target.*);
1718 if (int_info.bits <= 64) {
1719 // TODO add optimisations for multiplication
1720 // with immediates, for example a * 2 can be
1721 // lowered to a << 1
1722 return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1723 } else {
1724 return self.fail("TODO binary operations on int with bits > 64", .{});
1648 }1725 }
1649 },1726 },
1650 .div_float => {1727 else => unreachable,
1651 switch (lhs_ty.zigTypeTag()) {1728 }
1652 .Float => return self.fail("TODO div_float", .{}),1729}
1653 .Vector => return self.fail("TODO div_float on vectors", .{}),1730
1654 else => unreachable,1731fn divFloat(
1732 self: *Self,
1733 lhs_bind: ReadArg.Bind,
1734 rhs_bind: ReadArg.Bind,
1735 lhs_ty: Type,
1736 rhs_ty: Type,
1737 maybe_inst: ?Air.Inst.Index,
1738) InnerError!MCValue {
1739 _ = lhs_bind;
1740 _ = rhs_bind;
1741 _ = rhs_ty;
1742 _ = maybe_inst;
1743
1744 switch (lhs_ty.zigTypeTag()) {
1745 .Float => return self.fail("TODO div_float", .{}),
1746 .Vector => return self.fail("TODO div_float on vectors", .{}),
1747 else => unreachable,
1748 }
1749}
1750
1751fn divTrunc(
1752 self: *Self,
1753 lhs_bind: ReadArg.Bind,
1754 rhs_bind: ReadArg.Bind,
1755 lhs_ty: Type,
1756 rhs_ty: Type,
1757 maybe_inst: ?Air.Inst.Index,
1758) InnerError!MCValue {
1759 const mod = self.bin_file.options.module.?;
1760 switch (lhs_ty.zigTypeTag()) {
1761 .Float => return self.fail("TODO div on floats", .{}),
1762 .Vector => return self.fail("TODO div on vectors", .{}),
1763 .Int => {
1764 assert(lhs_ty.eql(rhs_ty, mod));
1765 const int_info = lhs_ty.intInfo(self.target.*);
1766 if (int_info.bits <= 64) {
1767 switch (int_info.signedness) {
1768 .signed => {
1769 // TODO optimize integer division by constants
1770 return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1771 },
1772 .unsigned => {
1773 // TODO optimize integer division by constants
1774 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1775 },
1776 }
1777 } else {
1778 return self.fail("TODO integer division for ints with bits > 64", .{});
1655 }1779 }
1656 },1780 },
1657 .div_trunc, .div_floor, .div_exact => {1781 else => unreachable,
1658 switch (lhs_ty.zigTypeTag()) {1782 }
1659 .Float => return self.fail("TODO div on floats", .{}),1783}
1660 .Vector => return self.fail("TODO div on vectors", .{}),1784
1661 .Int => {1785fn divFloor(
1662 assert(lhs_ty.eql(rhs_ty, mod));1786 self: *Self,
1663 const int_info = lhs_ty.intInfo(self.target.*);1787 lhs_bind: ReadArg.Bind,
1664 if (int_info.bits <= 64) {1788 rhs_bind: ReadArg.Bind,
1665 switch (int_info.signedness) {1789 lhs_ty: Type,
1666 .signed => {1790 rhs_ty: Type,
1667 switch (tag) {1791 maybe_inst: ?Air.Inst.Index,
1668 .div_trunc, .div_exact => {1792) InnerError!MCValue {
1669 // TODO optimize integer division by constants1793 const mod = self.bin_file.options.module.?;
1670 return try self.binOpRegister(.sdiv, lhs, rhs, lhs_ty, rhs_ty, metadata);1794 switch (lhs_ty.zigTypeTag()) {
1671 },1795 .Float => return self.fail("TODO div on floats", .{}),
1672 .div_floor => return self.fail("TODO div_floor on signed integers", .{}),1796 .Vector => return self.fail("TODO div on vectors", .{}),
1673 else => unreachable,1797 .Int => {
1674 }1798 assert(lhs_ty.eql(rhs_ty, mod));
1675 },1799 const int_info = lhs_ty.intInfo(self.target.*);
1676 .unsigned => {1800 if (int_info.bits <= 64) {
1677 // TODO optimize integer division by constants1801 switch (int_info.signedness) {
1678 return try self.binOpRegister(.udiv, lhs, rhs, lhs_ty, rhs_ty, metadata);1802 .signed => {
1679 },1803 return self.fail("TODO div_floor on signed integers", .{});
1680 }1804 },
1681 } else {1805 .unsigned => {
1682 return self.fail("TODO integer division for ints with bits > 64", .{});1806 // TODO optimize integer division by constants
1683 }1807 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1684 },1808 },
1685 else => unreachable,1809 }
1810 } else {
1811 return self.fail("TODO integer division for ints with bits > 64", .{});
1686 }1812 }
1687 },1813 },
1688 .rem, .mod => {1814 else => unreachable,
1689 switch (lhs_ty.zigTypeTag()) {1815 }
1690 .Float => return self.fail("TODO rem/mod on floats", .{}),1816}
1691 .Vector => return self.fail("TODO rem/mod on vectors", .{}),
1692 .Int => {
1693 assert(lhs_ty.eql(rhs_ty, mod));
1694 const int_info = lhs_ty.intInfo(self.target.*);
1695 if (int_info.bits <= 64) {
1696 if (int_info.signedness == .signed and tag == .mod) {
1697 return self.fail("TODO mod on signed integers", .{});
1698 } else {
1699 const lhs_is_register = lhs == .register;
1700 const rhs_is_register = rhs == .register;
1701
1702 const lhs_lock: ?RegisterLock = if (lhs_is_register)
1703 self.register_manager.lockReg(lhs.register)
1704 else
1705 null;
1706 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
1707
1708 const rhs_lock: ?RegisterLock = if (rhs_is_register)
1709 self.register_manager.lockReg(rhs.register)
1710 else
1711 null;
1712 defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);
1713
1714 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1715
1716 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1717 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1718 break :inst Air.refToIndex(md.lhs).?;
1719 } else null;
1720
1721 const raw_reg = try self.register_manager.allocReg(track_inst, gp);
1722 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1723
1724 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
17251817
1726 break :blk reg;1818fn divExact(
1727 };1819 self: *Self,
1728 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);1820 lhs_bind: ReadArg.Bind,
1729 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);1821 rhs_bind: ReadArg.Bind,
1822 lhs_ty: Type,
1823 rhs_ty: Type,
1824 maybe_inst: ?Air.Inst.Index,
1825) InnerError!MCValue {
1826 const mod = self.bin_file.options.module.?;
1827 switch (lhs_ty.zigTypeTag()) {
1828 .Float => return self.fail("TODO div on floats", .{}),
1829 .Vector => return self.fail("TODO div on vectors", .{}),
1830 .Int => {
1831 assert(lhs_ty.eql(rhs_ty, mod));
1832 const int_info = lhs_ty.intInfo(self.target.*);
1833 if (int_info.bits <= 64) {
1834 switch (int_info.signedness) {
1835 .signed => {
1836 // TODO optimize integer division by constants
1837 return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1838 },
1839 .unsigned => {
1840 // TODO optimize integer division by constants
1841 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1842 },
1843 }
1844 } else {
1845 return self.fail("TODO integer division for ints with bits > 64", .{});
1846 }
1847 },
1848 else => unreachable,
1849 }
1850}
17301851
1731 const rhs_reg = if (rhs_is_register) rhs.register else blk: {1852fn rem(
1732 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {1853 self: *Self,
1733 break :inst Air.refToIndex(md.rhs).?;1854 lhs_bind: ReadArg.Bind,
1734 } else null;1855 rhs_bind: ReadArg.Bind,
1856 lhs_ty: Type,
1857 rhs_ty: Type,
1858 maybe_inst: ?Air.Inst.Index,
1859) InnerError!MCValue {
1860 _ = maybe_inst;
17351861
1736 const raw_reg = try self.register_manager.allocReg(track_inst, gp);1862 const mod = self.bin_file.options.module.?;
1737 const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));1863 switch (lhs_ty.zigTypeTag()) {
1864 .Float => return self.fail("TODO rem/mod on floats", .{}),
1865 .Vector => return self.fail("TODO rem/mod on vectors", .{}),
1866 .Int => {
1867 assert(lhs_ty.eql(rhs_ty, mod));
1868 const int_info = lhs_ty.intInfo(self.target.*);
1869 if (int_info.bits <= 64) {
1870 var lhs_reg: Register = undefined;
1871 var rhs_reg: Register = undefined;
1872 var quotient_reg: Register = undefined;
1873 var remainder_reg: Register = undefined;
1874
1875 const read_args = [_]ReadArg{
1876 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1877 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1878 };
1879 const write_args = [_]WriteArg{
1880 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &quotient_reg },
1881 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &remainder_reg },
1882 };
1883 try self.allocRegs(
1884 &read_args,
1885 &write_args,
1886 null,
1887 );
17381888
1739 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });1889 _ = try self.addInst(.{
1890 .tag = switch (int_info.signedness) {
1891 .signed => .sdiv,
1892 .unsigned => .udiv,
1893 },
1894 .data = .{ .rrr = .{
1895 .rd = quotient_reg,
1896 .rn = lhs_reg,
1897 .rm = rhs_reg,
1898 } },
1899 });
17401900
1741 break :blk reg;1901 _ = try self.addInst(.{
1742 };1902 .tag = .msub,
1743 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);1903 .data = .{ .rrrr = .{
1744 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);1904 .rd = remainder_reg,
17451905 .rn = quotient_reg,
1746 const dest_regs: [2]Register = blk: {1906 .rm = rhs_reg,
1747 const raw_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);1907 .ra = lhs_reg,
1748 const abi_size = lhs_ty.abiSize(self.target.*);1908 } },
1749 break :blk .{1909 });
1750 registerAlias(raw_regs[0], abi_size),
1751 registerAlias(raw_regs[1], abi_size),
1752 };
1753 };
1754 const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs);
1755 defer for (dest_regs_locks) |reg| {
1756 self.register_manager.unlockReg(reg);
1757 };
1758 const quotient_reg = dest_regs[0];
1759 const remainder_reg = dest_regs[1];
17601910
1761 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);1911 return MCValue{ .register = remainder_reg };
1762 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);1912 } else {
1913 return self.fail("TODO rem/mod for integers with bits > 64", .{});
1914 }
1915 },
1916 else => unreachable,
1917 }
1918}
17631919
1764 _ = try self.addInst(.{1920fn modulo(
1765 .tag = switch (int_info.signedness) {1921 self: *Self,
1766 .signed => .sdiv,1922 lhs_bind: ReadArg.Bind,
1767 .unsigned => .udiv,1923 rhs_bind: ReadArg.Bind,
1768 },1924 lhs_ty: Type,
1769 .data = .{ .rrr = .{1925 rhs_ty: Type,
1770 .rd = quotient_reg,1926 maybe_inst: ?Air.Inst.Index,
1771 .rn = lhs_reg,1927) InnerError!MCValue {
1772 .rm = rhs_reg,1928 _ = lhs_bind;
1773 } },1929 _ = rhs_bind;
1774 });1930 _ = rhs_ty;
1931 _ = maybe_inst;
1932
1933 switch (lhs_ty.zigTypeTag()) {
1934 .Float => return self.fail("TODO mod on floats", .{}),
1935 .Vector => return self.fail("TODO mod on vectors", .{}),
1936 .Int => return self.fail("TODO mod on ints", .{}),
1937 else => unreachable,
1938 }
1939}
17751940
1776 _ = try self.addInst(.{1941fn wrappingArithmetic(
1777 .tag = .msub,1942 self: *Self,
1778 .data = .{ .rrrr = .{1943 tag: Air.Inst.Tag,
1779 .rd = remainder_reg,1944 lhs_bind: ReadArg.Bind,
1780 .rn = quotient_reg,1945 rhs_bind: ReadArg.Bind,
1781 .rm = rhs_reg,1946 lhs_ty: Type,
1782 .ra = lhs_reg,1947 rhs_ty: Type,
1783 } },1948 maybe_inst: ?Air.Inst.Index,
1784 });1949) InnerError!MCValue {
1950 switch (lhs_ty.zigTypeTag()) {
1951 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1952 .Int => {
1953 const int_info = lhs_ty.intInfo(self.target.*);
1954 if (int_info.bits <= 64) {
1955 // Generate an add/sub/mul
1956 const result: MCValue = switch (tag) {
1957 .addwrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
1958 .subwrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
1959 .mulwrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
1960 else => unreachable,
1961 };
17851962
1786 return MCValue{ .register = remainder_reg };1963 // Truncate if necessary
1787 }1964 const result_reg = result.register;
1788 } else {1965 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
1789 return self.fail("TODO rem/mod for integers with bits > 64", .{});1966 return result;
1790 }1967 } else {
1791 },1968 return self.fail("TODO binary operations on integers > u64/i64", .{});
1792 else => unreachable,
1793 }1969 }
1794 },1970 },
1795 .addwrap,1971 else => unreachable,
1796 .subwrap,1972 }
1797 .mulwrap,1973}
1798 => {
1799 const base_tag: Air.Inst.Tag = switch (tag) {
1800 .addwrap => .add,
1801 .subwrap => .sub,
1802 .mulwrap => .mul,
1803 else => unreachable,
1804 };
18051974
1806 // Generate an add/sub/mul1975fn bitwise(
1807 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);1976 self: *Self,
1977 tag: Air.Inst.Tag,
1978 lhs_bind: ReadArg.Bind,
1979 rhs_bind: ReadArg.Bind,
1980 lhs_ty: Type,
1981 rhs_ty: Type,
1982 maybe_inst: ?Air.Inst.Index,
1983) InnerError!MCValue {
1984 const mod = self.bin_file.options.module.?;
1985 switch (lhs_ty.zigTypeTag()) {
1986 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1987 .Int => {
1988 assert(lhs_ty.eql(rhs_ty, mod));
1989 const int_info = lhs_ty.intInfo(self.target.*);
1990 if (int_info.bits <= 64) {
1991 // TODO implement bitwise operations with immediates
1992 const mir_tag: Mir.Inst.Tag = switch (tag) {
1993 .bit_and => .and_shifted_register,
1994 .bit_or => .orr_shifted_register,
1995 .xor => .eor_shifted_register,
1996 else => unreachable,
1997 };
18081998
1809 // Truncate if necessary1999 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1810 switch (lhs_ty.zigTypeTag()) {2000 } else {
1811 .Vector => return self.fail("TODO binary operations on vectors", .{}),2001 return self.fail("TODO binary operations on int with bits > 64", .{});
1812 .Int => {
1813 const int_info = lhs_ty.intInfo(self.target.*);
1814 if (int_info.bits <= 64) {
1815 const result_reg = result.register;
1816 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
1817 return result;
1818 } else {
1819 return self.fail("TODO binary operations on integers > u64/i64", .{});
1820 }
1821 },
1822 else => unreachable,
1823 }2002 }
1824 },2003 },
1825 .bit_and,2004 else => unreachable,
1826 .bit_or,2005 }
1827 .xor,2006}
1828 => {
1829 switch (lhs_ty.zigTypeTag()) {
1830 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1831 .Int => {
1832 assert(lhs_ty.eql(rhs_ty, mod));
1833 const int_info = lhs_ty.intInfo(self.target.*);
1834 if (int_info.bits <= 64) {
1835 // TODO implement bitwise operations with immediates
1836 const mir_tag: Mir.Inst.Tag = switch (tag) {
1837 .bit_and => .and_shifted_register,
1838 .bit_or => .orr_shifted_register,
1839 .xor => .eor_shifted_register,
1840 else => unreachable,
1841 };
18422007
1843 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);2008fn shiftExact(
1844 } else {2009 self: *Self,
1845 return self.fail("TODO binary operations on int with bits > 64", .{});2010 tag: Air.Inst.Tag,
1846 }2011 lhs_bind: ReadArg.Bind,
1847 },2012 rhs_bind: ReadArg.Bind,
1848 else => unreachable,2013 lhs_ty: Type,
1849 }2014 rhs_ty: Type,
1850 },2015 maybe_inst: ?Air.Inst.Index,
1851 .shl_exact,2016) InnerError!MCValue {
1852 .shr_exact,2017 _ = rhs_ty;
1853 => {
1854 switch (lhs_ty.zigTypeTag()) {
1855 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1856 .Int => {
1857 const int_info = lhs_ty.intInfo(self.target.*);
1858 if (int_info.bits <= 64) {
1859 const rhs_immediate_ok = rhs == .immediate;
18602018
1861 const mir_tag_register: Mir.Inst.Tag = switch (tag) {2019 switch (lhs_ty.zigTypeTag()) {
1862 .shl_exact => .lsl_register,2020 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1863 .shr_exact => switch (int_info.signedness) {2021 .Int => {
1864 .signed => Mir.Inst.Tag.asr_register,2022 const int_info = lhs_ty.intInfo(self.target.*);
1865 .unsigned => Mir.Inst.Tag.lsr_register,2023 if (int_info.bits <= 64) {
1866 },2024 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
1867 else => unreachable,2025
1868 };2026 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1869 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {2027 .shl_exact => .lsl_register,
1870 .shl_exact => .lsl_immediate,2028 .shr_exact => switch (int_info.signedness) {
1871 .shr_exact => switch (int_info.signedness) {2029 .signed => Mir.Inst.Tag.asr_register,
1872 .signed => Mir.Inst.Tag.asr_immediate,2030 .unsigned => Mir.Inst.Tag.lsr_register,
1873 .unsigned => Mir.Inst.Tag.lsr_immediate,2031 },
1874 },2032 else => unreachable,
1875 else => unreachable,2033 };
1876 };2034 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
2035 .shl_exact => .lsl_immediate,
2036 .shr_exact => switch (int_info.signedness) {
2037 .signed => Mir.Inst.Tag.asr_immediate,
2038 .unsigned => Mir.Inst.Tag.lsr_immediate,
2039 },
2040 else => unreachable,
2041 };
18772042
1878 if (rhs_immediate_ok) {2043 if (rhs_immediate) |imm| {
1879 return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata);2044 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst);
1880 } else {2045 } else {
1881 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);2046 // We intentionally pass lhs_ty here in order to
1882 }2047 // prevent using the 32-bit register alias when
1883 } else {2048 // lhs_ty is > 32 bits.
1884 return self.fail("TODO binary operations on int with bits > 64", .{});2049 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst);
1885 }2050 }
1886 },2051 } else {
1887 else => unreachable,2052 return self.fail("TODO binary operations on int with bits > 64", .{});
1888 }2053 }
1889 },2054 },
1890 .shl,2055 else => unreachable,
1891 .shr,2056 }
1892 => {2057}
1893 const base_tag: Air.Inst.Tag = switch (tag) {
1894 .shl => .shl_exact,
1895 .shr => .shr_exact,
1896 else => unreachable,
1897 };
18982058
1899 // Generate a shl_exact/shr_exact2059fn shiftNormal(
1900 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);2060 self: *Self,
2061 tag: Air.Inst.Tag,
2062 lhs_bind: ReadArg.Bind,
2063 rhs_bind: ReadArg.Bind,
2064 lhs_ty: Type,
2065 rhs_ty: Type,
2066 maybe_inst: ?Air.Inst.Index,
2067) InnerError!MCValue {
2068 switch (lhs_ty.zigTypeTag()) {
2069 .Vector => return self.fail("TODO binary operations on vectors", .{}),
2070 .Int => {
2071 const int_info = lhs_ty.intInfo(self.target.*);
2072 if (int_info.bits <= 64) {
2073 // Generate a shl_exact/shr_exact
2074 const result: MCValue = switch (tag) {
2075 .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
2076 .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst),
2077 else => unreachable,
2078 };
19012079
1902 // Truncate if necessary2080 // Truncate if necessary
1903 switch (tag) {2081 switch (tag) {
1904 .shr => return result,2082 .shr => return result,
1905 .shl => switch (lhs_ty.zigTypeTag()) {2083 .shl => {
1906 .Vector => return self.fail("TODO binary operations on vectors", .{}),2084 const result_reg = result.register;
1907 .Int => {2085 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
1908 const int_info = lhs_ty.intInfo(self.target.*);2086 return result;
1909 if (int_info.bits <= 64) {
1910 const result_reg = result.register;
1911 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
1912 return result;
1913 } else {
1914 return self.fail("TODO binary operations on integers > u64/i64", .{});
1915 }
1916 },2087 },
1917 else => unreachable,2088 else => unreachable,
1918 },2089 }
1919 else => unreachable,2090 } else {
2091 return self.fail("TODO binary operations on integers > u64/i64", .{});
1920 }2092 }
1921 },2093 },
1922 .bool_and,2094 else => unreachable,
1923 .bool_or,2095 }
1924 => {2096}
1925 switch (lhs_ty.zigTypeTag()) {
1926 .Bool => {
1927 assert(lhs != .immediate); // should have been handled by Sema
1928 assert(rhs != .immediate); // should have been handled by Sema
1929
1930 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1931 .bool_and => .and_shifted_register,
1932 .bool_or => .orr_shifted_register,
1933 else => unreachable,
1934 };
19352097
1936 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);2098fn booleanOp(
1937 },2099 self: *Self,
2100 tag: Air.Inst.Tag,
2101 lhs_bind: ReadArg.Bind,
2102 rhs_bind: ReadArg.Bind,
2103 lhs_ty: Type,
2104 rhs_ty: Type,
2105 maybe_inst: ?Air.Inst.Index,
2106) InnerError!MCValue {
2107 switch (lhs_ty.zigTypeTag()) {
2108 .Bool => {
2109 assert((try lhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema
2110 assert((try rhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema
2111
2112 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
2113 .bool_and => .and_shifted_register,
2114 .bool_or => .orr_shifted_register,
1938 else => unreachable,2115 else => unreachable,
1939 }2116 };
2117
2118 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1940 },2119 },
1941 .ptr_add,2120 else => unreachable,
1942 .ptr_sub,2121 }
1943 => {2122}
1944 switch (lhs_ty.zigTypeTag()) {
1945 .Pointer => {
1946 const ptr_ty = lhs_ty;
1947 const elem_ty = switch (ptr_ty.ptrSize()) {
1948 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
1949 else => ptr_ty.childType(),
1950 };
1951 const elem_size = elem_ty.abiSize(self.target.*);
19522123
1953 if (elem_size == 1) {2124fn ptrArithmetic(
1954 const base_tag: Mir.Inst.Tag = switch (tag) {2125 self: *Self,
1955 .ptr_add => .add_shifted_register,2126 tag: Air.Inst.Tag,
1956 .ptr_sub => .sub_shifted_register,2127 lhs_bind: ReadArg.Bind,
1957 else => unreachable,2128 rhs_bind: ReadArg.Bind,
1958 };2129 lhs_ty: Type,
2130 rhs_ty: Type,
2131 maybe_inst: ?Air.Inst.Index,
2132) InnerError!MCValue {
2133 switch (lhs_ty.zigTypeTag()) {
2134 .Pointer => {
2135 const mod = self.bin_file.options.module.?;
2136 assert(rhs_ty.eql(Type.usize, mod));
19592137
1960 return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);2138 const ptr_ty = lhs_ty;
1961 } else {2139 const elem_ty = switch (ptr_ty.ptrSize()) {
1962 // convert the offset into a byte offset by2140 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
1963 // multiplying it with elem_size2141 else => ptr_ty.childType(),
1964 const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null);2142 };
1965 const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);2143 const elem_size = elem_ty.abiSize(self.target.*);
1966 return addr;2144
1967 }2145 const base_tag: Air.Inst.Tag = switch (tag) {
1968 },2146 .ptr_add => .add,
2147 .ptr_sub => .sub,
1969 else => unreachable,2148 else => unreachable,
2149 };
2150
2151 if (elem_size == 1) {
2152 return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst);
2153 } else {
2154 // convert the offset into a byte offset by
2155 // multiplying it with elem_size
2156 const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } };
2157
2158 const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null);
2159 const offset_bind = ReadArg.Bind{ .mcv = offset };
2160
2161 const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null);
2162 return addr;
1970 }2163 }
1971 },2164 },
1972 else => unreachable,2165 else => unreachable,
...@@ -1975,38 +2168,66 @@ fn binOp(...@@ -1975,38 +2168,66 @@ fn binOp(
19752168
1976fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {2169fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1977 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2170 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1978 const lhs = try self.resolveInst(bin_op.lhs);
1979 const rhs = try self.resolveInst(bin_op.rhs);
1980 const lhs_ty = self.air.typeOf(bin_op.lhs);2171 const lhs_ty = self.air.typeOf(bin_op.lhs);
1981 const rhs_ty = self.air.typeOf(bin_op.rhs);2172 const rhs_ty = self.air.typeOf(bin_op.rhs);
19822173
1983 const result: MCValue = if (self.liveness.isUnused(inst))2174 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1984 .dead2175 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1985 else2176 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1986 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{2177
1987 .inst = inst,2178 break :result switch (tag) {
1988 .lhs = bin_op.lhs,2179 .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1989 .rhs = bin_op.rhs,2180 .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1990 });2181
2182 .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2183
2184 .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2185
2186 .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2187
2188 .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2189
2190 .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2191
2192 .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2193
2194 .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2195
2196 .addwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2197 .subwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2198 .mulwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2199
2200 .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2201 .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2202 .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2203
2204 .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2205 .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2206
2207 .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2208 .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2209
2210 .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2211 .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2212
2213 else => unreachable,
2214 };
2215 };
1991 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2216 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1992}2217}
19932218
1994fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {2219fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1995 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2220 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1996 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;2221 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1997 const lhs = try self.resolveInst(bin_op.lhs);
1998 const rhs = try self.resolveInst(bin_op.rhs);
1999 const lhs_ty = self.air.typeOf(bin_op.lhs);2222 const lhs_ty = self.air.typeOf(bin_op.lhs);
2000 const rhs_ty = self.air.typeOf(bin_op.rhs);2223 const rhs_ty = self.air.typeOf(bin_op.rhs);
20012224
2002 const result: MCValue = if (self.liveness.isUnused(inst))2225 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2003 .dead2226 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2004 else2227 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2005 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{2228
2006 .inst = inst,2229 break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
2007 .lhs = bin_op.lhs,2230 };
2008 .rhs = bin_op.rhs,
2009 });
2010 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2231 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2011}2232}
20122233
...@@ -2033,8 +2254,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2033,8 +2254,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2033 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2254 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2034 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2255 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2035 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2256 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2036 const lhs = try self.resolveInst(extra.lhs);2257 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2037 const rhs = try self.resolveInst(extra.rhs);2258 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2038 const lhs_ty = self.air.typeOf(extra.lhs);2259 const lhs_ty = self.air.typeOf(extra.lhs);
2039 const rhs_ty = self.air.typeOf(extra.rhs);2260 const rhs_ty = self.air.typeOf(extra.rhs);
20402261
...@@ -2051,7 +2272,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2051,7 +2272,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2051 const int_info = lhs_ty.intInfo(self.target.*);2272 const int_info = lhs_ty.intInfo(self.target.*);
2052 switch (int_info.bits) {2273 switch (int_info.bits) {
2053 1...31, 33...63 => {2274 1...31, 33...63 => {
2054 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);2275 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
20552276
2056 try self.spillCompareFlagsIfOccupied();2277 try self.spillCompareFlagsIfOccupied();
2057 self.condition_flags_inst = null;2278 self.condition_flags_inst = null;
...@@ -2061,13 +2282,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2061,13 +2282,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2061 .sub_with_overflow => .sub,2282 .sub_with_overflow => .sub,
2062 else => unreachable,2283 else => unreachable,
2063 };2284 };
2064 const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);2285 const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
2065 const dest_reg = dest.register;2286 const dest_reg = dest.register;
2066 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);2287 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
2067 defer self.register_manager.unlockReg(dest_reg_lock);2288 defer self.register_manager.unlockReg(dest_reg_lock);
20682289
2069 const raw_truncated_reg = try self.register_manager.allocReg(null, gp);2290 const raw_truncated_reg = try self.register_manager.allocReg(null, gp);
2070 const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*));2291 const truncated_reg = self.registerAlias(raw_truncated_reg, lhs_ty);
2071 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);2292 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
2072 defer self.register_manager.unlockReg(truncated_reg_lock);2293 defer self.register_manager.unlockReg(truncated_reg_lock);
20732294
...@@ -2075,7 +2296,15 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2075,7 +2296,15 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2075 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);2296 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
20762297
2077 // cmp dest, truncated2298 // cmp dest, truncated
2078 _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, lhs_ty, lhs_ty, null);2299 _ = try self.addInst(.{
2300 .tag = .cmp_shifted_register,
2301 .data = .{ .rr_imm6_shift = .{
2302 .rn = dest_reg,
2303 .rm = truncated_reg,
2304 .imm6 = 0,
2305 .shift = .lsl,
2306 } },
2307 });
20792308
2080 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });2309 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
2081 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });2310 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });
...@@ -2083,18 +2312,21 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2083,18 +2312,21 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2083 break :result MCValue{ .stack_offset = stack_offset };2312 break :result MCValue{ .stack_offset = stack_offset };
2084 },2313 },
2085 32, 64 => {2314 32, 64 => {
2315 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
2316 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2317
2086 // Only say yes if the operation is2318 // Only say yes if the operation is
2087 // commutative, i.e. we can swap both of the2319 // commutative, i.e. we can swap both of the
2088 // operands2320 // operands
2089 const lhs_immediate_ok = switch (tag) {2321 const lhs_immediate_ok = switch (tag) {
2090 .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),2322 .add_with_overflow => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
2091 .sub_with_overflow => false,2323 .sub_with_overflow => false,
2092 else => unreachable,2324 else => unreachable,
2093 };2325 };
2094 const rhs_immediate_ok = switch (tag) {2326 const rhs_immediate_ok = switch (tag) {
2095 .add_with_overflow,2327 .add_with_overflow,
2096 .sub_with_overflow,2328 .sub_with_overflow,
2097 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),2329 => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
2098 else => unreachable,2330 else => unreachable,
2099 };2331 };
21002332
...@@ -2114,12 +2346,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2114,12 +2346,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
21142346
2115 const dest = blk: {2347 const dest = blk: {
2116 if (rhs_immediate_ok) {2348 if (rhs_immediate_ok) {
2117 break :blk try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, null);2349 break :blk try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, null);
2118 } else if (lhs_immediate_ok) {2350 } else if (lhs_immediate_ok) {
2119 // swap lhs and rhs2351 // swap lhs and rhs
2120 break :blk try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, null);2352 break :blk try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, null);
2121 } else {2353 } else {
2122 break :blk try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, null);2354 break :blk try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
2123 }2355 }
2124 };2356 };
21252357
...@@ -2150,8 +2382,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2150,8 +2382,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2150 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2382 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2151 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });2383 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
2152 const result: MCValue = result: {2384 const result: MCValue = result: {
2153 const lhs = try self.resolveInst(extra.lhs);2385 const mod = self.bin_file.options.module.?;
2154 const rhs = try self.resolveInst(extra.rhs);2386
2387 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2388 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2155 const lhs_ty = self.air.typeOf(extra.lhs);2389 const lhs_ty = self.air.typeOf(extra.lhs);
2156 const rhs_ty = self.air.typeOf(extra.rhs);2390 const rhs_ty = self.air.typeOf(extra.rhs);
21572391
...@@ -2163,20 +2397,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2163,20 +2397,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2163 switch (lhs_ty.zigTypeTag()) {2397 switch (lhs_ty.zigTypeTag()) {
2164 .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),2398 .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
2165 .Int => {2399 .Int => {
2400 assert(lhs_ty.eql(rhs_ty, mod));
2166 const int_info = lhs_ty.intInfo(self.target.*);2401 const int_info = lhs_ty.intInfo(self.target.*);
2167
2168 if (int_info.bits <= 32) {2402 if (int_info.bits <= 32) {
2169 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);2403 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
21702404
2171 try self.spillCompareFlagsIfOccupied();2405 try self.spillCompareFlagsIfOccupied();
2172 self.condition_flags_inst = null;
21732406
2174 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {2407 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
2175 .signed => .smull,2408 .signed => .smull,
2176 .unsigned => .umull,2409 .unsigned => .umull,
2177 };2410 };
21782411
2179 const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);2412 const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
2180 const dest_reg = dest.register;2413 const dest_reg = dest.register;
2181 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);2414 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
2182 defer self.register_manager.unlockReg(dest_reg_lock);2415 defer self.register_manager.unlockReg(dest_reg_lock);
...@@ -2186,8 +2419,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2186,8 +2419,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2186 defer self.register_manager.unlockReg(truncated_reg_lock);2419 defer self.register_manager.unlockReg(truncated_reg_lock);
21872420
2188 try self.truncRegister(2421 try self.truncRegister(
2189 dest_reg.to32(),2422 dest_reg.toW(),
2190 truncated_reg.to32(),2423 truncated_reg.toW(),
2191 int_info.signedness,2424 int_info.signedness,
2192 int_info.bits,2425 int_info.bits,
2193 );2426 );
...@@ -2197,8 +2430,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2197,8 +2430,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2197 _ = try self.addInst(.{2430 _ = try self.addInst(.{
2198 .tag = .cmp_extended_register,2431 .tag = .cmp_extended_register,
2199 .data = .{ .rr_extend_shift = .{2432 .data = .{ .rr_extend_shift = .{
2200 .rn = dest_reg.to64(),2433 .rn = dest_reg.toX(),
2201 .rm = truncated_reg.to32(),2434 .rm = truncated_reg.toW(),
2202 .ext_type = .sxtw,2435 .ext_type = .sxtw,
2203 .imm3 = 0,2436 .imm3 = 0,
2204 } },2437 } },
...@@ -2208,8 +2441,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2208,8 +2441,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2208 _ = try self.addInst(.{2441 _ = try self.addInst(.{
2209 .tag = .cmp_extended_register,2442 .tag = .cmp_extended_register,
2210 .data = .{ .rr_extend_shift = .{2443 .data = .{ .rr_extend_shift = .{
2211 .rn = dest_reg.to64(),2444 .rn = dest_reg.toX(),
2212 .rm = truncated_reg.to32(),2445 .rm = truncated_reg.toW(),
2213 .ext_type = .uxtw,2446 .ext_type = .uxtw,
2214 .imm3 = 0,2447 .imm3 = 0,
2215 } },2448 } },
...@@ -2222,53 +2455,30 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2222,53 +2455,30 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
22222455
2223 break :result MCValue{ .stack_offset = stack_offset };2456 break :result MCValue{ .stack_offset = stack_offset };
2224 } else if (int_info.bits <= 64) {2457 } else if (int_info.bits <= 64) {
2225 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);2458 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
22262459
2227 try self.spillCompareFlagsIfOccupied();2460 try self.spillCompareFlagsIfOccupied();
2228 self.condition_flags_inst = null;
2229
2230 // TODO this should really be put in a helper similar to `binOpRegister`
2231 const lhs_is_register = lhs == .register;
2232 const rhs_is_register = rhs == .register;
2233
2234 const lhs_lock: ?RegisterLock = if (lhs_is_register)
2235 self.register_manager.lockRegAssumeUnused(lhs.register)
2236 else
2237 null;
2238 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
2239
2240 const rhs_lock: ?RegisterLock = if (rhs_is_register)
2241 self.register_manager.lockRegAssumeUnused(rhs.register)
2242 else
2243 null;
2244 defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);
2245
2246 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2247 const raw_reg = try self.register_manager.allocReg(null, gp);
2248 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
2249 break :blk reg;
2250 };
2251 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
2252 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
2253
2254 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
2255 const raw_reg = try self.register_manager.allocReg(null, gp);
2256 const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));
2257 break :blk reg;
2258 };
2259 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
2260 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
22612461
2262 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);2462 var lhs_reg: Register = undefined;
2263 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);2463 var rhs_reg: Register = undefined;
2464 var dest_reg: Register = undefined;
2465 var dest_high_reg: Register = undefined;
2466 var truncated_reg: Register = undefined;
22642467
2265 const dest_reg = blk: {2468 const read_args = [_]ReadArg{
2266 const raw_reg = try self.register_manager.allocReg(null, gp);2469 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2267 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));2470 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
2268 break :blk reg;
2269 };2471 };
2270 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);2472 const write_args = [_]WriteArg{
2271 defer self.register_manager.unlockReg(dest_reg_lock);2473 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2474 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_high_reg },
2475 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg },
2476 };
2477 try self.allocRegs(
2478 &read_args,
2479 &write_args,
2480 null,
2481 );
22722482
2273 switch (int_info.signedness) {2483 switch (int_info.signedness) {
2274 .signed => {2484 .signed => {
...@@ -2282,10 +2492,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2282,10 +2492,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2282 } },2492 } },
2283 });2493 });
22842494
2285 const dest_high_reg = try self.register_manager.allocReg(null, gp);
2286 const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg);
2287 defer self.register_manager.unlockReg(dest_high_reg_lock);
2288
2289 // smulh dest_high, lhs, rhs2495 // smulh dest_high, lhs, rhs
2290 _ = try self.addInst(.{2496 _ = try self.addInst(.{
2291 .tag = .smulh,2497 .tag = .smulh,
...@@ -2332,10 +2538,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2332,10 +2538,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2332 }2538 }
2333 },2539 },
2334 .unsigned => {2540 .unsigned => {
2335 const dest_high_reg = try self.register_manager.allocReg(null, gp);
2336 const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg);
2337 defer self.register_manager.unlockReg(dest_high_reg_lock);
2338
2339 // umulh dest_high, lhs, rhs2541 // umulh dest_high, lhs, rhs
2340 _ = try self.addInst(.{2542 _ = try self.addInst(.{
2341 .tag = .umulh,2543 .tag = .umulh,
...@@ -2356,14 +2558,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2356,14 +2558,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2356 } },2558 } },
2357 });2559 });
23582560
2359 _ = try self.binOp(2561 _ = try self.addInst(.{
2360 .cmp_eq,2562 .tag = .cmp_immediate,
2361 .{ .register = dest_high_reg },2563 .data = .{ .r_imm12_sh = .{
2362 .{ .immediate = 0 },2564 .rn = dest_high_reg,
2363 Type.usize,2565 .imm12 = 0,
2364 Type.usize,2566 } },
2365 null,2567 });
2366 );
23672568
2368 if (int_info.bits < 64) {2569 if (int_info.bits < 64) {
2369 // lsr dest_high, dest, #shift2570 // lsr dest_high, dest, #shift
...@@ -2376,22 +2577,17 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2376,22 +2577,17 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2376 } },2577 } },
2377 });2578 });
23782579
2379 _ = try self.binOp(2580 _ = try self.addInst(.{
2380 .cmp_eq,2581 .tag = .cmp_immediate,
2381 .{ .register = dest_high_reg },2582 .data = .{ .r_imm12_sh = .{
2382 .{ .immediate = 0 },2583 .rn = dest_high_reg,
2383 Type.usize,2584 .imm12 = 0,
2384 Type.usize,2585 } },
2385 null,2586 });
2386 );
2387 }2587 }
2388 },2588 },
2389 }2589 }
23902590
2391 const truncated_reg = try self.register_manager.allocReg(null, gp);
2392 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
2393 defer self.register_manager.unlockReg(truncated_reg_lock);
2394
2395 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);2591 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
23962592
2397 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });2593 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
...@@ -2411,8 +2607,8 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2411,8 +2607,8 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2411 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2607 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2412 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });2608 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
2413 const result: MCValue = result: {2609 const result: MCValue = result: {
2414 const lhs = try self.resolveInst(extra.lhs);2610 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2415 const rhs = try self.resolveInst(extra.rhs);2611 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2416 const lhs_ty = self.air.typeOf(extra.lhs);2612 const lhs_ty = self.air.typeOf(extra.lhs);
2417 const rhs_ty = self.air.typeOf(extra.rhs);2613 const rhs_ty = self.air.typeOf(extra.rhs);
24182614
...@@ -2426,35 +2622,112 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2426,35 +2622,112 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2426 .Int => {2622 .Int => {
2427 const int_info = lhs_ty.intInfo(self.target.*);2623 const int_info = lhs_ty.intInfo(self.target.*);
2428 if (int_info.bits <= 64) {2624 if (int_info.bits <= 64) {
2429 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);2625 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
2430
2431 const lhs_lock: ?RegisterLock = if (lhs == .register)
2432 self.register_manager.lockRegAssumeUnused(lhs.register)
2433 else
2434 null;
2435 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
24362626
2437 try self.spillCompareFlagsIfOccupied();2627 try self.spillCompareFlagsIfOccupied();
2438 self.condition_flags_inst = null;
24392628
2440 // lsl dest, lhs, rhs2629 var lhs_reg: Register = undefined;
2441 const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null);2630 var rhs_reg: Register = undefined;
2442 const dest_reg = dest.register;2631 var dest_reg: Register = undefined;
2443 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);2632 var reconstructed_reg: Register = undefined;
2444 defer self.register_manager.unlockReg(dest_reg_lock);2633
2634 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2635 if (rhs_immediate) |imm| {
2636 const read_args = [_]ReadArg{
2637 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2638 };
2639 const write_args = [_]WriteArg{
2640 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2641 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
2642 };
2643 try self.allocRegs(
2644 &read_args,
2645 &write_args,
2646 null,
2647 );
2648
2649 // lsl dest, lhs, rhs
2650 _ = try self.addInst(.{
2651 .tag = .lsl_immediate,
2652 .data = .{ .rr_shift = .{
2653 .rd = dest_reg,
2654 .rn = lhs_reg,
2655 .shift = @intCast(u6, imm),
2656 } },
2657 });
2658
2659 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
2660
2661 // asr/lsr reconstructed, dest, rhs
2662 _ = try self.addInst(.{
2663 .tag = switch (int_info.signedness) {
2664 .signed => Mir.Inst.Tag.asr_immediate,
2665 .unsigned => Mir.Inst.Tag.lsr_immediate,
2666 },
2667 .data = .{ .rr_shift = .{
2668 .rd = reconstructed_reg,
2669 .rn = dest_reg,
2670 .shift = @intCast(u6, imm),
2671 } },
2672 });
2673 } else {
2674 const read_args = [_]ReadArg{
2675 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2676 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
2677 };
2678 const write_args = [_]WriteArg{
2679 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2680 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
2681 };
2682 try self.allocRegs(
2683 &read_args,
2684 &write_args,
2685 null,
2686 );
2687
2688 // lsl dest, lhs, rhs
2689 _ = try self.addInst(.{
2690 .tag = .lsl_register,
2691 .data = .{ .rrr = .{
2692 .rd = dest_reg,
2693 .rn = lhs_reg,
2694 .rm = rhs_reg,
2695 } },
2696 });
24452697
2446 // asr/lsr reconstructed, dest, rhs2698 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
2447 const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null);2699
2700 // asr/lsr reconstructed, dest, rhs
2701 _ = try self.addInst(.{
2702 .tag = switch (int_info.signedness) {
2703 .signed => Mir.Inst.Tag.asr_register,
2704 .unsigned => Mir.Inst.Tag.lsr_register,
2705 },
2706 .data = .{ .rrr = .{
2707 .rd = reconstructed_reg,
2708 .rn = dest_reg,
2709 .rm = rhs_reg,
2710 } },
2711 });
2712 }
24482713
2449 // cmp lhs, reconstructed2714 // cmp lhs, reconstructed
2450 _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null);2715 _ = try self.addInst(.{
2716 .tag = .cmp_shifted_register,
2717 .data = .{ .rr_imm6_shift = .{
2718 .rn = lhs_reg,
2719 .rm = reconstructed_reg,
2720 .imm6 = 0,
2721 .shift = .lsl,
2722 } },
2723 });
24512724
2452 try self.genSetStack(lhs_ty, stack_offset, dest);2725 try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg });
2453 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });2726 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });
24542727
2455 break :result MCValue{ .stack_offset = stack_offset };2728 break :result MCValue{ .stack_offset = stack_offset };
2456 } else {2729 } else {
2457 return self.fail("TODO overflow operations on integers > u64/i64", .{});2730 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
2458 }2731 }
2459 },2732 },
2460 else => unreachable,2733 else => unreachable,
...@@ -2712,63 +2985,59 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2712,63 +2985,59 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
2712}2985}
27132986
2714fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {2987fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2715 const is_volatile = false; // TODO
2716 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2988 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
27172989 const slice_ty = self.air.typeOf(bin_op.lhs);
2718 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });2990 const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
2719 const result: MCValue = result: {
2720 const slice_ty = self.air.typeOf(bin_op.lhs);
2721 const elem_ty = slice_ty.childType();
2722 const elem_size = elem_ty.abiSize(self.target.*);
2723 const slice_mcv = try self.resolveInst(bin_op.lhs);
2724
2725 // TODO optimize for the case where the index is a constant,
2726 // i.e. index_mcv == .immediate
2727 const index_mcv = try self.resolveInst(bin_op.rhs);
2728 const index_is_register = index_mcv == .register;
2729
2730 var buf: Type.SlicePtrFieldTypeBuffer = undefined;2991 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2731 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);2992 const ptr_ty = slice_ty.slicePtrFieldType(&buf);
2732
2733 const index_lock: ?RegisterLock = if (index_is_register)
2734 self.register_manager.lockRegAssumeUnused(index_mcv.register)
2735 else
2736 null;
2737 defer if (index_lock) |reg| self.register_manager.unlockReg(reg);
27382993
2994 const slice_mcv = try self.resolveInst(bin_op.lhs);
2739 const base_mcv = slicePtr(slice_mcv);2995 const base_mcv = slicePtr(slice_mcv);
27402996
2741 switch (elem_size) {2997 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
2742 else => {2998 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2743 const base_reg = switch (base_mcv) {
2744 .register => |r| r,
2745 else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv),
2746 };
2747 const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg);
2748 defer self.register_manager.unlockReg(base_reg_lock);
2749
2750 const dest = try self.allocRegOrMem(inst, true);
2751 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null);
2752 try self.load(dest, addr, slice_ptr_field_type);
27532999
2754 break :result dest;3000 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
2755 },
2756 }
2757 };3001 };
2758 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3002 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2759}3003}
27603004
3005fn ptrElemVal(
3006 self: *Self,
3007 ptr_bind: ReadArg.Bind,
3008 index_bind: ReadArg.Bind,
3009 ptr_ty: Type,
3010 maybe_inst: ?Air.Inst.Index,
3011) !MCValue {
3012 const elem_ty = ptr_ty.childType();
3013 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
3014
3015 // TODO optimize for elem_sizes of 1, 2, 4, 8
3016 switch (elem_size) {
3017 else => {
3018 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null);
3019
3020 const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst);
3021 try self.load(dest, addr, ptr_ty);
3022 return dest;
3023 },
3024 }
3025}
3026
2761fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {3027fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2762 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3028 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2763 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3029 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2764 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3030 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2765 const slice_mcv = try self.resolveInst(extra.lhs);3031 const slice_mcv = try self.resolveInst(extra.lhs);
2766 const index_mcv = try self.resolveInst(extra.rhs);
2767 const base_mcv = slicePtr(slice_mcv);3032 const base_mcv = slicePtr(slice_mcv);
27683033
3034 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
3035 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
3036
2769 const slice_ty = self.air.typeOf(extra.lhs);3037 const slice_ty = self.air.typeOf(extra.lhs);
3038 const index_ty = self.air.typeOf(extra.rhs);
27703039
2771 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null);3040 const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null);
2772 break :result addr;3041 break :result addr;
2773 };3042 };
2774 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });3043 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
...@@ -2791,12 +3060,13 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2791,12 +3060,13 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2791 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3060 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2792 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3061 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2793 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3062 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2794 const ptr_mcv = try self.resolveInst(extra.lhs);3063 const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2795 const index_mcv = try self.resolveInst(extra.rhs);3064 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
27963065
2797 const ptr_ty = self.air.typeOf(extra.lhs);3066 const ptr_ty = self.air.typeOf(extra.lhs);
3067 const index_ty = self.air.typeOf(extra.rhs);
27983068
2799 const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null);3069 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null);
2800 break :result addr;3070 break :result addr;
2801 };3071 };
2802 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });3072 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
...@@ -2853,7 +3123,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {...@@ -2853,7 +3123,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
2853 return self.finishAir(inst, result, .{ un_op, .none, .none });3123 return self.finishAir(inst, result, .{ un_op, .none, .none });
2854}3124}
28553125
2856fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {3126fn reuseOperand(
3127 self: *Self,
3128 inst: Air.Inst.Index,
3129 operand: Air.Inst.Ref,
3130 op_index: Liveness.OperandInt,
3131 mcv: MCValue,
3132) bool {
2857 if (!self.liveness.operandDies(inst, op_index))3133 if (!self.liveness.operandDies(inst, op_index))
2858 return false;3134 return false;
28593135
...@@ -2912,7 +3188,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -2912,7 +3188,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
2912 .stack_offset => |off| {3188 .stack_offset => |off| {
2913 if (elem_size <= 8) {3189 if (elem_size <= 8) {
2914 const raw_tmp_reg = try self.register_manager.allocReg(null, gp);3190 const raw_tmp_reg = try self.register_manager.allocReg(null, gp);
2915 const tmp_reg = registerAlias(raw_tmp_reg, elem_size);3191 const tmp_reg = self.registerAlias(raw_tmp_reg, elem_ty);
2916 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);3192 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
2917 defer self.register_manager.unlockReg(tmp_reg_lock);3193 defer self.register_manager.unlockReg(tmp_reg_lock);
29183194
...@@ -3050,11 +3326,11 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3050,11 +3326,11 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
3050 if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {3326 if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
3051 // The MCValue that holds the pointer can be re-used as the value.3327 // The MCValue that holds the pointer can be re-used as the value.
3052 break :blk switch (ptr) {3328 break :blk switch (ptr) {
3053 .register => |r| MCValue{ .register = registerAlias(r, elem_size) },3329 .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) },
3054 else => ptr,3330 else => ptr,
3055 };3331 };
3056 } else {3332 } else {
3057 break :blk try self.allocRegOrMem(inst, true);3333 break :blk try self.allocRegOrMem(elem_ty, true, inst);
3058 }3334 }
3059 };3335 };
3060 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));3336 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
...@@ -3136,7 +3412,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3136,7 +3412,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3136 else => {3412 else => {
3137 if (abi_size <= 8) {3413 if (abi_size <= 8) {
3138 const raw_tmp_reg = try self.register_manager.allocReg(null, gp);3414 const raw_tmp_reg = try self.register_manager.allocReg(null, gp);
3139 const tmp_reg = registerAlias(raw_tmp_reg, abi_size);3415 const tmp_reg = self.registerAlias(raw_tmp_reg, value_ty);
3140 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);3416 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
3141 defer self.register_manager.unlockReg(tmp_reg_lock);3417 defer self.register_manager.unlockReg(tmp_reg_lock);
31423418
...@@ -3229,26 +3505,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde...@@ -3229,26 +3505,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
3229 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };3505 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };
3230 },3506 },
3231 else => {3507 else => {
3232 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{3508 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
3233 .immediate = struct_field_offset,3509 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
3234 });
3235 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
3236 defer self.register_manager.unlockReg(offset_reg_lock);
3237
3238 const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv);
3239 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
3240 defer self.register_manager.unlockReg(addr_reg_lock);
3241
3242 const dest = try self.binOp(
3243 .add,
3244 .{ .register = addr_reg },
3245 .{ .register = offset_reg },
3246 Type.usize,
3247 Type.usize,
3248 null,
3249 );
32503510
3251 break :result dest;3511 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
3252 },3512 },
3253 }3513 }
3254 };3514 };
...@@ -3295,7 +3555,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3295,7 +3555,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
3295 } else {3555 } else {
3296 // Copy to new register3556 // Copy to new register
3297 const raw_dest_reg = try self.register_manager.allocReg(null, gp);3557 const raw_dest_reg = try self.register_manager.allocReg(null, gp);
3298 const dest_reg = registerAlias(raw_dest_reg, struct_field_ty.abiSize(self.target.*));3558 const dest_reg = self.registerAlias(raw_dest_reg, struct_field_ty);
3299 try self.genSetReg(struct_field_ty, dest_reg, field);3559 try self.genSetReg(struct_field_ty, dest_reg, field);
33003560
3301 break :result MCValue{ .register = dest_reg };3561 break :result MCValue{ .register = dest_reg };
...@@ -3330,7 +3590,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -3330,7 +3590,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3330 return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)});3590 return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)});
3331 };3591 };
3332 const abi_align = ty.abiAlignment(self.target.*);3592 const abi_align = ty.abiAlignment(self.target.*);
3333 const stack_offset = try self.allocMem(inst, abi_size, abi_align);3593 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
3334 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });3594 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
33353595
3336 break :blk MCValue{ .stack_offset = stack_offset };3596 break :blk MCValue{ .stack_offset = stack_offset };
...@@ -3408,11 +3668,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3408,11 +3668,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3408 const ret_ty = fn_ty.fnReturnType();3668 const ret_ty = fn_ty.fnReturnType();
3409 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));3669 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
3410 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));3670 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
3411 const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align);3671 const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst);
34123672
3413 const ptr_bits = self.target.cpu.arch.ptrBitWidth();3673 const ret_ptr_reg = self.registerAlias(.x0, Type.usize);
3414 const ptr_bytes = @divExact(ptr_bits, 8);
3415 const ret_ptr_reg = registerAlias(.x0, ptr_bytes);
34163674
3417 var ptr_ty_payload: Type.Payload.ElemType = .{3675 var ptr_ty_payload: Type.Payload.ElemType = .{
3418 .base = .{ .tag = .single_mut_pointer },3676 .base = .{ .tag = .single_mut_pointer },
...@@ -3636,14 +3894,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3636,14 +3894,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
3636 const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));3894 const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
3637 const abi_align = ret_ty.abiAlignment(self.target.*);3895 const abi_align = ret_ty.abiAlignment(self.target.*);
36383896
3639 // This is essentially allocMem without the3897 const offset = try self.allocMem(abi_size, abi_align, null);
3640 // instruction tracking
3641 if (abi_align > self.stack_align)
3642 self.stack_align = abi_align;
3643 // TODO find a free slot instead of always appending
3644 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
3645 self.next_stack_offset = offset;
3646 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
36473898
3648 const tmp_mcv = MCValue{ .stack_offset = offset };3899 const tmp_mcv = MCValue{ .stack_offset = offset };
3649 try self.load(tmp_mcv, ptr, ptr_ty);3900 try self.load(tmp_mcv, ptr, ptr_ty);
...@@ -3660,54 +3911,100 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3660,54 +3911,100 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
36603911
3661fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {3912fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
3662 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3913 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3663 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3914 const lhs_ty = self.air.typeOf(bin_op.lhs);
3664 const lhs = try self.resolveInst(bin_op.lhs);
3665 const rhs = try self.resolveInst(bin_op.rhs);
3666 const lhs_ty = self.air.typeOf(bin_op.lhs);
3667
3668 var int_buffer: Type.Payload.Bits = undefined;
3669 const int_ty = switch (lhs_ty.zigTypeTag()) {
3670 .Vector => return self.fail("TODO AArch64 cmp vectors", .{}),
3671 .Enum => lhs_ty.intTagType(&int_buffer),
3672 .Int => lhs_ty,
3673 .Bool => Type.initTag(.u1),
3674 .Pointer => Type.usize,
3675 .ErrorSet => Type.initTag(.u16),
3676 .Optional => blk: {
3677 var opt_buffer: Type.Payload.ElemType = undefined;
3678 const payload_ty = lhs_ty.optionalChild(&opt_buffer);
3679 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3680 break :blk Type.initTag(.u1);
3681 } else if (lhs_ty.isPtrLikeOptional()) {
3682 break :blk Type.usize;
3683 } else {
3684 return self.fail("TODO AArch64 cmp non-pointer optionals", .{});
3685 }
3686 },
3687 .Float => return self.fail("TODO AArch64 cmp floats", .{}),
3688 else => unreachable,
3689 };
36903915
3691 const int_info = int_ty.intInfo(self.target.*);3916 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
3692 if (int_info.bits <= 64) {3917 break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op);
3693 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{3918 };
3694 .inst = inst,3919
3695 .lhs = bin_op.lhs,3920 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3696 .rhs = bin_op.rhs,3921}
3697 });3922
3923fn cmp(
3924 self: *Self,
3925 lhs: ReadArg.Bind,
3926 rhs: ReadArg.Bind,
3927 lhs_ty: Type,
3928 op: math.CompareOperator,
3929) !MCValue {
3930 var int_buffer: Type.Payload.Bits = undefined;
3931 const int_ty = switch (lhs_ty.zigTypeTag()) {
3932 .Optional => blk: {
3933 var opt_buffer: Type.Payload.ElemType = undefined;
3934 const payload_ty = lhs_ty.optionalChild(&opt_buffer);
3935 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3936 break :blk Type.initTag(.u1);
3937 } else if (lhs_ty.isPtrLikeOptional()) {
3938 break :blk Type.usize;
3939 } else {
3940 return self.fail("TODO ARM cmp non-pointer optionals", .{});
3941 }
3942 },
3943 .Float => return self.fail("TODO ARM cmp floats", .{}),
3944 .Enum => lhs_ty.intTagType(&int_buffer),
3945 .Int => lhs_ty,
3946 .Bool => Type.initTag(.u1),
3947 .Pointer => Type.usize,
3948 .ErrorSet => Type.initTag(.u16),
3949 else => unreachable,
3950 };
36983951
3699 try self.spillCompareFlagsIfOccupied();3952 const int_info = int_ty.intInfo(self.target.*);
3700 self.condition_flags_inst = inst;3953 if (int_info.bits <= 64) {
3954 try self.spillCompareFlagsIfOccupied();
37013955
3702 break :result switch (int_info.signedness) {3956 var lhs_reg: Register = undefined;
3703 .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) },3957 var rhs_reg: Register = undefined;
3704 .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) },3958
3959 const rhs_immediate = try rhs.resolveToImmediate(self);
3960 const rhs_immediate_ok = if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false;
3961
3962 if (rhs_immediate_ok) {
3963 const read_args = [_]ReadArg{
3964 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
3705 };3965 };
3966 try self.allocRegs(
3967 &read_args,
3968 &.{},
3969 null, // we won't be able to reuse a register as there are no write_regs
3970 );
3971
3972 _ = try self.addInst(.{
3973 .tag = .cmp_immediate,
3974 .data = .{ .r_imm12_sh = .{
3975 .rn = lhs_reg,
3976 .imm12 = @intCast(u12, rhs_immediate.?),
3977 } },
3978 });
3706 } else {3979 } else {
3707 return self.fail("TODO AArch64 cmp for ints > 64 bits", .{});3980 const read_args = [_]ReadArg{
3981 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
3982 .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg },
3983 };
3984 try self.allocRegs(
3985 &read_args,
3986 &.{},
3987 null, // we won't be able to reuse a register as there are no write_regs
3988 );
3989
3990 _ = try self.addInst(.{
3991 .tag = .cmp_shifted_register,
3992 .data = .{ .rr_imm6_shift = .{
3993 .rn = lhs_reg,
3994 .rm = rhs_reg,
3995 .imm6 = 0,
3996 .shift = .lsl,
3997 } },
3998 });
3708 }3999 }
3709 };4000
3710 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });4001 return switch (int_info.signedness) {
4002 .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) },
4003 .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) },
4004 };
4005 } else {
4006 return self.fail("TODO AArch64 cmp for ints > 64 bits", .{});
4007 }
3711}4008}
37124009
3713fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {4010fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
...@@ -3952,15 +4249,13 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue {...@@ -3952,15 +4249,13 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue {
39524249
3953fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {4250fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
3954 const error_type = ty.errorUnionSet();4251 const error_type = ty.errorUnionSet();
3955 const error_int_type = Type.initTag(.u16);
39564252
3957 if (error_type.errorSetIsEmpty()) {4253 if (error_type.errorSetIsEmpty()) {
3958 return MCValue{ .immediate = 0 }; // always false4254 return MCValue{ .immediate = 0 }; // always false
3959 }4255 }
39604256
3961 const error_mcv = try self.errUnionErr(operand, ty);4257 const error_mcv = try self.errUnionErr(operand, ty);
3962 _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null);4258 return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt);
3963 return MCValue{ .condition_flags = .hi };
3964}4259}
39654260
3966fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {4261fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
...@@ -3991,15 +4286,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3991,15 +4286,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
3991 const un_op = self.air.instructions.items(.data)[inst].un_op;4286 const un_op = self.air.instructions.items(.data)[inst].un_op;
3992 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4287 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3993 const operand_ptr = try self.resolveInst(un_op);4288 const operand_ptr = try self.resolveInst(un_op);
3994 const operand: MCValue = blk: {4289 const ptr_ty = self.air.typeOf(un_op);
3995 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4290 const elem_ty = ptr_ty.elemType();
3996 // The MCValue that holds the pointer can be re-used as the value.4291
3997 break :blk operand_ptr;4292 const operand = try self.allocRegOrMem(elem_ty, true, null);
3998 } else {4293 try self.load(operand, operand_ptr, ptr_ty);
3999 break :blk try self.allocRegOrMem(inst, true);4294
4000 }
4001 };
4002 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4003 break :result try self.isNull(operand);4295 break :result try self.isNull(operand);
4004 };4296 };
4005 return self.finishAir(inst, result, .{ un_op, .none, .none });4297 return self.finishAir(inst, result, .{ un_op, .none, .none });
...@@ -4018,15 +4310,12 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4018,15 +4310,12 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4018 const un_op = self.air.instructions.items(.data)[inst].un_op;4310 const un_op = self.air.instructions.items(.data)[inst].un_op;
4019 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4311 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4020 const operand_ptr = try self.resolveInst(un_op);4312 const operand_ptr = try self.resolveInst(un_op);
4021 const operand: MCValue = blk: {4313 const ptr_ty = self.air.typeOf(un_op);
4022 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4314 const elem_ty = ptr_ty.elemType();
4023 // The MCValue that holds the pointer can be re-used as the value.4315
4024 break :blk operand_ptr;4316 const operand = try self.allocRegOrMem(elem_ty, true, null);
4025 } else {4317 try self.load(operand, operand_ptr, ptr_ty);
4026 break :blk try self.allocRegOrMem(inst, true);4318
4027 }
4028 };
4029 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4030 break :result try self.isNonNull(operand);4319 break :result try self.isNonNull(operand);
4031 };4320 };
4032 return self.finishAir(inst, result, .{ un_op, .none, .none });4321 return self.finishAir(inst, result, .{ un_op, .none, .none });
...@@ -4047,16 +4336,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4047,16 +4336,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4047 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4336 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4048 const operand_ptr = try self.resolveInst(un_op);4337 const operand_ptr = try self.resolveInst(un_op);
4049 const ptr_ty = self.air.typeOf(un_op);4338 const ptr_ty = self.air.typeOf(un_op);
4050 const operand: MCValue = blk: {4339 const elem_ty = ptr_ty.elemType();
4051 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4340
4052 // The MCValue that holds the pointer can be re-used as the value.4341 const operand = try self.allocRegOrMem(elem_ty, true, null);
4053 break :blk operand_ptr;4342 try self.load(operand, operand_ptr, ptr_ty);
4054 } else {4343
4055 break :blk try self.allocRegOrMem(inst, true);4344 break :result try self.isErr(elem_ty, operand);
4056 }
4057 };
4058 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4059 break :result try self.isErr(ptr_ty.elemType(), operand);
4060 };4345 };
4061 return self.finishAir(inst, result, .{ un_op, .none, .none });4346 return self.finishAir(inst, result, .{ un_op, .none, .none });
4062}4347}
...@@ -4076,16 +4361,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4076,16 +4361,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4076 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4361 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4077 const operand_ptr = try self.resolveInst(un_op);4362 const operand_ptr = try self.resolveInst(un_op);
4078 const ptr_ty = self.air.typeOf(un_op);4363 const ptr_ty = self.air.typeOf(un_op);
4079 const operand: MCValue = blk: {4364 const elem_ty = ptr_ty.elemType();
4080 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4365
4081 // The MCValue that holds the pointer can be re-used as the value.4366 const operand = try self.allocRegOrMem(elem_ty, true, null);
4082 break :blk operand_ptr;4367 try self.load(operand, operand_ptr, ptr_ty);
4083 } else {4368
4084 break :blk try self.allocRegOrMem(inst, true);4369 break :result try self.isNonErr(elem_ty, operand);
4085 }
4086 };
4087 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4088 break :result try self.isNonErr(ptr_ty.elemType(), operand);
4089 };4370 };
4090 return self.finishAir(inst, result, .{ un_op, .none, .none });4371 return self.finishAir(inst, result, .{ un_op, .none, .none });
4091}4372}
...@@ -4178,7 +4459,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {...@@ -4178,7 +4459,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
4178 .none, .dead, .unreach => unreachable,4459 .none, .dead, .unreach => unreachable,
4179 .register, .stack_offset, .memory => operand_mcv,4460 .register, .stack_offset, .memory => operand_mcv,
4180 .immediate, .stack_argument_offset, .condition_flags => blk: {4461 .immediate, .stack_argument_offset, .condition_flags => blk: {
4181 const new_mcv = try self.allocRegOrMem(block, true);4462 const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block);
4182 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);4463 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
4183 break :blk new_mcv;4464 break :blk new_mcv;
4184 },4465 },
...@@ -4376,7 +4657,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -4376,7 +4657,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
4376 4, 8 => .str_stack,4657 4, 8 => .str_stack,
4377 else => unreachable, // unexpected abi size4658 else => unreachable, // unexpected abi size
4378 };4659 };
4379 const rt = registerAlias(reg, abi_size);4660 const rt = self.registerAlias(reg, ty);
43804661
4381 _ = try self.addInst(.{4662 _ = try self.addInst(.{
4382 .tag = tag,4663 .tag = tag,
...@@ -4399,10 +4680,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -4399,10 +4680,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
4399 const overflow_bit_ty = ty.structFieldType(1);4680 const overflow_bit_ty = ty.structFieldType(1);
4400 const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));4681 const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));
4401 const raw_cond_reg = try self.register_manager.allocReg(null, gp);4682 const raw_cond_reg = try self.register_manager.allocReg(null, gp);
4402 const cond_reg = registerAlias(4683 const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty);
4403 raw_cond_reg,
4404 @intCast(u32, overflow_bit_ty.abiSize(self.target.*)),
4405 );
44064684
4407 _ = try self.addInst(.{4685 _ = try self.addInst(.{
4408 .tag = .cset,4686 .tag = .cset,
...@@ -4515,16 +4793,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4515,16 +4793,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4515 }4793 }
4516 },4794 },
4517 .ptr_stack_offset => |off| {4795 .ptr_stack_offset => |off| {
4518 // TODO: maybe addressing from sp instead of fp
4519 const imm12 = math.cast(u12, off) orelse
4520 return self.fail("TODO larger stack offsets", .{});
4521
4522 _ = try self.addInst(.{4796 _ = try self.addInst(.{
4523 .tag = .sub_immediate,4797 .tag = .ldr_ptr_stack,
4524 .data = .{ .rr_imm12_sh = .{4798 .data = .{ .load_store_stack = .{
4525 .rd = reg,4799 .rt = reg,
4526 .rn = .x29,4800 .offset = @intCast(u32, off),
4527 .imm12 = imm12,
4528 } },4801 } },
4529 });4802 });
4530 },4803 },
...@@ -4599,8 +4872,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4599,8 +4872,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4599 .memory => |addr| {4872 .memory => |addr| {
4600 // The value is in memory at a hard-coded address.4873 // The value is in memory at a hard-coded address.
4601 // If the type is a pointer, it means the pointer address is at this memory location.4874 // If the type is a pointer, it means the pointer address is at this memory location.
4602 try self.genSetReg(ty, reg.to64(), .{ .immediate = addr });4875 try self.genSetReg(ty, reg.toX(), .{ .immediate = addr });
4603 try self.genLdrRegister(reg, reg.to64(), ty);4876 try self.genLdrRegister(reg, reg.toX(), ty);
4604 },4877 },
4605 .stack_offset => |off| {4878 .stack_offset => |off| {
4606 const abi_size = ty.abiSize(self.target.*);4879 const abi_size = ty.abiSize(self.target.*);
...@@ -4679,7 +4952,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I...@@ -4679,7 +4952,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
4679 4, 8 => .str_immediate,4952 4, 8 => .str_immediate,
4680 else => unreachable, // unexpected abi size4953 else => unreachable, // unexpected abi size
4681 };4954 };
4682 const rt = registerAlias(reg, abi_size);4955 const rt = self.registerAlias(reg, ty);
4683 const offset = switch (abi_size) {4956 const offset = switch (abi_size) {
4684 1 => blk: {4957 1 => blk: {
4685 if (math.cast(u12, stack_offset)) |imm| {4958 if (math.cast(u12, stack_offset)) |imm| {
...@@ -4838,7 +5111,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -4838,7 +5111,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
4838 const ptr_bits = self.target.cpu.arch.ptrBitWidth();5111 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4839 const ptr_bytes = @divExact(ptr_bits, 8);5112 const ptr_bytes = @divExact(ptr_bits, 8);
48405113
4841 const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2);5114 const stack_offset = try self.allocMem(ptr_bytes * 2, ptr_bytes * 2, inst);
4842 try self.genSetStack(ptr_ty, stack_offset, ptr);5115 try self.genSetStack(ptr_ty, stack_offset, ptr);
4843 try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len });5116 try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len });
4844 break :result MCValue{ .stack_offset = stack_offset };5117 break :result MCValue{ .stack_offset = stack_offset };
...@@ -5300,7 +5573,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -5300,7 +5573,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
5300 assert(ret_ty.isError());5573 assert(ret_ty.isError());
5301 result.return_value = .{ .immediate = 0 };5574 result.return_value = .{ .immediate = 0 };
5302 } else if (ret_ty_size <= 8) {5575 } else if (ret_ty_size <= 8) {
5303 result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) };5576 result.return_value = .{ .register = self.registerAlias(c_abi_int_return_regs[0], ret_ty) };
5304 } else {5577 } else {
5305 return self.fail("TODO support more return types for ARM backend", .{});5578 return self.fail("TODO support more return types for ARM backend", .{});
5306 }5579 }
...@@ -5322,7 +5595,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -5322,7 +5595,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
53225595
5323 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {5596 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {
5324 if (param_size <= 8) {5597 if (param_size <= 8) {
5325 result.args[i] = .{ .register = registerAlias(c_abi_int_param_regs[ncrn], param_size) };5598 result.args[i] = .{ .register = self.registerAlias(c_abi_int_param_regs[ncrn], ty) };
5326 ncrn += 1;5599 ncrn += 1;
5327 } else {5600 } else {
5328 return self.fail("TODO MCValues with multiple registers", .{});5601 return self.fail("TODO MCValues with multiple registers", .{});
...@@ -5358,7 +5631,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -5358,7 +5631,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
5358 assert(ret_ty.isError());5631 assert(ret_ty.isError());
5359 result.return_value = .{ .immediate = 0 };5632 result.return_value = .{ .immediate = 0 };
5360 } else if (ret_ty_size <= 8) {5633 } else if (ret_ty_size <= 8) {
5361 result.return_value = .{ .register = registerAlias(.x0, ret_ty_size) };5634 result.return_value = .{ .register = self.registerAlias(.x0, ret_ty) };
5362 } else {5635 } else {
5363 // The result is returned by reference, not by5636 // The result is returned by reference, not by
5364 // value. This means that x0 (or w0 when pointer5637 // value. This means that x0 (or w0 when pointer
...@@ -5424,14 +5697,30 @@ fn parseRegName(name: []const u8) ?Register {...@@ -5424,14 +5697,30 @@ fn parseRegName(name: []const u8) ?Register {
5424 return std.meta.stringToEnum(Register, name);5697 return std.meta.stringToEnum(Register, name);
5425}5698}
54265699
5427fn registerAlias(reg: Register, size_bytes: u64) Register {5700fn registerAlias(self: *Self, reg: Register, ty: Type) Register {
5428 if (size_bytes == 0) {5701 const abi_size = ty.abiSize(self.target.*);
5429 unreachable; // should be comptime-known5702
5430 } else if (size_bytes <= 4) {5703 switch (reg.class()) {
5431 return reg.to32();5704 .general_purpose => {
5432 } else if (size_bytes <= 8) {5705 if (abi_size == 0) {
5433 return reg.to64();5706 unreachable; // should be comptime-known
5434 } else {5707 } else if (abi_size <= 4) {
5435 unreachable; // TODO handle floating-point registers5708 return reg.toW();
5709 } else if (abi_size <= 8) {
5710 return reg.toX();
5711 } else unreachable;
5712 },
5713 .stack_pointer => unreachable, // we can't store/load the sp
5714 .floating_point => {
5715 return switch (ty.floatBits(self.target.*)) {
5716 16 => reg.toH(),
5717 32 => reg.toS(),
5718 64 => reg.toD(),
5719 128 => reg.toQ(),
5720
5721 80 => unreachable, // f80 registers don't exist
5722 else => unreachable,
5723 };
5724 },
5436 }5725 }
5437}5726}
src/arch/aarch64/Emit.zig+43-30
...@@ -150,6 +150,7 @@ pub fn emitMir(...@@ -150,6 +150,7 @@ pub fn emitMir(
150 .ldp => try emit.mirLoadStoreRegisterPair(inst),150 .ldp => try emit.mirLoadStoreRegisterPair(inst),
151 .stp => try emit.mirLoadStoreRegisterPair(inst),151 .stp => try emit.mirLoadStoreRegisterPair(inst),
152152
153 .ldr_ptr_stack => try emit.mirLoadStoreStack(inst),
153 .ldr_stack => try emit.mirLoadStoreStack(inst),154 .ldr_stack => try emit.mirLoadStoreStack(inst),
154 .ldrb_stack => try emit.mirLoadStoreStack(inst),155 .ldrb_stack => try emit.mirLoadStoreStack(inst),
155 .ldrh_stack => try emit.mirLoadStoreStack(inst),156 .ldrh_stack => try emit.mirLoadStoreStack(inst),
...@@ -159,8 +160,8 @@ pub fn emitMir(...@@ -159,8 +160,8 @@ pub fn emitMir(
159 .strb_stack => try emit.mirLoadStoreStack(inst),160 .strb_stack => try emit.mirLoadStoreStack(inst),
160 .strh_stack => try emit.mirLoadStoreStack(inst),161 .strh_stack => try emit.mirLoadStoreStack(inst),
161162
162 .ldr_stack_argument => try emit.mirLoadStackArgument(inst),
163 .ldr_ptr_stack_argument => try emit.mirLoadStackArgument(inst),163 .ldr_ptr_stack_argument => try emit.mirLoadStackArgument(inst),
164 .ldr_stack_argument => try emit.mirLoadStackArgument(inst),
164 .ldrb_stack_argument => try emit.mirLoadStackArgument(inst),165 .ldrb_stack_argument => try emit.mirLoadStackArgument(inst),
165 .ldrh_stack_argument => try emit.mirLoadStackArgument(inst),166 .ldrh_stack_argument => try emit.mirLoadStackArgument(inst),
166 .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst),167 .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst),
...@@ -842,14 +843,14 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -842,14 +843,14 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
842 // PC-relative displacement to the entry in memory.843 // PC-relative displacement to the entry in memory.
843 // adrp844 // adrp
844 const offset = @intCast(u32, emit.code.items.len);845 const offset = @intCast(u32, emit.code.items.len);
845 try emit.writeInstruction(Instruction.adrp(reg.to64(), 0));846 try emit.writeInstruction(Instruction.adrp(reg.toX(), 0));
846847
847 switch (tag) {848 switch (tag) {
848 .load_memory_got => {849 .load_memory_got => {
849 // ldr reg, reg, offset850 // ldr reg, reg, offset
850 try emit.writeInstruction(Instruction.ldr(851 try emit.writeInstruction(Instruction.ldr(
851 reg,852 reg,
852 reg.to64(),853 reg.toX(),
853 Instruction.LoadStoreOffset.imm(0),854 Instruction.LoadStoreOffset.imm(0),
854 ));855 ));
855 },856 },
...@@ -863,11 +864,11 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -863,11 +864,11 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
863 // Note that this can potentially be optimised out by the codegen/linker if the864 // Note that this can potentially be optimised out by the codegen/linker if the
864 // target address is appropriately aligned.865 // target address is appropriately aligned.
865 // add reg, reg, offset866 // add reg, reg, offset
866 try emit.writeInstruction(Instruction.add(reg.to64(), reg.to64(), 0, false));867 try emit.writeInstruction(Instruction.add(reg.toX(), reg.toX(), 0, false));
867 // ldr reg, reg, offset868 // ldr reg, reg, offset
868 try emit.writeInstruction(Instruction.ldr(869 try emit.writeInstruction(Instruction.ldr(
869 reg,870 reg,
870 reg.to64(),871 reg.toX(),
871 Instruction.LoadStoreOffset.imm(0),872 Instruction.LoadStoreOffset.imm(0),
872 ));873 ));
873 },874 },
...@@ -1003,23 +1004,43 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -1003,23 +1004,43 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
1003 const rt = load_store_stack.rt;1004 const rt = load_store_stack.rt;
10041005
1005 const raw_offset = emit.stack_size - load_store_stack.offset;1006 const raw_offset = emit.stack_size - load_store_stack.offset;
1006 const offset = switch (tag) {1007 switch (tag) {
1007 .ldrb_stack, .ldrsb_stack, .strb_stack => blk: {1008 .ldr_ptr_stack => {
1008 if (math.cast(u12, raw_offset)) |imm| {1009 const offset = if (math.cast(u12, raw_offset)) |imm| imm else {
1009 break :blk Instruction.LoadStoreOffset.imm(imm);1010 return emit.fail("TODO load stack argument ptr with larger offset", .{});
1010 } else {1011 };
1012
1013 switch (tag) {
1014 .ldr_ptr_stack => try emit.writeInstruction(Instruction.add(rt, .sp, offset, false)),
1015 else => unreachable,
1016 }
1017 },
1018 .ldrb_stack, .ldrsb_stack, .strb_stack => {
1019 const offset = if (math.cast(u12, raw_offset)) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1011 return emit.fail("TODO load/store stack byte with larger offset", .{});1020 return emit.fail("TODO load/store stack byte with larger offset", .{});
1021 };
1022
1023 switch (tag) {
1024 .ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
1025 .ldrsb_stack => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)),
1026 .strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)),
1027 else => unreachable,
1012 }1028 }
1013 },1029 },
1014 .ldrh_stack, .ldrsh_stack, .strh_stack => blk: {1030 .ldrh_stack, .ldrsh_stack, .strh_stack => {
1015 assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry1031 assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry
1016 if (math.cast(u12, @divExact(raw_offset, 2))) |imm| {1032 const offset = if (math.cast(u12, @divExact(raw_offset, 2))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1017 break :blk Instruction.LoadStoreOffset.imm(imm);
1018 } else {
1019 return emit.fail("TODO load/store stack halfword with larger offset", .{});1033 return emit.fail("TODO load/store stack halfword with larger offset", .{});
1034 };
1035
1036 switch (tag) {
1037 .ldrh_stack => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)),
1038 .ldrsh_stack => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)),
1039 .strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)),
1040 else => unreachable,
1020 }1041 }
1021 },1042 },
1022 .ldr_stack, .str_stack => blk: {1043 .ldr_stack, .str_stack => {
1023 const alignment: u32 = switch (rt.size()) {1044 const alignment: u32 = switch (rt.size()) {
1024 32 => 4,1045 32 => 4,
1025 64 => 8,1046 64 => 8,
...@@ -1027,25 +1048,17 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -1027,25 +1048,17 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
1027 };1048 };
10281049
1029 assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry1050 assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry
1030 if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| {1051 const offset = if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
1031 break :blk Instruction.LoadStoreOffset.imm(imm);
1032 } else {
1033 return emit.fail("TODO load/store stack with larger offset", .{});1052 return emit.fail("TODO load/store stack with larger offset", .{});
1053 };
1054
1055 switch (tag) {
1056 .ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
1057 .str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)),
1058 else => unreachable,
1034 }1059 }
1035 },1060 },
1036 else => unreachable,1061 else => unreachable,
1037 };
1038
1039 switch (tag) {
1040 .ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
1041 .ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
1042 .ldrh_stack => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)),
1043 .ldrsb_stack => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)),
1044 .ldrsh_stack => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)),
1045 .str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)),
1046 .strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)),
1047 .strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)),
1048 else => unreachable,
1049 }1062 }
1050}1063}
10511064
src/arch/aarch64/Mir.zig+3-5
...@@ -92,6 +92,8 @@ pub const Inst = struct {...@@ -92,6 +92,8 @@ pub const Inst = struct {
92 load_memory_ptr_direct,92 load_memory_ptr_direct,
93 /// Load Pair of Registers93 /// Load Pair of Registers
94 ldp,94 ldp,
95 /// Pseudo-instruction: Load pointer to stack item
96 ldr_ptr_stack,
95 /// Pseudo-instruction: Load pointer to stack argument97 /// Pseudo-instruction: Load pointer to stack argument
96 ldr_ptr_stack_argument,98 ldr_ptr_stack_argument,
97 /// Pseudo-instruction: Load from stack99 /// Pseudo-instruction: Load from stack
...@@ -432,7 +434,7 @@ pub const Inst = struct {...@@ -432,7 +434,7 @@ pub const Inst = struct {
432 rn: Register,434 rn: Register,
433 offset: bits.Instruction.LoadStoreOffsetRegister,435 offset: bits.Instruction.LoadStoreOffsetRegister,
434 },436 },
435 /// A registers and a stack offset437 /// A register and a stack offset
436 ///438 ///
437 /// Used by e.g. str_stack439 /// Used by e.g. str_stack
438 load_store_stack: struct {440 load_store_stack: struct {
...@@ -464,10 +466,6 @@ pub const Inst = struct {...@@ -464,10 +466,6 @@ pub const Inst = struct {
464 line: u32,466 line: u32,
465 column: u32,467 column: u32,
466 },468 },
467 load_memory: struct {
468 register: u32,
469 addr: u32,
470 },
471 };469 };
472470
473 // Make sure we don't accidentally make instructions bigger than expected.471 // Make sure we don't accidentally make instructions bigger than expected.
src/arch/aarch64/bits.zig+250-139
...@@ -4,17 +4,22 @@ const DW = std.dwarf;...@@ -4,17 +4,22 @@ const DW = std.dwarf;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const testing = std.testing;5const testing = std.testing;
66
7// zig fmt: off7pub const RegisterClass = enum {
8 general_purpose,
9 stack_pointer,
10 floating_point,
11};
812
9/// General purpose registers in the AArch64 instruction set13/// General purpose registers in the AArch64 instruction set
10pub const Register = enum(u7) {14pub const Register = enum(u8) {
11 // 64-bit registers15 // zig fmt: off
16 // 64-bit general-purpose registers
12 x0, x1, x2, x3, x4, x5, x6, x7,17 x0, x1, x2, x3, x4, x5, x6, x7,
13 x8, x9, x10, x11, x12, x13, x14, x15,18 x8, x9, x10, x11, x12, x13, x14, x15,
14 x16, x17, x18, x19, x20, x21, x22, x23,19 x16, x17, x18, x19, x20, x21, x22, x23,
15 x24, x25, x26, x27, x28, x29, x30, xzr,20 x24, x25, x26, x27, x28, x29, x30, xzr,
1621
17 // 32-bit registers22 // 32-bit general-purpose registers
18 w0, w1, w2, w3, w4, w5, w6, w7,23 w0, w1, w2, w3, w4, w5, w6, w7,
19 w8, w9, w10, w11, w12, w13, w14, w15,24 w8, w9, w10, w11, w12, w13, w14, w15,
20 w16, w17, w18, w19, w20, w21, w22, w23,25 w16, w17, w18, w19, w20, w21, w22, w23,
...@@ -23,192 +28,298 @@ pub const Register = enum(u7) {...@@ -23,192 +28,298 @@ pub const Register = enum(u7) {
23 // Stack pointer28 // Stack pointer
24 sp, wsp,29 sp, wsp,
2530
26 pub fn id(self: Register) u6 {31 // 128-bit floating-point registers
27 return switch (@enumToInt(self)) {
28 0...63 => return @as(u6, @truncate(u5, @enumToInt(self))),
29 64...65 => 32,
30 else => unreachable,
31 };
32 }
33
34 pub fn enc(self: Register) u5 {
35 return switch (@enumToInt(self)) {
36 0...63 => return @truncate(u5, @enumToInt(self)),
37 64...65 => 31,
38 else => unreachable,
39 };
40 }
41
42 /// Returns the bit-width of the register.
43 pub fn size(self: Register) u7 {
44 return switch (@enumToInt(self)) {
45 0...31 => 64,
46 32...63 => 32,
47 64 => 64,
48 65 => 32,
49 else => unreachable,
50 };
51 }
52
53 /// Convert from any register to its 64 bit alias.
54 pub fn to64(self: Register) Register {
55 return switch (@enumToInt(self)) {
56 0...31 => self,
57 32...63 => @intToEnum(Register, @enumToInt(self) - 32),
58 64 => .sp,
59 65 => .sp,
60 else => unreachable,
61 };
62 }
63
64 /// Convert from any register to its 32 bit alias.
65 pub fn to32(self: Register) Register {
66 return switch (@enumToInt(self)) {
67 0...31 => @intToEnum(Register, @enumToInt(self) + 32),
68 32...63 => self,
69 64 => .wsp,
70 65 => .wsp,
71 else => unreachable,
72 };
73 }
74
75 pub fn dwarfLocOp(self: Register) u8 {
76 return @as(u8, self.enc()) + DW.OP.reg0;
77 }
78};
79
80// zig fmt: on
81
82test "Register.enc" {
83 try testing.expectEqual(@as(u5, 0), Register.x0.enc());
84 try testing.expectEqual(@as(u5, 0), Register.w0.enc());
85
86 try testing.expectEqual(@as(u5, 31), Register.xzr.enc());
87 try testing.expectEqual(@as(u5, 31), Register.wzr.enc());
88
89 try testing.expectEqual(@as(u5, 31), Register.sp.enc());
90 try testing.expectEqual(@as(u5, 31), Register.sp.enc());
91}
92
93test "Register.size" {
94 try testing.expectEqual(@as(u7, 64), Register.x19.size());
95 try testing.expectEqual(@as(u7, 32), Register.w3.size());
96}
97
98test "Register.to64/to32" {
99 try testing.expectEqual(Register.x0, Register.w0.to64());
100 try testing.expectEqual(Register.x0, Register.x0.to64());
101
102 try testing.expectEqual(Register.w3, Register.w3.to32());
103 try testing.expectEqual(Register.w3, Register.x3.to32());
104}
105
106// zig fmt: off
107
108/// Scalar floating point registers in the aarch64 instruction set
109pub const FloatingPointRegister = enum(u8) {
110 // 128-bit registers
111 q0, q1, q2, q3, q4, q5, q6, q7,32 q0, q1, q2, q3, q4, q5, q6, q7,
112 q8, q9, q10, q11, q12, q13, q14, q15,33 q8, q9, q10, q11, q12, q13, q14, q15,
113 q16, q17, q18, q19, q20, q21, q22, q23,34 q16, q17, q18, q19, q20, q21, q22, q23,
114 q24, q25, q26, q27, q28, q29, q30, q31,35 q24, q25, q26, q27, q28, q29, q30, q31,
11536
116 // 64-bit registers37 // 64-bit floating-point registers
117 d0, d1, d2, d3, d4, d5, d6, d7,38 d0, d1, d2, d3, d4, d5, d6, d7,
118 d8, d9, d10, d11, d12, d13, d14, d15,39 d8, d9, d10, d11, d12, d13, d14, d15,
119 d16, d17, d18, d19, d20, d21, d22, d23,40 d16, d17, d18, d19, d20, d21, d22, d23,
120 d24, d25, d26, d27, d28, d29, d30, d31,41 d24, d25, d26, d27, d28, d29, d30, d31,
12142
122 // 32-bit registers43 // 32-bit floating-point registers
123 s0, s1, s2, s3, s4, s5, s6, s7,44 s0, s1, s2, s3, s4, s5, s6, s7,
124 s8, s9, s10, s11, s12, s13, s14, s15,45 s8, s9, s10, s11, s12, s13, s14, s15,
125 s16, s17, s18, s19, s20, s21, s22, s23,46 s16, s17, s18, s19, s20, s21, s22, s23,
126 s24, s25, s26, s27, s28, s29, s30, s31,47 s24, s25, s26, s27, s28, s29, s30, s31,
12748
128 // 16-bit registers49 // 16-bit floating-point registers
129 h0, h1, h2, h3, h4, h5, h6, h7,50 h0, h1, h2, h3, h4, h5, h6, h7,
130 h8, h9, h10, h11, h12, h13, h14, h15,51 h8, h9, h10, h11, h12, h13, h14, h15,
131 h16, h17, h18, h19, h20, h21, h22, h23,52 h16, h17, h18, h19, h20, h21, h22, h23,
132 h24, h25, h26, h27, h28, h29, h30, h31,53 h24, h25, h26, h27, h28, h29, h30, h31,
13354
134 // 8-bit registers55 // 8-bit floating-point registers
135 b0, b1, b2, b3, b4, b5, b6, b7,56 b0, b1, b2, b3, b4, b5, b6, b7,
136 b8, b9, b10, b11, b12, b13, b14, b15,57 b8, b9, b10, b11, b12, b13, b14, b15,
137 b16, b17, b18, b19, b20, b21, b22, b23,58 b16, b17, b18, b19, b20, b21, b22, b23,
138 b24, b25, b26, b27, b28, b29, b30, b31,59 b24, b25, b26, b27, b28, b29, b30, b31,
60 // zig fmt: on
13961
140 pub fn id(self: FloatingPointRegister) u5 {62 pub fn class(self: Register) RegisterClass {
141 return @truncate(u5, @enumToInt(self));63 return switch (@enumToInt(self)) {
64 @enumToInt(Register.x0)...@enumToInt(Register.xzr) => .general_purpose,
65 @enumToInt(Register.w0)...@enumToInt(Register.wzr) => .general_purpose,
66
67 @enumToInt(Register.sp) => .stack_pointer,
68 @enumToInt(Register.wsp) => .stack_pointer,
69
70 @enumToInt(Register.q0)...@enumToInt(Register.q31) => .floating_point,
71 @enumToInt(Register.d0)...@enumToInt(Register.d31) => .floating_point,
72 @enumToInt(Register.s0)...@enumToInt(Register.s31) => .floating_point,
73 @enumToInt(Register.h0)...@enumToInt(Register.h31) => .floating_point,
74 @enumToInt(Register.b0)...@enumToInt(Register.b31) => .floating_point,
75 else => unreachable,
76 };
77 }
78
79 pub fn id(self: Register) u6 {
80 return switch (@enumToInt(self)) {
81 @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.x0)),
82 @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.w0)),
83
84 @enumToInt(Register.sp) => 32,
85 @enumToInt(Register.wsp) => 32,
86
87 @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.q0) + 33),
88 @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.d0) + 33),
89 @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.s0) + 33),
90 @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.h0) + 33),
91 @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.b0) + 33),
92 else => unreachable,
93 };
94 }
95
96 pub fn enc(self: Register) u5 {
97 return switch (@enumToInt(self)) {
98 @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.x0)),
99 @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.w0)),
100
101 @enumToInt(Register.sp) => 31,
102 @enumToInt(Register.wsp) => 31,
103
104 @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.q0)),
105 @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.d0)),
106 @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.s0)),
107 @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.h0)),
108 @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.b0)),
109 else => unreachable,
110 };
142 }111 }
143112
144 /// Returns the bit-width of the register.113 /// Returns the bit-width of the register.
145 pub fn size(self: FloatingPointRegister) u8 {114 pub fn size(self: Register) u8 {
115 return switch (@enumToInt(self)) {
116 @enumToInt(Register.x0)...@enumToInt(Register.xzr) => 64,
117 @enumToInt(Register.w0)...@enumToInt(Register.wzr) => 32,
118
119 @enumToInt(Register.sp) => 64,
120 @enumToInt(Register.wsp) => 32,
121
122 @enumToInt(Register.q0)...@enumToInt(Register.q31) => 128,
123 @enumToInt(Register.d0)...@enumToInt(Register.d31) => 64,
124 @enumToInt(Register.s0)...@enumToInt(Register.s31) => 32,
125 @enumToInt(Register.h0)...@enumToInt(Register.h31) => 16,
126 @enumToInt(Register.b0)...@enumToInt(Register.b31) => 8,
127 else => unreachable,
128 };
129 }
130
131 /// Convert from a general-purpose register to its 64 bit alias.
132 pub fn toX(self: Register) Register {
133 return switch (@enumToInt(self)) {
134 @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intToEnum(
135 Register,
136 @enumToInt(self) - @enumToInt(Register.x0) + @enumToInt(Register.x0),
137 ),
138 @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intToEnum(
139 Register,
140 @enumToInt(self) - @enumToInt(Register.w0) + @enumToInt(Register.x0),
141 ),
142 else => unreachable,
143 };
144 }
145
146 /// Convert from a general-purpose register to its 32 bit alias.
147 pub fn toW(self: Register) Register {
146 return switch (@enumToInt(self)) {148 return switch (@enumToInt(self)) {
147 0...31 => 128,149 @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intToEnum(
148 32...63 => 64,150 Register,
149 64...95 => 32,151 @enumToInt(self) - @enumToInt(Register.x0) + @enumToInt(Register.w0),
150 96...127 => 16,152 ),
151 128...159 => 8,153 @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intToEnum(
154 Register,
155 @enumToInt(self) - @enumToInt(Register.w0) + @enumToInt(Register.w0),
156 ),
152 else => unreachable,157 else => unreachable,
153 };158 };
154 }159 }
155160
156 /// Convert from any register to its 128 bit alias.161 /// Convert from a floating-point register to its 128 bit alias.
157 pub fn to128(self: FloatingPointRegister) FloatingPointRegister {162 pub fn toQ(self: Register) Register {
158 return @intToEnum(FloatingPointRegister, self.id());163 return switch (@enumToInt(self)) {
164 @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum(
165 Register,
166 @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.q0),
167 ),
168 @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum(
169 Register,
170 @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.q0),
171 ),
172 @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum(
173 Register,
174 @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.q0),
175 ),
176 @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum(
177 Register,
178 @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.q0),
179 ),
180 @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum(
181 Register,
182 @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.q0),
183 ),
184 else => unreachable,
185 };
186 }
187
188 /// Convert from a floating-point register to its 64 bit alias.
189 pub fn toD(self: Register) Register {
190 return switch (@enumToInt(self)) {
191 @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum(
192 Register,
193 @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.d0),
194 ),
195 @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum(
196 Register,
197 @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.d0),
198 ),
199 @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum(
200 Register,
201 @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.d0),
202 ),
203 @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum(
204 Register,
205 @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.d0),
206 ),
207 @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum(
208 Register,
209 @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.d0),
210 ),
211 else => unreachable,
212 };
159 }213 }
160214
161 /// Convert from any register to its 64 bit alias.215 /// Convert from a floating-point register to its 32 bit alias.
162 pub fn to64(self: FloatingPointRegister) FloatingPointRegister {216 pub fn toS(self: Register) Register {
163 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32);217 return switch (@enumToInt(self)) {
218 @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum(
219 Register,
220 @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.s0),
221 ),
222 @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum(
223 Register,
224 @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.s0),
225 ),
226 @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum(
227 Register,
228 @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.s0),
229 ),
230 @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum(
231 Register,
232 @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.s0),
233 ),
234 @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum(
235 Register,
236 @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.s0),
237 ),
238 else => unreachable,
239 };
164 }240 }
165241
166 /// Convert from any register to its 32 bit alias.242 /// Convert from a floating-point register to its 16 bit alias.
167 pub fn to32(self: FloatingPointRegister) FloatingPointRegister {243 pub fn toH(self: Register) Register {
168 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64);244 return switch (@enumToInt(self)) {
245 @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum(
246 Register,
247 @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.h0),
248 ),
249 @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum(
250 Register,
251 @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.h0),
252 ),
253 @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum(
254 Register,
255 @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.h0),
256 ),
257 @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum(
258 Register,
259 @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.h0),
260 ),
261 @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum(
262 Register,
263 @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.h0),
264 ),
265 else => unreachable,
266 };
169 }267 }
170268
171 /// Convert from any register to its 16 bit alias.269 /// Convert from a floating-point register to its 8 bit alias.
172 pub fn to16(self: FloatingPointRegister) FloatingPointRegister {270 pub fn toB(self: Register) Register {
173 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96);271 return switch (@enumToInt(self)) {
272 @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum(
273 Register,
274 @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.b0),
275 ),
276 @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum(
277 Register,
278 @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.b0),
279 ),
280 @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum(
281 Register,
282 @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.b0),
283 ),
284 @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum(
285 Register,
286 @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.b0),
287 ),
288 @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum(
289 Register,
290 @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.b0),
291 ),
292 else => unreachable,
293 };
174 }294 }
175295
176 /// Convert from any register to its 8 bit alias.296 pub fn dwarfLocOp(self: Register) u8 {
177 pub fn to8(self: FloatingPointRegister) FloatingPointRegister {297 return @as(u8, self.enc()) + DW.OP.reg0;
178 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128);
179 }298 }
180};299};
181300
182// zig fmt: on301test "Register.enc" {
302 try testing.expectEqual(@as(u5, 0), Register.x0.enc());
303 try testing.expectEqual(@as(u5, 0), Register.w0.enc());
183304
184test "FloatingPointRegister.id" {305 try testing.expectEqual(@as(u5, 31), Register.xzr.enc());
185 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id());306 try testing.expectEqual(@as(u5, 31), Register.wzr.enc());
186 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id());
187 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id());
188 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id());
189 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id());
190307
191 try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id());308 try testing.expectEqual(@as(u5, 31), Register.sp.enc());
192 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id());309 try testing.expectEqual(@as(u5, 31), Register.sp.enc());
193}310}
194311
195test "FloatingPointRegister.size" {312test "Register.size" {
196 try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size());313 try testing.expectEqual(@as(u8, 64), Register.x19.size());
197 try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size());314 try testing.expectEqual(@as(u8, 32), Register.w3.size());
198 try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size());
199 try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size());
200 try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size());
201}315}
202316
203test "FloatingPointRegister.toX" {317test "Register.toX/toW" {
204 try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128());318 try testing.expectEqual(Register.x0, Register.w0.toX());
205 try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128());319 try testing.expectEqual(Register.x0, Register.x0.toX());
206 try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128());
207320
208 try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64());321 try testing.expectEqual(Register.w3, Register.w3.toW());
209 try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32());322 try testing.expectEqual(Register.w3, Register.x3.toW());
210 try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16());
211 try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8());
212}323}
213324
214/// Represents an instruction in the AArch64 instruction set325/// Represents an instruction in the AArch64 instruction set