| author | |
| committer | |
| log | f95a8ddafa53d4cee591c82bd5ed3eb0eac4a51b |
| tree | 36b7429dcfedc02cbed4c310e5f72f868b913882 |
| parent | c78daeb642e742af3ac42bac0468776ccc4cd452 |
| signature |
5 files changed, 224 insertions(+), 53 deletions(-)
src/arch/aarch64/CodeGen.zig+105-17| ... | ... | @@ -939,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 939 | 939 | return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); |
| 940 | 940 | } |
| 941 | 941 | |
| 942 | fn truncRegister( | |
| 943 | self: *Self, | |
| 944 | operand_reg: Register, | |
| 945 | dest_reg: Register, | |
| 946 | int_signedness: std.builtin.Signedness, | |
| 947 | int_bits: u16, | |
| 948 | ) !void { | |
| 949 | switch (int_bits) { | |
| 950 | 1...31, 33...63 => { | |
| 951 | _ = try self.addInst(.{ | |
| 952 | .tag = switch (int_signedness) { | |
| 953 | .signed => .sbfx, | |
| 954 | .unsigned => .ubfx, | |
| 955 | }, | |
| 956 | .data = .{ .rr_lsb_width = .{ | |
| 957 | .rd = dest_reg, | |
| 958 | .rn = operand_reg, | |
| 959 | .lsb = 0, | |
| 960 | .width = @intCast(u6, int_bits), | |
| 961 | } }, | |
| 962 | }); | |
| 963 | }, | |
| 964 | 32, 64 => { | |
| 965 | _ = try self.addInst(.{ | |
| 966 | .tag = .mov_register, | |
| 967 | .data = .{ .rr = .{ | |
| 968 | .rd = dest_reg, | |
| 969 | .rn = operand_reg, | |
| 970 | } }, | |
| 971 | }); | |
| 972 | }, | |
| 973 | else => unreachable, | |
| 974 | } | |
| 975 | } | |
| 976 | ||
| 977 | fn trunc( | |
| 978 | self: *Self, | |
| 979 | maybe_inst: ?Air.Inst.Index, | |
| 980 | operand: MCValue, | |
| 981 | operand_ty: Type, | |
| 982 | dest_ty: Type, | |
| 983 | ) !MCValue { | |
| 984 | const info_a = operand_ty.intInfo(self.target.*); | |
| 985 | const info_b = dest_ty.intInfo(self.target.*); | |
| 986 | ||
| 987 | if (info_b.bits <= 64) { | |
| 988 | const operand_reg = switch (operand) { | |
| 989 | .register => |r| r, | |
| 990 | else => operand_reg: { | |
| 991 | if (info_a.bits <= 64) { | |
| 992 | const raw_reg = try self.copyToTmpRegister(operand_ty, operand); | |
| 993 | break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); | |
| 994 | } else { | |
| 995 | return self.fail("TODO load least significant word into register", .{}); | |
| 996 | } | |
| 997 | }, | |
| 998 | }; | |
| 999 | self.register_manager.freezeRegs(&.{operand_reg}); | |
| 1000 | defer self.register_manager.unfreezeRegs(&.{operand_reg}); | |
| 1001 | ||
| 1002 | const dest_reg = if (maybe_inst) |inst| blk: { | |
| 1003 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1004 | ||
| 1005 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | |
| 1006 | break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); | |
| 1007 | } else { | |
| 1008 | const raw_reg = try self.register_manager.allocReg(inst); | |
| 1009 | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); | |
| 1010 | } | |
| 1011 | } else blk: { | |
| 1012 | const raw_reg = try self.register_manager.allocReg(null); | |
| 1013 | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); | |
| 1014 | }; | |
| 1015 | ||
| 1016 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); | |
| 1017 | ||
| 1018 | return MCValue{ .register = dest_reg }; | |
| 1019 | } else { | |
| 1020 | return self.fail("TODO: truncate to ints > 32 bits", .{}); | |
| 1021 | } | |
| 1022 | } | |
| 1023 | ||
| 942 | 1024 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 943 | 1025 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 944 | if (self.liveness.isUnused(inst)) | |
| 945 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); | |
| 946 | ||
| 947 | 1026 | const operand = try self.resolveInst(ty_op.operand); |
| 948 | _ = operand; | |
| 949 | return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}); | |
| 1027 | const operand_ty = self.air.typeOf(ty_op.operand); | |
| 1028 | const dest_ty = self.air.typeOfIndex(inst); | |
| 1029 | ||
| 1030 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { | |
| 1031 | break :blk try self.trunc(inst, operand, operand_ty, dest_ty); | |
| 1032 | }; | |
| 1033 | ||
| 1034 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 950 | 1035 | } |
| 951 | 1036 | |
| 952 | 1037 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3483,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3483 | 3568 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, |
| 3484 | 3569 | }); |
| 3485 | 3570 | |
| 3486 | if (x > math.maxInt(u16)) { | |
| 3571 | if (x & 0x0000_0000_ffff_0000 != 0) { | |
| 3487 | 3572 | _ = try self.addInst(.{ |
| 3488 | 3573 | .tag = .movk, |
| 3489 | 3574 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, |
| 3490 | 3575 | }); |
| 3491 | 3576 | } |
| 3492 | if (x > math.maxInt(u32)) { | |
| 3493 | _ = try self.addInst(.{ | |
| 3494 | .tag = .movk, | |
| 3495 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, | |
| 3496 | }); | |
| 3497 | } | |
| 3498 | if (x > math.maxInt(u48)) { | |
| 3499 | _ = try self.addInst(.{ | |
| 3500 | .tag = .movk, | |
| 3501 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, | |
| 3502 | }); | |
| 3577 | ||
| 3578 | if (reg.size() == 64) { | |
| 3579 | if (x & 0x0000_ffff_0000_0000 != 0) { | |
| 3580 | _ = try self.addInst(.{ | |
| 3581 | .tag = .movk, | |
| 3582 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, | |
| 3583 | }); | |
| 3584 | } | |
| 3585 | if (x & 0xffff_0000_0000_0000 != 0) { | |
| 3586 | _ = try self.addInst(.{ | |
| 3587 | .tag = .movk, | |
| 3588 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, | |
| 3589 | }); | |
| 3590 | } | |
| 3503 | 3591 | } |
| 3504 | 3592 | }, |
| 3505 | 3593 | .register => |src_reg| { |
src/arch/aarch64/Emit.zig+40| ... | ... | @@ -162,6 +162,17 @@ pub fn emitMir( |
| 162 | 162 | |
| 163 | 163 | .push_regs => try emit.mirPushPopRegs(inst), |
| 164 | 164 | .pop_regs => try emit.mirPushPopRegs(inst), |
| 165 | ||
| 166 | .sbfx, | |
| 167 | .ubfx, | |
| 168 | => try emit.mirBitfieldExtract(inst), | |
| 169 | ||
| 170 | .sxtb, | |
| 171 | .sxth, | |
| 172 | .sxtw, | |
| 173 | .uxtb, | |
| 174 | .uxth, | |
| 175 | => try emit.mirExtend(inst), | |
| 165 | 176 | } |
| 166 | 177 | } |
| 167 | 178 | } |
| ... | ... | @@ -1050,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 1050 | 1061 | else => unreachable, |
| 1051 | 1062 | } |
| 1052 | 1063 | } |
| 1064 | ||
| 1065 | fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 1066 | const tag = emit.mir.instructions.items(.tag)[inst]; | |
| 1067 | const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; | |
| 1068 | const rd = rr_lsb_width.rd; | |
| 1069 | const rn = rr_lsb_width.rn; | |
| 1070 | const lsb = rr_lsb_width.lsb; | |
| 1071 | const width = rr_lsb_width.width; | |
| 1072 | ||
| 1073 | switch (tag) { | |
| 1074 | .sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)), | |
| 1075 | .ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)), | |
| 1076 | else => unreachable, | |
| 1077 | } | |
| 1078 | } | |
| 1079 | ||
| 1080 | fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 1081 | const tag = emit.mir.instructions.items(.tag)[inst]; | |
| 1082 | const rr = emit.mir.instructions.items(.data)[inst].rr; | |
| 1083 | ||
| 1084 | switch (tag) { | |
| 1085 | .sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)), | |
| 1086 | .sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)), | |
| 1087 | .sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)), | |
| 1088 | .uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)), | |
| 1089 | .uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)), | |
| 1090 | else => unreachable, | |
| 1091 | } | |
| 1092 | } |
src/arch/aarch64/Mir.zig+31-7| ... | ... | @@ -130,6 +130,14 @@ pub const Inst = struct { |
| 130 | 130 | push_regs, |
| 131 | 131 | /// Return from subroutine |
| 132 | 132 | ret, |
| 133 | /// Signed bitfield extract | |
| 134 | sbfx, | |
| 135 | /// Signed extend byte | |
| 136 | sxtb, | |
| 137 | /// Signed extend halfword | |
| 138 | sxth, | |
| 139 | /// Signed extend word | |
| 140 | sxtw, | |
| 133 | 141 | /// Store Pair of Registers |
| 134 | 142 | stp, |
| 135 | 143 | /// Pseudo-instruction: Store to stack |
| ... | ... | @@ -156,6 +164,12 @@ pub const Inst = struct { |
| 156 | 164 | sub_shifted_register, |
| 157 | 165 | /// Supervisor Call |
| 158 | 166 | svc, |
| 167 | /// Unsigned bitfield extract | |
| 168 | ubfx, | |
| 169 | /// Unsigned extend byte | |
| 170 | uxtb, | |
| 171 | /// Unsigned extend halfword | |
| 172 | uxth, | |
| 159 | 173 | }; |
| 160 | 174 | |
| 161 | 175 | /// The position of an MIR instruction within the `Mir` instructions array. |
| ... | ... | @@ -225,13 +239,6 @@ pub const Inst = struct { |
| 225 | 239 | rt: Register, |
| 226 | 240 | inst: Index, |
| 227 | 241 | }, |
| 228 | /// Two registers | |
| 229 | /// | |
| 230 | /// Used by e.g. mov_register | |
| 231 | rr: struct { | |
| 232 | rd: Register, | |
| 233 | rn: Register, | |
| 234 | }, | |
| 235 | 242 | /// A register, an unsigned 12-bit immediate, and an optional shift |
| 236 | 243 | /// |
| 237 | 244 | /// Used by e.g. cmp_immediate |
| ... | ... | @@ -240,6 +247,13 @@ pub const Inst = struct { |
| 240 | 247 | imm12: u12, |
| 241 | 248 | sh: u1 = 0, |
| 242 | 249 | }, |
| 250 | /// Two registers | |
| 251 | /// | |
| 252 | /// Used by e.g. mov_register | |
| 253 | rr: struct { | |
| 254 | rd: Register, | |
| 255 | rn: Register, | |
| 256 | }, | |
| 243 | 257 | /// Two registers, an unsigned 12-bit immediate, and an optional shift |
| 244 | 258 | /// |
| 245 | 259 | /// Used by e.g. sub_immediate |
| ... | ... | @@ -268,6 +282,16 @@ pub const Inst = struct { |
| 268 | 282 | imm6: u6, |
| 269 | 283 | shift: bits.Instruction.LogicalShiftedRegisterShift, |
| 270 | 284 | }, |
| 285 | /// Two registers and a lsb (range 0-63) and a width (range | |
| 286 | /// 1-64) | |
| 287 | /// | |
| 288 | /// Used by e.g. ubfx | |
| 289 | rr_lsb_width: struct { | |
| 290 | rd: Register, | |
| 291 | rn: Register, | |
| 292 | lsb: u6, | |
| 293 | width: u7, | |
| 294 | }, | |
| 271 | 295 | /// Two registers and a bitmask immediate |
| 272 | 296 | /// |
| 273 | 297 | /// Used by e.g. eor_immediate |
src/arch/aarch64/bits.zig+48-27| ... | ... | @@ -510,33 +510,23 @@ pub const Instruction = union(enum) { |
| 510 | 510 | imm16: u16, |
| 511 | 511 | shift: u6, |
| 512 | 512 | ) Instruction { |
| 513 | switch (rd.size()) { | |
| 514 | 32 => { | |
| 515 | assert(shift % 16 == 0 and shift <= 16); | |
| 516 | return Instruction{ | |
| 517 | .move_wide_immediate = .{ | |
| 518 | .rd = rd.enc(), | |
| 519 | .imm16 = imm16, | |
| 520 | .hw = @intCast(u2, shift / 16), | |
| 521 | .opc = opc, | |
| 522 | .sf = 0, | |
| 523 | }, | |
| 524 | }; | |
| 525 | }, | |
| 526 | 64 => { | |
| 527 | assert(shift % 16 == 0 and shift <= 48); | |
| 528 | return Instruction{ | |
| 529 | .move_wide_immediate = .{ | |
| 530 | .rd = rd.enc(), | |
| 531 | .imm16 = imm16, | |
| 532 | .hw = @intCast(u2, shift / 16), | |
| 533 | .opc = opc, | |
| 534 | .sf = 1, | |
| 535 | }, | |
| 536 | }; | |
| 513 | assert(shift % 16 == 0); | |
| 514 | assert(!(rd.size() == 32 and shift > 16)); | |
| 515 | assert(!(rd.size() == 64 and shift > 48)); | |
| 516 | ||
| 517 | return Instruction{ | |
| 518 | .move_wide_immediate = .{ | |
| 519 | .rd = rd.enc(), | |
| 520 | .imm16 = imm16, | |
| 521 | .hw = @intCast(u2, shift / 16), | |
| 522 | .opc = opc, | |
| 523 | .sf = switch (rd.size()) { | |
| 524 | 32 => 0, | |
| 525 | 64 => 1, | |
| 526 | else => unreachable, // unexpected register size | |
| 527 | }, | |
| 537 | 528 | }, |
| 538 | else => unreachable, // unexpected register size | |
| 539 | } | |
| 529 | }; | |
| 540 | 530 | } |
| 541 | 531 | |
| 542 | 532 | fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { |
| ... | ... | @@ -914,7 +904,7 @@ pub const Instruction = union(enum) { |
| 914 | 904 | n: u1, |
| 915 | 905 | ) Instruction { |
| 916 | 906 | assert(rd.size() == rn.size()); |
| 917 | assert(!(rd.size() == 32 and n == 1)); | |
| 907 | assert(!(rd.size() == 32 and n != 0)); | |
| 918 | 908 | |
| 919 | 909 | return Instruction{ |
| 920 | 910 | .logical_immediate = .{ |
| ... | ... | @@ -942,6 +932,8 @@ pub const Instruction = union(enum) { |
| 942 | 932 | imms: u6, |
| 943 | 933 | ) Instruction { |
| 944 | 934 | assert(rd.size() == rn.size()); |
| 935 | assert(!(rd.size() == 64 and n != 1)); | |
| 936 | assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0))); | |
| 945 | 937 | |
| 946 | 938 | return Instruction{ |
| 947 | 939 | .bitfield = .{ |
| ... | ... | @@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) { |
| 1417 | 1409 | return sbfm(rd, rn, shift, imms); |
| 1418 | 1410 | } |
| 1419 | 1411 | |
| 1412 | pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { | |
| 1413 | return sbfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); | |
| 1414 | } | |
| 1415 | ||
| 1416 | pub fn sxtb(rd: Register, rn: Register) Instruction { | |
| 1417 | return sbfm(rd, rn, 0, 7); | |
| 1418 | } | |
| 1419 | ||
| 1420 | pub fn sxth(rd: Register, rn: Register) Instruction { | |
| 1421 | return sbfm(rd, rn, 0, 15); | |
| 1422 | } | |
| 1423 | ||
| 1424 | pub fn sxtw(rd: Register, rn: Register) Instruction { | |
| 1425 | assert(rd.size() == 64); | |
| 1426 | return sbfm(rd, rn, 0, 31); | |
| 1427 | } | |
| 1428 | ||
| 1420 | 1429 | pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction { |
| 1421 | 1430 | const size = @intCast(u6, rd.size() - 1); |
| 1422 | 1431 | return ubfm(rd, rn, size - shift + 1, size - shift); |
| ... | ... | @@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) { |
| 1427 | 1436 | return ubfm(rd, rn, shift, imms); |
| 1428 | 1437 | } |
| 1429 | 1438 | |
| 1439 | pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { | |
| 1440 | return ubfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); | |
| 1441 | } | |
| 1442 | ||
| 1443 | pub fn uxtb(rd: Register, rn: Register) Instruction { | |
| 1444 | return ubfm(rd, rn, 0, 7); | |
| 1445 | } | |
| 1446 | ||
| 1447 | pub fn uxth(rd: Register, rn: Register) Instruction { | |
| 1448 | return ubfm(rd, rn, 0, 15); | |
| 1449 | } | |
| 1450 | ||
| 1430 | 1451 | // Add/subtract (shifted register) |
| 1431 | 1452 | |
| 1432 | 1453 | pub fn addShiftedRegister( |
test/behavior/basic.zig-2| ... | ... | @@ -15,8 +15,6 @@ test "empty function with comments" { |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | 17 | test "truncate" { |
| 18 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 19 | ||
| 20 | 18 | try expect(testTruncate(0x10fd) == 0xfd); |
| 21 | 19 | comptime try expect(testTruncate(0x10fd) == 0xfd); |
| 22 | 20 | } |