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) {
157157 condition_flags: Condition,
158158 /// The value is a function argument passed via the stack.
159159 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 }
194160};
195161
196162const Branch = struct {
......@@ -414,11 +380,9 @@ fn gen(self: *Self) !void {
414380 // to the stack.
415381 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
416382 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;
420 self.next_stack_offset = stack_offset;
421 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
385 const stack_offset = try self.allocMem(ptr_bytes, ptr_bytes, null);
422386
423387 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg });
424388 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
......@@ -879,17 +843,30 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
879843 }
880844}
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
883855 if (abi_align > self.stack_align)
884856 self.stack_align = abi_align;
857
885858 // TODO find a free slot instead of always appending
886859 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
887860 self.next_stack_offset = offset;
888861 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset);
889 try self.stack.putNoClobber(self.gpa, offset, .{
890 .inst = inst,
891 .size = abi_size,
892 });
862
863 if (maybe_inst) |inst| {
864 try self.stack.putNoClobber(self.gpa, offset, .{
865 .inst = inst,
866 .size = abi_size,
867 });
868 }
869
893870 return offset;
894871}
895872
......@@ -910,40 +887,41 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
910887 };
911888 // TODO swap this for inst.ty.ptrAlign
912889 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);
914892}
915893
916fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
917 const elem_ty = self.air.typeOfIndex(inst);
894fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue {
918895 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {
919896 const mod = self.bin_file.options.module.?;
920897 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
921898 };
922899 const abi_align = elem_ty.abiAlignment(self.target.*);
923 if (abi_align > self.stack_align)
924 self.stack_align = abi_align;
925900
926901 if (reg_ok) {
927902 // Make sure the type can fit in a register before we try to allocate one.
928903 if (abi_size <= 8) {
929 if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
930 return MCValue{ .register = registerAlias(reg, abi_size) };
904 if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| {
905 return MCValue{ .register = self.registerAlias(reg, elem_ty) };
931906 }
932907 }
933908 }
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);
935911 return MCValue{ .stack_offset = stack_offset };
936912}
937913
938914pub 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);
940916 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
917
941918 const reg_mcv = self.getResolvedInstValue(inst);
942919 switch (reg_mcv) {
943920 .register => |r| assert(reg.id() == r.id()),
944921 .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()),
945922 else => unreachable, // not a register
946923 }
924
947925 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
948926 try branch.inst_table.put(self.gpa, inst, stack_mcv);
949927 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
953931/// occupied
954932fn spillCompareFlagsIfOccupied(self: *Self) !void {
955933 if (self.condition_flags_inst) |inst_to_save| {
934 const ty = self.air.typeOfIndex(inst_to_save);
956935 const mcv = self.getResolvedInstValue(inst_to_save);
957936 const new_mcv = switch (mcv) {
958 .condition_flags => try self.allocRegOrMem(inst_to_save, true),
959 .register_with_overflow => try self.allocRegOrMem(inst_to_save, false),
937 .condition_flags => try self.allocRegOrMem(ty, true, inst_to_save),
938 .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save),
960939 else => unreachable, // mcv doesn't occupy the compare flags
961940 };
962941
......@@ -982,7 +961,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
982961/// This can have a side effect of spilling instructions to the stack to free up a register.
983962fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
984963 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);
986965 try self.genSetReg(ty, reg, mcv);
987966 return reg;
988967}
......@@ -993,7 +972,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
993972fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
994973 const raw_reg = try self.register_manager.allocReg(reg_owner, gp);
995974 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);
997976 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
998977 return MCValue{ .register = reg };
999978}
......@@ -1031,7 +1010,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
10311010 const operand_info = operand_ty.intInfo(self.target.*);
10321011
10331012 const dest_ty = self.air.typeOfIndex(inst);
1034 const dest_abi_size = dest_ty.abiSize(self.target.*);
10351013 const dest_info = dest_ty.intInfo(self.target.*);
10361014
10371015 const result: MCValue = result: {
......@@ -1042,19 +1020,19 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
10421020 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
10431021
10441022 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) },
10461024 else => operand_mcv,
10471025 };
10481026
10491027 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);
10511029 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);
10521030 break :result dest_mcv;
10531031 } else {
10541032 if (self.reuseOperand(inst, operand, 0, truncated)) {
10551033 break :result truncated;
10561034 } else {
1057 const dest_mcv = try self.allocRegOrMem(inst, true);
1035 const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst);
10581036 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);
10591037 break :result dest_mcv;
10601038 }
......@@ -1117,7 +1095,7 @@ fn trunc(
11171095 else => operand_reg: {
11181096 if (info_a.bits <= 64) {
11191097 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);
11211099 } else {
11221100 return self.fail("TODO load least significant word into register", .{});
11231101 }
......@@ -1130,14 +1108,14 @@ fn trunc(
11301108 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
11311109
11321110 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);
11341112 } else {
11351113 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);
11371115 }
11381116 } else blk: {
11391117 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);
11411119 };
11421120
11431121 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 {
11941172 }
11951173
11961174 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);
11981176 };
11991177
12001178 _ = try self.addInst(.{
......@@ -1227,7 +1205,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
12271205 }
12281206
12291207 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);
12311209 };
12321210
12331211 _ = try self.addInst(.{
......@@ -1279,7 +1257,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
12791257 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
12801258 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);
12831261 try self.genSetStack(ptr_ty, stack_offset, ptr);
12841262 try self.genSetStack(len_ty, stack_offset - ptr_bytes, len);
12851263 break :result MCValue{ .stack_offset = stack_offset };
......@@ -1287,101 +1265,266 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
12871265 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12881266}
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
12911341///
1292/// Calling this function signals an intention to generate a Mir
1293/// instruction of the form
1342/// If the Mir instruction these registers are allocated for
1343/// 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).
12941347///
1295/// op dest, lhs, rhs
1348/// 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.
12961352///
1297/// Asserts that generating an instruction of that form is possible.
1298fn binOpRegister(
1353/// Furthermore, allocReg guarantees that all arguments which are
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(
12991358 self: *Self,
1300 mir_tag: Mir.Inst.Tag,
1301 lhs: MCValue,
1302 rhs: MCValue,
1303 lhs_ty: Type,
1304 rhs_ty: Type,
1305 metadata: ?BinOpMetadata,
1306) !MCValue {
1307 const lhs_is_register = lhs == .register;
1308 const rhs_is_register = rhs == .register;
1359 read_args: []const ReadArg,
1360 write_args: []const WriteArg,
1361 reuse_metadata: ?ReuseMetadata,
1362) InnerError!void {
1363 // Air instructions have exactly one output
1364 assert(!(reuse_metadata != null and write_args.len != 1)); // see note above
1365
1366 // The operand mapping is a 1:1 mapping of read args to their
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.*)));
1311 if (rhs_is_register) assert(rhs.register == registerAlias(rhs.register, rhs_ty.abiSize(self.target.*)));
1380 // When we reuse a read_arg as a destination, the corresponding
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)
1314 self.register_manager.lockReg(lhs.register)
1315 else
1316 null;
1317 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
1385 // Lock all args which are already allocated to registers
1386 for (read_args) |arg, i| {
1387 const mcv = try arg.bind.resolveToMcv(self);
1388 if (mcv == .register) {
1389 read_locks[i] = self.register_manager.lockReg(mcv.register);
1390 }
1391 }
13181392
1319 const rhs_lock: ?RegisterLock = if (rhs_is_register)
1320 self.register_manager.lockReg(rhs.register)
1321 else
1322 null;
1323 defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);
1393 for (write_args) |arg, i| {
1394 if (arg.bind == .reg) {
1395 write_locks[i] = self.register_manager.lockReg(arg.bind.reg);
1396 }
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: {
1328 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1329 break :inst Air.refToIndex(md.lhs).?;
1330 } else null;
1417 if (reuse_metadata != null) {
1418 const inst = reuse_metadata.?.corresponding_inst;
1419 const operand_mapping = reuse_metadata.?.operand_mapping;
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);
1333 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1459 // For all read_args which need to be moved from non-register to
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;
1338 };
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;
1473 // Overwrite the MCValue associated with this inst
1474 branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* });
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 in
1356 // the case of shifts. See comment above.
1357 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
1491 try self.genSetReg(arg.ty, arg.reg.*, mcv);
1492 }
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 },
13621516 };
1363 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
1364 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(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 },
1517 const write_args = [_]WriteArg{
1518 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
13811519 };
1382
1383 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1384 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
1520 try self.allocRegs(
1521 &read_args,
1522 &write_args,
1523 if (maybe_inst) |inst| .{
1524 .corresponding_inst = inst,
1525 .operand_mapping = &.{ 0, 1 },
1526 } else null,
1527 );
13851528
13861529 const mir_data: Mir.Inst.Data = switch (mir_tag) {
13871530 .add_shifted_register,
......@@ -1395,12 +1538,6 @@ fn binOpRegister(
13951538 .imm6 = 0,
13961539 .shift = .lsl,
13971540 } },
1398 .cmp_shifted_register => .{ .rr_imm6_shift = .{
1399 .rn = lhs_reg,
1400 .rm = rhs_reg,
1401 .imm6 = 0,
1402 .shift = .lsl,
1403 } },
14041541 .mul,
14051542 .lsl_register,
14061543 .asr_register,
......@@ -1415,7 +1552,7 @@ fn binOpRegister(
14151552 .smull,
14161553 .umull,
14171554 => .{ .rrr = .{
1418 .rd = dest_reg.to64(),
1555 .rd = dest_reg.toX(),
14191556 .rn = lhs_reg,
14201557 .rm = rhs_reg,
14211558 } },
......@@ -1440,77 +1577,38 @@ fn binOpRegister(
14401577 return MCValue{ .register = dest_reg };
14411578}
14421579
1443/// Don't call this function directly. Use binOp instead.
1444///
1445/// Calling this function signals an intention to generate a Mir
1446/// instruction of the form
1580/// Wrapper around allocRegs and addInst tailored for specific Mir
1581/// instructions which are binary operations acting on a register and
1582/// an immediate
14471583///
1448/// op dest, lhs, #rhs_imm
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.
1584/// Returns the destination register
14551585fn binOpImmediate(
14561586 self: *Self,
14571587 mir_tag: Mir.Inst.Tag,
1458 lhs: MCValue,
1459 rhs: MCValue,
1588 lhs_bind: ReadArg.Bind,
1589 rhs_immediate: u64,
14601590 lhs_ty: Type,
14611591 lhs_and_rhs_swapped: bool,
1462 metadata: ?BinOpMetadata,
1592 maybe_inst: ?Air.Inst.Index,
14631593) !MCValue {
1464 const lhs_is_register = lhs == .register;
1465
1466 if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*)));
1594 var lhs_reg: Register = undefined;
1595 var dest_reg: Register = undefined;
14671596
1468 const lhs_lock: ?RegisterLock = if (lhs_is_register)
1469 self.register_manager.lockReg(lhs.register)
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;
1597 const read_args = [_]ReadArg{
1598 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
14891599 };
1490 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
1491 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(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 },
1600 const write_args = [_]WriteArg{
1601 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
15111602 };
1512
1513 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1603 const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0};
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
15151613 const mir_data: Mir.Inst.Data = switch (mir_tag) {
15161614 .add_immediate,
......@@ -1520,7 +1618,7 @@ fn binOpImmediate(
15201618 => .{ .rr_imm12_sh = .{
15211619 .rd = dest_reg,
15221620 .rn = lhs_reg,
1523 .imm12 = @intCast(u12, rhs.immediate),
1621 .imm12 = @intCast(u12, rhs_immediate),
15241622 } },
15251623 .lsl_immediate,
15261624 .asr_immediate,
......@@ -1528,11 +1626,7 @@ fn binOpImmediate(
15281626 => .{ .rr_shift = .{
15291627 .rd = dest_reg,
15301628 .rn = lhs_reg,
1531 .shift = @intCast(u6, rhs.immediate),
1532 } },
1533 .cmp_immediate => .{ .r_imm12_sh = .{
1534 .rn = lhs_reg,
1535 .imm12 = @intCast(u12, rhs.immediate),
1629 .shift = @intCast(u6, rhs_immediate),
15361630 } },
15371631 else => unreachable,
15381632 };
......@@ -1545,428 +1639,527 @@ fn binOpImmediate(
15451639 return MCValue{ .register = dest_reg };
15461640}
15471641
1548const BinOpMetadata = struct {
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(
1642fn addSub(
15671643 self: *Self,
15681644 tag: Air.Inst.Tag,
1569 lhs: MCValue,
1570 rhs: MCValue,
1645 lhs_bind: ReadArg.Bind,
1646 rhs_bind: ReadArg.Bind,
15711647 lhs_ty: Type,
15721648 rhs_ty: Type,
1573 metadata: ?BinOpMetadata,
1649 maybe_inst: ?Air.Inst.Index,
15741650) InnerError!MCValue {
15751651 const mod = self.bin_file.options.module.?;
1576 switch (tag) {
1577 .add,
1578 .sub,
1579 .cmp_eq,
1580 => {
1581 switch (lhs_ty.zigTypeTag()) {
1582 .Float => return self.fail("TODO binary operations on floats", .{}),
1583 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1584 .Int => {
1585 assert(lhs_ty.eql(rhs_ty, mod));
1586 const int_info = lhs_ty.intInfo(self.target.*);
1587 if (int_info.bits <= 64) {
1588 // Only say yes if the operation is
1589 // commutative, i.e. we can swap both of the
1590 // operands
1591 const lhs_immediate_ok = switch (tag) {
1592 .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
1593 .sub, .cmp_eq => false,
1594 else => unreachable,
1595 };
1596 const rhs_immediate_ok = switch (tag) {
1597 .add,
1598 .sub,
1599 .cmp_eq,
1600 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
1601 else => unreachable,
1602 };
1652 switch (lhs_ty.zigTypeTag()) {
1653 .Float => return self.fail("TODO binary operations on floats", .{}),
1654 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1655 .Int => {
1656 assert(lhs_ty.eql(rhs_ty, mod));
1657 const int_info = lhs_ty.intInfo(self.target.*);
1658 if (int_info.bits <= 64) {
1659 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
1660 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
1661
1662 // Only say yes if the operation is
1663 // commutative, i.e. we can swap both of the
1664 // operands
1665 const lhs_immediate_ok = switch (tag) {
1666 .add => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
1667 .sub => false,
1668 else => unreachable,
1669 };
1670 const rhs_immediate_ok = switch (tag) {
1671 .add,
1672 .sub,
1673 => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false,
1674 else => unreachable,
1675 };
16031676
1604 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1605 .add => .add_shifted_register,
1606 .sub => .sub_shifted_register,
1607 .cmp_eq => .cmp_shifted_register,
1608 else => unreachable,
1609 };
1610 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
1611 .add => .add_immediate,
1612 .sub => .sub_immediate,
1613 .cmp_eq => .cmp_immediate,
1614 else => unreachable,
1615 };
1677 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1678 .add => .add_shifted_register,
1679 .sub => .sub_shifted_register,
1680 else => unreachable,
1681 };
1682 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
1683 .add => .add_immediate,
1684 .sub => .sub_immediate,
1685 else => unreachable,
1686 };
16161687
1617 if (rhs_immediate_ok) {
1618 return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata);
1619 } else if (lhs_immediate_ok) {
1620 // swap lhs and rhs
1621 return try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, metadata);
1622 } else {
1623 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);
1624 }
1625 } else {
1626 return self.fail("TODO binary operations on int with bits > 64", .{});
1627 }
1628 },
1629 else => unreachable,
1688 if (rhs_immediate_ok) {
1689 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
1690 } else if (lhs_immediate_ok) {
1691 // swap lhs and rhs
1692 return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
1693 } else {
1694 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1695 }
1696 } else {
1697 return self.fail("TODO binary operations on int with bits > 64", .{});
16301698 }
16311699 },
1632 .mul => {
1633 switch (lhs_ty.zigTypeTag()) {
1634 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1635 .Int => {
1636 assert(lhs_ty.eql(rhs_ty, mod));
1637 const int_info = lhs_ty.intInfo(self.target.*);
1638 if (int_info.bits <= 64) {
1639 // TODO add optimisations for multiplication
1640 // with immediates, for example a * 2 can be
1641 // lowered to a << 1
1642 return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata);
1643 } else {
1644 return self.fail("TODO binary operations on int with bits > 64", .{});
1645 }
1646 },
1647 else => unreachable,
1700 else => unreachable,
1701 }
1702}
1703
1704fn mul(
1705 self: *Self,
1706 lhs_bind: ReadArg.Bind,
1707 rhs_bind: ReadArg.Bind,
1708 lhs_ty: Type,
1709 rhs_ty: Type,
1710 maybe_inst: ?Air.Inst.Index,
1711) InnerError!MCValue {
1712 const mod = self.bin_file.options.module.?;
1713 switch (lhs_ty.zigTypeTag()) {
1714 .Vector => return self.fail("TODO binary operations on vectors", .{}),
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", .{});
16481725 }
16491726 },
1650 .div_float => {
1651 switch (lhs_ty.zigTypeTag()) {
1652 .Float => return self.fail("TODO div_float", .{}),
1653 .Vector => return self.fail("TODO div_float on vectors", .{}),
1654 else => unreachable,
1727 else => unreachable,
1728 }
1729}
1730
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", .{});
16551779 }
16561780 },
1657 .div_trunc, .div_floor, .div_exact => {
1658 switch (lhs_ty.zigTypeTag()) {
1659 .Float => return self.fail("TODO div on floats", .{}),
1660 .Vector => return self.fail("TODO div on vectors", .{}),
1661 .Int => {
1662 assert(lhs_ty.eql(rhs_ty, mod));
1663 const int_info = lhs_ty.intInfo(self.target.*);
1664 if (int_info.bits <= 64) {
1665 switch (int_info.signedness) {
1666 .signed => {
1667 switch (tag) {
1668 .div_trunc, .div_exact => {
1669 // TODO optimize integer division by constants
1670 return try self.binOpRegister(.sdiv, lhs, rhs, lhs_ty, rhs_ty, metadata);
1671 },
1672 .div_floor => return self.fail("TODO div_floor on signed integers", .{}),
1673 else => unreachable,
1674 }
1675 },
1676 .unsigned => {
1677 // TODO optimize integer division by constants
1678 return try self.binOpRegister(.udiv, lhs, rhs, lhs_ty, rhs_ty, metadata);
1679 },
1680 }
1681 } else {
1682 return self.fail("TODO integer division for ints with bits > 64", .{});
1683 }
1684 },
1685 else => unreachable,
1781 else => unreachable,
1782 }
1783}
1784
1785fn divFloor(
1786 self: *Self,
1787 lhs_bind: ReadArg.Bind,
1788 rhs_bind: ReadArg.Bind,
1789 lhs_ty: Type,
1790 rhs_ty: Type,
1791 maybe_inst: ?Air.Inst.Index,
1792) InnerError!MCValue {
1793 const mod = self.bin_file.options.module.?;
1794 switch (lhs_ty.zigTypeTag()) {
1795 .Float => return self.fail("TODO div on floats", .{}),
1796 .Vector => return self.fail("TODO div on vectors", .{}),
1797 .Int => {
1798 assert(lhs_ty.eql(rhs_ty, mod));
1799 const int_info = lhs_ty.intInfo(self.target.*);
1800 if (int_info.bits <= 64) {
1801 switch (int_info.signedness) {
1802 .signed => {
1803 return self.fail("TODO div_floor on signed integers", .{});
1804 },
1805 .unsigned => {
1806 // TODO optimize integer division by constants
1807 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
1808 },
1809 }
1810 } else {
1811 return self.fail("TODO integer division for ints with bits > 64", .{});
16861812 }
16871813 },
1688 .rem, .mod => {
1689 switch (lhs_ty.zigTypeTag()) {
1690 .Float => return self.fail("TODO rem/mod on floats", .{}),
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 });
1814 else => unreachable,
1815 }
1816}
17251817
1726 break :blk reg;
1727 };
1728 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
1729 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
1818fn divExact(
1819 self: *Self,
1820 lhs_bind: ReadArg.Bind,
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: {
1732 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1733 break :inst Air.refToIndex(md.rhs).?;
1734 } else null;
1852fn rem(
1853 self: *Self,
1854 lhs_bind: ReadArg.Bind,
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);
1737 const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));
1862 const mod = self.bin_file.options.module.?;
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;
1742 };
1743 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
1744 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
1745
1746 const dest_regs: [2]Register = blk: {
1747 const raw_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
1748 const abi_size = lhs_ty.abiSize(self.target.*);
1749 break :blk .{
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];
1901 _ = try self.addInst(.{
1902 .tag = .msub,
1903 .data = .{ .rrrr = .{
1904 .rd = remainder_reg,
1905 .rn = quotient_reg,
1906 .rm = rhs_reg,
1907 .ra = lhs_reg,
1908 } },
1909 });
17601910
1761 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1762 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
1911 return MCValue{ .register = remainder_reg };
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(.{
1765 .tag = switch (int_info.signedness) {
1766 .signed => .sdiv,
1767 .unsigned => .udiv,
1768 },
1769 .data = .{ .rrr = .{
1770 .rd = quotient_reg,
1771 .rn = lhs_reg,
1772 .rm = rhs_reg,
1773 } },
1774 });
1920fn modulo(
1921 self: *Self,
1922 lhs_bind: ReadArg.Bind,
1923 rhs_bind: ReadArg.Bind,
1924 lhs_ty: Type,
1925 rhs_ty: Type,
1926 maybe_inst: ?Air.Inst.Index,
1927) InnerError!MCValue {
1928 _ = lhs_bind;
1929 _ = rhs_bind;
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(.{
1777 .tag = .msub,
1778 .data = .{ .rrrr = .{
1779 .rd = remainder_reg,
1780 .rn = quotient_reg,
1781 .rm = rhs_reg,
1782 .ra = lhs_reg,
1783 } },
1784 });
1941fn wrappingArithmetic(
1942 self: *Self,
1943 tag: Air.Inst.Tag,
1944 lhs_bind: ReadArg.Bind,
1945 rhs_bind: ReadArg.Bind,
1946 lhs_ty: Type,
1947 rhs_ty: Type,
1948 maybe_inst: ?Air.Inst.Index,
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 };
1787 }
1788 } else {
1789 return self.fail("TODO rem/mod for integers with bits > 64", .{});
1790 }
1791 },
1792 else => unreachable,
1963 // Truncate if necessary
1964 const result_reg = result.register;
1965 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
1966 return result;
1967 } else {
1968 return self.fail("TODO binary operations on integers > u64/i64", .{});
17931969 }
17941970 },
1795 .addwrap,
1796 .subwrap,
1797 .mulwrap,
1798 => {
1799 const base_tag: Air.Inst.Tag = switch (tag) {
1800 .addwrap => .add,
1801 .subwrap => .sub,
1802 .mulwrap => .mul,
1803 else => unreachable,
1804 };
1971 else => unreachable,
1972 }
1973}
18051974
1806 // Generate an add/sub/mul
1807 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
1975fn bitwise(
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 necessary
1810 switch (lhs_ty.zigTypeTag()) {
1811 .Vector => return self.fail("TODO binary operations on vectors", .{}),
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,
1999 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2000 } else {
2001 return self.fail("TODO binary operations on int with bits > 64", .{});
18232002 }
18242003 },
1825 .bit_and,
1826 .bit_or,
1827 .xor,
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 };
2004 else => unreachable,
2005 }
2006}
18422007
1843 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
1844 } else {
1845 return self.fail("TODO binary operations on int with bits > 64", .{});
1846 }
1847 },
1848 else => unreachable,
1849 }
1850 },
1851 .shl_exact,
1852 .shr_exact,
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;
2008fn shiftExact(
2009 self: *Self,
2010 tag: Air.Inst.Tag,
2011 lhs_bind: ReadArg.Bind,
2012 rhs_bind: ReadArg.Bind,
2013 lhs_ty: Type,
2014 rhs_ty: Type,
2015 maybe_inst: ?Air.Inst.Index,
2016) InnerError!MCValue {
2017 _ = rhs_ty;
18602018
1861 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
1862 .shl_exact => .lsl_register,
1863 .shr_exact => switch (int_info.signedness) {
1864 .signed => Mir.Inst.Tag.asr_register,
1865 .unsigned => Mir.Inst.Tag.lsr_register,
1866 },
1867 else => unreachable,
1868 };
1869 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
1870 .shl_exact => .lsl_immediate,
1871 .shr_exact => switch (int_info.signedness) {
1872 .signed => Mir.Inst.Tag.asr_immediate,
1873 .unsigned => Mir.Inst.Tag.lsr_immediate,
1874 },
1875 else => unreachable,
1876 };
2019 switch (lhs_ty.zigTypeTag()) {
2020 .Vector => return self.fail("TODO binary operations on vectors", .{}),
2021 .Int => {
2022 const int_info = lhs_ty.intInfo(self.target.*);
2023 if (int_info.bits <= 64) {
2024 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2025
2026 const mir_tag_register: Mir.Inst.Tag = switch (tag) {
2027 .shl_exact => .lsl_register,
2028 .shr_exact => switch (int_info.signedness) {
2029 .signed => Mir.Inst.Tag.asr_register,
2030 .unsigned => Mir.Inst.Tag.lsr_register,
2031 },
2032 else => unreachable,
2033 };
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) {
1879 return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata);
1880 } else {
1881 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);
1882 }
1883 } else {
1884 return self.fail("TODO binary operations on int with bits > 64", .{});
1885 }
1886 },
1887 else => unreachable,
2043 if (rhs_immediate) |imm| {
2044 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst);
2045 } else {
2046 // We intentionally pass lhs_ty here in order to
2047 // prevent using the 32-bit register alias when
2048 // lhs_ty is > 32 bits.
2049 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst);
2050 }
2051 } else {
2052 return self.fail("TODO binary operations on int with bits > 64", .{});
18882053 }
18892054 },
1890 .shl,
1891 .shr,
1892 => {
1893 const base_tag: Air.Inst.Tag = switch (tag) {
1894 .shl => .shl_exact,
1895 .shr => .shr_exact,
1896 else => unreachable,
1897 };
2055 else => unreachable,
2056 }
2057}
18982058
1899 // Generate a shl_exact/shr_exact
1900 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
2059fn shiftNormal(
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 necessary
1903 switch (tag) {
1904 .shr => return result,
1905 .shl => switch (lhs_ty.zigTypeTag()) {
1906 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1907 .Int => {
1908 const int_info = lhs_ty.intInfo(self.target.*);
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 }
2080 // Truncate if necessary
2081 switch (tag) {
2082 .shr => return result,
2083 .shl => {
2084 const result_reg = result.register;
2085 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
2086 return result;
19162087 },
19172088 else => unreachable,
1918 },
1919 else => unreachable,
2089 }
2090 } else {
2091 return self.fail("TODO binary operations on integers > u64/i64", .{});
19202092 }
19212093 },
1922 .bool_and,
1923 .bool_or,
1924 => {
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 };
2094 else => unreachable,
2095 }
2096}
19352097
1936 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);
1937 },
2098fn booleanOp(
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,
19382115 else => unreachable,
1939 }
2116 };
2117
2118 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
19402119 },
1941 .ptr_add,
1942 .ptr_sub,
1943 => {
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.*);
2120 else => unreachable,
2121 }
2122}
19522123
1953 if (elem_size == 1) {
1954 const base_tag: Mir.Inst.Tag = switch (tag) {
1955 .ptr_add => .add_shifted_register,
1956 .ptr_sub => .sub_shifted_register,
1957 else => unreachable,
1958 };
2124fn ptrArithmetic(
2125 self: *Self,
2126 tag: Air.Inst.Tag,
2127 lhs_bind: ReadArg.Bind,
2128 rhs_bind: ReadArg.Bind,
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);
1961 } else {
1962 // convert the offset into a byte offset by
1963 // multiplying it with elem_size
1964 const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null);
1965 const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);
1966 return addr;
1967 }
1968 },
2138 const ptr_ty = lhs_ty;
2139 const elem_ty = switch (ptr_ty.ptrSize()) {
2140 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
2141 else => ptr_ty.childType(),
2142 };
2143 const elem_size = elem_ty.abiSize(self.target.*);
2144
2145 const base_tag: Air.Inst.Tag = switch (tag) {
2146 .ptr_add => .add,
2147 .ptr_sub => .sub,
19692148 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;
19702163 }
19712164 },
19722165 else => unreachable,
......@@ -1975,38 +2168,66 @@ fn binOp(
19752168
19762169fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
19772170 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);
19802171 const lhs_ty = self.air.typeOf(bin_op.lhs);
19812172 const rhs_ty = self.air.typeOf(bin_op.rhs);
19822173
1983 const result: MCValue = if (self.liveness.isUnused(inst))
1984 .dead
1985 else
1986 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
1987 .inst = inst,
1988 .lhs = bin_op.lhs,
1989 .rhs = bin_op.rhs,
1990 });
2174 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2175 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2176 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2177
2178 break :result switch (tag) {
2179 .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
2180 .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
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 };
19912216 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
19922217}
19932218
19942219fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
19952220 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
19962221 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);
19992222 const lhs_ty = self.air.typeOf(bin_op.lhs);
20002223 const rhs_ty = self.air.typeOf(bin_op.rhs);
20012224
2002 const result: MCValue = if (self.liveness.isUnused(inst))
2003 .dead
2004 else
2005 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
2006 .inst = inst,
2007 .lhs = bin_op.lhs,
2008 .rhs = bin_op.rhs,
2009 });
2225 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2226 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2227 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2228
2229 break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
2230 };
20102231 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
20112232}
20122233
......@@ -2033,8 +2254,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
20332254 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
20342255 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
20352256 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2036 const lhs = try self.resolveInst(extra.lhs);
2037 const rhs = try self.resolveInst(extra.rhs);
2257 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2258 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
20382259 const lhs_ty = self.air.typeOf(extra.lhs);
20392260 const rhs_ty = self.air.typeOf(extra.rhs);
20402261
......@@ -2051,7 +2272,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
20512272 const int_info = lhs_ty.intInfo(self.target.*);
20522273 switch (int_info.bits) {
20532274 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
20562277 try self.spillCompareFlagsIfOccupied();
20572278 self.condition_flags_inst = null;
......@@ -2061,13 +2282,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
20612282 .sub_with_overflow => .sub,
20622283 else => unreachable,
20632284 };
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);
20652286 const dest_reg = dest.register;
20662287 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
20672288 defer self.register_manager.unlockReg(dest_reg_lock);
20682289
20692290 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);
20712292 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
20722293 defer self.register_manager.unlockReg(truncated_reg_lock);
20732294
......@@ -2075,7 +2296,15 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
20752296 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
20762297
20772298 // 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
20802309 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
20812310 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 {
20832312 break :result MCValue{ .stack_offset = stack_offset };
20842313 },
20852314 32, 64 => {
2315 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
2316 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2317
20862318 // Only say yes if the operation is
20872319 // commutative, i.e. we can swap both of the
20882320 // operands
20892321 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,
20912323 .sub_with_overflow => false,
20922324 else => unreachable,
20932325 };
20942326 const rhs_immediate_ok = switch (tag) {
20952327 .add_with_overflow,
20962328 .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,
20982330 else => unreachable,
20992331 };
21002332
......@@ -2114,12 +2346,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
21142346
21152347 const dest = blk: {
21162348 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);
21182350 } else if (lhs_immediate_ok) {
21192351 // 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);
21212353 } 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);
21232355 }
21242356 };
21252357
......@@ -2150,8 +2382,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
21502382 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
21512383 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
21522384 const result: MCValue = result: {
2153 const lhs = try self.resolveInst(extra.lhs);
2154 const rhs = try self.resolveInst(extra.rhs);
2385 const mod = self.bin_file.options.module.?;
2386
2387 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2388 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
21552389 const lhs_ty = self.air.typeOf(extra.lhs);
21562390 const rhs_ty = self.air.typeOf(extra.rhs);
21572391
......@@ -2163,20 +2397,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
21632397 switch (lhs_ty.zigTypeTag()) {
21642398 .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
21652399 .Int => {
2400 assert(lhs_ty.eql(rhs_ty, mod));
21662401 const int_info = lhs_ty.intInfo(self.target.*);
2167
21682402 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
21712405 try self.spillCompareFlagsIfOccupied();
2172 self.condition_flags_inst = null;
21732406
21742407 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
21752408 .signed => .smull,
21762409 .unsigned => .umull,
21772410 };
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);
21802413 const dest_reg = dest.register;
21812414 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
21822415 defer self.register_manager.unlockReg(dest_reg_lock);
......@@ -2186,8 +2419,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
21862419 defer self.register_manager.unlockReg(truncated_reg_lock);
21872420
21882421 try self.truncRegister(
2189 dest_reg.to32(),
2190 truncated_reg.to32(),
2422 dest_reg.toW(),
2423 truncated_reg.toW(),
21912424 int_info.signedness,
21922425 int_info.bits,
21932426 );
......@@ -2197,8 +2430,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
21972430 _ = try self.addInst(.{
21982431 .tag = .cmp_extended_register,
21992432 .data = .{ .rr_extend_shift = .{
2200 .rn = dest_reg.to64(),
2201 .rm = truncated_reg.to32(),
2433 .rn = dest_reg.toX(),
2434 .rm = truncated_reg.toW(),
22022435 .ext_type = .sxtw,
22032436 .imm3 = 0,
22042437 } },
......@@ -2208,8 +2441,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
22082441 _ = try self.addInst(.{
22092442 .tag = .cmp_extended_register,
22102443 .data = .{ .rr_extend_shift = .{
2211 .rn = dest_reg.to64(),
2212 .rm = truncated_reg.to32(),
2444 .rn = dest_reg.toX(),
2445 .rm = truncated_reg.toW(),
22132446 .ext_type = .uxtw,
22142447 .imm3 = 0,
22152448 } },
......@@ -2222,53 +2455,30 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
22222455
22232456 break :result MCValue{ .stack_offset = stack_offset };
22242457 } 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
22272460 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);
2263 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
2462 var lhs_reg: Register = undefined;
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: {
2266 const raw_reg = try self.register_manager.allocReg(null, gp);
2267 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
2268 break :blk reg;
2468 const read_args = [_]ReadArg{
2469 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2470 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
22692471 };
2270 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
2271 defer self.register_manager.unlockReg(dest_reg_lock);
2472 const write_args = [_]WriteArg{
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
22732483 switch (int_info.signedness) {
22742484 .signed => {
......@@ -2282,10 +2492,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
22822492 } },
22832493 });
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
22892495 // smulh dest_high, lhs, rhs
22902496 _ = try self.addInst(.{
22912497 .tag = .smulh,
......@@ -2332,10 +2538,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
23322538 }
23332539 },
23342540 .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
23392541 // umulh dest_high, lhs, rhs
23402542 _ = try self.addInst(.{
23412543 .tag = .umulh,
......@@ -2356,14 +2558,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
23562558 } },
23572559 });
23582560
2359 _ = try self.binOp(
2360 .cmp_eq,
2361 .{ .register = dest_high_reg },
2362 .{ .immediate = 0 },
2363 Type.usize,
2364 Type.usize,
2365 null,
2366 );
2561 _ = try self.addInst(.{
2562 .tag = .cmp_immediate,
2563 .data = .{ .r_imm12_sh = .{
2564 .rn = dest_high_reg,
2565 .imm12 = 0,
2566 } },
2567 });
23672568
23682569 if (int_info.bits < 64) {
23692570 // lsr dest_high, dest, #shift
......@@ -2376,22 +2577,17 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
23762577 } },
23772578 });
23782579
2379 _ = try self.binOp(
2380 .cmp_eq,
2381 .{ .register = dest_high_reg },
2382 .{ .immediate = 0 },
2383 Type.usize,
2384 Type.usize,
2385 null,
2386 );
2580 _ = try self.addInst(.{
2581 .tag = .cmp_immediate,
2582 .data = .{ .r_imm12_sh = .{
2583 .rn = dest_high_reg,
2584 .imm12 = 0,
2585 } },
2586 });
23872587 }
23882588 },
23892589 }
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
23952591 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
23962592
23972593 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
......@@ -2411,8 +2607,8 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
24112607 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
24122608 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
24132609 const result: MCValue = result: {
2414 const lhs = try self.resolveInst(extra.lhs);
2415 const rhs = try self.resolveInst(extra.rhs);
2610 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2611 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
24162612 const lhs_ty = self.air.typeOf(extra.lhs);
24172613 const rhs_ty = self.air.typeOf(extra.rhs);
24182614
......@@ -2426,35 +2622,112 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
24262622 .Int => {
24272623 const int_info = lhs_ty.intInfo(self.target.*);
24282624 if (int_info.bits <= 64) {
2429 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
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);
2625 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
24362626
24372627 try self.spillCompareFlagsIfOccupied();
2438 self.condition_flags_inst = null;
24392628
2440 // lsl dest, lhs, rhs
2441 const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null);
2442 const dest_reg = dest.register;
2443 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
2444 defer self.register_manager.unlockReg(dest_reg_lock);
2629 var lhs_reg: Register = undefined;
2630 var rhs_reg: Register = undefined;
2631 var dest_reg: Register = undefined;
2632 var reconstructed_reg: Register = undefined;
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, rhs
2447 const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null);
2698 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
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
24492714 // 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 });
24532726 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });
24542727
24552728 break :result MCValue{ .stack_offset = stack_offset };
24562729 } else {
2457 return self.fail("TODO overflow operations on integers > u64/i64", .{});
2730 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
24582731 }
24592732 },
24602733 else => unreachable,
......@@ -2712,63 +2985,59 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
27122985}
27132986
27142987fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2715 const is_volatile = false; // TODO
27162988 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2717
2718 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
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
2989 const slice_ty = self.air.typeOf(bin_op.lhs);
2990 const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
27302991 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2731 const slice_ptr_field_type = 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);
2992 const ptr_ty = slice_ty.slicePtrFieldType(&buf);
27382993
2994 const slice_mcv = try self.resolveInst(bin_op.lhs);
27392995 const base_mcv = slicePtr(slice_mcv);
27402996
2741 switch (elem_size) {
2742 else => {
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);
2997 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
2998 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
27532999
2754 break :result dest;
2755 },
2756 }
3000 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
27573001 };
27583002 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
27593003}
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
27613027fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
27623028 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
27633029 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
27643030 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
27653031 const slice_mcv = try self.resolveInst(extra.lhs);
2766 const index_mcv = try self.resolveInst(extra.rhs);
27673032 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
27693037 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);
27723041 break :result addr;
27733042 };
27743043 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
......@@ -2791,12 +3060,13 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
27913060 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
27923061 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
27933062 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2794 const ptr_mcv = try self.resolveInst(extra.lhs);
2795 const index_mcv = try self.resolveInst(extra.rhs);
3063 const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs };
3064 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
27963065
27973066 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);
28003070 break :result addr;
28013071 };
28023072 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
......@@ -2853,7 +3123,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
28533123 return self.finishAir(inst, result, .{ un_op, .none, .none });
28543124}
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 {
28573133 if (!self.liveness.operandDies(inst, op_index))
28583134 return false;
28593135
......@@ -2912,7 +3188,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
29123188 .stack_offset => |off| {
29133189 if (elem_size <= 8) {
29143190 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);
29163192 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
29173193 defer self.register_manager.unlockReg(tmp_reg_lock);
29183194
......@@ -3050,11 +3326,11 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
30503326 if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
30513327 // The MCValue that holds the pointer can be re-used as the value.
30523328 break :blk switch (ptr) {
3053 .register => |r| MCValue{ .register = registerAlias(r, elem_size) },
3329 .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) },
30543330 else => ptr,
30553331 };
30563332 } else {
3057 break :blk try self.allocRegOrMem(inst, true);
3333 break :blk try self.allocRegOrMem(elem_ty, true, inst);
30583334 }
30593335 };
30603336 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
31363412 else => {
31373413 if (abi_size <= 8) {
31383414 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);
31403416 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
31413417 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
32293505 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };
32303506 },
32313507 else => {
3232 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
3233 .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 );
3508 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
3509 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
32503510
3251 break :result dest;
3511 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
32523512 },
32533513 }
32543514 };
......@@ -3295,7 +3555,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
32953555 } else {
32963556 // Copy to new register
32973557 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);
32993559 try self.genSetReg(struct_field_ty, dest_reg, field);
33003560
33013561 break :result MCValue{ .register = dest_reg };
......@@ -3330,7 +3590,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
33303590 return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)});
33313591 };
33323592 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);
33343594 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
33353595
33363596 break :blk MCValue{ .stack_offset = stack_offset };
......@@ -3408,11 +3668,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
34083668 const ret_ty = fn_ty.fnReturnType();
34093669 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
34103670 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();
3414 const ptr_bytes = @divExact(ptr_bits, 8);
3415 const ret_ptr_reg = registerAlias(.x0, ptr_bytes);
3673 const ret_ptr_reg = self.registerAlias(.x0, Type.usize);
34163674
34173675 var ptr_ty_payload: Type.Payload.ElemType = .{
34183676 .base = .{ .tag = .single_mut_pointer },
......@@ -3636,14 +3894,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
36363894 const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
36373895 const abi_align = ret_ty.abiAlignment(self.target.*);
36383896
3639 // This is essentially allocMem without the
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);
3897 const offset = try self.allocMem(abi_size, abi_align, null);
36473898
36483899 const tmp_mcv = MCValue{ .stack_offset = offset };
36493900 try self.load(tmp_mcv, ptr, ptr_ty);
......@@ -3660,54 +3911,100 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
36603911
36613912fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
36623913 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3663 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
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 };
3914 const lhs_ty = self.air.typeOf(bin_op.lhs);
36903915
3691 const int_info = int_ty.intInfo(self.target.*);
3692 if (int_info.bits <= 64) {
3693 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{
3694 .inst = inst,
3695 .lhs = bin_op.lhs,
3696 .rhs = bin_op.rhs,
3697 });
3916 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
3917 break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op);
3918 };
3919
3920 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3921}
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();
3700 self.condition_flags_inst = inst;
3952 const int_info = int_ty.intInfo(self.target.*);
3953 if (int_info.bits <= 64) {
3954 try self.spillCompareFlagsIfOccupied();
37013955
3702 break :result switch (int_info.signedness) {
3703 .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) },
3704 .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) },
3956 var lhs_reg: Register = undefined;
3957 var rhs_reg: Register = undefined;
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 },
37053965 };
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 });
37063979 } 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 });
37083999 }
3709 };
3710 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
4000
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 }
37114008}
37124009
37134010fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
......@@ -3952,15 +4249,13 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue {
39524249
39534250fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
39544251 const error_type = ty.errorUnionSet();
3955 const error_int_type = Type.initTag(.u16);
39564252
39574253 if (error_type.errorSetIsEmpty()) {
39584254 return MCValue{ .immediate = 0 }; // always false
39594255 }
39604256
39614257 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);
3963 return MCValue{ .condition_flags = .hi };
4258 return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt);
39644259}
39654260
39664261fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
......@@ -3991,15 +4286,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
39914286 const un_op = self.air.instructions.items(.data)[inst].un_op;
39924287 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
39934288 const operand_ptr = try self.resolveInst(un_op);
3994 const operand: MCValue = blk: {
3995 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3996 // The MCValue that holds the pointer can be re-used as the value.
3997 break :blk operand_ptr;
3998 } else {
3999 break :blk try self.allocRegOrMem(inst, true);
4000 }
4001 };
4002 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4289 const ptr_ty = self.air.typeOf(un_op);
4290 const elem_ty = ptr_ty.elemType();
4291
4292 const operand = try self.allocRegOrMem(elem_ty, true, null);
4293 try self.load(operand, operand_ptr, ptr_ty);
4294
40034295 break :result try self.isNull(operand);
40044296 };
40054297 return self.finishAir(inst, result, .{ un_op, .none, .none });
......@@ -4018,15 +4310,12 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
40184310 const un_op = self.air.instructions.items(.data)[inst].un_op;
40194311 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
40204312 const operand_ptr = try self.resolveInst(un_op);
4021 const operand: MCValue = blk: {
4022 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4023 // The MCValue that holds the pointer can be re-used as the value.
4024 break :blk operand_ptr;
4025 } else {
4026 break :blk try self.allocRegOrMem(inst, true);
4027 }
4028 };
4029 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4313 const ptr_ty = self.air.typeOf(un_op);
4314 const elem_ty = ptr_ty.elemType();
4315
4316 const operand = try self.allocRegOrMem(elem_ty, true, null);
4317 try self.load(operand, operand_ptr, ptr_ty);
4318
40304319 break :result try self.isNonNull(operand);
40314320 };
40324321 return self.finishAir(inst, result, .{ un_op, .none, .none });
......@@ -4047,16 +4336,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
40474336 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
40484337 const operand_ptr = try self.resolveInst(un_op);
40494338 const ptr_ty = self.air.typeOf(un_op);
4050 const operand: MCValue = blk: {
4051 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4052 // The MCValue that holds the pointer can be re-used as the value.
4053 break :blk operand_ptr;
4054 } else {
4055 break :blk try self.allocRegOrMem(inst, true);
4056 }
4057 };
4058 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4059 break :result try self.isErr(ptr_ty.elemType(), operand);
4339 const elem_ty = ptr_ty.elemType();
4340
4341 const operand = try self.allocRegOrMem(elem_ty, true, null);
4342 try self.load(operand, operand_ptr, ptr_ty);
4343
4344 break :result try self.isErr(elem_ty, operand);
40604345 };
40614346 return self.finishAir(inst, result, .{ un_op, .none, .none });
40624347}
......@@ -4076,16 +4361,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
40764361 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
40774362 const operand_ptr = try self.resolveInst(un_op);
40784363 const ptr_ty = self.air.typeOf(un_op);
4079 const operand: MCValue = blk: {
4080 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4081 // The MCValue that holds the pointer can be re-used as the value.
4082 break :blk operand_ptr;
4083 } else {
4084 break :blk try self.allocRegOrMem(inst, true);
4085 }
4086 };
4087 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
4088 break :result try self.isNonErr(ptr_ty.elemType(), operand);
4364 const elem_ty = ptr_ty.elemType();
4365
4366 const operand = try self.allocRegOrMem(elem_ty, true, null);
4367 try self.load(operand, operand_ptr, ptr_ty);
4368
4369 break :result try self.isNonErr(elem_ty, operand);
40894370 };
40904371 return self.finishAir(inst, result, .{ un_op, .none, .none });
40914372}
......@@ -4178,7 +4459,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
41784459 .none, .dead, .unreach => unreachable,
41794460 .register, .stack_offset, .memory => operand_mcv,
41804461 .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);
41824463 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
41834464 break :blk new_mcv;
41844465 },
......@@ -4376,7 +4657,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
43764657 4, 8 => .str_stack,
43774658 else => unreachable, // unexpected abi size
43784659 };
4379 const rt = registerAlias(reg, abi_size);
4660 const rt = self.registerAlias(reg, ty);
43804661
43814662 _ = try self.addInst(.{
43824663 .tag = tag,
......@@ -4399,10 +4680,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
43994680 const overflow_bit_ty = ty.structFieldType(1);
44004681 const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));
44014682 const raw_cond_reg = try self.register_manager.allocReg(null, gp);
4402 const cond_reg = registerAlias(
4403 raw_cond_reg,
4404 @intCast(u32, overflow_bit_ty.abiSize(self.target.*)),
4405 );
4683 const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty);
44064684
44074685 _ = try self.addInst(.{
44084686 .tag = .cset,
......@@ -4515,16 +4793,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
45154793 }
45164794 },
45174795 .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
45224796 _ = try self.addInst(.{
4523 .tag = .sub_immediate,
4524 .data = .{ .rr_imm12_sh = .{
4525 .rd = reg,
4526 .rn = .x29,
4527 .imm12 = imm12,
4797 .tag = .ldr_ptr_stack,
4798 .data = .{ .load_store_stack = .{
4799 .rt = reg,
4800 .offset = @intCast(u32, off),
45284801 } },
45294802 });
45304803 },
......@@ -4599,8 +4872,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
45994872 .memory => |addr| {
46004873 // The value is in memory at a hard-coded address.
46014874 // 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 });
4603 try self.genLdrRegister(reg, reg.to64(), ty);
4875 try self.genSetReg(ty, reg.toX(), .{ .immediate = addr });
4876 try self.genLdrRegister(reg, reg.toX(), ty);
46044877 },
46054878 .stack_offset => |off| {
46064879 const abi_size = ty.abiSize(self.target.*);
......@@ -4679,7 +4952,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
46794952 4, 8 => .str_immediate,
46804953 else => unreachable, // unexpected abi size
46814954 };
4682 const rt = registerAlias(reg, abi_size);
4955 const rt = self.registerAlias(reg, ty);
46834956 const offset = switch (abi_size) {
46844957 1 => blk: {
46854958 if (math.cast(u12, stack_offset)) |imm| {
......@@ -4838,7 +5111,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
48385111 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
48395112 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);
48425115 try self.genSetStack(ptr_ty, stack_offset, ptr);
48435116 try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len });
48445117 break :result MCValue{ .stack_offset = stack_offset };
......@@ -5300,7 +5573,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
53005573 assert(ret_ty.isError());
53015574 result.return_value = .{ .immediate = 0 };
53025575 } 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) };
53045577 } else {
53055578 return self.fail("TODO support more return types for ARM backend", .{});
53065579 }
......@@ -5322,7 +5595,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
53225595
53235596 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {
53245597 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) };
53265599 ncrn += 1;
53275600 } else {
53285601 return self.fail("TODO MCValues with multiple registers", .{});
......@@ -5358,7 +5631,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
53585631 assert(ret_ty.isError());
53595632 result.return_value = .{ .immediate = 0 };
53605633 } 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) };
53625635 } else {
53635636 // The result is returned by reference, not by
53645637 // value. This means that x0 (or w0 when pointer
......@@ -5424,14 +5697,30 @@ fn parseRegName(name: []const u8) ?Register {
54245697 return std.meta.stringToEnum(Register, name);
54255698}
54265699
5427fn registerAlias(reg: Register, size_bytes: u64) Register {
5428 if (size_bytes == 0) {
5429 unreachable; // should be comptime-known
5430 } else if (size_bytes <= 4) {
5431 return reg.to32();
5432 } else if (size_bytes <= 8) {
5433 return reg.to64();
5434 } else {
5435 unreachable; // TODO handle floating-point registers
5700fn registerAlias(self: *Self, reg: Register, ty: Type) Register {
5701 const abi_size = ty.abiSize(self.target.*);
5702
5703 switch (reg.class()) {
5704 .general_purpose => {
5705 if (abi_size == 0) {
5706 unreachable; // should be comptime-known
5707 } else if (abi_size <= 4) {
5708 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 },
54365725 }
54375726}
src/arch/aarch64/Emit.zig+43-30
......@@ -150,6 +150,7 @@ pub fn emitMir(
150150 .ldp => try emit.mirLoadStoreRegisterPair(inst),
151151 .stp => try emit.mirLoadStoreRegisterPair(inst),
152152
153 .ldr_ptr_stack => try emit.mirLoadStoreStack(inst),
153154 .ldr_stack => try emit.mirLoadStoreStack(inst),
154155 .ldrb_stack => try emit.mirLoadStoreStack(inst),
155156 .ldrh_stack => try emit.mirLoadStoreStack(inst),
......@@ -159,8 +160,8 @@ pub fn emitMir(
159160 .strb_stack => try emit.mirLoadStoreStack(inst),
160161 .strh_stack => try emit.mirLoadStoreStack(inst),
161162
162 .ldr_stack_argument => try emit.mirLoadStackArgument(inst),
163163 .ldr_ptr_stack_argument => try emit.mirLoadStackArgument(inst),
164 .ldr_stack_argument => try emit.mirLoadStackArgument(inst),
164165 .ldrb_stack_argument => try emit.mirLoadStackArgument(inst),
165166 .ldrh_stack_argument => try emit.mirLoadStackArgument(inst),
166167 .ldrsb_stack_argument => try emit.mirLoadStackArgument(inst),
......@@ -842,14 +843,14 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
842843 // PC-relative displacement to the entry in memory.
843844 // adrp
844845 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
847848 switch (tag) {
848849 .load_memory_got => {
849850 // ldr reg, reg, offset
850851 try emit.writeInstruction(Instruction.ldr(
851852 reg,
852 reg.to64(),
853 reg.toX(),
853854 Instruction.LoadStoreOffset.imm(0),
854855 ));
855856 },
......@@ -863,11 +864,11 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
863864 // Note that this can potentially be optimised out by the codegen/linker if the
864865 // target address is appropriately aligned.
865866 // 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));
867868 // ldr reg, reg, offset
868869 try emit.writeInstruction(Instruction.ldr(
869870 reg,
870 reg.to64(),
871 reg.toX(),
871872 Instruction.LoadStoreOffset.imm(0),
872873 ));
873874 },
......@@ -1003,23 +1004,43 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
10031004 const rt = load_store_stack.rt;
10041005
10051006 const raw_offset = emit.stack_size - load_store_stack.offset;
1006 const offset = switch (tag) {
1007 .ldrb_stack, .ldrsb_stack, .strb_stack => blk: {
1008 if (math.cast(u12, raw_offset)) |imm| {
1009 break :blk Instruction.LoadStoreOffset.imm(imm);
1010 } else {
1007 switch (tag) {
1008 .ldr_ptr_stack => {
1009 const offset = if (math.cast(u12, raw_offset)) |imm| imm else {
1010 return emit.fail("TODO load stack argument ptr with larger offset", .{});
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 {
10111020 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,
10121028 }
10131029 },
1014 .ldrh_stack, .ldrsh_stack, .strh_stack => blk: {
1030 .ldrh_stack, .ldrsh_stack, .strh_stack => {
10151031 assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry
1016 if (math.cast(u12, @divExact(raw_offset, 2))) |imm| {
1017 break :blk Instruction.LoadStoreOffset.imm(imm);
1018 } else {
1032 const offset = if (math.cast(u12, @divExact(raw_offset, 2))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
10191033 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,
10201041 }
10211042 },
1022 .ldr_stack, .str_stack => blk: {
1043 .ldr_stack, .str_stack => {
10231044 const alignment: u32 = switch (rt.size()) {
10241045 32 => 4,
10251046 64 => 8,
......@@ -1027,25 +1048,17 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
10271048 };
10281049
10291050 assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry
1030 if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| {
1031 break :blk Instruction.LoadStoreOffset.imm(imm);
1032 } else {
1051 const offset = if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| Instruction.LoadStoreOffset.imm(imm) else {
10331052 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,
10341059 }
10351060 },
10361061 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,
10491062 }
10501063}
10511064
src/arch/aarch64/Mir.zig+3-5
......@@ -92,6 +92,8 @@ pub const Inst = struct {
9292 load_memory_ptr_direct,
9393 /// Load Pair of Registers
9494 ldp,
95 /// Pseudo-instruction: Load pointer to stack item
96 ldr_ptr_stack,
9597 /// Pseudo-instruction: Load pointer to stack argument
9698 ldr_ptr_stack_argument,
9799 /// Pseudo-instruction: Load from stack
......@@ -432,7 +434,7 @@ pub const Inst = struct {
432434 rn: Register,
433435 offset: bits.Instruction.LoadStoreOffsetRegister,
434436 },
435 /// A registers and a stack offset
437 /// A register and a stack offset
436438 ///
437439 /// Used by e.g. str_stack
438440 load_store_stack: struct {
......@@ -464,10 +466,6 @@ pub const Inst = struct {
464466 line: u32,
465467 column: u32,
466468 },
467 load_memory: struct {
468 register: u32,
469 addr: u32,
470 },
471469 };
472470
473471 // 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;
44const assert = std.debug.assert;
55const testing = std.testing;
66
7// zig fmt: off
7pub const RegisterClass = enum {
8 general_purpose,
9 stack_pointer,
10 floating_point,
11};
812
913/// General purpose registers in the AArch64 instruction set
10pub const Register = enum(u7) {
11 // 64-bit registers
14pub const Register = enum(u8) {
15 // zig fmt: off
16 // 64-bit general-purpose registers
1217 x0, x1, x2, x3, x4, x5, x6, x7,
1318 x8, x9, x10, x11, x12, x13, x14, x15,
1419 x16, x17, x18, x19, x20, x21, x22, x23,
1520 x24, x25, x26, x27, x28, x29, x30, xzr,
1621
17 // 32-bit registers
22 // 32-bit general-purpose registers
1823 w0, w1, w2, w3, w4, w5, w6, w7,
1924 w8, w9, w10, w11, w12, w13, w14, w15,
2025 w16, w17, w18, w19, w20, w21, w22, w23,
......@@ -23,192 +28,298 @@ pub const Register = enum(u7) {
2328 // Stack pointer
2429 sp, wsp,
2530
26 pub fn id(self: Register) u6 {
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
31 // 128-bit floating-point registers
11132 q0, q1, q2, q3, q4, q5, q6, q7,
11233 q8, q9, q10, q11, q12, q13, q14, q15,
11334 q16, q17, q18, q19, q20, q21, q22, q23,
11435 q24, q25, q26, q27, q28, q29, q30, q31,
11536
116 // 64-bit registers
37 // 64-bit floating-point registers
11738 d0, d1, d2, d3, d4, d5, d6, d7,
11839 d8, d9, d10, d11, d12, d13, d14, d15,
11940 d16, d17, d18, d19, d20, d21, d22, d23,
12041 d24, d25, d26, d27, d28, d29, d30, d31,
12142
122 // 32-bit registers
43 // 32-bit floating-point registers
12344 s0, s1, s2, s3, s4, s5, s6, s7,
12445 s8, s9, s10, s11, s12, s13, s14, s15,
12546 s16, s17, s18, s19, s20, s21, s22, s23,
12647 s24, s25, s26, s27, s28, s29, s30, s31,
12748
128 // 16-bit registers
49 // 16-bit floating-point registers
12950 h0, h1, h2, h3, h4, h5, h6, h7,
13051 h8, h9, h10, h11, h12, h13, h14, h15,
13152 h16, h17, h18, h19, h20, h21, h22, h23,
13253 h24, h25, h26, h27, h28, h29, h30, h31,
13354
134 // 8-bit registers
55 // 8-bit floating-point registers
13556 b0, b1, b2, b3, b4, b5, b6, b7,
13657 b8, b9, b10, b11, b12, b13, b14, b15,
13758 b16, b17, b18, b19, b20, b21, b22, b23,
13859 b24, b25, b26, b27, b28, b29, b30, b31,
60 // zig fmt: on
13961
140 pub fn id(self: FloatingPointRegister) u5 {
141 return @truncate(u5, @enumToInt(self));
62 pub fn class(self: Register) RegisterClass {
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 };
142111 }
143112
144113 /// 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 {
146148 return switch (@enumToInt(self)) {
147 0...31 => 128,
148 32...63 => 64,
149 64...95 => 32,
150 96...127 => 16,
151 128...159 => 8,
149 @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intToEnum(
150 Register,
151 @enumToInt(self) - @enumToInt(Register.x0) + @enumToInt(Register.w0),
152 ),
153 @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intToEnum(
154 Register,
155 @enumToInt(self) - @enumToInt(Register.w0) + @enumToInt(Register.w0),
156 ),
152157 else => unreachable,
153158 };
154159 }
155160
156 /// Convert from any register to its 128 bit alias.
157 pub fn to128(self: FloatingPointRegister) FloatingPointRegister {
158 return @intToEnum(FloatingPointRegister, self.id());
161 /// Convert from a floating-point register to its 128 bit alias.
162 pub fn toQ(self: Register) Register {
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 };
159213 }
160214
161 /// Convert from any register to its 64 bit alias.
162 pub fn to64(self: FloatingPointRegister) FloatingPointRegister {
163 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32);
215 /// Convert from a floating-point register to its 32 bit alias.
216 pub fn toS(self: Register) Register {
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 };
164240 }
165241
166 /// Convert from any register to its 32 bit alias.
167 pub fn to32(self: FloatingPointRegister) FloatingPointRegister {
168 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64);
242 /// Convert from a floating-point register to its 16 bit alias.
243 pub fn toH(self: Register) Register {
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 };
169267 }
170268
171 /// Convert from any register to its 16 bit alias.
172 pub fn to16(self: FloatingPointRegister) FloatingPointRegister {
173 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96);
269 /// Convert from a floating-point register to its 8 bit alias.
270 pub fn toB(self: Register) Register {
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 };
174294 }
175295
176 /// Convert from any register to its 8 bit alias.
177 pub fn to8(self: FloatingPointRegister) FloatingPointRegister {
178 return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128);
296 pub fn dwarfLocOp(self: Register) u8 {
297 return @as(u8, self.enc()) + DW.OP.reg0;
179298 }
180299};
181300
182// zig fmt: on
301test "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" {
185 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id());
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());
305 try testing.expectEqual(@as(u5, 31), Register.xzr.enc());
306 try testing.expectEqual(@as(u5, 31), Register.wzr.enc());
190307
191 try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id());
192 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id());
308 try testing.expectEqual(@as(u5, 31), Register.sp.enc());
309 try testing.expectEqual(@as(u5, 31), Register.sp.enc());
193310}
194311
195test "FloatingPointRegister.size" {
196 try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size());
197 try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.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());
312test "Register.size" {
313 try testing.expectEqual(@as(u8, 64), Register.x19.size());
314 try testing.expectEqual(@as(u8, 32), Register.w3.size());
201315}
202316
203test "FloatingPointRegister.toX" {
204 try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128());
205 try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128());
206 try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128());
317test "Register.toX/toW" {
318 try testing.expectEqual(Register.x0, Register.w0.toX());
319 try testing.expectEqual(Register.x0, Register.x0.toX());
207320
208 try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64());
209 try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32());
210 try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16());
211 try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8());
321 try testing.expectEqual(Register.w3, Register.w3.toW());
322 try testing.expectEqual(Register.w3, Register.x3.toW());
212323}
213324
214325/// Represents an instruction in the AArch64 instruction set