diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.cpp b/llvm/lib/Target/AVR/AVRInstrInfo.cpp --- a/llvm/lib/Target/AVR/AVRInstrInfo.cpp +++ b/llvm/lib/Target/AVR/AVRInstrInfo.cpp @@ -46,8 +46,9 @@ const AVRRegisterInfo &TRI = *STI.getRegisterInfo(); unsigned Opc; - // Not all AVR devices support the 16-bit `MOVW` instruction. if (AVR::DREGSRegClass.contains(DestReg, SrcReg)) { + // If our AVR has `movw`, let's emit that; otherwise let's emit two separate + // `mov`s. if (STI.hasMOVW() && AVR::DREGSMOVWRegClass.contains(DestReg, SrcReg)) { BuildMI(MBB, MI, DL, get(AVR::MOVWRdRr), DestReg) .addReg(SrcReg, getKillRegState(KillSrc)); @@ -57,11 +58,44 @@ TRI.splitReg(DestReg, DestLo, DestHi); TRI.splitReg(SrcReg, SrcLo, SrcHi); - // Copy each individual register with the `MOV` instruction. - BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestLo) - .addReg(SrcLo, getKillRegState(KillSrc)); - BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestHi) - .addReg(SrcHi, getKillRegState(KillSrc)); + bool CopyHasOverlappingRegs = DestLo == SrcHi; + + // Usually, for readability & consistency, we emit: + // + // ``` + // mov $DestLo, $SrcLo + // mov $DestHi, $SrcHi + // ``` + // + // ... but if our registers overlap, then we have to switch the order: + // + // ``` + // mov $DestHi, $SrcHi + // mov $DestLo, $SrcLo + // ``` + // + // ... as not to garble the operation. + // + // E.g. imagine trying to expand `$r25r24 = COPY $r24r23` - doing it the + // first way would yield: + // + // ``` + // mov r24, r23 + // mov r25, r24 + // ``` + // + // ... which would always set both registers to `r23`. + if (CopyHasOverlappingRegs) { + BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestHi) + .addReg(SrcHi, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestLo) + .addReg(SrcLo, getKillRegState(KillSrc)); + } else { + BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestLo) + .addReg(SrcLo, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestHi) + .addReg(SrcHi, getKillRegState(KillSrc)); + } } } else { if (AVR::GPR8RegClass.contains(DestReg, SrcReg)) { diff --git a/llvm/test/CodeGen/AVR/pseudo/COPY.mir b/llvm/test/CodeGen/AVR/pseudo/COPY.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/pseudo/COPY.mir @@ -0,0 +1,47 @@ +# RUN: llc -O0 %s -o - | FileCheck %s + +--- | + target triple = "avr--" + + define void @test_copy_nonoverlapping() { + entry: + ret void + } + + define void @test_copy_overlapping() { + entry: + ret void + } + + declare void @foo(i16 %0) +... + +--- +name: test_copy_nonoverlapping +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $r25r24 + + ; CHECK-LABEL: test_copy_nonoverlapping: + ; CHECK: mov r22, r24 + ; CHECK-NEXT: mov r23, r25 + + $r23r22 = COPY $r25r24 + RCALLk @foo, implicit $sp, implicit killed $r24r23 +... + +--- +name: test_copy_overlapping +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $r24r23 + + ; CHECK-LABEL: test_copy_overlapping: + ; CHECK: mov r25, r24 + ; CHECK-NEXT: mov r24, r23 + + $r25r24 = COPY $r24r23 + RCALLk @foo, implicit $sp, implicit killed $r25r24 +... diff --git a/llvm/test/CodeGen/AVR/rust-bug-98167.ll b/llvm/test/CodeGen/AVR/rust-bug-98167.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/rust-bug-98167.ll @@ -0,0 +1,22 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +; The bug can be found here: +; https://github.com/rust-lang/rust/issues/98167 +; +; In this test, `extractvalue` + `call` generate a copy with overlapping +; registers (`$r25r24 = COPY $r24r23`) that used to be expanded incorrectly. + +define void @main() { +; CHECK-LABEL: main: +; CHECK: rcall foo +; CHECK-NEXT: mov r25, r24 +; CHECK-NEXT: mov r24, r23 +; CHECK-NEXT: rcall bar + %1 = call { i8, i16 } @foo() + %2 = extractvalue { i8, i16 } %1, 1 + call void @bar(i16 %2) + ret void +} + +declare { i8, i16 } @foo() +declare void @bar(i16 %0)