| author | |
| committer | |
| log | 151e15e444cc691546bc30646967ef08dbcc073f |
| tree | 4ca72326c6f61fa346b123aa773881e1922c4854 |
| parent | b41b35f5789f39ef12d07997a74a7dc35224538c |
| signature |
3 files changed, 336 insertions(+), 210 deletions(-)
src/arch/aarch64/CodeGen.zig+82-67| ... | ... | @@ -414,7 +414,7 @@ fn gen(self: *Self) !void { |
| 414 | 414 | // to the stack. |
| 415 | 415 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 416 | 416 | const ptr_bytes = @divExact(ptr_bits, 8); |
| 417 | const ret_ptr_reg = registerAlias(.x0, ptr_bytes); | |
| 417 | const ret_ptr_reg = self.registerAlias(.x0, Type.usize); | |
| 418 | 418 | |
| 419 | 419 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, ptr_bytes) + ptr_bytes; |
| 420 | 420 | self.next_stack_offset = stack_offset; |
| ... | ... | @@ -927,7 +927,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 927 | 927 | // Make sure the type can fit in a register before we try to allocate one. |
| 928 | 928 | if (abi_size <= 8) { |
| 929 | 929 | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { |
| 930 | return MCValue{ .register = registerAlias(reg, abi_size) }; | |
| 930 | return MCValue{ .register = self.registerAlias(reg, elem_ty) }; | |
| 931 | 931 | } |
| 932 | 932 | } |
| 933 | 933 | } |
| ... | ... | @@ -982,7 +982,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 982 | 982 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 983 | 983 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 984 | 984 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 985 | const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); | |
| 985 | const reg = self.registerAlias(raw_reg, ty); | |
| 986 | 986 | try self.genSetReg(ty, reg, mcv); |
| 987 | 987 | return reg; |
| 988 | 988 | } |
| ... | ... | @@ -993,7 +993,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 993 | 993 | fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { |
| 994 | 994 | const raw_reg = try self.register_manager.allocReg(reg_owner, gp); |
| 995 | 995 | const ty = self.air.typeOfIndex(reg_owner); |
| 996 | const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); | |
| 996 | const reg = self.registerAlias(raw_reg, ty); | |
| 997 | 997 | try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); |
| 998 | 998 | return MCValue{ .register = reg }; |
| 999 | 999 | } |
| ... | ... | @@ -1031,7 +1031,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1031 | 1031 | const operand_info = operand_ty.intInfo(self.target.*); |
| 1032 | 1032 | |
| 1033 | 1033 | const dest_ty = self.air.typeOfIndex(inst); |
| 1034 | const dest_abi_size = dest_ty.abiSize(self.target.*); | |
| 1035 | 1034 | const dest_info = dest_ty.intInfo(self.target.*); |
| 1036 | 1035 | |
| 1037 | 1036 | const result: MCValue = result: { |
| ... | ... | @@ -1042,7 +1041,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1042 | 1041 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 1043 | 1042 | |
| 1044 | 1043 | const truncated: MCValue = switch (operand_mcv) { |
| 1045 | .register => |r| MCValue{ .register = registerAlias(r, dest_abi_size) }, | |
| 1044 | .register => |r| MCValue{ .register = self.registerAlias(r, dest_ty) }, | |
| 1046 | 1045 | else => operand_mcv, |
| 1047 | 1046 | }; |
| 1048 | 1047 | |
| ... | ... | @@ -1117,7 +1116,7 @@ fn trunc( |
| 1117 | 1116 | else => operand_reg: { |
| 1118 | 1117 | if (info_a.bits <= 64) { |
| 1119 | 1118 | const raw_reg = try self.copyToTmpRegister(operand_ty, operand); |
| 1120 | break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); | |
| 1119 | break :operand_reg self.registerAlias(raw_reg, operand_ty); | |
| 1121 | 1120 | } else { |
| 1122 | 1121 | return self.fail("TODO load least significant word into register", .{}); |
| 1123 | 1122 | } |
| ... | ... | @@ -1130,14 +1129,14 @@ fn trunc( |
| 1130 | 1129 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1131 | 1130 | |
| 1132 | 1131 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { |
| 1133 | break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); | |
| 1132 | break :blk self.registerAlias(operand_reg, dest_ty); | |
| 1134 | 1133 | } else { |
| 1135 | 1134 | const raw_reg = try self.register_manager.allocReg(inst, gp); |
| 1136 | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); | |
| 1135 | break :blk self.registerAlias(raw_reg, dest_ty); | |
| 1137 | 1136 | } |
| 1138 | 1137 | } else blk: { |
| 1139 | 1138 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1140 | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); | |
| 1139 | break :blk self.registerAlias(raw_reg, dest_ty); | |
| 1141 | 1140 | }; |
| 1142 | 1141 | |
| 1143 | 1142 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); |
| ... | ... | @@ -1194,7 +1193,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1194 | 1193 | } |
| 1195 | 1194 | |
| 1196 | 1195 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1197 | break :blk raw_reg.to32(); | |
| 1196 | break :blk self.registerAlias(raw_reg, operand_ty); | |
| 1198 | 1197 | }; |
| 1199 | 1198 | |
| 1200 | 1199 | _ = try self.addInst(.{ |
| ... | ... | @@ -1227,7 +1226,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1227 | 1226 | } |
| 1228 | 1227 | |
| 1229 | 1228 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1230 | break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); | |
| 1229 | break :blk self.registerAlias(raw_reg, operand_ty); | |
| 1231 | 1230 | }; |
| 1232 | 1231 | |
| 1233 | 1232 | _ = try self.addInst(.{ |
| ... | ... | @@ -1307,8 +1306,8 @@ fn binOpRegister( |
| 1307 | 1306 | const lhs_is_register = lhs == .register; |
| 1308 | 1307 | const rhs_is_register = rhs == .register; |
| 1309 | 1308 | |
| 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.*))); | |
| 1309 | if (lhs_is_register) assert(lhs.register == self.registerAlias(lhs.register, lhs_ty)); | |
| 1310 | if (rhs_is_register) assert(rhs.register == self.registerAlias(rhs.register, rhs_ty)); | |
| 1312 | 1311 | |
| 1313 | 1312 | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 1314 | 1313 | self.register_manager.lockReg(lhs.register) |
| ... | ... | @@ -1330,7 +1329,7 @@ fn binOpRegister( |
| 1330 | 1329 | } else null; |
| 1331 | 1330 | |
| 1332 | 1331 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1333 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1332 | const reg = self.registerAlias(raw_reg, lhs_ty); | |
| 1334 | 1333 | |
| 1335 | 1334 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1336 | 1335 | |
| ... | ... | @@ -1344,7 +1343,7 @@ fn binOpRegister( |
| 1344 | 1343 | // order to guarantee that registers will have equal sizes, we |
| 1345 | 1344 | // use the register alias of rhs corresponding to the size of |
| 1346 | 1345 | // lhs. |
| 1347 | registerAlias(rhs.register, lhs_ty.abiSize(self.target.*)) | |
| 1346 | self.registerAlias(rhs.register, lhs_ty) | |
| 1348 | 1347 | else blk: { |
| 1349 | 1348 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1350 | 1349 | break :inst Air.refToIndex(md.rhs).?; |
| ... | ... | @@ -1354,7 +1353,7 @@ fn binOpRegister( |
| 1354 | 1353 | |
| 1355 | 1354 | // Here, we deliberately use lhs as lhs and rhs may differ in |
| 1356 | 1355 | // the case of shifts. See comment above. |
| 1357 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1356 | const reg = self.registerAlias(raw_reg, lhs_ty); | |
| 1358 | 1357 | |
| 1359 | 1358 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1360 | 1359 | |
| ... | ... | @@ -1372,11 +1371,11 @@ fn binOpRegister( |
| 1372 | 1371 | break :blk rhs_reg; |
| 1373 | 1372 | } else { |
| 1374 | 1373 | const raw_reg = try self.register_manager.allocReg(md.inst, gp); |
| 1375 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1374 | break :blk self.registerAlias(raw_reg, lhs_ty); | |
| 1376 | 1375 | } |
| 1377 | 1376 | } else blk: { |
| 1378 | 1377 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1379 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1378 | break :blk self.registerAlias(raw_reg, lhs_ty); | |
| 1380 | 1379 | }, |
| 1381 | 1380 | }; |
| 1382 | 1381 | |
| ... | ... | @@ -1415,7 +1414,7 @@ fn binOpRegister( |
| 1415 | 1414 | .smull, |
| 1416 | 1415 | .umull, |
| 1417 | 1416 | => .{ .rrr = .{ |
| 1418 | .rd = dest_reg.to64(), | |
| 1417 | .rd = dest_reg.toX(), | |
| 1419 | 1418 | .rn = lhs_reg, |
| 1420 | 1419 | .rm = rhs_reg, |
| 1421 | 1420 | } }, |
| ... | ... | @@ -1463,7 +1462,7 @@ fn binOpImmediate( |
| 1463 | 1462 | ) !MCValue { |
| 1464 | 1463 | const lhs_is_register = lhs == .register; |
| 1465 | 1464 | |
| 1466 | if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*))); | |
| 1465 | if (lhs_is_register) assert(lhs.register == self.registerAlias(lhs.register, lhs_ty)); | |
| 1467 | 1466 | |
| 1468 | 1467 | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 1469 | 1468 | self.register_manager.lockReg(lhs.register) |
| ... | ... | @@ -1481,7 +1480,7 @@ fn binOpImmediate( |
| 1481 | 1480 | } else null; |
| 1482 | 1481 | |
| 1483 | 1482 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1484 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1483 | const reg = self.registerAlias(raw_reg, lhs_ty); | |
| 1485 | 1484 | |
| 1486 | 1485 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1487 | 1486 | |
| ... | ... | @@ -1502,11 +1501,11 @@ fn binOpImmediate( |
| 1502 | 1501 | break :blk lhs_reg; |
| 1503 | 1502 | } else { |
| 1504 | 1503 | const raw_reg = try self.register_manager.allocReg(md.inst, gp); |
| 1505 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1504 | break :blk self.registerAlias(raw_reg, lhs_ty); | |
| 1506 | 1505 | } |
| 1507 | 1506 | } else blk: { |
| 1508 | 1507 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1509 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1508 | break :blk self.registerAlias(raw_reg, lhs_ty); | |
| 1510 | 1509 | }, |
| 1511 | 1510 | }; |
| 1512 | 1511 | |
| ... | ... | @@ -1719,7 +1718,7 @@ fn binOp( |
| 1719 | 1718 | } else null; |
| 1720 | 1719 | |
| 1721 | 1720 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1722 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 1721 | const reg = self.registerAlias(raw_reg, lhs_ty); | |
| 1723 | 1722 | |
| 1724 | 1723 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1725 | 1724 | |
| ... | ... | @@ -1734,7 +1733,7 @@ fn binOp( |
| 1734 | 1733 | } else null; |
| 1735 | 1734 | |
| 1736 | 1735 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1737 | const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); | |
| 1736 | const reg = self.registerAlias(raw_reg, rhs_ty); | |
| 1738 | 1737 | |
| 1739 | 1738 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1740 | 1739 | |
| ... | ... | @@ -1745,10 +1744,9 @@ fn binOp( |
| 1745 | 1744 | |
| 1746 | 1745 | const dest_regs: [2]Register = blk: { |
| 1747 | 1746 | const raw_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); |
| 1748 | const abi_size = lhs_ty.abiSize(self.target.*); | |
| 1749 | 1747 | break :blk .{ |
| 1750 | registerAlias(raw_regs[0], abi_size), | |
| 1751 | registerAlias(raw_regs[1], abi_size), | |
| 1748 | self.registerAlias(raw_regs[0], lhs_ty), | |
| 1749 | self.registerAlias(raw_regs[1], lhs_ty), | |
| 1752 | 1750 | }; |
| 1753 | 1751 | }; |
| 1754 | 1752 | const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs); |
| ... | ... | @@ -2067,7 +2065,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2067 | 2065 | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2068 | 2066 | |
| 2069 | 2067 | const raw_truncated_reg = try self.register_manager.allocReg(null, gp); |
| 2070 | const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); | |
| 2068 | const truncated_reg = self.registerAlias(raw_truncated_reg, lhs_ty); | |
| 2071 | 2069 | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); |
| 2072 | 2070 | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 2073 | 2071 | |
| ... | ... | @@ -2186,8 +2184,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2186 | 2184 | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 2187 | 2185 | |
| 2188 | 2186 | try self.truncRegister( |
| 2189 | dest_reg.to32(), | |
| 2190 | truncated_reg.to32(), | |
| 2187 | dest_reg.toW(), | |
| 2188 | truncated_reg.toW(), | |
| 2191 | 2189 | int_info.signedness, |
| 2192 | 2190 | int_info.bits, |
| 2193 | 2191 | ); |
| ... | ... | @@ -2197,8 +2195,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2197 | 2195 | _ = try self.addInst(.{ |
| 2198 | 2196 | .tag = .cmp_extended_register, |
| 2199 | 2197 | .data = .{ .rr_extend_shift = .{ |
| 2200 | .rn = dest_reg.to64(), | |
| 2201 | .rm = truncated_reg.to32(), | |
| 2198 | .rn = dest_reg.toX(), | |
| 2199 | .rm = truncated_reg.toW(), | |
| 2202 | 2200 | .ext_type = .sxtw, |
| 2203 | 2201 | .imm3 = 0, |
| 2204 | 2202 | } }, |
| ... | ... | @@ -2208,8 +2206,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2208 | 2206 | _ = try self.addInst(.{ |
| 2209 | 2207 | .tag = .cmp_extended_register, |
| 2210 | 2208 | .data = .{ .rr_extend_shift = .{ |
| 2211 | .rn = dest_reg.to64(), | |
| 2212 | .rm = truncated_reg.to32(), | |
| 2209 | .rn = dest_reg.toX(), | |
| 2210 | .rm = truncated_reg.toW(), | |
| 2213 | 2211 | .ext_type = .uxtw, |
| 2214 | 2212 | .imm3 = 0, |
| 2215 | 2213 | } }, |
| ... | ... | @@ -2245,7 +2243,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2245 | 2243 | |
| 2246 | 2244 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 2247 | 2245 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2248 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 2246 | const reg = self.registerAlias(raw_reg, lhs_ty); | |
| 2249 | 2247 | break :blk reg; |
| 2250 | 2248 | }; |
| 2251 | 2249 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| ... | ... | @@ -2253,7 +2251,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2253 | 2251 | |
| 2254 | 2252 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 2255 | 2253 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2256 | const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); | |
| 2254 | const reg = self.registerAlias(raw_reg, rhs_ty); | |
| 2257 | 2255 | break :blk reg; |
| 2258 | 2256 | }; |
| 2259 | 2257 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); |
| ... | ... | @@ -2264,7 +2262,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2264 | 2262 | |
| 2265 | 2263 | const dest_reg = blk: { |
| 2266 | 2264 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2267 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | |
| 2265 | const reg = self.registerAlias(raw_reg, lhs_ty); | |
| 2268 | 2266 | break :blk reg; |
| 2269 | 2267 | }; |
| 2270 | 2268 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| ... | ... | @@ -2853,7 +2851,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 2853 | 2851 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2854 | 2852 | } |
| 2855 | 2853 | |
| 2856 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { | |
| 2854 | fn reuseOperand( | |
| 2855 | self: *Self, | |
| 2856 | inst: Air.Inst.Index, | |
| 2857 | operand: Air.Inst.Ref, | |
| 2858 | op_index: Liveness.OperandInt, | |
| 2859 | mcv: MCValue, | |
| 2860 | ) bool { | |
| 2857 | 2861 | if (!self.liveness.operandDies(inst, op_index)) |
| 2858 | 2862 | return false; |
| 2859 | 2863 | |
| ... | ... | @@ -2912,7 +2916,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2912 | 2916 | .stack_offset => |off| { |
| 2913 | 2917 | if (elem_size <= 8) { |
| 2914 | 2918 | const raw_tmp_reg = try self.register_manager.allocReg(null, gp); |
| 2915 | const tmp_reg = registerAlias(raw_tmp_reg, elem_size); | |
| 2919 | const tmp_reg = self.registerAlias(raw_tmp_reg, elem_ty); | |
| 2916 | 2920 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 2917 | 2921 | defer self.register_manager.unlockReg(tmp_reg_lock); |
| 2918 | 2922 | |
| ... | ... | @@ -3050,7 +3054,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3050 | 3054 | if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { |
| 3051 | 3055 | // The MCValue that holds the pointer can be re-used as the value. |
| 3052 | 3056 | break :blk switch (ptr) { |
| 3053 | .register => |r| MCValue{ .register = registerAlias(r, elem_size) }, | |
| 3057 | .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) }, | |
| 3054 | 3058 | else => ptr, |
| 3055 | 3059 | }; |
| 3056 | 3060 | } else { |
| ... | ... | @@ -3136,7 +3140,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3136 | 3140 | else => { |
| 3137 | 3141 | if (abi_size <= 8) { |
| 3138 | 3142 | const raw_tmp_reg = try self.register_manager.allocReg(null, gp); |
| 3139 | const tmp_reg = registerAlias(raw_tmp_reg, abi_size); | |
| 3143 | const tmp_reg = self.registerAlias(raw_tmp_reg, value_ty); | |
| 3140 | 3144 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 3141 | 3145 | defer self.register_manager.unlockReg(tmp_reg_lock); |
| 3142 | 3146 | |
| ... | ... | @@ -3295,7 +3299,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3295 | 3299 | } else { |
| 3296 | 3300 | // Copy to new register |
| 3297 | 3301 | 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.*)); | |
| 3302 | const dest_reg = self.registerAlias(raw_dest_reg, struct_field_ty); | |
| 3299 | 3303 | try self.genSetReg(struct_field_ty, dest_reg, field); |
| 3300 | 3304 | |
| 3301 | 3305 | break :result MCValue{ .register = dest_reg }; |
| ... | ... | @@ -3410,9 +3414,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3410 | 3414 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| 3411 | 3415 | const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align); |
| 3412 | 3416 | |
| 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); | |
| 3417 | const ret_ptr_reg = self.registerAlias(.x0, Type.usize); | |
| 3416 | 3418 | |
| 3417 | 3419 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 3418 | 3420 | .base = .{ .tag = .single_mut_pointer }, |
| ... | ... | @@ -4376,7 +4378,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4376 | 4378 | 4, 8 => .str_stack, |
| 4377 | 4379 | else => unreachable, // unexpected abi size |
| 4378 | 4380 | }; |
| 4379 | const rt = registerAlias(reg, abi_size); | |
| 4381 | const rt = self.registerAlias(reg, ty); | |
| 4380 | 4382 | |
| 4381 | 4383 | _ = try self.addInst(.{ |
| 4382 | 4384 | .tag = tag, |
| ... | ... | @@ -4399,10 +4401,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4399 | 4401 | const overflow_bit_ty = ty.structFieldType(1); |
| 4400 | 4402 | const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); |
| 4401 | 4403 | 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 | ); | |
| 4404 | const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty); | |
| 4406 | 4405 | |
| 4407 | 4406 | _ = try self.addInst(.{ |
| 4408 | 4407 | .tag = .cset, |
| ... | ... | @@ -4599,8 +4598,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4599 | 4598 | .memory => |addr| { |
| 4600 | 4599 | // The value is in memory at a hard-coded address. |
| 4601 | 4600 | // 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); | |
| 4601 | try self.genSetReg(ty, reg.toX(), .{ .immediate = addr }); | |
| 4602 | try self.genLdrRegister(reg, reg.toX(), ty); | |
| 4604 | 4603 | }, |
| 4605 | 4604 | .stack_offset => |off| { |
| 4606 | 4605 | const abi_size = ty.abiSize(self.target.*); |
| ... | ... | @@ -4679,7 +4678,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4679 | 4678 | 4, 8 => .str_immediate, |
| 4680 | 4679 | else => unreachable, // unexpected abi size |
| 4681 | 4680 | }; |
| 4682 | const rt = registerAlias(reg, abi_size); | |
| 4681 | const rt = self.registerAlias(reg, ty); | |
| 4683 | 4682 | const offset = switch (abi_size) { |
| 4684 | 4683 | 1 => blk: { |
| 4685 | 4684 | if (math.cast(u12, stack_offset)) |imm| { |
| ... | ... | @@ -5300,7 +5299,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5300 | 5299 | assert(ret_ty.isError()); |
| 5301 | 5300 | result.return_value = .{ .immediate = 0 }; |
| 5302 | 5301 | } else if (ret_ty_size <= 8) { |
| 5303 | result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; | |
| 5302 | result.return_value = .{ .register = self.registerAlias(c_abi_int_return_regs[0], ret_ty) }; | |
| 5304 | 5303 | } else { |
| 5305 | 5304 | return self.fail("TODO support more return types for ARM backend", .{}); |
| 5306 | 5305 | } |
| ... | ... | @@ -5322,7 +5321,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5322 | 5321 | |
| 5323 | 5322 | if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { |
| 5324 | 5323 | if (param_size <= 8) { |
| 5325 | result.args[i] = .{ .register = registerAlias(c_abi_int_param_regs[ncrn], param_size) }; | |
| 5324 | result.args[i] = .{ .register = self.registerAlias(c_abi_int_param_regs[ncrn], ty) }; | |
| 5326 | 5325 | ncrn += 1; |
| 5327 | 5326 | } else { |
| 5328 | 5327 | return self.fail("TODO MCValues with multiple registers", .{}); |
| ... | ... | @@ -5358,7 +5357,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5358 | 5357 | assert(ret_ty.isError()); |
| 5359 | 5358 | result.return_value = .{ .immediate = 0 }; |
| 5360 | 5359 | } else if (ret_ty_size <= 8) { |
| 5361 | result.return_value = .{ .register = registerAlias(.x0, ret_ty_size) }; | |
| 5360 | result.return_value = .{ .register = self.registerAlias(.x0, ret_ty) }; | |
| 5362 | 5361 | } else { |
| 5363 | 5362 | // The result is returned by reference, not by |
| 5364 | 5363 | // value. This means that x0 (or w0 when pointer |
| ... | ... | @@ -5424,14 +5423,30 @@ fn parseRegName(name: []const u8) ?Register { |
| 5424 | 5423 | return std.meta.stringToEnum(Register, name); |
| 5425 | 5424 | } |
| 5426 | 5425 | |
| 5427 | fn 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 | |
| 5426 | fn registerAlias(self: *Self, reg: Register, ty: Type) Register { | |
| 5427 | const abi_size = ty.abiSize(self.target.*); | |
| 5428 | ||
| 5429 | switch (reg.class()) { | |
| 5430 | .general_purpose => { | |
| 5431 | if (abi_size == 0) { | |
| 5432 | unreachable; // should be comptime-known | |
| 5433 | } else if (abi_size <= 4) { | |
| 5434 | return reg.toW(); | |
| 5435 | } else if (abi_size <= 8) { | |
| 5436 | return reg.toX(); | |
| 5437 | } else unreachable; | |
| 5438 | }, | |
| 5439 | .stack_pointer => unreachable, // we can't store/load the sp | |
| 5440 | .floating_point => { | |
| 5441 | return switch (ty.floatBits(self.target.*)) { | |
| 5442 | 16 => reg.toH(), | |
| 5443 | 32 => reg.toS(), | |
| 5444 | 64 => reg.toD(), | |
| 5445 | 128 => reg.toQ(), | |
| 5446 | ||
| 5447 | 80 => unreachable, // f80 registers don't exist | |
| 5448 | else => unreachable, | |
| 5449 | }; | |
| 5450 | }, | |
| 5436 | 5451 | } |
| 5437 | 5452 | } |
src/arch/aarch64/Emit.zig+4-4| ... | ... | @@ -842,14 +842,14 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 842 | 842 | // PC-relative displacement to the entry in memory. |
| 843 | 843 | // adrp |
| 844 | 844 | const offset = @intCast(u32, emit.code.items.len); |
| 845 | try emit.writeInstruction(Instruction.adrp(reg.to64(), 0)); | |
| 845 | try emit.writeInstruction(Instruction.adrp(reg.toX(), 0)); | |
| 846 | 846 | |
| 847 | 847 | switch (tag) { |
| 848 | 848 | .load_memory_got => { |
| 849 | 849 | // ldr reg, reg, offset |
| 850 | 850 | try emit.writeInstruction(Instruction.ldr( |
| 851 | 851 | reg, |
| 852 | reg.to64(), | |
| 852 | reg.toX(), | |
| 853 | 853 | Instruction.LoadStoreOffset.imm(0), |
| 854 | 854 | )); |
| 855 | 855 | }, |
| ... | ... | @@ -863,11 +863,11 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 863 | 863 | // Note that this can potentially be optimised out by the codegen/linker if the |
| 864 | 864 | // target address is appropriately aligned. |
| 865 | 865 | // add reg, reg, offset |
| 866 | try emit.writeInstruction(Instruction.add(reg.to64(), reg.to64(), 0, false)); | |
| 866 | try emit.writeInstruction(Instruction.add(reg.toX(), reg.toX(), 0, false)); | |
| 867 | 867 | // ldr reg, reg, offset |
| 868 | 868 | try emit.writeInstruction(Instruction.ldr( |
| 869 | 869 | reg, |
| 870 | reg.to64(), | |
| 870 | reg.toX(), | |
| 871 | 871 | Instruction.LoadStoreOffset.imm(0), |
| 872 | 872 | )); |
| 873 | 873 | }, |
src/arch/aarch64/bits.zig+250-139| ... | ... | @@ -4,17 +4,22 @@ const DW = std.dwarf; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | |
| 7 | // zig fmt: off | |
| 7 | pub const RegisterClass = enum { | |
| 8 | general_purpose, | |
| 9 | stack_pointer, | |
| 10 | floating_point, | |
| 11 | }; | |
| 8 | 12 | |
| 9 | 13 | /// General purpose registers in the AArch64 instruction set |
| 10 | pub const Register = enum(u7) { | |
| 11 | // 64-bit registers | |
| 14 | pub const Register = enum(u8) { | |
| 15 | // zig fmt: off | |
| 16 | // 64-bit general-purpose registers | |
| 12 | 17 | x0, x1, x2, x3, x4, x5, x6, x7, |
| 13 | 18 | x8, x9, x10, x11, x12, x13, x14, x15, |
| 14 | 19 | x16, x17, x18, x19, x20, x21, x22, x23, |
| 15 | 20 | x24, x25, x26, x27, x28, x29, x30, xzr, |
| 16 | 21 | |
| 17 | // 32-bit registers | |
| 22 | // 32-bit general-purpose registers | |
| 18 | 23 | w0, w1, w2, w3, w4, w5, w6, w7, |
| 19 | 24 | w8, w9, w10, w11, w12, w13, w14, w15, |
| 20 | 25 | w16, w17, w18, w19, w20, w21, w22, w23, |
| ... | ... | @@ -23,192 +28,298 @@ pub const Register = enum(u7) { |
| 23 | 28 | // Stack pointer |
| 24 | 29 | sp, wsp, |
| 25 | 30 | |
| 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 | ||
| 82 | test "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 | ||
| 93 | test "Register.size" { | |
| 94 | try testing.expectEqual(@as(u7, 64), Register.x19.size()); | |
| 95 | try testing.expectEqual(@as(u7, 32), Register.w3.size()); | |
| 96 | } | |
| 97 | ||
| 98 | test "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 | |
| 109 | pub const FloatingPointRegister = enum(u8) { | |
| 110 | // 128-bit registers | |
| 31 | // 128-bit floating-point registers | |
| 111 | 32 | q0, q1, q2, q3, q4, q5, q6, q7, |
| 112 | 33 | q8, q9, q10, q11, q12, q13, q14, q15, |
| 113 | 34 | q16, q17, q18, q19, q20, q21, q22, q23, |
| 114 | 35 | q24, q25, q26, q27, q28, q29, q30, q31, |
| 115 | 36 | |
| 116 | // 64-bit registers | |
| 37 | // 64-bit floating-point registers | |
| 117 | 38 | d0, d1, d2, d3, d4, d5, d6, d7, |
| 118 | 39 | d8, d9, d10, d11, d12, d13, d14, d15, |
| 119 | 40 | d16, d17, d18, d19, d20, d21, d22, d23, |
| 120 | 41 | d24, d25, d26, d27, d28, d29, d30, d31, |
| 121 | 42 | |
| 122 | // 32-bit registers | |
| 43 | // 32-bit floating-point registers | |
| 123 | 44 | s0, s1, s2, s3, s4, s5, s6, s7, |
| 124 | 45 | s8, s9, s10, s11, s12, s13, s14, s15, |
| 125 | 46 | s16, s17, s18, s19, s20, s21, s22, s23, |
| 126 | 47 | s24, s25, s26, s27, s28, s29, s30, s31, |
| 127 | 48 | |
| 128 | // 16-bit registers | |
| 49 | // 16-bit floating-point registers | |
| 129 | 50 | h0, h1, h2, h3, h4, h5, h6, h7, |
| 130 | 51 | h8, h9, h10, h11, h12, h13, h14, h15, |
| 131 | 52 | h16, h17, h18, h19, h20, h21, h22, h23, |
| 132 | 53 | h24, h25, h26, h27, h28, h29, h30, h31, |
| 133 | 54 | |
| 134 | // 8-bit registers | |
| 55 | // 8-bit floating-point registers | |
| 135 | 56 | b0, b1, b2, b3, b4, b5, b6, b7, |
| 136 | 57 | b8, b9, b10, b11, b12, b13, b14, b15, |
| 137 | 58 | b16, b17, b18, b19, b20, b21, b22, b23, |
| 138 | 59 | b24, b25, b26, b27, b28, b29, b30, b31, |
| 60 | // zig fmt: on | |
| 139 | 61 | |
| 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 | }; | |
| 142 | 111 | } |
| 143 | 112 | |
| 144 | 113 | /// Returns the bit-width of the register. |
| 145 | pub fn size(self: FloatingPointRegister) u8 { | |
| 114 | pub fn size(self: Register) u8 { | |
| 115 | return switch (@enumToInt(self)) { | |
| 116 | @enumToInt(Register.x0)...@enumToInt(Register.xzr) => 64, | |
| 117 | @enumToInt(Register.w0)...@enumToInt(Register.wzr) => 32, | |
| 118 | ||
| 119 | @enumToInt(Register.sp) => 64, | |
| 120 | @enumToInt(Register.wsp) => 32, | |
| 121 | ||
| 122 | @enumToInt(Register.q0)...@enumToInt(Register.q31) => 128, | |
| 123 | @enumToInt(Register.d0)...@enumToInt(Register.d31) => 64, | |
| 124 | @enumToInt(Register.s0)...@enumToInt(Register.s31) => 32, | |
| 125 | @enumToInt(Register.h0)...@enumToInt(Register.h31) => 16, | |
| 126 | @enumToInt(Register.b0)...@enumToInt(Register.b31) => 8, | |
| 127 | else => unreachable, | |
| 128 | }; | |
| 129 | } | |
| 130 | ||
| 131 | /// Convert from a general-purpose register to its 64 bit alias. | |
| 132 | pub fn toX(self: Register) Register { | |
| 133 | return switch (@enumToInt(self)) { | |
| 134 | @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intToEnum( | |
| 135 | Register, | |
| 136 | @enumToInt(self) - @enumToInt(Register.x0) + @enumToInt(Register.x0), | |
| 137 | ), | |
| 138 | @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intToEnum( | |
| 139 | Register, | |
| 140 | @enumToInt(self) - @enumToInt(Register.w0) + @enumToInt(Register.x0), | |
| 141 | ), | |
| 142 | else => unreachable, | |
| 143 | }; | |
| 144 | } | |
| 145 | ||
| 146 | /// Convert from a general-purpose register to its 32 bit alias. | |
| 147 | pub fn toW(self: Register) Register { | |
| 146 | 148 | 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 | ), | |
| 152 | 157 | else => unreachable, |
| 153 | 158 | }; |
| 154 | 159 | } |
| 155 | 160 | |
| 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 | }; | |
| 159 | 213 | } |
| 160 | 214 | |
| 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 | }; | |
| 164 | 240 | } |
| 165 | 241 | |
| 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 | }; | |
| 169 | 267 | } |
| 170 | 268 | |
| 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 | }; | |
| 174 | 294 | } |
| 175 | 295 | |
| 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; | |
| 179 | 298 | } |
| 180 | 299 | }; |
| 181 | 300 | |
| 182 | // zig fmt: on | |
| 301 | test "Register.enc" { | |
| 302 | try testing.expectEqual(@as(u5, 0), Register.x0.enc()); | |
| 303 | try testing.expectEqual(@as(u5, 0), Register.w0.enc()); | |
| 183 | 304 | |
| 184 | test "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()); | |
| 190 | 307 | |
| 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()); | |
| 193 | 310 | } |
| 194 | 311 | |
| 195 | test "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()); | |
| 312 | test "Register.size" { | |
| 313 | try testing.expectEqual(@as(u8, 64), Register.x19.size()); | |
| 314 | try testing.expectEqual(@as(u8, 32), Register.w3.size()); | |
| 201 | 315 | } |
| 202 | 316 | |
| 203 | test "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()); | |
| 317 | test "Register.toX/toW" { | |
| 318 | try testing.expectEqual(Register.x0, Register.w0.toX()); | |
| 319 | try testing.expectEqual(Register.x0, Register.x0.toX()); | |
| 207 | 320 | |
| 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()); | |
| 212 | 323 | } |
| 213 | 324 | |
| 214 | 325 | /// Represents an instruction in the AArch64 instruction set |