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 {
939939 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
940940}
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
9421024fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
9431025 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
9471026 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 });
9501035}
9511036
9521037fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
......@@ -3483,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
34833568 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },
34843569 });
34853570
3486 if (x > math.maxInt(u16)) {
3571 if (x & 0x0000_0000_ffff_0000 != 0) {
34873572 _ = try self.addInst(.{
34883573 .tag = .movk,
34893574 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },
34903575 });
34913576 }
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 }
35033591 }
35043592 },
35053593 .register => |src_reg| {
src/arch/aarch64/Emit.zig+40
......@@ -162,6 +162,17 @@ pub fn emitMir(
162162
163163 .push_regs => try emit.mirPushPopRegs(inst),
164164 .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),
165176 }
166177 }
167178}
......@@ -1050,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
10501061 else => unreachable,
10511062 }
10521063}
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 {
130130 push_regs,
131131 /// Return from subroutine
132132 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,
133141 /// Store Pair of Registers
134142 stp,
135143 /// Pseudo-instruction: Store to stack
......@@ -156,6 +164,12 @@ pub const Inst = struct {
156164 sub_shifted_register,
157165 /// Supervisor Call
158166 svc,
167 /// Unsigned bitfield extract
168 ubfx,
169 /// Unsigned extend byte
170 uxtb,
171 /// Unsigned extend halfword
172 uxth,
159173 };
160174
161175 /// The position of an MIR instruction within the `Mir` instructions array.
......@@ -225,13 +239,6 @@ pub const Inst = struct {
225239 rt: Register,
226240 inst: Index,
227241 },
228 /// Two registers
229 ///
230 /// Used by e.g. mov_register
231 rr: struct {
232 rd: Register,
233 rn: Register,
234 },
235242 /// A register, an unsigned 12-bit immediate, and an optional shift
236243 ///
237244 /// Used by e.g. cmp_immediate
......@@ -240,6 +247,13 @@ pub const Inst = struct {
240247 imm12: u12,
241248 sh: u1 = 0,
242249 },
250 /// Two registers
251 ///
252 /// Used by e.g. mov_register
253 rr: struct {
254 rd: Register,
255 rn: Register,
256 },
243257 /// Two registers, an unsigned 12-bit immediate, and an optional shift
244258 ///
245259 /// Used by e.g. sub_immediate
......@@ -268,6 +282,16 @@ pub const Inst = struct {
268282 imm6: u6,
269283 shift: bits.Instruction.LogicalShiftedRegisterShift,
270284 },
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 },
271295 /// Two registers and a bitmask immediate
272296 ///
273297 /// Used by e.g. eor_immediate
src/arch/aarch64/bits.zig+48-27
......@@ -510,33 +510,23 @@ pub const Instruction = union(enum) {
510510 imm16: u16,
511511 shift: u6,
512512 ) 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 },
537528 },
538 else => unreachable, // unexpected register size
539 }
529 };
540530 }
541531
542532 fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
......@@ -914,7 +904,7 @@ pub const Instruction = union(enum) {
914904 n: u1,
915905 ) Instruction {
916906 assert(rd.size() == rn.size());
917 assert(!(rd.size() == 32 and n == 1));
907 assert(!(rd.size() == 32 and n != 0));
918908
919909 return Instruction{
920910 .logical_immediate = .{
......@@ -942,6 +932,8 @@ pub const Instruction = union(enum) {
942932 imms: u6,
943933 ) Instruction {
944934 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
946938 return Instruction{
947939 .bitfield = .{
......@@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) {
14171409 return sbfm(rd, rn, shift, imms);
14181410 }
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
14201429 pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction {
14211430 const size = @intCast(u6, rd.size() - 1);
14221431 return ubfm(rd, rn, size - shift + 1, size - shift);
......@@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) {
14271436 return ubfm(rd, rn, shift, imms);
14281437 }
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
14301451 // Add/subtract (shifted register)
14311452
14321453 pub fn addShiftedRegister(
test/behavior/basic.zig-2
......@@ -15,8 +15,6 @@ test "empty function with comments" {
1515}
1616
1717test "truncate" {
18 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
19
2018 try expect(testTruncate(0x10fd) == 0xfd);
2119 comptime try expect(testTruncate(0x10fd) == 0xfd);
2220}