FunctionPass *createAVRDynAllocaSRPass();
FunctionPass *createAVRBranchSelectionPass();
+void initializeAVRExpandPseudoPass(PassRegistry&);
+
/// Contains the AVR backend.
namespace AVR {
using namespace llvm;
+#define AVR_EXPAND_PSEUDO_NAME "AVR pseudo instruction expansion pass"
+
namespace {
/// Expands "placeholder" instructions marked as pseudo into
public:
static char ID;
- AVRExpandPseudo() : MachineFunctionPass(ID) {}
+ AVRExpandPseudo() : MachineFunctionPass(ID) {
+ initializeAVRExpandPseudoPass(*PassRegistry::getPassRegistry());
+ }
bool runOnMachineFunction(MachineFunction &MF) override;
- StringRef getPassName() const override {
- return "AVR pseudo instruction expansion pass";
- }
+ StringRef getPassName() const override { return AVR_EXPAND_PSEUDO_NAME; }
private:
typedef MachineBasicBlock Block;
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
assert(Imm < 63 && "Offset is out of range");
- assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same");
+
+ unsigned TmpLoReg = DstLoReg;
+ unsigned TmpHiReg = DstHiReg;
+
+ // HACK: We shouldn't have instances of this instruction
+ // where src==dest because the instruction itself is
+ // marked earlyclobber. We do however get this instruction when
+ // loading from stack slots where the earlyclobber isn't useful.
+ //
+ // In this case, just use a temporary register.
+ if (DstReg == SrcReg) {
+ TmpLoReg = SCRATCH_REGISTER;
+ TmpHiReg = SCRATCH_REGISTER;
+ }
auto MIBLO = buildMI(MBB, MBBI, OpLo)
- .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
+ .addReg(TmpLoReg, RegState::Define | getDeadRegState(DstIsDead))
.addReg(SrcReg)
.addImm(Imm);
+ // Push the low part of the temporary register to the stack.
+ if (TmpLoReg != DstLoReg)
+ buildMI(MBB, MBBI, AVR::PUSHRr)
+ .addReg(AVR::R0);
+
auto MIBHI = buildMI(MBB, MBBI, OpHi)
- .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
+ .addReg(TmpHiReg, RegState::Define | getDeadRegState(DstIsDead))
.addReg(SrcReg, getKillRegState(SrcIsKill))
.addImm(Imm + 1);
+ // If we need to use a temporary register.
+ if (TmpHiReg != DstHiReg) {
+ // Move the hi result from the tmp register to the destination.
+ buildMI(MBB, MBBI, AVR::MOVRdRr)
+ .addReg(DstHiReg).addReg(SCRATCH_REGISTER);
+
+ // Pop the lo result calculated previously and put it into
+ // the lo destination.
+ buildMI(MBB, MBBI, AVR::POPRd).addReg(DstLoReg);
+ }
+
MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end());
MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end());
} // end of anonymous namespace
+INITIALIZE_PASS(AVRExpandPseudo, "avr-expand-pseudo",
+ AVR_EXPAND_PSEUDO_NAME, false, false)
namespace llvm {
FunctionPass *createAVRExpandPseudoPass() { return new AVRExpandPseudo(); }
let canFoldAsLoad = 1,
isReMaterializable = 1 in
{
+ let Constraints = "@earlyclobber $reg" in
def LDDRdPtrQ : FSTDLDD<0,
(outs GPR8:$reg),
(ins memri:$memri),
Requires<[HasSRAM]>;
let mayLoad = 1,
- hasSideEffects = 0 in
+ hasSideEffects = 0,
+ Constraints = "@earlyclobber $dst" in
def LDDWRdYQ : Pseudo<(outs DREGS:$dst),
(ins memri:$memri),
"lddw\t$dst, $memri",
extern "C" void LLVMInitializeAVRTarget() {
// Register the target.
RegisterTargetMachine<AVRTargetMachine> X(getTheAVRTarget());
+
+ auto &PR = *PassRegistry::getPassRegistry();
+ initializeAVRExpandPseudoPass(PR);
}
const AVRSubtarget *AVRTargetMachine::getSubtargetImpl() const {
+++ /dev/null
-; RUN: llc < %s -march=avr | FileCheck %s
-; XFAIL: *
-
-; This occurs when compiling Rust libcore.
-;
-; Assertion failed:
-; (DstReg != SrcReg && "SrcReg and DstReg cannot be the same")
-; lib/Target/AVR/AVRExpandPseudoInsts.cpp, line 817
-;
-; https://github.com/avr-llvm/llvm/issues/229
-
-; CHECK-LABEL: rust_eh_personality
-declare void @rust_eh_personality()
-
-; CHECK-LABEL: __udivmoddi4
-define void @__udivmoddi4(i64 %arg, i64 %arg1) personality i32 (...)* bitcast (void ()* @rust_eh_personality to i32 (...)*) {
-entry-block:
- %tmp = lshr i64 %arg, 32
- %tmp2 = trunc i64 %tmp to i32
- %tmp3 = trunc i64 %arg to i32
- %tmp4 = add i64 %arg1, -1
- br label %bb135
-
-bb133.loopexit:
- ret void
-
-bb135:
- %carry.0120 = phi i64 [ 0, %entry-block ], [ %phitmp, %bb135 ]
- %q.sroa.12.1119 = phi i32 [ %tmp3, %entry-block ], [ %q.sroa.12.0.extract.trunc, %bb135 ]
- %q.sroa.0.1118 = phi i32 [ 0, %entry-block ], [ %q.sroa.0.0.extract.trunc, %bb135 ]
- %r.sroa.0.1116 = phi i32 [ %tmp2, %entry-block ], [ undef, %bb135 ]
- %r.sroa.0.0.insert.ext62 = zext i32 %r.sroa.0.1116 to i64
- %r.sroa.0.0.insert.insert64 = or i64 0, %r.sroa.0.0.insert.ext62
- %tmp5 = shl nuw nsw i64 %r.sroa.0.0.insert.ext62, 1
- %q.sroa.12.0.insert.ext101 = zext i32 %q.sroa.12.1119 to i64
- %q.sroa.12.0.insert.shift102 = shl nuw i64 %q.sroa.12.0.insert.ext101, 32
- %q.sroa.0.0.insert.ext87 = zext i32 %q.sroa.0.1118 to i64
- %q.sroa.0.0.insert.insert89 = or i64 %q.sroa.12.0.insert.shift102, %q.sroa.0.0.insert.ext87
- %tmp6 = lshr i64 %q.sroa.12.0.insert.ext101, 31
- %tmp7 = lshr i64 %r.sroa.0.0.insert.insert64, 31
- %tmp8 = shl nuw nsw i64 %q.sroa.0.0.insert.ext87, 1
- %tmp9 = or i64 %tmp8, %carry.0120
- %q.sroa.0.0.extract.trunc = trunc i64 %tmp9 to i32
- %tmp10 = lshr i64 %q.sroa.0.0.insert.insert89, 31
- %q.sroa.12.0.extract.trunc = trunc i64 %tmp10 to i32
- %r.sroa.13.0.insert.shift72 = shl i64 %tmp7, 32
- %.masked114 = and i64 %tmp5, 4294967294
- %r.sroa.0.0.insert.ext57 = or i64 %tmp6, %.masked114
- %r.sroa.0.0.insert.insert59 = or i64 %r.sroa.0.0.insert.ext57, %r.sroa.13.0.insert.shift72
- %tmp11 = sub i64 %tmp4, %r.sroa.0.0.insert.insert59
- %tmp12 = ashr i64 %tmp11, 63
- %phitmp = and i64 %tmp12, 1
- %tmp13 = icmp ult i32 undef, 32
- br i1 %tmp13, label %bb135, label %bb133.loopexit
-}
-
--- /dev/null
+# RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - 2>&1 | FileCheck %s
+
+# This test ensures that the pseudo expander can correctly handle the case
+# where we are expanding a 16-bit LDD instruction where the source and
+# destination registers are the same.
+#
+# The instruction itself is earlyclobber and so ISel will never produce an
+# instruction like this, but the stack slot loading can and will.
+
+--- |
+ target triple = "avr--"
+ define void @test_lddw() {
+ entry:
+ ret void
+ }
+...
+
+---
+name: test_lddw
+registers:
+ - { id: 0, class: _ }
+body: |
+ ; CHECK-LABEL: bb.0.entry
+ bb.0.entry:
+
+ ; CHECK-NEXT: early-clobber %r0 = LDDRdPtrQ %r29r28, 1
+ ; CHECK-NEXT: PUSHRr %r0, implicit-def %sp, implicit %sp
+ ; CHECK-NEXT: early-clobber %r0 = LDDRdPtrQ %r29r28, 2
+ ; CHECK-NEXT: MOVRdRr %r29, %r0
+ ; CHECK-NEXT: POPRd %r28, implicit-def %sp, implicit %sp
+
+ early-clobber %r29r28 = LDDWRdYQ %r29r28, 1
+...