From b53cc014d0f47b898c9daca34566c16dda6c4c1e Mon Sep 17 00:00:00 2001 From: David Goodwin Date: Thu, 23 Jul 2009 17:06:46 +0000 Subject: [PATCH] Fix frame index elimination to correctly handle thumb-2 addressing modes that don't allow negative offsets. During frame elimination convert *i12 opcode to a *i8 when necessary due to a negative offset. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@76883 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/ARMBaseInstrInfo.h | 7 +++ lib/Target/ARM/ARMBaseRegisterInfo.cpp | 71 ++++++++++++++++++++------ lib/Target/ARM/ARMBaseRegisterInfo.h | 7 +++ lib/Target/ARM/ARMInstrInfo.cpp | 5 ++ lib/Target/ARM/ARMInstrInfo.h | 7 +++ lib/Target/ARM/ARMInstrThumb2.td | 2 +- lib/Target/ARM/Thumb1InstrInfo.cpp | 6 +++ lib/Target/ARM/Thumb1InstrInfo.h | 7 +++ lib/Target/ARM/Thumb2InstrInfo.cpp | 23 +++++++++ lib/Target/ARM/Thumb2InstrInfo.h | 7 +++ 10 files changed, 124 insertions(+), 18 deletions(-) diff --git a/lib/Target/ARM/ARMBaseInstrInfo.h b/lib/Target/ARM/ARMBaseInstrInfo.h index 63e08da9b6e..be952d8b8c4 100644 --- a/lib/Target/ARM/ARMBaseInstrInfo.h +++ b/lib/Target/ARM/ARMBaseInstrInfo.h @@ -215,6 +215,13 @@ public: // Return the opcode that implements 'Op', or 0 if no opcode virtual unsigned getOpcode(ARMII::Op Op) const =0; + // If 'opcode' is an instruction with an unsigned offset that also + // has a version with a signed offset, return the opcode for the + // version with the signed offset. In 'NumBits' return the number of + // bits for the signed offset. + virtual unsigned unsignedOffsetOpcodeToSigned(unsigned opcode, + unsigned *NumBits) const = 0; + // Return true if the block does not fall through. virtual bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const =0; diff --git a/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/lib/Target/ARM/ARMBaseRegisterInfo.cpp index 9e21f3cf719..ec5e89f045b 100644 --- a/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ b/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -142,6 +142,11 @@ getOpcode(int Op) const { return TII.getOpcode((ARMII::Op)Op); } +unsigned ARMBaseRegisterInfo:: +unsignedOffsetOpcodeToSigned(unsigned opcode, unsigned *NumBits) const { + return TII.unsignedOffsetOpcodeToSigned(opcode, NumBits); +} + const unsigned* ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { static const unsigned CalleeSavedRegs[] = { @@ -1109,6 +1114,8 @@ eliminateFrameIndex(MachineBasicBlock::iterator II, int InstrOffs = 0; unsigned NumBits = 0; unsigned Scale = 1; + bool encodedOffset = true; + bool HandlesNeg = true; switch (AddrMode) { case ARMII::AddrMode2: { ImmIdx = i+2; @@ -1139,17 +1146,21 @@ eliminateFrameIndex(MachineBasicBlock::iterator II, ImmIdx = i+1; InstrOffs = MI.getOperand(ImmIdx).getImm(); NumBits = 12; + encodedOffset = false; + HandlesNeg = false; break; } case ARMII::AddrModeT2_i8: { ImmIdx = i+1; InstrOffs = MI.getOperand(ImmIdx).getImm(); NumBits = 8; + encodedOffset = false; break; } case ARMII::AddrModeT2_so: { ImmIdx = i+2; InstrOffs = MI.getOperand(ImmIdx).getImm(); + encodedOffset = false; break; } default: @@ -1160,29 +1171,55 @@ eliminateFrameIndex(MachineBasicBlock::iterator II, Offset += InstrOffs * Scale; assert((Offset & (Scale-1)) == 0 && "Can't encode this offset!"); if (Offset < 0) { + // For addrmodes that cannot handle negative offsets, convert to + // an opcode that can, or set NumBits == 0 to avoid folding + // address computation + if (!HandlesNeg) { + unsigned usop = unsignedOffsetOpcodeToSigned(Opcode, &NumBits); + if (usop != 0) { + MI.setDesc(TII.get(usop)); + HandlesNeg = true; + Opcode = usop; + } + else { + NumBits = 0; + } + } + Offset = -Offset; isSub = true; } - // Common case: small offset, fits into instruction. - MachineOperand &ImmOp = MI.getOperand(ImmIdx); - int ImmedOffset = Offset / Scale; - unsigned Mask = (1 << NumBits) - 1; - if ((unsigned)Offset <= Mask * Scale) { - // Replace the FrameIndex with sp - MI.getOperand(i).ChangeToRegister(FrameReg, false); - if (isSub) - ImmedOffset |= 1 << NumBits; + // Attempt to fold address comp. if opcode has offset bits + if (NumBits > 0) { + // Common case: small offset, fits into instruction. + MachineOperand &ImmOp = MI.getOperand(ImmIdx); + int ImmedOffset = Offset / Scale; + unsigned Mask = (1 << NumBits) - 1; + if ((unsigned)Offset <= Mask * Scale) { + // Replace the FrameIndex with sp + MI.getOperand(i).ChangeToRegister(FrameReg, false); + if (isSub) { + if (encodedOffset) + ImmedOffset |= 1 << NumBits; + else + ImmedOffset = -ImmedOffset; + } + ImmOp.ChangeToImmediate(ImmedOffset); + return; + } + + // Otherwise, it didn't fit. Pull in what we can to simplify the immed. + ImmedOffset = ImmedOffset & Mask; + if (isSub) { + if (encodedOffset) + ImmedOffset |= 1 << NumBits; + else + ImmedOffset = -ImmedOffset; + } ImmOp.ChangeToImmediate(ImmedOffset); - return; + Offset &= ~(Mask*Scale); } - - // Otherwise, it didn't fit. Pull in what we can to simplify the immed. - ImmedOffset = ImmedOffset & Mask; - if (isSub) - ImmedOffset |= 1 << NumBits; - ImmOp.ChangeToImmediate(ImmedOffset); - Offset &= ~(Mask*Scale); } // If we get here, the immediate doesn't fit into the instruction. We folded diff --git a/lib/Target/ARM/ARMBaseRegisterInfo.h b/lib/Target/ARM/ARMBaseRegisterInfo.h index 9165bbc883e..ac5e6b67338 100644 --- a/lib/Target/ARM/ARMBaseRegisterInfo.h +++ b/lib/Target/ARM/ARMBaseRegisterInfo.h @@ -59,6 +59,13 @@ protected: // Return the opcode that implements 'Op', or 0 if no opcode unsigned getOpcode(int Op) const; + // If 'opcode' is an instruction with an unsigned offset that also + // has a version with a signed offset, return the opcode for the + // version with the signed offset. In 'NumBits' return the number of + // bits for the signed offset. + unsigned unsignedOffsetOpcodeToSigned(unsigned opcode, + unsigned *NumBits) const; + public: /// getRegisterNumbering - Given the enum value for some register, e.g. /// ARM::LR, return the number that it corresponds to (e.g. 14). It diff --git a/lib/Target/ARM/ARMInstrInfo.cpp b/lib/Target/ARM/ARMInstrInfo.cpp index 688dc31c139..45b77c83bee 100644 --- a/lib/Target/ARM/ARMInstrInfo.cpp +++ b/lib/Target/ARM/ARMInstrInfo.cpp @@ -29,6 +29,11 @@ ARMInstrInfo::ARMInstrInfo(const ARMSubtarget &STI) : ARMBaseInstrInfo(STI), RI(*this, STI) { } +unsigned ARMInstrInfo:: +unsignedOffsetOpcodeToSigned(unsigned opcode, unsigned *NumBits) const { + return 0; +} + unsigned ARMInstrInfo:: getUnindexedOpcode(unsigned Opc) const { switch (Opc) { diff --git a/lib/Target/ARM/ARMInstrInfo.h b/lib/Target/ARM/ARMInstrInfo.h index 3e9f0204fe0..8ff09123da9 100644 --- a/lib/Target/ARM/ARMInstrInfo.h +++ b/lib/Target/ARM/ARMInstrInfo.h @@ -35,6 +35,13 @@ public: // Return the opcode that implements 'Op', or 0 if no opcode unsigned getOpcode(ARMII::Op Op) const; + // If 'opcode' is an instruction with an unsigned offset that also + // has a version with a signed offset, return the opcode for the + // version with the signed offset. In 'NumBits' return the number of + // bits for the signed offset. + unsigned unsignedOffsetOpcodeToSigned(unsigned opcode, + unsigned *NumBits) const; + // Return true if the block does not fall through. bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const; diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td index 5361bb59cfe..80b0d68aa29 100644 --- a/lib/Target/ARM/ARMInstrThumb2.td +++ b/lib/Target/ARM/ARMInstrThumb2.td @@ -109,7 +109,7 @@ def t2addrmode_imm12 : Operand, let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); } -// t2addrmode_imm8 := reg - imm8 +// t2addrmode_imm8 := reg +/- imm8 def t2addrmode_imm8 : Operand, ComplexPattern { let PrintMethod = "printT2AddrModeImm8Operand"; diff --git a/lib/Target/ARM/Thumb1InstrInfo.cpp b/lib/Target/ARM/Thumb1InstrInfo.cpp index ddc6e0d154a..7bec736503e 100644 --- a/lib/Target/ARM/Thumb1InstrInfo.cpp +++ b/lib/Target/ARM/Thumb1InstrInfo.cpp @@ -30,6 +30,12 @@ unsigned Thumb1InstrInfo::getUnindexedOpcode(unsigned Opc) const { return 0; } +unsigned +Thumb1InstrInfo::unsignedOffsetOpcodeToSigned(unsigned opcode, + unsigned *NumBits) const { + return 0; +} + unsigned Thumb1InstrInfo::getOpcode(ARMII::Op Op) const { switch (Op) { case ARMII::ADDri: return ARM::tADDi8; diff --git a/lib/Target/ARM/Thumb1InstrInfo.h b/lib/Target/ARM/Thumb1InstrInfo.h index 67b78fbedfa..a1c9f04ef74 100644 --- a/lib/Target/ARM/Thumb1InstrInfo.h +++ b/lib/Target/ARM/Thumb1InstrInfo.h @@ -34,6 +34,13 @@ public: // Return the opcode that implements 'Op', or 0 if no opcode unsigned getOpcode(ARMII::Op Op) const; + // If 'opcode' is an instruction with an unsigned offset that also + // has a version with a signed offset, return the opcode for the + // version with the signed offset. In 'NumBits' return the number of + // bits for the signed offset. + unsigned unsignedOffsetOpcodeToSigned(unsigned opcode, + unsigned *NumBits) const; + // Return true if the block does not fall through. bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const; diff --git a/lib/Target/ARM/Thumb2InstrInfo.cpp b/lib/Target/ARM/Thumb2InstrInfo.cpp index 081cf4f5503..d92856cf0c3 100644 --- a/lib/Target/ARM/Thumb2InstrInfo.cpp +++ b/lib/Target/ARM/Thumb2InstrInfo.cpp @@ -88,6 +88,29 @@ Thumb2InstrInfo::BlockHasNoFallThrough(const MachineBasicBlock &MBB) const { return false; } +unsigned +Thumb2InstrInfo::unsignedOffsetOpcodeToSigned(unsigned opcode, + unsigned *NumBits) const +{ + if (NumBits != NULL) + *NumBits = 8; + + switch (opcode) { + case ARM::t2LDRi12: return ARM::t2LDRi8; + case ARM::t2LDRHi12: return ARM::t2LDRHi8; + case ARM::t2LDRBi12: return ARM::t2LDRBi8; + case ARM::t2LDRSHi12: return ARM::t2LDRSHi8; + case ARM::t2LDRSBi12: return ARM::t2LDRSBi8; + case ARM::t2STRi12: return ARM::t2STRi8; + case ARM::t2STRBi12: return ARM::t2STRBi8; + case ARM::t2STRHi12: return ARM::t2STRHi8; + default: + break; + } + + return 0; +} + bool Thumb2InstrInfo::copyRegToReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, diff --git a/lib/Target/ARM/Thumb2InstrInfo.h b/lib/Target/ARM/Thumb2InstrInfo.h index ac31707ab78..43ea56e7925 100644 --- a/lib/Target/ARM/Thumb2InstrInfo.h +++ b/lib/Target/ARM/Thumb2InstrInfo.h @@ -34,6 +34,13 @@ public: // Return the opcode that implements 'Op', or 0 if no opcode unsigned getOpcode(ARMII::Op Op) const; + // If 'opcode' is an instruction with an unsigned offset that also + // has a version with a signed offset, return the opcode for the + // version with the signed offset. In 'NumBits' return the number of + // bits for the signed offset. + unsigned unsignedOffsetOpcodeToSigned(unsigned opcode, + unsigned *NumBits) const; + // Return true if the block does not fall through. bool BlockHasNoFallThrough(const MachineBasicBlock &MBB) const;