]> granicus.if.org Git - llvm/commitdiff
[AVR] Fix displacement overflow for LDDW/STDW
authorDylan McKay <me@dylanmckay.io>
Wed, 4 Oct 2017 09:51:21 +0000 (09:51 +0000)
committerDylan McKay <me@dylanmckay.io>
Wed, 4 Oct 2017 09:51:21 +0000 (09:51 +0000)
In some cases, the code generator attempts to generate instructions such as:

lddw r24, Y+63

which expands to:

ldd r24, Y+63
ldd r25, Y+64 # Oops! This is actually ld r25, Y in the binary

This commit limits the first offset to 62, and thus the second to 63.
It also updates some asserts in AVRExpandPseudoInsts.cpp, including for
INW and OUTW, which appear to be unused.

Patch by Thomas Backman.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@314890 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/AVR/AVRExpandPseudoInsts.cpp
lib/Target/AVR/AVRRegisterInfo.cpp
test/CodeGen/AVR/std-ldd-immediate-overflow.ll [new file with mode: 0644]

index 540e05a929971f818d4c4f9b2fe762d94c6df6aa..400e9bd8fdca89562b3c04db453755f42944b631 100644 (file)
@@ -699,7 +699,9 @@ bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) {
   OpHi = AVR::LDDRdPtrQ;
   TRI->splitReg(DstReg, DstLoReg, DstHiReg);
 
-  assert(Imm <= 63 && "Offset is out of range");
+  // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
+  // allowed for the instruction, 62 is the limit here.
+  assert(Imm <= 62 && "Offset is out of range");
 
   // Use a temporary register if src and dst registers are the same.
   if (DstReg == SrcReg)
@@ -1074,7 +1076,9 @@ bool AVRExpandPseudo::expand<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) {
   OpHi = AVR::STDPtrQRr;
   TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
 
-  assert(Imm <= 63 && "Offset is out of range");
+  // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
+  // allowed for the instruction, 62 is the limit here.
+  assert(Imm <= 62 && "Offset is out of range");
 
   auto MIBLO = buildMI(MBB, MBBI, OpLo)
     .addReg(DstReg)
@@ -1104,7 +1108,9 @@ bool AVRExpandPseudo::expand<AVR::INWRdA>(Block &MBB, BlockIt MBBI) {
   OpHi = AVR::INRdA;
   TRI->splitReg(DstReg, DstLoReg, DstHiReg);
 
-  assert(Imm <= 63 && "Address is out of range");
+  // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
+  // allowed for the instruction, 62 is the limit here.
+  assert(Imm <= 62 && "Address is out of range");
 
   auto MIBLO = buildMI(MBB, MBBI, OpLo)
     .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
@@ -1132,7 +1138,9 @@ bool AVRExpandPseudo::expand<AVR::OUTWARr>(Block &MBB, BlockIt MBBI) {
   OpHi = AVR::OUTARr;
   TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
 
-  assert(Imm <= 63 && "Address is out of range");
+  // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
+  // allowed for the instruction, 62 is the limit here.
+  assert(Imm <= 62 && "Address is out of range");
 
   // 16 bit I/O writes need the high byte first
   auto MIBHI = buildMI(MBB, MBBI, OpHi)
index 249dc5512c2898459ddfd0d332676b1d8afcc67d..7099b29a8bcdfb14afa827d48197866dbba9460f 100644 (file)
@@ -203,7 +203,7 @@ void AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
   // If the offset is too big we have to adjust and restore the frame pointer
   // to materialize a valid load/store with displacement.
   //:TODO: consider using only one adiw/sbiw chain for more than one frame index
-  if (Offset > 63) {
+  if (Offset > 62) {
     unsigned AddOpc = AVR::ADIWRdK, SubOpc = AVR::SBIWRdK;
     int AddOffset = Offset - 63 + 1;
 
diff --git a/test/CodeGen/AVR/std-ldd-immediate-overflow.ll b/test/CodeGen/AVR/std-ldd-immediate-overflow.ll
new file mode 100644 (file)
index 0000000..290e349
--- /dev/null
@@ -0,0 +1,18 @@
+; RUN: llc -O0 < %s -march=avr | FileCheck %s
+
+define i32 @std_ldd_overflow() {
+  %src = alloca [4 x i8]
+  %dst = alloca [4 x i8]
+  %buf = alloca [28 x i16]
+  %1 = bitcast [4 x i8]* %src to i32*
+  store i32 0, i32 *%1
+  %2 = bitcast [4 x i8]* %dst to i8*
+  %3 = bitcast [4 x i8]* %src to i8*
+  call void @llvm.memcpy.p0i8.p0i8.i16(i8* %2, i8* %3, i16 4, i32 1, i1 false)
+; CHECK-NOT: std {{[XYZ]}}+64, {{r[0-9]+}}
+; CHECK-NOT: ldd {{r[0-9]+}}, {{[XYZ]}}+64
+
+  ret i32 0
+}
+
+declare void @llvm.memcpy.p0i8.p0i8.i16(i8* nocapture writeonly, i8* nocapture readonly, i16, i32, i1)