//===- ARMConstantIslandPass.cpp - ARM constant islands -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains a pass that splits the constant pool up into 'islands' // which are scattered through-out the function. This is required due to the // limited pc-relative displacements that ARM has. // //===----------------------------------------------------------------------===// #include "ARM.h" #include "ARMBaseInstrInfo.h" #include "ARMBasicBlockInfo.h" #include "ARMMachineFunctionInfo.h" #include "ARMSubtarget.h" #include "MCTargetDesc/ARMBaseInfo.h" #include "MVETailPredUtils.h" #include "Thumb2InstrInfo.h" #include "Utils/ARMBaseInfo.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/LivePhysRegs.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineDominators.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugLoc.h" #include "llvm/MC/MCInstrDesc.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> #include <cstdint> #include <iterator> #include <utility> #include <vector> usingnamespacellvm; #define DEBUG_TYPE … #define ARM_CP_ISLANDS_OPT_NAME … STATISTIC(NumCPEs, "Number of constpool entries"); STATISTIC(NumSplit, "Number of uncond branches inserted"); STATISTIC(NumCBrFixed, "Number of cond branches fixed"); STATISTIC(NumUBrFixed, "Number of uncond branches fixed"); STATISTIC(NumTBs, "Number of table branches generated"); STATISTIC(NumT2CPShrunk, "Number of Thumb2 constantpool instructions shrunk"); STATISTIC(NumT2BrShrunk, "Number of Thumb2 immediate branches shrunk"); STATISTIC(NumCBZ, "Number of CBZ / CBNZ formed"); STATISTIC(NumJTMoved, "Number of jump table destination blocks moved"); STATISTIC(NumJTInserted, "Number of jump table intermediate blocks inserted"); STATISTIC(NumLEInserted, "Number of LE backwards branches inserted"); static cl::opt<bool> AdjustJumpTableBlocks("arm-adjust-jump-tables", cl::Hidden, cl::init(true), cl::desc("Adjust basic block layout to better use TB[BH]")); static cl::opt<unsigned> CPMaxIteration("arm-constant-island-max-iteration", cl::Hidden, cl::init(30), cl::desc("The max number of iteration for converge")); static cl::opt<bool> SynthesizeThumb1TBB( "arm-synthesize-thumb-1-tbb", cl::Hidden, cl::init(true), cl::desc("Use compressed jump tables in Thumb-1 by synthesizing an " "equivalent to the TBB/TBH instructions")); namespace { /// ARMConstantIslands - Due to limited PC-relative displacements, ARM /// requires constant pool entries to be scattered among the instructions /// inside a function. To do this, it completely ignores the normal LLVM /// constant pool; instead, it places constants wherever it feels like with /// special instructions. /// /// The terminology used in this pass includes: /// Islands - Clumps of constants placed in the function. /// Water - Potential places where an island could be formed. /// CPE - A constant pool entry that has been placed somewhere, which /// tracks a list of users. class ARMConstantIslands : public MachineFunctionPass { … }; } // end anonymous namespace char ARMConstantIslands::ID = …; /// verify - check BBOffsets, BBSizes, alignment of islands void ARMConstantIslands::verify() { … } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) /// print block size and offset information - debugging LLVM_DUMP_METHOD void ARMConstantIslands::dumpBBs() { LLVM_DEBUG({ BBInfoVector &BBInfo = BBUtils->getBBInfo(); for (unsigned J = 0, E = BBInfo.size(); J !=E; ++J) { const BasicBlockInfo &BBI = BBInfo[J]; dbgs() << format("%08x %bb.%u\t", BBI.Offset, J) << " kb=" << unsigned(BBI.KnownBits) << " ua=" << unsigned(BBI.Unalign) << " pa=" << Log2(BBI.PostAlign) << format(" size=%#x\n", BBInfo[J].Size); } }); } #endif // Align blocks where the previous block does not fall through. This may add // extra NOP's but they will not be executed. It uses the PrefLoopAlignment as a // measure of how much to align, and only runs at CodeGenOptLevel::Aggressive. static bool AlignBlocks(MachineFunction *MF, const ARMSubtarget *STI) { … } bool ARMConstantIslands::runOnMachineFunction(MachineFunction &mf) { … } /// Perform the initial placement of the regular constant pool entries. /// To start with, we put them all at the end of the function. void ARMConstantIslands::doInitialConstPlacement(std::vector<MachineInstr*> &CPEMIs) { … } /// Do initial placement of the jump tables. Because Thumb2's TBB and TBH /// instructions can be made more efficient if the jump table immediately /// follows the instruction, it's best to place them immediately next to their /// jumps to begin with. In almost all cases they'll never be moved from that /// position. void ARMConstantIslands::doInitialJumpTablePlacement( std::vector<MachineInstr *> &CPEMIs) { … } /// BBHasFallthrough - Return true if the specified basic block can fallthrough /// into the block immediately after it. bool ARMConstantIslands::BBHasFallthrough(MachineBasicBlock *MBB) { … } /// findConstPoolEntry - Given the constpool index and CONSTPOOL_ENTRY MI, /// look up the corresponding CPEntry. ARMConstantIslands::CPEntry * ARMConstantIslands::findConstPoolEntry(unsigned CPI, const MachineInstr *CPEMI) { … } /// getCPEAlign - Returns the required alignment of the constant pool entry /// represented by CPEMI. Align ARMConstantIslands::getCPEAlign(const MachineInstr *CPEMI) { … } // Exception landing pads, blocks that has their adress taken, and function // entry blocks will always be (potential) indirect jump targets, regardless of // whether they are referenced by or not by jump tables. static bool isAlwaysIndirectTarget(const MachineBasicBlock &MBB) { … } /// scanFunctionJumpTables - Do a scan of the function, building up /// information about the sizes of each block and the locations of all /// the jump tables. void ARMConstantIslands::scanFunctionJumpTables() { … } /// initializeFunctionInfo - Do the initial scan of the function, building up /// information about the sizes of each block, the location of all the water, /// and finding all of the constant pool users. void ARMConstantIslands:: initializeFunctionInfo(const std::vector<MachineInstr*> &CPEMIs) { … } /// CompareMBBNumbers - Little predicate function to sort the WaterList by MBB /// ID. static bool CompareMBBNumbers(const MachineBasicBlock *LHS, const MachineBasicBlock *RHS) { … } /// updateForInsertedWaterBlock - When a block is newly inserted into the /// machine function, it upsets all of the block numbers. Renumber the blocks /// and update the arrays that parallel this numbering. void ARMConstantIslands::updateForInsertedWaterBlock(MachineBasicBlock *NewBB) { … } /// Split the basic block containing MI into two blocks, which are joined by /// an unconditional branch. Update data structures and renumber blocks to /// account for this change and returns the newly created block. MachineBasicBlock *ARMConstantIslands::splitBlockBeforeInstr(MachineInstr *MI) { … } /// getUserOffset - Compute the offset of U.MI as seen by the hardware /// displacement computation. Update U.KnownAlignment to match its current /// basic block location. unsigned ARMConstantIslands::getUserOffset(CPUser &U) const { … } /// isOffsetInRange - Checks whether UserOffset (the location of a constant pool /// reference) is within MaxDisp of TrialOffset (a proposed location of a /// constant pool entry). /// UserOffset is computed by getUserOffset above to include PC adjustments. If /// the mod 4 alignment of UserOffset is not known, the uncertainty must be /// subtracted from MaxDisp instead. CPUser::getMaxDisp() does that. bool ARMConstantIslands::isOffsetInRange(unsigned UserOffset, unsigned TrialOffset, unsigned MaxDisp, bool NegativeOK, bool IsSoImm) { … } /// isWaterInRange - Returns true if a CPE placed after the specified /// Water (a basic block) will be in range for the specific MI. /// /// Compute how much the function will grow by inserting a CPE after Water. bool ARMConstantIslands::isWaterInRange(unsigned UserOffset, MachineBasicBlock* Water, CPUser &U, unsigned &Growth) { … } /// isCPEntryInRange - Returns true if the distance between specific MI and /// specific ConstPool entry instruction can fit in MI's displacement field. bool ARMConstantIslands::isCPEntryInRange(MachineInstr *MI, unsigned UserOffset, MachineInstr *CPEMI, unsigned MaxDisp, bool NegOk, bool DoDump) { … } #ifndef NDEBUG /// BBIsJumpedOver - Return true of the specified basic block's only predecessor /// unconditionally branches to its only successor. static bool BBIsJumpedOver(MachineBasicBlock *MBB) { if (MBB->pred_size() != 1 || MBB->succ_size() != 1) return false; MachineBasicBlock *Succ = *MBB->succ_begin(); MachineBasicBlock *Pred = *MBB->pred_begin(); MachineInstr *PredMI = &Pred->back(); if (PredMI->getOpcode() == ARM::B || PredMI->getOpcode() == ARM::tB || PredMI->getOpcode() == ARM::t2B) return PredMI->getOperand(0).getMBB() == Succ; return false; } #endif // NDEBUG /// decrementCPEReferenceCount - find the constant pool entry with index CPI /// and instruction CPEMI, and decrement its refcount. If the refcount /// becomes 0 remove the entry and instruction. Returns true if we removed /// the entry, false if we didn't. bool ARMConstantIslands::decrementCPEReferenceCount(unsigned CPI, MachineInstr *CPEMI) { … } unsigned ARMConstantIslands::getCombinedIndex(const MachineInstr *CPEMI) { … } /// LookForCPEntryInRange - see if the currently referenced CPE is in range; /// if not, see if an in-range clone of the CPE is in range, and if so, /// change the data structures so the user references the clone. Returns: /// 0 = no existing entry found /// 1 = entry found, and there were no code insertions or deletions /// 2 = entry found, and there were code insertions or deletions int ARMConstantIslands::findInRangeCPEntry(CPUser& U, unsigned UserOffset) { … } /// getUnconditionalBrDisp - Returns the maximum displacement that can fit in /// the specific unconditional branch instruction. static inline unsigned getUnconditionalBrDisp(int Opc) { … } /// findAvailableWater - Look for an existing entry in the WaterList in which /// we can place the CPE referenced from U so it's within range of U's MI. /// Returns true if found, false if not. If it returns true, WaterIter /// is set to the WaterList entry. For Thumb, prefer water that will not /// introduce padding to water that will. To ensure that this pass /// terminates, the CPE location for a particular CPUser is only allowed to /// move to a lower address, so search backward from the end of the list and /// prefer the first water that is in range. bool ARMConstantIslands::findAvailableWater(CPUser &U, unsigned UserOffset, water_iterator &WaterIter, bool CloserWater) { … } /// createNewWater - No existing WaterList entry will work for /// CPUsers[CPUserIndex], so create a place to put the CPE. The end of the /// block is used if in range, and the conditional branch munged so control /// flow is correct. Otherwise the block is split to create a hole with an /// unconditional branch around it. In either case NewMBB is set to a /// block following which the new island can be inserted (the WaterList /// is not adjusted). void ARMConstantIslands::createNewWater(unsigned CPUserIndex, unsigned UserOffset, MachineBasicBlock *&NewMBB) { … } /// handleConstantPoolUser - Analyze the specified user, checking to see if it /// is out-of-range. If so, pick up the constant pool value and move it some /// place in-range. Return true if we changed any addresses (thus must run /// another pass of branch lengthening), false otherwise. bool ARMConstantIslands::handleConstantPoolUser(unsigned CPUserIndex, bool CloserWater) { … } /// removeDeadCPEMI - Remove a dead constant pool entry instruction. Update /// sizes and offsets of impacted basic blocks. void ARMConstantIslands::removeDeadCPEMI(MachineInstr *CPEMI) { … } /// removeUnusedCPEntries - Remove constant pool entries whose refcounts /// are zero. bool ARMConstantIslands::removeUnusedCPEntries() { … } /// fixupImmediateBr - Fix up an immediate branch whose destination is too far /// away to fit in its displacement field. bool ARMConstantIslands::fixupImmediateBr(ImmBranch &Br) { … } /// fixupUnconditionalBr - Fix up an unconditional branch whose destination is /// too far away to fit in its displacement field. If the LR register has been /// spilled in the epilogue, then we can use BL to implement a far jump. /// Otherwise, add an intermediate branch instruction to a branch. bool ARMConstantIslands::fixupUnconditionalBr(ImmBranch &Br) { … } /// fixupConditionalBr - Fix up a conditional branch whose destination is too /// far away to fit in its displacement field. It is converted to an inverse /// conditional branch + an unconditional branch to the destination. bool ARMConstantIslands::fixupConditionalBr(ImmBranch &Br) { … } bool ARMConstantIslands::optimizeThumb2Instructions() { … } bool ARMConstantIslands::optimizeThumb2Branches() { … } static bool isSimpleIndexCalc(MachineInstr &I, unsigned EntryReg, unsigned BaseReg) { … } /// While trying to form a TBB/TBH instruction, we may (if the table /// doesn't immediately follow the BR_JT) need access to the start of the /// jump-table. We know one instruction that produces such a register; this /// function works out whether that definition can be preserved to the BR_JT, /// possibly by removing an intervening addition (which is usually needed to /// calculate the actual entry to jump to). bool ARMConstantIslands::preserveBaseRegister(MachineInstr *JumpMI, MachineInstr *LEAMI, unsigned &DeadSize, bool &CanDeleteLEA, bool &BaseRegKill) { … } /// Returns whether CPEMI is the first instruction in the block /// immediately following JTMI (assumed to be a TBB or TBH terminator). If so, /// we can switch the first register to PC and usually remove the address /// calculation that preceded it. static bool jumpTableFollowsTB(MachineInstr *JTMI, MachineInstr *CPEMI) { … } static void RemoveDeadAddBetweenLEAAndJT(MachineInstr *LEAMI, MachineInstr *JumpMI, unsigned &DeadSize) { … } /// optimizeThumb2JumpTables - Use tbb / tbh instructions to generate smaller /// jumptables when it's possible. bool ARMConstantIslands::optimizeThumb2JumpTables() { … } /// reorderThumb2JumpTables - Adjust the function's block layout to ensure that /// jump tables always branch forwards, since that's what tbb and tbh need. bool ARMConstantIslands::reorderThumb2JumpTables() { … } MachineBasicBlock *ARMConstantIslands::adjustJTTargetBlockForward( unsigned JTI, MachineBasicBlock *BB, MachineBasicBlock *JTBB) { … } /// createARMConstantIslandPass - returns an instance of the constpool /// island pass. FunctionPass *llvm::createARMConstantIslandPass() { … } INITIALIZE_PASS(…)