| ... | ... | @@ -928,7 +928,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 928 | 928 | // TODO separate architectures with registers from |
| 929 | 929 | // stack-based architectures (spu_2) |
| 930 | 930 | if (callee_preserved_regs.len > 0) { |
| 931 | | if (self.register_manager.tryAllocReg(inst)) |reg| { |
| 931 | if (self.register_manager.tryAllocReg(inst, &.{})) |reg| { |
| 932 | 932 | return MCValue{ .register = registerAlias(reg, abi_size) }; |
| 933 | 933 | } |
| 934 | 934 | } |
| ... | ... | @@ -940,6 +940,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 940 | 940 | |
| 941 | 941 | pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void { |
| 942 | 942 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 943 | log.debug("spilling {*} to stack mcv {any}", .{ inst, stack_mcv }); |
| 943 | 944 | const reg_mcv = self.getResolvedInstValue(inst); |
| 944 | 945 | assert(reg == toCanonicalReg(reg_mcv.register)); |
| 945 | 946 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| ... | ... | @@ -951,7 +952,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 951 | 952 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 952 | 953 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 953 | 954 | fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register { |
| 954 | | const reg = try self.register_manager.allocRegWithoutTracking(); |
| 955 | const reg = try self.register_manager.allocRegWithoutTracking(&.{}); |
| 955 | 956 | try self.genSetReg(src, ty, reg, mcv); |
| 956 | 957 | return reg; |
| 957 | 958 | } |
| ... | ... | @@ -960,7 +961,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 960 | 961 | /// `reg_owner` is the instruction that gets associated with the register in the register table. |
| 961 | 962 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 962 | 963 | fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue { |
| 963 | | const reg = try self.register_manager.allocReg(reg_owner); |
| 964 | const reg = try self.register_manager.allocReg(reg_owner, &.{}); |
| 964 | 965 | try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); |
| 965 | 966 | return MCValue{ .register = reg }; |
| 966 | 967 | } |
| ... | ... | @@ -1380,36 +1381,124 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1380 | 1381 | } |
| 1381 | 1382 | } |
| 1382 | 1383 | |
| 1384 | fn armOperandShouldBeRegister(self: *Self, src: LazySrcLoc, mcv: MCValue) !bool { |
| 1385 | return switch (mcv) { |
| 1386 | .none => unreachable, |
| 1387 | .undef => unreachable, |
| 1388 | .dead, .unreach => unreachable, |
| 1389 | .compare_flags_unsigned => unreachable, |
| 1390 | .compare_flags_signed => unreachable, |
| 1391 | .ptr_stack_offset => unreachable, |
| 1392 | .ptr_embedded_in_code => unreachable, |
| 1393 | .immediate => |imm| blk: { |
| 1394 | if (imm > std.math.maxInt(u32)) return self.fail(src, "TODO ARM binary arithmetic immediate larger than u32", .{}); |
| 1395 | |
| 1396 | // Load immediate into register if it doesn't fit |
| 1397 | // in an operand |
| 1398 | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) == null; |
| 1399 | }, |
| 1400 | .register => true, |
| 1401 | .stack_offset, |
| 1402 | .embedded_in_code, |
| 1403 | .memory, |
| 1404 | => true, |
| 1405 | }; |
| 1406 | } |
| 1407 | |
| 1383 | 1408 | fn genArmBinOp(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, op: ir.Inst.Tag) !MCValue { |
| 1384 | 1409 | const lhs = try self.resolveInst(op_lhs); |
| 1385 | 1410 | const rhs = try self.resolveInst(op_rhs); |
| 1386 | 1411 | |
| 1412 | const lhs_is_register = lhs == .register; |
| 1413 | const rhs_is_register = rhs == .register; |
| 1414 | const lhs_should_be_register = try self.armOperandShouldBeRegister(op_lhs.src, lhs); |
| 1415 | const rhs_should_be_register = try self.armOperandShouldBeRegister(op_rhs.src, rhs); |
| 1416 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs); |
| 1417 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs); |
| 1418 | |
| 1387 | 1419 | // Destination must be a register |
| 1388 | 1420 | var dst_mcv: MCValue = undefined; |
| 1389 | | var lhs_mcv: MCValue = undefined; |
| 1390 | | var rhs_mcv: MCValue = undefined; |
| 1391 | | if (self.reuseOperand(inst, 0, lhs)) { |
| 1392 | | // LHS is the destination |
| 1393 | | // RHS is the source |
| 1394 | | lhs_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; |
| 1395 | | rhs_mcv = rhs; |
| 1396 | | dst_mcv = lhs_mcv; |
| 1397 | | } else if (self.reuseOperand(inst, 1, rhs)) { |
| 1398 | | // RHS is the destination |
| 1399 | | // LHS is the source |
| 1400 | | lhs_mcv = lhs; |
| 1401 | | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; |
| 1402 | | dst_mcv = rhs_mcv; |
| 1421 | var lhs_mcv = lhs; |
| 1422 | var rhs_mcv = rhs; |
| 1423 | var swap_lhs_and_rhs = false; |
| 1424 | |
| 1425 | // Allocate registers for operands and/or destination |
| 1426 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1427 | if (reuse_lhs) { |
| 1428 | // Allocate 0 or 1 registers |
| 1429 | if (!rhs_is_register and rhs_should_be_register) { |
| 1430 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) }; |
| 1431 | branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv); |
| 1432 | } |
| 1433 | dst_mcv = lhs; |
| 1434 | } else if (reuse_rhs) { |
| 1435 | // Allocate 0 or 1 registers |
| 1436 | if (!lhs_is_register and lhs_should_be_register) { |
| 1437 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) }; |
| 1438 | branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv); |
| 1439 | } |
| 1440 | dst_mcv = rhs; |
| 1441 | |
| 1442 | swap_lhs_and_rhs = true; |
| 1403 | 1443 | } else { |
| 1404 | | // TODO save 1 copy instruction by directly allocating the destination register |
| 1405 | | // LHS is the destination |
| 1406 | | // RHS is the source |
| 1407 | | lhs_mcv = try self.copyToNewRegister(inst, lhs); |
| 1408 | | rhs_mcv = rhs; |
| 1409 | | dst_mcv = lhs_mcv; |
| 1444 | // Allocate 1 or 2 registers |
| 1445 | if (lhs_should_be_register and rhs_should_be_register) { |
| 1446 | if (lhs_is_register and rhs_is_register) { |
| 1447 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) }; |
| 1448 | } else if (lhs_is_register) { |
| 1449 | // Move RHS to register |
| 1450 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) }; |
| 1451 | rhs_mcv = dst_mcv; |
| 1452 | } else if (rhs_is_register) { |
| 1453 | // Move LHS to register |
| 1454 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; |
| 1455 | lhs_mcv = dst_mcv; |
| 1456 | } else { |
| 1457 | // Move LHS and RHS to register |
| 1458 | const regs = try self.register_manager.allocRegs(2, .{ inst, op_rhs }, &.{}); |
| 1459 | lhs_mcv = MCValue{ .register = regs[0] }; |
| 1460 | rhs_mcv = MCValue{ .register = regs[1] }; |
| 1461 | dst_mcv = lhs_mcv; |
| 1462 | |
| 1463 | branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv); |
| 1464 | } |
| 1465 | } else if (lhs_should_be_register) { |
| 1466 | // RHS is immediate |
| 1467 | if (lhs_is_register) { |
| 1468 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) }; |
| 1469 | } else { |
| 1470 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; |
| 1471 | lhs_mcv = dst_mcv; |
| 1472 | } |
| 1473 | } else if (rhs_should_be_register) { |
| 1474 | // LHS is immediate |
| 1475 | if (rhs_is_register) { |
| 1476 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; |
| 1477 | } else { |
| 1478 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; |
| 1479 | rhs_mcv = dst_mcv; |
| 1480 | } |
| 1481 | |
| 1482 | swap_lhs_and_rhs = true; |
| 1483 | } else unreachable; // binary operation on two immediates |
| 1410 | 1484 | } |
| 1411 | 1485 | |
| 1412 | | try self.genArmBinOpCode(inst.src, dst_mcv.register, lhs_mcv, rhs_mcv, op); |
| 1486 | // Move the operands to the newly allocated registers |
| 1487 | if (lhs_mcv == .register and !lhs_is_register) { |
| 1488 | try self.genSetReg(op_lhs.src, op_lhs.ty, lhs_mcv.register, lhs); |
| 1489 | } |
| 1490 | if (rhs_mcv == .register and !rhs_is_register) { |
| 1491 | try self.genSetReg(op_rhs.src, op_rhs.ty, rhs_mcv.register, rhs); |
| 1492 | } |
| 1493 | |
| 1494 | try self.genArmBinOpCode( |
| 1495 | inst.src, |
| 1496 | dst_mcv.register, |
| 1497 | lhs_mcv, |
| 1498 | rhs_mcv, |
| 1499 | swap_lhs_and_rhs, |
| 1500 | op, |
| 1501 | ); |
| 1413 | 1502 | return dst_mcv; |
| 1414 | 1503 | } |
| 1415 | 1504 | |
| ... | ... | @@ -1419,11 +1508,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1419 | 1508 | dst_reg: Register, |
| 1420 | 1509 | lhs_mcv: MCValue, |
| 1421 | 1510 | rhs_mcv: MCValue, |
| 1511 | swap_lhs_and_rhs: bool, |
| 1422 | 1512 | op: ir.Inst.Tag, |
| 1423 | 1513 | ) !void { |
| 1424 | | assert(lhs_mcv == .register or lhs_mcv == .register); |
| 1514 | assert(lhs_mcv == .register or rhs_mcv == .register); |
| 1425 | 1515 | |
| 1426 | | const swap_lhs_and_rhs = rhs_mcv == .register and lhs_mcv != .register; |
| 1427 | 1516 | const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register; |
| 1428 | 1517 | const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv; |
| 1429 | 1518 | |
| ... | ... | @@ -1435,19 +1524,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1435 | 1524 | .compare_flags_signed => unreachable, |
| 1436 | 1525 | .ptr_stack_offset => unreachable, |
| 1437 | 1526 | .ptr_embedded_in_code => unreachable, |
| 1438 | | .immediate => |imm| blk: { |
| 1439 | | if (imm > std.math.maxInt(u32)) return self.fail(src, "TODO ARM binary arithmetic immediate larger than u32", .{}); |
| 1440 | | |
| 1441 | | // Load immediate into register if it doesn't fit |
| 1442 | | // as an operand |
| 1443 | | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse |
| 1444 | | Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none); |
| 1445 | | }, |
| 1527 | .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?, |
| 1446 | 1528 | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), |
| 1447 | 1529 | .stack_offset, |
| 1448 | 1530 | .embedded_in_code, |
| 1449 | 1531 | .memory, |
| 1450 | | => Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none), |
| 1532 | => unreachable, |
| 1451 | 1533 | }; |
| 1452 | 1534 | |
| 1453 | 1535 | switch (op) { |
| ... | ... | @@ -2613,10 +2695,42 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2613 | 2695 | const lhs = try self.resolveInst(inst.lhs); |
| 2614 | 2696 | const rhs = try self.resolveInst(inst.rhs); |
| 2615 | 2697 | |
| 2616 | | const src_mcv = rhs; |
| 2617 | | const dst_mcv = if (lhs != .register) try self.copyToNewRegister(inst.lhs, lhs) else lhs; |
| 2698 | const lhs_is_register = lhs == .register; |
| 2699 | const rhs_is_register = rhs == .register; |
| 2700 | // lhs should always be a register |
| 2701 | const rhs_should_be_register = try self.armOperandShouldBeRegister(inst.rhs.src, rhs); |
| 2702 | |
| 2703 | var lhs_mcv = lhs; |
| 2704 | var rhs_mcv = rhs; |
| 2705 | |
| 2706 | // Allocate registers |
| 2707 | if (rhs_should_be_register) { |
| 2708 | if (!lhs_is_register and !rhs_is_register) { |
| 2709 | const regs = try self.register_manager.allocRegs(2, .{ inst.rhs, inst.lhs }, &.{}); |
| 2710 | lhs_mcv = MCValue{ .register = regs[0] }; |
| 2711 | rhs_mcv = MCValue{ .register = regs[1] }; |
| 2712 | } else if (!rhs_is_register) { |
| 2713 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.rhs, &.{}) }; |
| 2714 | } |
| 2715 | } |
| 2716 | if (!lhs_is_register) { |
| 2717 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.lhs, &.{}) }; |
| 2718 | } |
| 2719 | |
| 2720 | // Move the operands to the newly allocated registers |
| 2721 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 2722 | if (lhs_mcv == .register and !lhs_is_register) { |
| 2723 | try self.genSetReg(inst.lhs.src, inst.lhs.ty, lhs_mcv.register, lhs); |
| 2724 | branch.inst_table.putAssumeCapacity(inst.lhs, lhs); |
| 2725 | } |
| 2726 | if (rhs_mcv == .register and !rhs_is_register) { |
| 2727 | try self.genSetReg(inst.rhs.src, inst.rhs.ty, rhs_mcv.register, rhs); |
| 2728 | branch.inst_table.putAssumeCapacity(inst.rhs, rhs); |
| 2729 | } |
| 2730 | |
| 2731 | // The destination register is not present in the cmp instruction |
| 2732 | try self.genArmBinOpCode(inst.base.src, undefined, lhs_mcv, rhs_mcv, false, .cmp_eq); |
| 2618 | 2733 | |
| 2619 | | try self.genArmBinOpCode(inst.base.src, dst_mcv.register, dst_mcv, src_mcv, .cmp_eq); |
| 2620 | 2734 | const info = inst.lhs.ty.intInfo(self.target.*); |
| 2621 | 2735 | return switch (info.signedness) { |
| 2622 | 2736 | .signed => MCValue{ .compare_flags_signed = op }, |