authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-31 18:25:53+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-04-01 22:02:56+02:00
logc4778fc0292b7024bf815e20e31029955a7a7241
treeaad614cc803ed17dc46a0dfa7b025d9858b8bfb9
parent77e70189f438316a8d4e48b2457be0b5eb5974f3
signature Commit is signed but in an unrecognized format.

stage2 ARM: implement mul_with_overflow for ints <= 16 bits


5 files changed, 120 insertions(+), 5 deletions(-)

src/arch/arm/CodeGen.zig+60-3
...@@ -1452,8 +1452,63 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1452,8 +1452,63 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1452}1452}
14531453
1454fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1454fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1455 _ = inst;1455 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1456 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});1456 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1457 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
1458 const result: MCValue = result: {
1459 const lhs = try self.resolveInst(extra.lhs);
1460 const rhs = try self.resolveInst(extra.rhs);
1461 const lhs_ty = self.air.typeOf(extra.lhs);
1462 const rhs_ty = self.air.typeOf(extra.rhs);
1463
1464 const tuple_ty = self.air.typeOfIndex(inst);
1465 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1466 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1467 const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*));
1468
1469 switch (lhs_ty.zigTypeTag()) {
1470 .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
1471 .Int => {
1472 assert(lhs_ty.eql(rhs_ty, self.target.*));
1473 const int_info = lhs_ty.intInfo(self.target.*);
1474 if (int_info.bits <= 16) {
1475 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
1476
1477 try self.spillCompareFlagsIfOccupied();
1478 self.compare_flags_inst = null;
1479
1480 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1481 .signed => .smulbb,
1482 .unsigned => .mul,
1483 };
1484
1485 const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1486 const dest_reg = dest.register;
1487 self.register_manager.freezeRegs(&.{dest_reg});
1488 defer self.register_manager.unfreezeRegs(&.{dest_reg});
1489
1490 const truncated_reg = try self.register_manager.allocReg(null);
1491 self.register_manager.freezeRegs(&.{truncated_reg});
1492 defer self.register_manager.unfreezeRegs(&.{truncated_reg});
1493
1494 // sbfx/ubfx truncated, dest, #0, #bits
1495 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
1496
1497 // cmp dest, truncated
1498 _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize);
1499
1500 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1501 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
1502
1503 break :result MCValue{ .stack_offset = stack_offset };
1504 } else {
1505 return self.fail("TODO ARM overflow operations on integers > u16/i16", .{});
1506 }
1507 },
1508 else => unreachable,
1509 }
1510 };
1511 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1457}1512}
14581513
1459fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1514fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2382,7 +2437,9 @@ fn binOpRegister(...@@ -2382,7 +2437,9 @@ fn binOpRegister(
2382 .rm = lhs_reg,2437 .rm = lhs_reg,
2383 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),2438 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
2384 } },2439 } },
2385 .mul => .{ .rrr = .{2440 .mul,
2441 .smulbb,
2442 => .{ .rrr = .{
2386 .rd = dest_reg,2443 .rd = dest_reg,
2387 .rn = lhs_reg,2444 .rn = lhs_reg,
2388 .rm = rhs_reg,2445 .rm = rhs_reg,
src/arch/arm/Emit.zig+3-1
...@@ -122,7 +122,7 @@ pub fn emitMir(...@@ -122,7 +122,7 @@ pub fn emitMir(
122 .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),122 .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),
123123
124 .ldrh => try emit.mirLoadStoreExtra(inst),124 .ldrh => try emit.mirLoadStoreExtra(inst),
125 .ldrsb => try emit.mirLoadStore(inst),125 .ldrsb => try emit.mirLoadStoreExtra(inst),
126 .ldrsh => try emit.mirLoadStoreExtra(inst),126 .ldrsh => try emit.mirLoadStoreExtra(inst),
127 .strh => try emit.mirLoadStoreExtra(inst),127 .strh => try emit.mirLoadStoreExtra(inst),
128128
...@@ -130,6 +130,7 @@ pub fn emitMir(...@@ -130,6 +130,7 @@ pub fn emitMir(
130 .movt => try emit.mirSpecialMove(inst),130 .movt => try emit.mirSpecialMove(inst),
131131
132 .mul => try emit.mirMultiply(inst),132 .mul => try emit.mirMultiply(inst),
133 .smulbb => try emit.mirMultiply(inst),
133134
134 .nop => try emit.mirNop(),135 .nop => try emit.mirNop(),
135136
...@@ -689,6 +690,7 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -689,6 +690,7 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {
689690
690 switch (tag) {691 switch (tag) {
691 .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)),692 .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)),
693 .smulbb => try emit.writeInstruction(Instruction.smulbb(cond, rrr.rd, rrr.rn, rrr.rm)),
692 else => unreachable,694 else => unreachable,
693 }695 }
694}696}
src/arch/arm/Mir.zig+2
...@@ -102,6 +102,8 @@ pub const Inst = struct {...@@ -102,6 +102,8 @@ pub const Inst = struct {
102 rsb,102 rsb,
103 /// Signed Bit Field Extract103 /// Signed Bit Field Extract
104 sbfx,104 sbfx,
105 /// Signed Multiply (halfwords), bottom half, bottom half
106 smulbb,
105 /// Store Register107 /// Store Register
106 str,108 str,
107 /// Store Register Byte109 /// Store Register Byte
src/arch/arm/bits.zig+55
...@@ -216,6 +216,18 @@ pub const Instruction = union(enum) {...@@ -216,6 +216,18 @@ pub const Instruction = union(enum) {
216 fixed_2: u5 = 0b00001,216 fixed_2: u5 = 0b00001,
217 cond: u4,217 cond: u4,
218 },218 },
219 signed_multiply_halfwords: packed struct {
220 rn: u4,
221 fixed_1: u1 = 0b0,
222 n: u1,
223 m: u1,
224 fixed_2: u1 = 0b1,
225 rm: u4,
226 fixed_3: u4 = 0b0000,
227 rd: u4,
228 fixed_4: u8 = 0b00010110,
229 cond: u4,
230 },
219 integer_saturating_arithmetic: packed struct {231 integer_saturating_arithmetic: packed struct {
220 rm: u4,232 rm: u4,
221 fixed_1: u8 = 0b0000_0101,233 fixed_1: u8 = 0b0000_0101,
...@@ -592,6 +604,7 @@ pub const Instruction = union(enum) {...@@ -592,6 +604,7 @@ pub const Instruction = union(enum) {
592 .data_processing => |v| @bitCast(u32, v),604 .data_processing => |v| @bitCast(u32, v),
593 .multiply => |v| @bitCast(u32, v),605 .multiply => |v| @bitCast(u32, v),
594 .multiply_long => |v| @bitCast(u32, v),606 .multiply_long => |v| @bitCast(u32, v),
607 .signed_multiply_halfwords => |v| @bitCast(u32, v),
595 .integer_saturating_arithmetic => |v| @bitCast(u32, v),608 .integer_saturating_arithmetic => |v| @bitCast(u32, v),
596 .bit_field_extract => |v| @bitCast(u32, v),609 .bit_field_extract => |v| @bitCast(u32, v),
597 .single_data_transfer => |v| @bitCast(u32, v),610 .single_data_transfer => |v| @bitCast(u32, v),
...@@ -691,6 +704,26 @@ pub const Instruction = union(enum) {...@@ -691,6 +704,26 @@ pub const Instruction = union(enum) {
691 };704 };
692 }705 }
693706
707 fn signedMultiplyHalfwords(
708 n: u1,
709 m: u1,
710 cond: Condition,
711 rd: Register,
712 rn: Register,
713 rm: Register,
714 ) Instruction {
715 return Instruction{
716 .signed_multiply_halfwords = .{
717 .rn = rn.id(),
718 .n = n,
719 .m = m,
720 .rm = rm.id(),
721 .rd = rd.id(),
722 .cond = @enumToInt(cond),
723 },
724 };
725 }
726
694 fn integerSaturationArithmetic(727 fn integerSaturationArithmetic(
695 cond: Condition,728 cond: Condition,
696 rd: Register,729 rd: Register,
...@@ -1093,6 +1126,24 @@ pub const Instruction = union(enum) {...@@ -1093,6 +1126,24 @@ pub const Instruction = union(enum) {
1093 return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);1126 return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
1094 }1127 }
10951128
1129 // Signed Multiply (halfwords)
1130
1131 pub fn smulbb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1132 return signedMultiplyHalfwords(0, 0, cond, rd, rn, rm);
1133 }
1134
1135 pub fn smulbt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1136 return signedMultiplyHalfwords(0, 1, cond, rd, rn, rm);
1137 }
1138
1139 pub fn smultb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1140 return signedMultiplyHalfwords(1, 0, cond, rd, rn, rm);
1141 }
1142
1143 pub fn smultt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
1144 return signedMultiplyHalfwords(1, 1, cond, rd, rn, rm);
1145 }
1146
1096 // Bit field extract1147 // Bit field extract
10971148
1098 pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {1149 pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
...@@ -1440,6 +1491,10 @@ test "serialize instructions" {...@@ -1440,6 +1491,10 @@ test "serialize instructions" {
1440 .inst = Instruction.qadd(.al, .r0, .r7, .r8),1491 .inst = Instruction.qadd(.al, .r0, .r7, .r8),
1441 .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,1492 .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,
1442 },1493 },
1494 .{ // smulbt r0, r0, r0
1495 .inst = Instruction.smulbt(.al, .r0, .r0, .r0),
1496 .expected = 0b1110_00010110_0000_0000_0000_1_1_0_0_0000,
1497 },
1443 };1498 };
14441499
1445 for (testcases) |case| {1500 for (testcases) |case| {
test/behavior/math.zig-1
...@@ -678,7 +678,6 @@ test "small int addition" {...@@ -678,7 +678,6 @@ test "small int addition" {
678test "@mulWithOverflow" {678test "@mulWithOverflow" {
679 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO679 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
680 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO680 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
681 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
682681
683 var result: u8 = undefined;682 var result: u8 = undefined;
684 try expect(@mulWithOverflow(u8, 86, 3, &result));683 try expect(@mulWithOverflow(u8, 86, 3, &result));