authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-04-16 18:58:48+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-04-19 22:39:14+02:00
logf95a8ddafa53d4cee591c82bd5ed3eb0eac4a51b
tree36b7429dcfedc02cbed4c310e5f72f868b913882
parentc78daeb642e742af3ac42bac0468776ccc4cd452
signaturelock-open Commit is signed but in an unrecognized format.

stage2 AArch64: Implement basic truncate functionality


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,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
939 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});939 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
940}940}
941941
942fn 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
977fn 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
942fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {1024fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
943 const ty_op = self.air.instructions.items(.data)[inst].ty_op;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 const operand = try self.resolveInst(ty_op.operand);1026 const operand = try self.resolveInst(ty_op.operand);
948 _ = operand;1027 const operand_ty = self.air.typeOf(ty_op.operand);
949 return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});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}
9511036
952fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {1037fn 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,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3483 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },3568 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },
3484 });3569 });
34853570
3486 if (x > math.maxInt(u16)) {3571 if (x & 0x0000_0000_ffff_0000 != 0) {
3487 _ = try self.addInst(.{3572 _ = try self.addInst(.{
3488 .tag = .movk,3573 .tag = .movk,
3489 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },3574 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },
3490 });3575 });
3491 }3576 }
3492 if (x > math.maxInt(u32)) {3577
3493 _ = try self.addInst(.{3578 if (reg.size() == 64) {
3494 .tag = .movk,3579 if (x & 0x0000_ffff_0000_0000 != 0) {
3495 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },3580 _ = try self.addInst(.{
3496 });3581 .tag = .movk,
3497 }3582 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },
3498 if (x > math.maxInt(u48)) {3583 });
3499 _ = try self.addInst(.{3584 }
3500 .tag = .movk,3585 if (x & 0xffff_0000_0000_0000 != 0) {
3501 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },3586 _ = try self.addInst(.{
3502 });3587 .tag = .movk,
3588 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },
3589 });
3590 }
3503 }3591 }
3504 },3592 },
3505 .register => |src_reg| {3593 .register => |src_reg| {
src/arch/aarch64/Emit.zig+40
...@@ -162,6 +162,17 @@ pub fn emitMir(...@@ -162,6 +162,17 @@ pub fn emitMir(
162162
163 .push_regs => try emit.mirPushPopRegs(inst),163 .push_regs => try emit.mirPushPopRegs(inst),
164 .pop_regs => try emit.mirPushPopRegs(inst),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,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
1050 else => unreachable,1061 else => unreachable,
1051 }1062 }
1052}1063}
1064
1065fn 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
1080fn 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,6 +130,14 @@ pub const Inst = struct {
130 push_regs,130 push_regs,
131 /// Return from subroutine131 /// Return from subroutine
132 ret,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 /// Store Pair of Registers141 /// Store Pair of Registers
134 stp,142 stp,
135 /// Pseudo-instruction: Store to stack143 /// Pseudo-instruction: Store to stack
...@@ -156,6 +164,12 @@ pub const Inst = struct {...@@ -156,6 +164,12 @@ pub const Inst = struct {
156 sub_shifted_register,164 sub_shifted_register,
157 /// Supervisor Call165 /// Supervisor Call
158 svc,166 svc,
167 /// Unsigned bitfield extract
168 ubfx,
169 /// Unsigned extend byte
170 uxtb,
171 /// Unsigned extend halfword
172 uxth,
159 };173 };
160174
161 /// The position of an MIR instruction within the `Mir` instructions array.175 /// The position of an MIR instruction within the `Mir` instructions array.
...@@ -225,13 +239,6 @@ pub const Inst = struct {...@@ -225,13 +239,6 @@ pub const Inst = struct {
225 rt: Register,239 rt: Register,
226 inst: Index,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 /// A register, an unsigned 12-bit immediate, and an optional shift242 /// A register, an unsigned 12-bit immediate, and an optional shift
236 ///243 ///
237 /// Used by e.g. cmp_immediate244 /// Used by e.g. cmp_immediate
...@@ -240,6 +247,13 @@ pub const Inst = struct {...@@ -240,6 +247,13 @@ pub const Inst = struct {
240 imm12: u12,247 imm12: u12,
241 sh: u1 = 0,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 /// Two registers, an unsigned 12-bit immediate, and an optional shift257 /// Two registers, an unsigned 12-bit immediate, and an optional shift
244 ///258 ///
245 /// Used by e.g. sub_immediate259 /// Used by e.g. sub_immediate
...@@ -268,6 +282,16 @@ pub const Inst = struct {...@@ -268,6 +282,16 @@ pub const Inst = struct {
268 imm6: u6,282 imm6: u6,
269 shift: bits.Instruction.LogicalShiftedRegisterShift,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 /// Two registers and a bitmask immediate295 /// Two registers and a bitmask immediate
272 ///296 ///
273 /// Used by e.g. eor_immediate297 /// Used by e.g. eor_immediate
src/arch/aarch64/bits.zig+48-27
...@@ -510,33 +510,23 @@ pub const Instruction = union(enum) {...@@ -510,33 +510,23 @@ pub const Instruction = union(enum) {
510 imm16: u16,510 imm16: u16,
511 shift: u6,511 shift: u6,
512 ) Instruction {512 ) Instruction {
513 switch (rd.size()) {513 assert(shift % 16 == 0);
514 32 => {514 assert(!(rd.size() == 32 and shift > 16));
515 assert(shift % 16 == 0 and shift <= 16);515 assert(!(rd.size() == 64 and shift > 48));
516 return Instruction{516
517 .move_wide_immediate = .{517 return Instruction{
518 .rd = rd.enc(),518 .move_wide_immediate = .{
519 .imm16 = imm16,519 .rd = rd.enc(),
520 .hw = @intCast(u2, shift / 16),520 .imm16 = imm16,
521 .opc = opc,521 .hw = @intCast(u2, shift / 16),
522 .sf = 0,522 .opc = opc,
523 },523 .sf = switch (rd.size()) {
524 };524 32 => 0,
525 },525 64 => 1,
526 64 => {526 else => unreachable, // unexpected register size
527 assert(shift % 16 == 0 and shift <= 48);527 },
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 };
537 },528 },
538 else => unreachable, // unexpected register size529 };
539 }
540 }530 }
541531
542 fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {532 fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
...@@ -914,7 +904,7 @@ pub const Instruction = union(enum) {...@@ -914,7 +904,7 @@ pub const Instruction = union(enum) {
914 n: u1,904 n: u1,
915 ) Instruction {905 ) Instruction {
916 assert(rd.size() == rn.size());906 assert(rd.size() == rn.size());
917 assert(!(rd.size() == 32 and n == 1));907 assert(!(rd.size() == 32 and n != 0));
918908
919 return Instruction{909 return Instruction{
920 .logical_immediate = .{910 .logical_immediate = .{
...@@ -942,6 +932,8 @@ pub const Instruction = union(enum) {...@@ -942,6 +932,8 @@ pub const Instruction = union(enum) {
942 imms: u6,932 imms: u6,
943 ) Instruction {933 ) Instruction {
944 assert(rd.size() == rn.size());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)));
945937
946 return Instruction{938 return Instruction{
947 .bitfield = .{939 .bitfield = .{
...@@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) {...@@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) {
1417 return sbfm(rd, rn, shift, imms);1409 return sbfm(rd, rn, shift, imms);
1418 }1410 }
14191411
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 pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction {1429 pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction {
1421 const size = @intCast(u6, rd.size() - 1);1430 const size = @intCast(u6, rd.size() - 1);
1422 return ubfm(rd, rn, size - shift + 1, size - shift);1431 return ubfm(rd, rn, size - shift + 1, size - shift);
...@@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) {...@@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) {
1427 return ubfm(rd, rn, shift, imms);1436 return ubfm(rd, rn, shift, imms);
1428 }1437 }
14291438
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 // Add/subtract (shifted register)1451 // Add/subtract (shifted register)
14311452
1432 pub fn addShiftedRegister(1453 pub fn addShiftedRegister(
test/behavior/basic.zig-2
...@@ -15,8 +15,6 @@ test "empty function with comments" {...@@ -15,8 +15,6 @@ test "empty function with comments" {
15}15}
1616
17test "truncate" {17test "truncate" {
18 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
19
20 try expect(testTruncate(0x10fd) == 0xfd);18 try expect(testTruncate(0x10fd) == 0xfd);
21 comptime try expect(testTruncate(0x10fd) == 0xfd);19 comptime try expect(testTruncate(0x10fd) == 0xfd);
22}20}