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,...@@ -105,9 +105,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
105const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};105const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
106106
107const MCValue = union(enum) {107const MCValue = union(enum) {
108 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.108 /// No runtime bits. `void` types, empty structs, u0, enums with 1
109 /// TODO Look into deleting this tag and using `dead` instead, since every use109 /// tag, etc.
110 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.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.
111 none,114 none,
112 /// Control flow will not allow this value to be observed.115 /// Control flow will not allow this value to be observed.
113 unreach,116 unreach,
...@@ -116,20 +119,41 @@ const MCValue = union(enum) {...@@ -116,20 +119,41 @@ const MCValue = union(enum) {
116 /// The value is undefined.119 /// The value is undefined.
117 undef,120 undef,
118 /// A pointer-sized integer that fits in a register.121 /// 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.
120 immediate: u32,125 immediate: u32,
121 /// The value is in a target-specific register.126 /// The value is in a target-specific register.
122 register: Register,127 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,
123 /// The value is in memory at a hard-coded address.142 /// 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.
125 memory: u64,146 memory: u64,
126 /// The value is one of the stack variables.147 /// 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.
128 stack_offset: u32,151 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).
130 ptr_stack_offset: u32,154 ptr_stack_offset: u32,
131 /// The value is in the compare flags assuming an unsigned operation,155 /// The value is in the compare flags assuming an unsigned
132 /// with this operator applied on top of it.156 /// operation, with this operator applied on top of it.
133 compare_flags_unsigned: math.CompareOperator,157 compare_flags_unsigned: math.CompareOperator,
134 /// The value is in the compare flags assuming a signed operation,158 /// The value is in the compare flags assuming a signed operation,
135 /// with this operator applied on top of it.159 /// with this operator applied on top of it.
...@@ -554,8 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -554,8 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
554 .trunc_float,578 .trunc_float,
555 => try self.airUnaryMath(inst),579 => try self.airUnaryMath(inst),
556580
557 .add_with_overflow => try self.airAddWithOverflow(inst),581 .add_with_overflow => try self.airOverflow(inst),
558 .sub_with_overflow => try self.airSubWithOverflow(inst),582 .sub_with_overflow => try self.airOverflow(inst),
559 .mul_with_overflow => try self.airMulWithOverflow(inst),583 .mul_with_overflow => try self.airMulWithOverflow(inst),
560 .shl_with_overflow => try self.airShlWithOverflow(inst),584 .shl_with_overflow => try self.airShlWithOverflow(inst),
561585
...@@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {...@@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
726 .register => |reg| {750 .register => |reg| {
727 self.register_manager.freeReg(reg);751 self.register_manager.freeReg(reg);
728 },752 },
753 .register_c_flag,
754 .register_v_flag,
755 => |reg| {
756 self.register_manager.freeReg(reg);
757 self.compare_flags_inst = null;
758 },
729 .compare_flags_signed, .compare_flags_unsigned => {759 .compare_flags_signed, .compare_flags_unsigned => {
730 self.compare_flags_inst = null;760 self.compare_flags_inst = null;
731 },761 },
...@@ -841,8 +871,16 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {...@@ -841,8 +871,16 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
841pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {871pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
842 const stack_mcv = try self.allocRegOrMem(inst, false);872 const stack_mcv = try self.allocRegOrMem(inst, false);
843 log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });873 log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
874
844 const reg_mcv = self.getResolvedInstValue(inst);875 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
846 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];884 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
847 try branch.inst_table.put(self.gpa, inst, stack_mcv);885 try branch.inst_table.put(self.gpa, inst, stack_mcv);
848 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);886 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...@@ -853,7 +891,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
853fn spillCompareFlagsIfOccupied(self: *Self) !void {891fn spillCompareFlagsIfOccupied(self: *Self) !void {
854 if (self.compare_flags_inst) |inst_to_save| {892 if (self.compare_flags_inst) |inst_to_save| {
855 const mcv = self.getResolvedInstValue(inst_to_save);893 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
858 const new_mcv = try self.allocRegOrMem(inst_to_save, true);903 const new_mcv = try self.allocRegOrMem(inst_to_save, true);
859 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);904 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
...@@ -1196,7 +1241,7 @@ fn minMax(...@@ -1196,7 +1241,7 @@ fn minMax(
1196 // register.1241 // register.
1197 assert(lhs_reg != rhs_reg); // see note above1242 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
1201 const cond_choose_lhs: Condition = switch (tag) {1246 const cond_choose_lhs: Condition = switch (tag) {
1202 .max => switch (int_info.signedness) {1247 .max => switch (int_info.signedness) {
...@@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1268 const len = try self.resolveInst(bin_op.rhs);1313 const len = try self.resolveInst(bin_op.rhs);
1269 const len_ty = self.air.typeOf(bin_op.rhs);1314 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);
1272 try self.genSetStack(ptr_ty, stack_offset, ptr);1317 try self.genSetStack(ptr_ty, stack_offset, ptr);
1273 try self.genSetStack(len_ty, stack_offset - 4, len);1318 try self.genSetStack(len_ty, stack_offset - 4, len);
1274 break :result MCValue{ .stack_offset = stack_offset };1319 break :result MCValue{ .stack_offset = stack_offset };
...@@ -1306,24 +1351,321 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1306,24 +1351,321 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1306 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1351 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1307}1352}
13081353
1309fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1354fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1310 _ = inst;1355 const tag = self.air.instructions.items(.tag)[inst];
1311 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});1356 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1312}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 {1420 const mir_tag: Mir.Inst.Tag = switch (tag) {
1315 _ = inst;1421 .add_with_overflow => .adds,
1316 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});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 });
1317}1452}
13181453
1319fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1454fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1320 _ = inst;1455 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1321 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 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 });
1322}1618}
13231619
1324fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1620fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1325 _ = inst;1621 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1326 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});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 });
1327}1669}
13281670
1329fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1671fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1424,7 +1766,6 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)...@@ -1424,7 +1766,6 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
1424 const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*));1766 const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*));
1425 const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align);1767 const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align);
14261768
1427 // TODO optimization for small error unions: put into register
1428 switch (error_union_mcv) {1769 switch (error_union_mcv) {
1429 .register => return self.fail("TODO errUnionPayload for registers", .{}),1770 .register => return self.fail("TODO errUnionPayload for registers", .{}),
1430 .stack_argument_offset => |off| {1771 .stack_argument_offset => |off| {
...@@ -1791,8 +2132,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1791,8 +2132,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1791 .undef => unreachable,2132 .undef => unreachable,
1792 .unreach => unreachable,2133 .unreach => unreachable,
1793 .dead => unreachable,2134 .dead => unreachable,
1794 .compare_flags_unsigned => unreachable,2135 .compare_flags_unsigned,
1795 .compare_flags_signed => unreachable,2136 .compare_flags_signed,
2137 .register_c_flag,
2138 .register_v_flag,
2139 => unreachable, // cannot hold an address
1796 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),2140 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
1797 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),2141 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
1798 .register => |reg| {2142 .register => |reg| {
...@@ -1887,8 +2231,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1887,8 +2231,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1887 .undef => unreachable,2231 .undef => unreachable,
1888 .unreach => unreachable,2232 .unreach => unreachable,
1889 .dead => unreachable,2233 .dead => unreachable,
1890 .compare_flags_unsigned => unreachable,2234 .compare_flags_unsigned,
1891 .compare_flags_signed => unreachable,2235 .compare_flags_signed,
2236 .register_c_flag,
2237 .register_v_flag,
2238 => unreachable, // cannot hold an address
1892 .immediate => |imm| {2239 .immediate => |imm| {
1893 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);2240 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
1894 },2241 },
...@@ -2043,6 +2390,50 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2043,6 +2390,50 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2043 .memory => |addr| {2390 .memory => |addr| {
2044 break :result MCValue{ .memory = addr + struct_field_offset };2391 break :result MCValue{ .memory = addr + struct_field_offset };
2045 },2392 },
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 },
2046 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),2437 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
2047 }2438 }
2048 };2439 };
...@@ -2067,7 +2458,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2067,7 +2458,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
2067/// Asserts that generating an instruction of that form is possible.2458/// Asserts that generating an instruction of that form is possible.
2068fn binOpRegister(2459fn binOpRegister(
2069 self: *Self,2460 self: *Self,
2070 tag: Air.Inst.Tag,2461 mir_tag: Mir.Inst.Tag,
2071 maybe_inst: ?Air.Inst.Index,2462 maybe_inst: ?Air.Inst.Index,
2072 lhs: MCValue,2463 lhs: MCValue,
2073 rhs: MCValue,2464 rhs: MCValue,
...@@ -2112,8 +2503,8 @@ fn binOpRegister(...@@ -2112,8 +2503,8 @@ fn binOpRegister(
2112 };2503 };
2113 defer self.register_manager.unfreezeRegs(&.{rhs_reg});2504 defer self.register_manager.unfreezeRegs(&.{rhs_reg});
21142505
2115 const dest_reg = switch (tag) {2506 const dest_reg = switch (mir_tag) {
2116 .cmp_eq => .r0, // cmp has no destination regardless2507 .cmp => .r0, // cmp has no destination regardless
2117 else => if (maybe_inst) |inst| blk: {2508 else => if (maybe_inst) |inst| blk: {
2118 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2509 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
21192510
...@@ -2130,47 +2521,31 @@ fn binOpRegister(...@@ -2130,47 +2521,31 @@ fn binOpRegister(
2130 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);2521 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
2131 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);2522 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
21322523
2133 const mir_tag: Mir.Inst.Tag = switch (tag) {2524 const mir_data: Mir.Inst.Data = switch (mir_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) {
2153 .add,2525 .add,
2526 .adds,
2154 .sub,2527 .sub,
2155 .cmp_eq,2528 .subs,
2156 .bit_and,2529 .cmp,
2157 .bool_and,2530 .@"and",
2158 .bit_or,2531 .orr,
2159 .bool_or,2532 .eor,
2160 .xor,
2161 => .{ .rr_op = .{2533 => .{ .rr_op = .{
2162 .rd = dest_reg,2534 .rd = dest_reg,
2163 .rn = lhs_reg,2535 .rn = lhs_reg,
2164 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),2536 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
2165 } },2537 } },
2166 .shl_exact,2538 .lsl,
2167 .shr_exact,2539 .asr,
2540 .lsr,
2168 => .{ .rr_shift = .{2541 => .{ .rr_shift = .{
2169 .rd = dest_reg,2542 .rd = dest_reg,
2170 .rm = lhs_reg,2543 .rm = lhs_reg,
2171 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),2544 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
2172 } },2545 } },
2173 .mul => .{ .rrr = .{2546 .mul,
2547 .smulbb,
2548 => .{ .rrr = .{
2174 .rd = dest_reg,2549 .rd = dest_reg,
2175 .rn = lhs_reg,2550 .rn = lhs_reg,
2176 .rm = rhs_reg,2551 .rm = rhs_reg,
...@@ -2200,7 +2575,7 @@ fn binOpRegister(...@@ -2200,7 +2575,7 @@ fn binOpRegister(
2200/// Asserts that generating an instruction of that form is possible.2575/// Asserts that generating an instruction of that form is possible.
2201fn binOpImmediate(2576fn binOpImmediate(
2202 self: *Self,2577 self: *Self,
2203 tag: Air.Inst.Tag,2578 mir_tag: Mir.Inst.Tag,
2204 maybe_inst: ?Air.Inst.Index,2579 maybe_inst: ?Air.Inst.Index,
2205 lhs: MCValue,2580 lhs: MCValue,
2206 rhs: MCValue,2581 rhs: MCValue,
...@@ -2230,8 +2605,8 @@ fn binOpImmediate(...@@ -2230,8 +2605,8 @@ fn binOpImmediate(
2230 };2605 };
2231 defer self.register_manager.unfreezeRegs(&.{lhs_reg});2606 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
22322607
2233 const dest_reg = switch (tag) {2608 const dest_reg = switch (mir_tag) {
2234 .cmp_eq => .r0, // cmp has no destination reg2609 .cmp => .r0, // cmp has no destination reg
2235 else => if (maybe_inst) |inst| blk: {2610 else => if (maybe_inst) |inst| blk: {
2236 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2611 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
22372612
...@@ -2250,40 +2625,23 @@ fn binOpImmediate(...@@ -2250,40 +2625,23 @@ fn binOpImmediate(
22502625
2251 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);2626 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
22522627
2253 const mir_tag: Mir.Inst.Tag = switch (tag) {2628 const mir_data: Mir.Inst.Data = switch (mir_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) {
2272 .add,2629 .add,
2630 .adds,
2273 .sub,2631 .sub,
2274 .cmp_eq,2632 .subs,
2275 .bit_and,2633 .cmp,
2276 .bool_and,2634 .@"and",
2277 .bit_or,2635 .orr,
2278 .bool_or,2636 .eor,
2279 .xor,
2280 => .{ .rr_op = .{2637 => .{ .rr_op = .{
2281 .rd = dest_reg,2638 .rd = dest_reg,
2282 .rn = lhs_reg,2639 .rn = lhs_reg,
2283 .op = Instruction.Operand.fromU32(rhs.immediate).?,2640 .op = Instruction.Operand.fromU32(rhs.immediate).?,
2284 } },2641 } },
2285 .shl_exact,2642 .lsl,
2286 .shr_exact,2643 .asr,
2644 .lsr,
2287 => .{ .rr_shift = .{2645 => .{ .rr_shift = .{
2288 .rd = dest_reg,2646 .rd = dest_reg,
2289 .rm = lhs_reg,2647 .rm = lhs_reg,
...@@ -2352,13 +2710,20 @@ fn binOp(...@@ -2352,13 +2710,20 @@ fn binOp(
2352 else => unreachable,2710 else => unreachable,
2353 };2711 };
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
2355 if (rhs_immediate_ok) {2720 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);
2357 } else if (lhs_immediate_ok) {2722 } else if (lhs_immediate_ok) {
2358 // swap lhs and rhs2723 // 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);
2360 } else {2725 } 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);
2362 }2727 }
2363 } else {2728 } else {
2364 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});2729 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
...@@ -2378,7 +2743,7 @@ fn binOp(...@@ -2378,7 +2743,7 @@ fn binOp(
2378 // TODO add optimisations for multiplication2743 // TODO add optimisations for multiplication
2379 // with immediates, for example a * 2 can be2744 // with immediates, for example a * 2 can be
2380 // lowered to a << 12745 // 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);
2382 } else {2747 } else {
2383 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});2748 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2384 }2749 }
...@@ -2432,13 +2797,20 @@ fn binOp(...@@ -2432,13 +2797,20 @@ fn binOp(
2432 const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;2797 const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;
2433 const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null;2798 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
2435 if (rhs_immediate_ok) {2807 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);
2437 } else if (lhs_immediate_ok) {2809 } else if (lhs_immediate_ok) {
2438 // swap lhs and rhs2810 // 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);
2440 } else {2812 } 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);
2442 }2814 }
2443 } else {2815 } else {
2444 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});2816 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
...@@ -2457,10 +2829,19 @@ fn binOp(...@@ -2457,10 +2829,19 @@ fn binOp(
2457 if (int_info.bits <= 32) {2829 if (int_info.bits <= 32) {
2458 const rhs_immediate_ok = rhs == .immediate;2830 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
2460 if (rhs_immediate_ok) {2841 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);
2462 } else {2843 } 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);
2464 }2845 }
2465 } else {2846 } else {
2466 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});2847 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
...@@ -2512,13 +2893,19 @@ fn binOp(...@@ -2512,13 +2893,19 @@ fn binOp(
2512 const lhs_immediate_ok = lhs == .immediate;2893 const lhs_immediate_ok = lhs == .immediate;
2513 const rhs_immediate_ok = rhs == .immediate;2894 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
2515 if (rhs_immediate_ok) {2902 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);
2517 } else if (lhs_immediate_ok) {2904 } else if (lhs_immediate_ok) {
2518 // swap lhs and rhs2905 // 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);
2520 } else {2907 } 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);
2522 }2909 }
2523 },2910 },
2524 else => unreachable,2911 else => unreachable,
...@@ -2537,7 +2924,7 @@ fn binOp(...@@ -2537,7 +2924,7 @@ fn binOp(
2537 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));2924 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
25382925
2539 if (elem_size == 1) {2926 if (elem_size == 1) {
2540 const base_tag: Air.Inst.Tag = switch (tag) {2927 const base_tag: Mir.Inst.Tag = switch (tag) {
2541 .ptr_add => .add,2928 .ptr_add => .add,
2542 .ptr_sub => .sub,2929 .ptr_sub => .sub,
2543 else => unreachable,2930 else => unreachable,
...@@ -2824,14 +3211,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -2824,14 +3211,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
28243211
2825 switch (mc_arg) {3212 switch (mc_arg) {
2826 .none => continue,3213 .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,
2835 .register => |reg| {3214 .register => |reg| {
2836 try self.register_manager.getReg(reg, null);3215 try self.register_manager.getReg(reg, null);
2837 try self.genSetReg(arg_ty, reg, arg_mcv);3216 try self.genSetReg(arg_ty, reg, arg_mcv);
...@@ -2842,6 +3221,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -2842,6 +3221,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
2842 info.stack_byte_count - offset,3221 info.stack_byte_count - offset,
2843 arg_mcv,3222 arg_mcv,
2844 ),3223 ),
3224 else => unreachable,
2845 }3225 }
2846 }3226 }
28473227
...@@ -3784,6 +4164,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -3784,6 +4164,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
3784 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),4164 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
3785 }4165 }
3786 },4166 },
4167 .register_c_flag,
4168 .register_v_flag,
4169 => {
4170 return self.fail("TODO implement genSetStack {}", .{mcv});
4171 },
3787 .memory,4172 .memory,
3788 .stack_argument_offset,4173 .stack_argument_offset,
3789 .stack_offset,4174 .stack_offset,
...@@ -4025,6 +4410,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4025,6 +4410,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4025 } },4410 } },
4026 });4411 });
4027 },4412 },
4413 .register_c_flag => unreachable, // doesn't fit into a register
4414 .register_v_flag => unreachable, // doesn't fit into a register
4028 .memory => |addr| {4415 .memory => |addr| {
4029 // The value is in memory at a hard-coded address.4416 // The value is in memory at a hard-coded address.
4030 // If the type is a pointer, it means the pointer address is at this memory location.4417 // 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...@@ -4159,6 +4546,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
4159 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),4546 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
4160 }4547 }
4161 },4548 },
4549 .register_c_flag,
4550 .register_v_flag,
4551 => {
4552 return self.fail("TODO implement genSetStack {}", .{mcv});
4553 },
4162 .stack_offset,4554 .stack_offset,
4163 .memory,4555 .memory,
4164 .stack_argument_offset,4556 .stack_argument_offset,
src/arch/arm/Emit.zig+22-1
...@@ -79,6 +79,7 @@ pub fn emitMir(...@@ -79,6 +79,7 @@ pub fn emitMir(
79 const inst = @intCast(u32, index);79 const inst = @intCast(u32, index);
80 switch (tag) {80 switch (tag) {
81 .add => try emit.mirDataProcessing(inst),81 .add => try emit.mirDataProcessing(inst),
82 .adds => try emit.mirDataProcessing(inst),
82 .@"and" => try emit.mirDataProcessing(inst),83 .@"and" => try emit.mirDataProcessing(inst),
83 .cmp => try emit.mirDataProcessing(inst),84 .cmp => try emit.mirDataProcessing(inst),
84 .eor => try emit.mirDataProcessing(inst),85 .eor => try emit.mirDataProcessing(inst),
...@@ -87,6 +88,7 @@ pub fn emitMir(...@@ -87,6 +88,7 @@ pub fn emitMir(
87 .orr => try emit.mirDataProcessing(inst),88 .orr => try emit.mirDataProcessing(inst),
88 .rsb => try emit.mirDataProcessing(inst),89 .rsb => try emit.mirDataProcessing(inst),
89 .sub => try emit.mirDataProcessing(inst),90 .sub => try emit.mirDataProcessing(inst),
91 .subs => try emit.mirDataProcessing(inst),
9092
91 .asr => try emit.mirShift(inst),93 .asr => try emit.mirShift(inst),
92 .lsl => try emit.mirShift(inst),94 .lsl => try emit.mirShift(inst),
...@@ -120,7 +122,7 @@ pub fn emitMir(...@@ -120,7 +122,7 @@ pub fn emitMir(
120 .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),122 .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst),
121123
122 .ldrh => try emit.mirLoadStoreExtra(inst),124 .ldrh => try emit.mirLoadStoreExtra(inst),
123 .ldrsb => try emit.mirLoadStore(inst),125 .ldrsb => try emit.mirLoadStoreExtra(inst),
124 .ldrsh => try emit.mirLoadStoreExtra(inst),126 .ldrsh => try emit.mirLoadStoreExtra(inst),
125 .strh => try emit.mirLoadStoreExtra(inst),127 .strh => try emit.mirLoadStoreExtra(inst),
126128
...@@ -128,6 +130,10 @@ pub fn emitMir(...@@ -128,6 +130,10 @@ pub fn emitMir(
128 .movt => try emit.mirSpecialMove(inst),130 .movt => try emit.mirSpecialMove(inst),
129131
130 .mul => try emit.mirMultiply(inst),132 .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
132 .nop => try emit.mirNop(),138 .nop => try emit.mirNop(),
133139
...@@ -474,6 +480,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -474,6 +480,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
474480
475 switch (tag) {481 switch (tag) {
476 .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)),482 .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)),
477 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),484 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),
478 .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)),485 .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)),
479 .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)),486 .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 {...@@ -482,6 +489,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
482 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),489 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),
483 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),490 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),
484 .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),491 .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)),
485 else => unreachable,493 else => unreachable,
486 }494 }
487}495}
...@@ -685,6 +693,19 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -685,6 +693,19 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {
685693
686 switch (tag) {694 switch (tag) {
687 .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)),695 .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)),
688 else => unreachable,709 else => unreachable,
689 }710 }
690}711}
src/arch/arm/Mir.zig+19
...@@ -28,6 +28,8 @@ pub const Inst = struct {...@@ -28,6 +28,8 @@ pub const Inst = struct {
28 pub const Tag = enum(u16) {28 pub const Tag = enum(u16) {
29 /// Add29 /// Add
30 add,30 add,
31 /// Add, update condition flags
32 adds,
31 /// Bitwise AND33 /// Bitwise AND
32 @"and",34 @"and",
33 /// Arithmetic Shift Right35 /// Arithmetic Shift Right
...@@ -100,6 +102,10 @@ pub const Inst = struct {...@@ -100,6 +102,10 @@ pub const Inst = struct {
100 rsb,102 rsb,
101 /// Signed Bit Field Extract103 /// Signed Bit Field Extract
102 sbfx,104 sbfx,
105 /// Signed Multiply (halfwords), bottom half, bottom half
106 smulbb,
107 /// Signed Multiply Long
108 smull,
103 /// Store Register109 /// Store Register
104 str,110 str,
105 /// Store Register Byte111 /// Store Register Byte
...@@ -108,10 +114,14 @@ pub const Inst = struct {...@@ -108,10 +114,14 @@ pub const Inst = struct {
108 strh,114 strh,
109 /// Subtract115 /// Subtract
110 sub,116 sub,
117 /// Subtract, update condition flags
118 subs,
111 /// Supervisor Call119 /// Supervisor Call
112 svc,120 svc,
113 /// Unsigned Bit Field Extract121 /// Unsigned Bit Field Extract
114 ubfx,122 ubfx,
123 /// Unsigned Multiply Long
124 umull,
115 };125 };
116126
117 /// The position of an MIR instruction within the `Mir` instructions array.127 /// The position of an MIR instruction within the `Mir` instructions array.
...@@ -209,6 +219,15 @@ pub const Inst = struct {...@@ -209,6 +219,15 @@ pub const Inst = struct {
209 rn: Register,219 rn: Register,
210 rm: Register,220 rm: Register,
211 },221 },
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 },
212 /// An unordered list of registers231 /// An unordered list of registers
213 ///232 ///
214 /// Used by e.g. push233 /// Used by e.g. push
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-4
...@@ -635,7 +635,6 @@ test "128-bit multiplication" {...@@ -635,7 +635,6 @@ test "128-bit multiplication" {
635test "@addWithOverflow" {635test "@addWithOverflow" {
636 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO636 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
637 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO637 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
638 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
639638
640 var result: u8 = undefined;639 var result: u8 = undefined;
641 try expect(@addWithOverflow(u8, 250, 100, &result));640 try expect(@addWithOverflow(u8, 250, 100, &result));
...@@ -679,7 +678,6 @@ test "small int addition" {...@@ -679,7 +678,6 @@ test "small int addition" {
679test "@mulWithOverflow" {678test "@mulWithOverflow" {
680 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO679 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
681 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO680 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
682 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
683681
684 var result: u8 = undefined;682 var result: u8 = undefined;
685 try expect(@mulWithOverflow(u8, 86, 3, &result));683 try expect(@mulWithOverflow(u8, 86, 3, &result));
...@@ -700,7 +698,6 @@ test "@mulWithOverflow" {...@@ -700,7 +698,6 @@ test "@mulWithOverflow" {
700test "@subWithOverflow" {698test "@subWithOverflow" {
701 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO699 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
702 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO700 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
703 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
704701
705 var result: u8 = undefined;702 var result: u8 = undefined;
706 try expect(@subWithOverflow(u8, 1, 2, &result));703 try expect(@subWithOverflow(u8, 1, 2, &result));
...@@ -721,7 +718,6 @@ test "@shlWithOverflow" {...@@ -721,7 +718,6 @@ test "@shlWithOverflow" {
721 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO718 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
722 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO719 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
723 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO720 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
724 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
725721
726 var result: u16 = undefined;722 var result: u16 = undefined;
727 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));723 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));