authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-04-02 10:52:34+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-04-02 10:52:34+02:00
log3ee44ce949117e8e91348ef870b18b23571a408d
tree7b2259cc40e411fdec841677cc81aa19eeffb3a0
parent8b5d5f44e23c86082fba480f01092f2f7ffb3dfe
parent8c12ad98b857cee3f6a8bc557f08b8dfcba2db7e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11373 from joachimschmidt557/stage2-arm

stage2 ARM: implement overflow operations for ints <= 32 bits

5 files changed, 602 insertions(+), 119 deletions(-)

src/arch/arm/CodeGen.zig+506-114
......@@ -105,9 +105,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
105105const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
106106
107107const MCValue = union(enum) {
108 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
109 /// TODO Look into deleting this tag and using `dead` instead, since every use
110 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
108 /// No runtime bits. `void` types, empty structs, u0, enums with 1
109 /// tag, etc.
110 ///
111 /// TODO Look into deleting this tag and using `dead` instead,
112 /// since every use of MCValue.none should be instead looking at
113 /// the type and noticing it is 0 bits.
111114 none,
112115 /// Control flow will not allow this value to be observed.
113116 unreach,
......@@ -116,20 +119,41 @@ const MCValue = union(enum) {
116119 /// The value is undefined.
117120 undef,
118121 /// A pointer-sized integer that fits in a register.
119 /// If the type is a pointer, this is the pointer address in virtual address space.
122 ///
123 /// If the type is a pointer, this is the pointer address in
124 /// virtual address space.
120125 immediate: u32,
121126 /// The value is in a target-specific register.
122127 register: Register,
128 /// The value is a tuple { wrapped: u32, overflow: u1 } where
129 /// wrapped is stored in the register and the overflow bit is
130 /// stored in the C flag of the CPSR.
131 ///
132 /// This MCValue is only generated by a add_with_overflow or
133 /// sub_with_overflow instruction operating on u32.
134 register_c_flag: Register,
135 /// The value is a tuple { wrapped: i32, overflow: u1 } where
136 /// wrapped is stored in the register and the overflow bit is
137 /// stored in the V flag of the CPSR.
138 ///
139 /// This MCValue is only generated by a add_with_overflow or
140 /// sub_with_overflow instruction operating on i32.
141 register_v_flag: Register,
123142 /// The value is in memory at a hard-coded address.
124 /// If the type is a pointer, it means the pointer address is at this memory location.
143 ///
144 /// If the type is a pointer, it means the pointer address is at
145 /// this memory location.
125146 memory: u64,
126147 /// The value is one of the stack variables.
127 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
148 ///
149 /// If the type is a pointer, it means the pointer address is in
150 /// the stack at this offset.
128151 stack_offset: u32,
129 /// The value is a pointer to one of the stack variables (payload is stack offset).
152 /// The value is a pointer to one of the stack variables (payload
153 /// is stack offset).
130154 ptr_stack_offset: u32,
131 /// The value is in the compare flags assuming an unsigned operation,
132 /// with this operator applied on top of it.
155 /// The value is in the compare flags assuming an unsigned
156 /// operation, with this operator applied on top of it.
133157 compare_flags_unsigned: math.CompareOperator,
134158 /// The value is in the compare flags assuming a signed operation,
135159 /// with this operator applied on top of it.
......@@ -554,8 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
554578 .trunc_float,
555579 => try self.airUnaryMath(inst),
556580
557 .add_with_overflow => try self.airAddWithOverflow(inst),
558 .sub_with_overflow => try self.airSubWithOverflow(inst),
581 .add_with_overflow => try self.airOverflow(inst),
582 .sub_with_overflow => try self.airOverflow(inst),
559583 .mul_with_overflow => try self.airMulWithOverflow(inst),
560584 .shl_with_overflow => try self.airShlWithOverflow(inst),
561585
......@@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
726750 .register => |reg| {
727751 self.register_manager.freeReg(reg);
728752 },
753 .register_c_flag,
754 .register_v_flag,
755 => |reg| {
756 self.register_manager.freeReg(reg);
757 self.compare_flags_inst = null;
758 },
729759 .compare_flags_signed, .compare_flags_unsigned => {
730760 self.compare_flags_inst = null;
731761 },
......@@ -841,8 +871,16 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
841871pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
842872 const stack_mcv = try self.allocRegOrMem(inst, false);
843873 log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
874
844875 const reg_mcv = self.getResolvedInstValue(inst);
845 assert(reg == reg_mcv.register);
876 switch (reg_mcv) {
877 .register,
878 .register_c_flag,
879 .register_v_flag,
880 => |r| assert(r == reg),
881 else => unreachable, // not a register
882 }
883
846884 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
847885 try branch.inst_table.put(self.gpa, inst, stack_mcv);
848886 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
......@@ -853,7 +891,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
853891fn spillCompareFlagsIfOccupied(self: *Self) !void {
854892 if (self.compare_flags_inst) |inst_to_save| {
855893 const mcv = self.getResolvedInstValue(inst_to_save);
856 assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned);
894 switch (mcv) {
895 .compare_flags_signed,
896 .compare_flags_unsigned,
897 .register_c_flag,
898 .register_v_flag,
899 => {},
900 else => unreachable, // mcv doesn't occupy the compare flags
901 }
857902
858903 const new_mcv = try self.allocRegOrMem(inst_to_save, true);
859904 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
......@@ -1196,7 +1241,7 @@ fn minMax(
11961241 // register.
11971242 assert(lhs_reg != rhs_reg); // see note above
11981243
1199 _ = try self.binOpRegister(.cmp_eq, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty);
1244 _ = try self.binOpRegister(.cmp, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty);
12001245
12011246 const cond_choose_lhs: Condition = switch (tag) {
12021247 .max => switch (int_info.signedness) {
......@@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
12681313 const len = try self.resolveInst(bin_op.rhs);
12691314 const len_ty = self.air.typeOf(bin_op.rhs);
12701315
1271 const stack_offset = try self.allocMem(inst, 8, 8);
1316 const stack_offset = try self.allocMem(inst, 8, 4);
12721317 try self.genSetStack(ptr_ty, stack_offset, ptr);
12731318 try self.genSetStack(len_ty, stack_offset - 4, len);
12741319 break :result MCValue{ .stack_offset = stack_offset };
......@@ -1306,24 +1351,321 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
13061351 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13071352}
13081353
1309fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1310 _ = inst;
1311 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
1312}
1354fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1355 const tag = self.air.instructions.items(.tag)[inst];
1356 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1357 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1358 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1359 const lhs = try self.resolveInst(extra.lhs);
1360 const rhs = try self.resolveInst(extra.rhs);
1361 const lhs_ty = self.air.typeOf(extra.lhs);
1362 const rhs_ty = self.air.typeOf(extra.rhs);
1363
1364 const tuple_ty = self.air.typeOfIndex(inst);
1365 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1366 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1367 const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*));
1368
1369 switch (lhs_ty.zigTypeTag()) {
1370 .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
1371 .Int => {
1372 assert(lhs_ty.eql(rhs_ty, self.target.*));
1373 const int_info = lhs_ty.intInfo(self.target.*);
1374 if (int_info.bits < 32) {
1375 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
1376
1377 try self.spillCompareFlagsIfOccupied();
1378 self.compare_flags_inst = null;
1379
1380 const base_tag: Air.Inst.Tag = switch (tag) {
1381 .add_with_overflow => .add,
1382 .sub_with_overflow => .sub,
1383 else => unreachable,
1384 };
1385 const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1386 const dest_reg = dest.register;
1387 self.register_manager.freezeRegs(&.{dest_reg});
1388 defer self.register_manager.unfreezeRegs(&.{dest_reg});
1389
1390 const truncated_reg = try self.register_manager.allocReg(null);
1391 self.register_manager.freezeRegs(&.{truncated_reg});
1392 defer self.register_manager.unfreezeRegs(&.{truncated_reg});
1393
1394 // sbfx/ubfx truncated, dest, #0, #bits
1395 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
1396
1397 // cmp dest, truncated
1398 _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize);
1399
1400 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1401 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
1402
1403 break :result MCValue{ .stack_offset = stack_offset };
1404 } else if (int_info.bits == 32) {
1405 // Only say yes if the operation is
1406 // commutative, i.e. we can swap both of the
1407 // operands
1408 const lhs_immediate_ok = switch (tag) {
1409 .add_with_overflow => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null,
1410 .sub_with_overflow => false,
1411 else => unreachable,
1412 };
1413 const rhs_immediate_ok = switch (tag) {
1414 .add_with_overflow,
1415 .sub_with_overflow,
1416 => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null,
1417 else => unreachable,
1418 };
13131419
1314fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1315 _ = inst;
1316 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
1420 const mir_tag: Mir.Inst.Tag = switch (tag) {
1421 .add_with_overflow => .adds,
1422 .sub_with_overflow => .subs,
1423 else => unreachable,
1424 };
1425
1426 try self.spillCompareFlagsIfOccupied();
1427 self.compare_flags_inst = inst;
1428
1429 const dest = blk: {
1430 if (rhs_immediate_ok) {
1431 break :blk try self.binOpImmediate(mir_tag, null, lhs, rhs, lhs_ty, false);
1432 } else if (lhs_immediate_ok) {
1433 // swap lhs and rhs
1434 break :blk try self.binOpImmediate(mir_tag, null, rhs, lhs, rhs_ty, true);
1435 } else {
1436 break :blk try self.binOpRegister(mir_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1437 }
1438 };
1439
1440 switch (int_info.signedness) {
1441 .unsigned => break :result MCValue{ .register_c_flag = dest.register },
1442 .signed => break :result MCValue{ .register_v_flag = dest.register },
1443 }
1444 } else {
1445 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
1446 }
1447 },
1448 else => unreachable,
1449 }
1450 };
1451 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
13171452}
13181453
13191454fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1320 _ = inst;
1321 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 if (int_info.bits <= 32) {
1505 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
1506
1507 try self.spillCompareFlagsIfOccupied();
1508 self.compare_flags_inst = null;
1509
1510 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1511 .signed => .smull,
1512 .unsigned => .umull,
1513 };
1514
1515 // TODO extract umull etc. to binOpTwoRegister
1516 // once MCValue.rr is implemented
1517 const lhs_is_register = lhs == .register;
1518 const rhs_is_register = rhs == .register;
1519
1520 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
1521 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
1522
1523 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1524 const reg = try self.register_manager.allocReg(null);
1525 self.register_manager.freezeRegs(&.{reg});
1526
1527 break :blk reg;
1528 };
1529 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
1530
1531 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
1532 const reg = try self.register_manager.allocReg(null);
1533 self.register_manager.freezeRegs(&.{reg});
1534
1535 break :blk reg;
1536 };
1537 defer self.register_manager.unfreezeRegs(&.{rhs_reg});
1538
1539 const dest_regs = try self.register_manager.allocRegs(2, .{ null, null });
1540 self.register_manager.freezeRegs(&dest_regs);
1541 defer self.register_manager.unfreezeRegs(&dest_regs);
1542 const rdlo = dest_regs[0];
1543 const rdhi = dest_regs[1];
1544
1545 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1546 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
1547
1548 const truncated_reg = try self.register_manager.allocReg(null);
1549 self.register_manager.freezeRegs(&.{truncated_reg});
1550 defer self.register_manager.unfreezeRegs(&.{truncated_reg});
1551
1552 _ = try self.addInst(.{
1553 .tag = base_tag,
1554 .data = .{ .rrrr = .{
1555 .rdlo = rdlo,
1556 .rdhi = rdhi,
1557 .rn = lhs_reg,
1558 .rm = rhs_reg,
1559 } },
1560 });
1561
1562 // sbfx/ubfx truncated, rdlo, #0, #bits
1563 try self.truncRegister(rdlo, truncated_reg, int_info.signedness, int_info.bits);
1564
1565 // str truncated, [...]
1566 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1567
1568 // cmp truncated, rdlo
1569 _ = try self.binOp(.cmp_eq, null, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize);
1570
1571 // mov rdlo, #0
1572 _ = try self.addInst(.{
1573 .tag = .mov,
1574 .data = .{ .rr_op = .{
1575 .rd = rdlo,
1576 .rn = .r0,
1577 .op = Instruction.Operand.fromU32(0).?,
1578 } },
1579 });
1580
1581 // movne rdlo, #1
1582 _ = try self.addInst(.{
1583 .tag = .mov,
1584 .cond = .ne,
1585 .data = .{ .rr_op = .{
1586 .rd = rdlo,
1587 .rn = .r0,
1588 .op = Instruction.Operand.fromU32(1).?,
1589 } },
1590 });
1591
1592 // cmp rdhi, #0
1593 _ = try self.binOp(.cmp_eq, null, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize);
1594
1595 // movne rdlo, #1
1596 _ = try self.addInst(.{
1597 .tag = .mov,
1598 .cond = .ne,
1599 .data = .{ .rr_op = .{
1600 .rd = rdlo,
1601 .rn = .r0,
1602 .op = Instruction.Operand.fromU32(1).?,
1603 } },
1604 });
1605
1606 // strb rdlo, [...]
1607 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .register = rdlo });
1608
1609 break :result MCValue{ .stack_offset = stack_offset };
1610 } else {
1611 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
1612 }
1613 },
1614 else => unreachable,
1615 }
1616 };
1617 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
13221618}
13231619
13241620fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1325 _ = inst;
1326 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
1621 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1622 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1623 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
1624 const result: MCValue = result: {
1625 const lhs = try self.resolveInst(extra.lhs);
1626 const rhs = try self.resolveInst(extra.rhs);
1627 const lhs_ty = self.air.typeOf(extra.lhs);
1628 const rhs_ty = self.air.typeOf(extra.rhs);
1629
1630 const tuple_ty = self.air.typeOfIndex(inst);
1631 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1632 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1633 const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*));
1634
1635 switch (lhs_ty.zigTypeTag()) {
1636 .Vector => return self.fail("TODO implement shl_with_overflow for vectors", .{}),
1637 .Int => {
1638 const int_info = lhs_ty.intInfo(self.target.*);
1639 if (int_info.bits <= 32) {
1640 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
1641
1642 if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register});
1643 defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register});
1644
1645 try self.spillCompareFlagsIfOccupied();
1646 self.compare_flags_inst = null;
1647
1648 // lsl dest, lhs, rhs
1649 const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty);
1650
1651 // asr/lsr reconstructed, dest, rhs
1652 const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty);
1653
1654 // cmp lhs, reconstructed
1655 _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty);
1656
1657 try self.genSetStack(lhs_ty, stack_offset, dest);
1658 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
1659
1660 break :result MCValue{ .stack_offset = stack_offset };
1661 } else {
1662 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
1663 }
1664 },
1665 else => unreachable,
1666 }
1667 };
1668 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
13271669}
13281670
13291671fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
......@@ -1424,7 +1766,6 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
14241766 const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*));
14251767 const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align);
14261768
1427 // TODO optimization for small error unions: put into register
14281769 switch (error_union_mcv) {
14291770 .register => return self.fail("TODO errUnionPayload for registers", .{}),
14301771 .stack_argument_offset => |off| {
......@@ -1791,8 +2132,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
17912132 .undef => unreachable,
17922133 .unreach => unreachable,
17932134 .dead => unreachable,
1794 .compare_flags_unsigned => unreachable,
1795 .compare_flags_signed => unreachable,
2135 .compare_flags_unsigned,
2136 .compare_flags_signed,
2137 .register_c_flag,
2138 .register_v_flag,
2139 => unreachable, // cannot hold an address
17962140 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
17972141 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
17982142 .register => |reg| {
......@@ -1887,8 +2231,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
18872231 .undef => unreachable,
18882232 .unreach => unreachable,
18892233 .dead => unreachable,
1890 .compare_flags_unsigned => unreachable,
1891 .compare_flags_signed => unreachable,
2234 .compare_flags_unsigned,
2235 .compare_flags_signed,
2236 .register_c_flag,
2237 .register_v_flag,
2238 => unreachable, // cannot hold an address
18922239 .immediate => |imm| {
18932240 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
18942241 },
......@@ -2043,6 +2390,50 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
20432390 .memory => |addr| {
20442391 break :result MCValue{ .memory = addr + struct_field_offset };
20452392 },
2393 .register_c_flag,
2394 .register_v_flag,
2395 => |reg| {
2396 switch (index) {
2397 0 => {
2398 // get wrapped value: return register
2399 break :result MCValue{ .register = reg };
2400 },
2401 1 => {
2402 // get overflow bit: set register to C flag
2403 // resp. V flag
2404 const dest_reg = try self.register_manager.allocReg(null);
2405
2406 // mov reg, #0
2407 _ = try self.addInst(.{
2408 .tag = .mov,
2409 .data = .{ .rr_op = .{
2410 .rd = dest_reg,
2411 .rn = .r0,
2412 .op = Instruction.Operand.fromU32(0).?,
2413 } },
2414 });
2415
2416 // C flag: movcs reg, #1
2417 // V flag: movvs reg, #1
2418 _ = try self.addInst(.{
2419 .tag = .mov,
2420 .cond = switch (mcv) {
2421 .register_c_flag => .cs,
2422 .register_v_flag => .vs,
2423 else => unreachable,
2424 },
2425 .data = .{ .rr_op = .{
2426 .rd = dest_reg,
2427 .rn = .r0,
2428 .op = Instruction.Operand.fromU32(1).?,
2429 } },
2430 });
2431
2432 break :result MCValue{ .register = dest_reg };
2433 },
2434 else => unreachable,
2435 }
2436 },
20462437 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
20472438 }
20482439 };
......@@ -2067,7 +2458,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
20672458/// Asserts that generating an instruction of that form is possible.
20682459fn binOpRegister(
20692460 self: *Self,
2070 tag: Air.Inst.Tag,
2461 mir_tag: Mir.Inst.Tag,
20712462 maybe_inst: ?Air.Inst.Index,
20722463 lhs: MCValue,
20732464 rhs: MCValue,
......@@ -2112,8 +2503,8 @@ fn binOpRegister(
21122503 };
21132504 defer self.register_manager.unfreezeRegs(&.{rhs_reg});
21142505
2115 const dest_reg = switch (tag) {
2116 .cmp_eq => .r0, // cmp has no destination regardless
2506 const dest_reg = switch (mir_tag) {
2507 .cmp => .r0, // cmp has no destination regardless
21172508 else => if (maybe_inst) |inst| blk: {
21182509 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
21192510
......@@ -2130,47 +2521,31 @@ fn binOpRegister(
21302521 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
21312522 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
21322523
2133 const mir_tag: Mir.Inst.Tag = switch (tag) {
2134 .add => .add,
2135 .sub => .sub,
2136 .cmp_eq => .cmp,
2137 .mul => .mul,
2138 .bit_and,
2139 .bool_and,
2140 => .@"and",
2141 .bit_or,
2142 .bool_or,
2143 => .orr,
2144 .shl_exact => .lsl,
2145 .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
2146 .signed => Mir.Inst.Tag.asr,
2147 .unsigned => Mir.Inst.Tag.lsr,
2148 },
2149 .xor => .eor,
2150 else => unreachable,
2151 };
2152 const mir_data: Mir.Inst.Data = switch (tag) {
2524 const mir_data: Mir.Inst.Data = switch (mir_tag) {
21532525 .add,
2526 .adds,
21542527 .sub,
2155 .cmp_eq,
2156 .bit_and,
2157 .bool_and,
2158 .bit_or,
2159 .bool_or,
2160 .xor,
2528 .subs,
2529 .cmp,
2530 .@"and",
2531 .orr,
2532 .eor,
21612533 => .{ .rr_op = .{
21622534 .rd = dest_reg,
21632535 .rn = lhs_reg,
21642536 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
21652537 } },
2166 .shl_exact,
2167 .shr_exact,
2538 .lsl,
2539 .asr,
2540 .lsr,
21682541 => .{ .rr_shift = .{
21692542 .rd = dest_reg,
21702543 .rm = lhs_reg,
21712544 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
21722545 } },
2173 .mul => .{ .rrr = .{
2546 .mul,
2547 .smulbb,
2548 => .{ .rrr = .{
21742549 .rd = dest_reg,
21752550 .rn = lhs_reg,
21762551 .rm = rhs_reg,
......@@ -2200,7 +2575,7 @@ fn binOpRegister(
22002575/// Asserts that generating an instruction of that form is possible.
22012576fn binOpImmediate(
22022577 self: *Self,
2203 tag: Air.Inst.Tag,
2578 mir_tag: Mir.Inst.Tag,
22042579 maybe_inst: ?Air.Inst.Index,
22052580 lhs: MCValue,
22062581 rhs: MCValue,
......@@ -2230,8 +2605,8 @@ fn binOpImmediate(
22302605 };
22312606 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
22322607
2233 const dest_reg = switch (tag) {
2234 .cmp_eq => .r0, // cmp has no destination reg
2608 const dest_reg = switch (mir_tag) {
2609 .cmp => .r0, // cmp has no destination reg
22352610 else => if (maybe_inst) |inst| blk: {
22362611 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
22372612
......@@ -2250,40 +2625,23 @@ fn binOpImmediate(
22502625
22512626 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
22522627
2253 const mir_tag: Mir.Inst.Tag = switch (tag) {
2254 .add => .add,
2255 .sub => .sub,
2256 .cmp_eq => .cmp,
2257 .bit_and,
2258 .bool_and,
2259 => .@"and",
2260 .bit_or,
2261 .bool_or,
2262 => .orr,
2263 .shl_exact => .lsl,
2264 .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
2265 .signed => Mir.Inst.Tag.asr,
2266 .unsigned => Mir.Inst.Tag.lsr,
2267 },
2268 .xor => .eor,
2269 else => unreachable,
2270 };
2271 const mir_data: Mir.Inst.Data = switch (tag) {
2628 const mir_data: Mir.Inst.Data = switch (mir_tag) {
22722629 .add,
2630 .adds,
22732631 .sub,
2274 .cmp_eq,
2275 .bit_and,
2276 .bool_and,
2277 .bit_or,
2278 .bool_or,
2279 .xor,
2632 .subs,
2633 .cmp,
2634 .@"and",
2635 .orr,
2636 .eor,
22802637 => .{ .rr_op = .{
22812638 .rd = dest_reg,
22822639 .rn = lhs_reg,
22832640 .op = Instruction.Operand.fromU32(rhs.immediate).?,
22842641 } },
2285 .shl_exact,
2286 .shr_exact,
2642 .lsl,
2643 .asr,
2644 .lsr,
22872645 => .{ .rr_shift = .{
22882646 .rd = dest_reg,
22892647 .rm = lhs_reg,
......@@ -2352,13 +2710,20 @@ fn binOp(
23522710 else => unreachable,
23532711 };
23542712
2713 const mir_tag: Mir.Inst.Tag = switch (tag) {
2714 .add => .add,
2715 .sub => .sub,
2716 .cmp_eq => .cmp,
2717 else => unreachable,
2718 };
2719
23552720 if (rhs_immediate_ok) {
2356 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2721 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
23572722 } else if (lhs_immediate_ok) {
23582723 // swap lhs and rhs
2359 return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
2724 return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true);
23602725 } else {
2361 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2726 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
23622727 }
23632728 } else {
23642729 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
......@@ -2378,7 +2743,7 @@ fn binOp(
23782743 // TODO add optimisations for multiplication
23792744 // with immediates, for example a * 2 can be
23802745 // lowered to a << 1
2381 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2746 return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
23822747 } else {
23832748 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
23842749 }
......@@ -2432,13 +2797,20 @@ fn binOp(
24322797 const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;
24332798 const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null;
24342799
2800 const mir_tag: Mir.Inst.Tag = switch (tag) {
2801 .bit_and => .@"and",
2802 .bit_or => .orr,
2803 .xor => .eor,
2804 else => unreachable,
2805 };
2806
24352807 if (rhs_immediate_ok) {
2436 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2808 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
24372809 } else if (lhs_immediate_ok) {
24382810 // swap lhs and rhs
2439 return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
2811 return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true);
24402812 } else {
2441 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2813 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
24422814 }
24432815 } else {
24442816 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
......@@ -2457,10 +2829,19 @@ fn binOp(
24572829 if (int_info.bits <= 32) {
24582830 const rhs_immediate_ok = rhs == .immediate;
24592831
2832 const mir_tag: Mir.Inst.Tag = switch (tag) {
2833 .shl_exact => .lsl,
2834 .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
2835 .signed => Mir.Inst.Tag.asr,
2836 .unsigned => Mir.Inst.Tag.lsr,
2837 },
2838 else => unreachable,
2839 };
2840
24602841 if (rhs_immediate_ok) {
2461 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2842 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
24622843 } else {
2463 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2844 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
24642845 }
24652846 } else {
24662847 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
......@@ -2512,13 +2893,19 @@ fn binOp(
25122893 const lhs_immediate_ok = lhs == .immediate;
25132894 const rhs_immediate_ok = rhs == .immediate;
25142895
2896 const mir_tag: Mir.Inst.Tag = switch (tag) {
2897 .bool_and => .@"and",
2898 .bool_or => .orr,
2899 else => unreachable,
2900 };
2901
25152902 if (rhs_immediate_ok) {
2516 return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
2903 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
25172904 } else if (lhs_immediate_ok) {
25182905 // swap lhs and rhs
2519 return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
2906 return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true);
25202907 } else {
2521 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2908 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
25222909 }
25232910 },
25242911 else => unreachable,
......@@ -2537,7 +2924,7 @@ fn binOp(
25372924 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
25382925
25392926 if (elem_size == 1) {
2540 const base_tag: Air.Inst.Tag = switch (tag) {
2927 const base_tag: Mir.Inst.Tag = switch (tag) {
25412928 .ptr_add => .add,
25422929 .ptr_sub => .sub,
25432930 else => unreachable,
......@@ -2824,14 +3211,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
28243211
28253212 switch (mc_arg) {
28263213 .none => continue,
2827 .undef => unreachable,
2828 .immediate => unreachable,
2829 .unreach => unreachable,
2830 .dead => unreachable,
2831 .memory => unreachable,
2832 .compare_flags_signed => unreachable,
2833 .compare_flags_unsigned => unreachable,
2834 .ptr_stack_offset => unreachable,
28353214 .register => |reg| {
28363215 try self.register_manager.getReg(reg, null);
28373216 try self.genSetReg(arg_ty, reg, arg_mcv);
......@@ -2842,6 +3221,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
28423221 info.stack_byte_count - offset,
28433222 arg_mcv,
28443223 ),
3224 else => unreachable,
28453225 }
28463226 }
28473227
......@@ -3784,6 +4164,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
37844164 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
37854165 }
37864166 },
4167 .register_c_flag,
4168 .register_v_flag,
4169 => {
4170 return self.fail("TODO implement genSetStack {}", .{mcv});
4171 },
37874172 .memory,
37884173 .stack_argument_offset,
37894174 .stack_offset,
......@@ -4025,6 +4410,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
40254410 } },
40264411 });
40274412 },
4413 .register_c_flag => unreachable, // doesn't fit into a register
4414 .register_v_flag => unreachable, // doesn't fit into a register
40284415 .memory => |addr| {
40294416 // The value is in memory at a hard-coded address.
40304417 // If the type is a pointer, it means the pointer address is at this memory location.
......@@ -4159,6 +4546,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
41594546 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
41604547 }
41614548 },
4549 .register_c_flag,
4550 .register_v_flag,
4551 => {
4552 return self.fail("TODO implement genSetStack {}", .{mcv});
4553 },
41624554 .stack_offset,
41634555 .memory,
41644556 .stack_argument_offset,
src/arch/arm/Emit.zig+22-1
......@@ -79,6 +79,7 @@ pub fn emitMir(
7979 const inst = @intCast(u32, index);
8080 switch (tag) {
8181 .add => try emit.mirDataProcessing(inst),
82 .adds => try emit.mirDataProcessing(inst),
8283 .@"and" => try emit.mirDataProcessing(inst),
8384 .cmp => try emit.mirDataProcessing(inst),
8485 .eor => try emit.mirDataProcessing(inst),
......@@ -87,6 +88,7 @@ pub fn emitMir(
8788 .orr => try emit.mirDataProcessing(inst),
8889 .rsb => try emit.mirDataProcessing(inst),
8990 .sub => try emit.mirDataProcessing(inst),
91 .subs => try emit.mirDataProcessing(inst),
9092
9193 .asr => try emit.mirShift(inst),
9294 .lsl => try emit.mirShift(inst),
......@@ -120,7 +122,7 @@ pub fn emitMir(
120122 .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),
121123
122124 .ldrh => try emit.mirLoadStoreExtra(inst),
123 .ldrsb => try emit.mirLoadStore(inst),
125 .ldrsb => try emit.mirLoadStoreExtra(inst),
124126 .ldrsh => try emit.mirLoadStoreExtra(inst),
125127 .strh => try emit.mirLoadStoreExtra(inst),
126128
......@@ -128,6 +130,10 @@ pub fn emitMir(
128130 .movt => try emit.mirSpecialMove(inst),
129131
130132 .mul => try emit.mirMultiply(inst),
133 .smulbb => try emit.mirMultiply(inst),
134
135 .smull => try emit.mirMultiplyLong(inst),
136 .umull => try emit.mirMultiplyLong(inst),
131137
132138 .nop => try emit.mirNop(),
133139
......@@ -474,6 +480,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
474480
475481 switch (tag) {
476482 .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)),
483 .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)),
477484 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),
478485 .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)),
479486 .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)),
......@@ -482,6 +489,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
482489 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),
483490 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),
484491 .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),
492 .subs => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),
485493 else => unreachable,
486494 }
487495}
......@@ -685,6 +693,19 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {
685693
686694 switch (tag) {
687695 .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)),
696 .smulbb => try emit.writeInstruction(Instruction.smulbb(cond, rrr.rd, rrr.rn, rrr.rm)),
697 else => unreachable,
698 }
699}
700
701fn mirMultiplyLong(emit: *Emit, inst: Mir.Inst.Index) !void {
702 const tag = emit.mir.instructions.items(.tag)[inst];
703 const cond = emit.mir.instructions.items(.cond)[inst];
704 const rrrr = emit.mir.instructions.items(.data)[inst].rrrr;
705
706 switch (tag) {
707 .smull => try emit.writeInstruction(Instruction.smull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)),
708 .umull => try emit.writeInstruction(Instruction.umull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)),
688709 else => unreachable,
689710 }
690711}
src/arch/arm/Mir.zig+19
......@@ -28,6 +28,8 @@ pub const Inst = struct {
2828 pub const Tag = enum(u16) {
2929 /// Add
3030 add,
31 /// Add, update condition flags
32 adds,
3133 /// Bitwise AND
3234 @"and",
3335 /// Arithmetic Shift Right
......@@ -100,6 +102,10 @@ pub const Inst = struct {
100102 rsb,
101103 /// Signed Bit Field Extract
102104 sbfx,
105 /// Signed Multiply (halfwords), bottom half, bottom half
106 smulbb,
107 /// Signed Multiply Long
108 smull,
103109 /// Store Register
104110 str,
105111 /// Store Register Byte
......@@ -108,10 +114,14 @@ pub const Inst = struct {
108114 strh,
109115 /// Subtract
110116 sub,
117 /// Subtract, update condition flags
118 subs,
111119 /// Supervisor Call
112120 svc,
113121 /// Unsigned Bit Field Extract
114122 ubfx,
123 /// Unsigned Multiply Long
124 umull,
115125 };
116126
117127 /// The position of an MIR instruction within the `Mir` instructions array.
......@@ -209,6 +219,15 @@ pub const Inst = struct {
209219 rn: Register,
210220 rm: Register,
211221 },
222 /// Four registers
223 ///
224 /// Used by e.g. smull
225 rrrr: struct {
226 rdlo: Register,
227 rdhi: Register,
228 rn: Register,
229 rm: Register,
230 },
212231 /// An unordered list of registers
213232 ///
214233 /// Used by e.g. push
src/arch/arm/bits.zig+55
......@@ -216,6 +216,18 @@ pub const Instruction = union(enum) {
216216 fixed_2: u5 = 0b00001,
217217 cond: u4,
218218 },
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 },
219231 integer_saturating_arithmetic: packed struct {
220232 rm: u4,
221233 fixed_1: u8 = 0b0000_0101,
......@@ -592,6 +604,7 @@ pub const Instruction = union(enum) {
592604 .data_processing => |v| @bitCast(u32, v),
593605 .multiply => |v| @bitCast(u32, v),
594606 .multiply_long => |v| @bitCast(u32, v),
607 .signed_multiply_halfwords => |v| @bitCast(u32, v),
595608 .integer_saturating_arithmetic => |v| @bitCast(u32, v),
596609 .bit_field_extract => |v| @bitCast(u32, v),
597610 .single_data_transfer => |v| @bitCast(u32, v),
......@@ -691,6 +704,26 @@ pub const Instruction = union(enum) {
691704 };
692705 }
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
694727 fn integerSaturationArithmetic(
695728 cond: Condition,
696729 rd: Register,
......@@ -1093,6 +1126,24 @@ pub const Instruction = union(enum) {
10931126 return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn);
10941127 }
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
10961147 // Bit field extract
10971148
10981149 pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction {
......@@ -1440,6 +1491,10 @@ test "serialize instructions" {
14401491 .inst = Instruction.qadd(.al, .r0, .r7, .r8),
14411492 .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,
14421493 },
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 },
14431498 };
14441499
14451500 for (testcases) |case| {
test/behavior/math.zig-4
......@@ -635,7 +635,6 @@ test "128-bit multiplication" {
635635test "@addWithOverflow" {
636636 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
637637 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
638 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
639638
640639 var result: u8 = undefined;
641640 try expect(@addWithOverflow(u8, 250, 100, &result));
......@@ -679,7 +678,6 @@ test "small int addition" {
679678test "@mulWithOverflow" {
680679 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
681680 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
682 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
683681
684682 var result: u8 = undefined;
685683 try expect(@mulWithOverflow(u8, 86, 3, &result));
......@@ -700,7 +698,6 @@ test "@mulWithOverflow" {
700698test "@subWithOverflow" {
701699 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
702700 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
703 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
704701
705702 var result: u8 = undefined;
706703 try expect(@subWithOverflow(u8, 1, 2, &result));
......@@ -721,7 +718,6 @@ test "@shlWithOverflow" {
721718 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
722719 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
723720 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
724 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
725721
726722 var result: u16 = undefined;
727723 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));