| author | |
| committer | |
| log | c4778fc0292b7024bf815e20e31029955a7a7241 |
| tree | aad614cc803ed17dc46a0dfa7b025d9858b8bfb9 |
| parent | 77e70189f438316a8d4e48b2457be0b5eb5974f3 |
| signature | Commit is signed but in an unrecognized format. |
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 | 1452 | } |
| 1453 | 1453 | |
| 1454 | 1454 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1455 | _ = inst; | |
| 1456 | return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); | |
| 1455 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 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 | } |
| 1458 | 1513 | |
| 1459 | 1514 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2382,7 +2437,9 @@ fn binOpRegister( |
| 2382 | 2437 | .rm = lhs_reg, |
| 2383 | 2438 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), |
| 2384 | 2439 | } }, |
| 2385 | .mul => .{ .rrr = .{ | |
| 2440 | .mul, | |
| 2441 | .smulbb, | |
| 2442 | => .{ .rrr = .{ | |
| 2386 | 2443 | .rd = dest_reg, |
| 2387 | 2444 | .rn = lhs_reg, |
| 2388 | 2445 | .rm = rhs_reg, |
src/arch/arm/Emit.zig+3-1| ... | ... | @@ -122,7 +122,7 @@ pub fn emitMir( |
| 122 | 122 | .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst), |
| 123 | 123 | |
| 124 | 124 | .ldrh => try emit.mirLoadStoreExtra(inst), |
| 125 | .ldrsb => try emit.mirLoadStore(inst), | |
| 125 | .ldrsb => try emit.mirLoadStoreExtra(inst), | |
| 126 | 126 | .ldrsh => try emit.mirLoadStoreExtra(inst), |
| 127 | 127 | .strh => try emit.mirLoadStoreExtra(inst), |
| 128 | 128 | |
| ... | ... | @@ -130,6 +130,7 @@ pub fn emitMir( |
| 130 | 130 | .movt => try emit.mirSpecialMove(inst), |
| 131 | 131 | |
| 132 | 132 | .mul => try emit.mirMultiply(inst), |
| 133 | .smulbb => try emit.mirMultiply(inst), | |
| 133 | 134 | |
| 134 | 135 | .nop => try emit.mirNop(), |
| 135 | 136 | |
| ... | ... | @@ -689,6 +690,7 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 689 | 690 | |
| 690 | 691 | switch (tag) { |
| 691 | 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 | 694 | else => unreachable, |
| 693 | 695 | } |
| 694 | 696 | } |
src/arch/arm/Mir.zig+2| ... | ... | @@ -102,6 +102,8 @@ pub const Inst = struct { |
| 102 | 102 | rsb, |
| 103 | 103 | /// Signed Bit Field Extract |
| 104 | 104 | sbfx, |
| 105 | /// Signed Multiply (halfwords), bottom half, bottom half | |
| 106 | smulbb, | |
| 105 | 107 | /// Store Register |
| 106 | 108 | str, |
| 107 | 109 | /// Store Register Byte |
src/arch/arm/bits.zig+55| ... | ... | @@ -216,6 +216,18 @@ pub const Instruction = union(enum) { |
| 216 | 216 | fixed_2: u5 = 0b00001, |
| 217 | 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 | 231 | integer_saturating_arithmetic: packed struct { |
| 220 | 232 | rm: u4, |
| 221 | 233 | fixed_1: u8 = 0b0000_0101, |
| ... | ... | @@ -592,6 +604,7 @@ pub const Instruction = union(enum) { |
| 592 | 604 | .data_processing => |v| @bitCast(u32, v), |
| 593 | 605 | .multiply => |v| @bitCast(u32, v), |
| 594 | 606 | .multiply_long => |v| @bitCast(u32, v), |
| 607 | .signed_multiply_halfwords => |v| @bitCast(u32, v), | |
| 595 | 608 | .integer_saturating_arithmetic => |v| @bitCast(u32, v), |
| 596 | 609 | .bit_field_extract => |v| @bitCast(u32, v), |
| 597 | 610 | .single_data_transfer => |v| @bitCast(u32, v), |
| ... | ... | @@ -691,6 +704,26 @@ pub const Instruction = union(enum) { |
| 691 | 704 | }; |
| 692 | 705 | } |
| 693 | 706 | |
| 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 | 727 | fn integerSaturationArithmetic( |
| 695 | 728 | cond: Condition, |
| 696 | 729 | rd: Register, |
| ... | ... | @@ -1093,6 +1126,24 @@ pub const Instruction = union(enum) { |
| 1093 | 1126 | return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); |
| 1094 | 1127 | } |
| 1095 | 1128 | |
| 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 | 1147 | // Bit field extract |
| 1097 | 1148 | |
| 1098 | 1149 | pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction { |
| ... | ... | @@ -1440,6 +1491,10 @@ test "serialize instructions" { |
| 1440 | 1491 | .inst = Instruction.qadd(.al, .r0, .r7, .r8), |
| 1441 | 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 | }; |
| 1444 | 1499 | |
| 1445 | 1500 | for (testcases) |case| { |
test/behavior/math.zig-1| ... | ... | @@ -678,7 +678,6 @@ test "small int addition" { |
| 678 | 678 | test "@mulWithOverflow" { |
| 679 | 679 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 680 | 680 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 681 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 682 | 681 | |
| 683 | 682 | var result: u8 = undefined; |
| 684 | 683 | try expect(@mulWithOverflow(u8, 86, 3, &result)); |