xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64CompressJumpTables.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //==-- AArch64CompressJumpTables.cpp - Compress jump tables for AArch64 --====//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric // This pass looks at the basic blocks each jump-table refers to and works out
80b57cec5SDimitry Andric // whether they can be emitted in a compressed form (with 8 or 16-bit
90b57cec5SDimitry Andric // entries). If so, it changes the opcode and flags them in the associated
100b57cec5SDimitry Andric // AArch64FunctionInfo.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "AArch64.h"
150b57cec5SDimitry Andric #include "AArch64MachineFunctionInfo.h"
160b57cec5SDimitry Andric #include "AArch64Subtarget.h"
170b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h"
180b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
190b57cec5SDimitry Andric #include "llvm/CodeGen/MachineJumpTableInfo.h"
200b57cec5SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
210b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
220b57cec5SDimitry Andric #include "llvm/MC/MCContext.h"
23480093f4SDimitry Andric #include "llvm/Support/Alignment.h"
240b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric using namespace llvm;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric #define DEBUG_TYPE "aarch64-jump-tables"
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric STATISTIC(NumJT8, "Number of jump-tables with 1-byte entries");
310b57cec5SDimitry Andric STATISTIC(NumJT16, "Number of jump-tables with 2-byte entries");
320b57cec5SDimitry Andric STATISTIC(NumJT32, "Number of jump-tables with 4-byte entries");
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric namespace {
350b57cec5SDimitry Andric class AArch64CompressJumpTables : public MachineFunctionPass {
360b57cec5SDimitry Andric   const TargetInstrInfo *TII;
370b57cec5SDimitry Andric   MachineFunction *MF;
380b57cec5SDimitry Andric   SmallVector<int, 8> BlockInfo;
390b57cec5SDimitry Andric 
40*06c3fb27SDimitry Andric   /// Returns the size of instructions in the block \p MBB, or std::nullopt if
41bdd1243dSDimitry Andric   /// we couldn't get a safe upper bound.
42bdd1243dSDimitry Andric   std::optional<int> computeBlockSize(MachineBasicBlock &MBB);
43e8d8bef9SDimitry Andric 
44e8d8bef9SDimitry Andric   /// Gather information about the function, returns false if we can't perform
45e8d8bef9SDimitry Andric   /// this optimization for some reason.
46e8d8bef9SDimitry Andric   bool scanFunction();
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric   bool compressJumpTable(MachineInstr &MI, int Offset);
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric public:
510b57cec5SDimitry Andric   static char ID;
AArch64CompressJumpTables()520b57cec5SDimitry Andric   AArch64CompressJumpTables() : MachineFunctionPass(ID) {
530b57cec5SDimitry Andric     initializeAArch64CompressJumpTablesPass(*PassRegistry::getPassRegistry());
540b57cec5SDimitry Andric   }
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
570b57cec5SDimitry Andric 
getRequiredProperties() const580b57cec5SDimitry Andric   MachineFunctionProperties getRequiredProperties() const override {
590b57cec5SDimitry Andric     return MachineFunctionProperties().set(
600b57cec5SDimitry Andric         MachineFunctionProperties::Property::NoVRegs);
610b57cec5SDimitry Andric   }
getPassName() const620b57cec5SDimitry Andric   StringRef getPassName() const override {
630b57cec5SDimitry Andric     return "AArch64 Compress Jump Tables";
640b57cec5SDimitry Andric   }
650b57cec5SDimitry Andric };
660b57cec5SDimitry Andric char AArch64CompressJumpTables::ID = 0;
67e8d8bef9SDimitry Andric } // namespace
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric INITIALIZE_PASS(AArch64CompressJumpTables, DEBUG_TYPE,
700b57cec5SDimitry Andric                 "AArch64 compress jump tables pass", false, false)
710b57cec5SDimitry Andric 
72bdd1243dSDimitry Andric std::optional<int>
computeBlockSize(MachineBasicBlock & MBB)73e8d8bef9SDimitry Andric AArch64CompressJumpTables::computeBlockSize(MachineBasicBlock &MBB) {
740b57cec5SDimitry Andric   int Size = 0;
75e8d8bef9SDimitry Andric   for (const MachineInstr &MI : MBB) {
76e8d8bef9SDimitry Andric     // Inline asm may contain some directives like .bytes which we don't
77e8d8bef9SDimitry Andric     // currently have the ability to parse accurately. To be safe, just avoid
78e8d8bef9SDimitry Andric     // computing a size and bail out.
79e8d8bef9SDimitry Andric     if (MI.getOpcode() == AArch64::INLINEASM ||
80e8d8bef9SDimitry Andric         MI.getOpcode() == AArch64::INLINEASM_BR)
81bdd1243dSDimitry Andric       return std::nullopt;
820b57cec5SDimitry Andric     Size += TII->getInstSizeInBytes(MI);
83e8d8bef9SDimitry Andric   }
840b57cec5SDimitry Andric   return Size;
850b57cec5SDimitry Andric }
860b57cec5SDimitry Andric 
scanFunction()87e8d8bef9SDimitry Andric bool AArch64CompressJumpTables::scanFunction() {
880b57cec5SDimitry Andric   BlockInfo.clear();
890b57cec5SDimitry Andric   BlockInfo.resize(MF->getNumBlockIDs());
900b57cec5SDimitry Andric 
91*06c3fb27SDimitry Andric   // NOTE: BlockSize, Offset, OffsetAfterAlignment are all upper bounds.
92*06c3fb27SDimitry Andric 
93480093f4SDimitry Andric   unsigned Offset = 0;
940b57cec5SDimitry Andric   for (MachineBasicBlock &MBB : *MF) {
95480093f4SDimitry Andric     const Align Alignment = MBB.getAlignment();
96*06c3fb27SDimitry Andric     unsigned OffsetAfterAlignment = Offset;
97*06c3fb27SDimitry Andric     // We don't know the exact size of MBB so assume worse case padding.
98*06c3fb27SDimitry Andric     if (Alignment > Align(4))
99*06c3fb27SDimitry Andric       OffsetAfterAlignment += Alignment.value() - 4;
100*06c3fb27SDimitry Andric     BlockInfo[MBB.getNumber()] = OffsetAfterAlignment;
101e8d8bef9SDimitry Andric     auto BlockSize = computeBlockSize(MBB);
102e8d8bef9SDimitry Andric     if (!BlockSize)
103e8d8bef9SDimitry Andric       return false;
104*06c3fb27SDimitry Andric     Offset = OffsetAfterAlignment + *BlockSize;
1050b57cec5SDimitry Andric   }
106e8d8bef9SDimitry Andric   return true;
1070b57cec5SDimitry Andric }
1080b57cec5SDimitry Andric 
compressJumpTable(MachineInstr & MI,int Offset)1090b57cec5SDimitry Andric bool AArch64CompressJumpTables::compressJumpTable(MachineInstr &MI,
1100b57cec5SDimitry Andric                                                   int Offset) {
1110b57cec5SDimitry Andric   if (MI.getOpcode() != AArch64::JumpTableDest32)
1120b57cec5SDimitry Andric     return false;
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric   int JTIdx = MI.getOperand(4).getIndex();
1150b57cec5SDimitry Andric   auto &JTInfo = *MF->getJumpTableInfo();
1160b57cec5SDimitry Andric   const MachineJumpTableEntry &JT = JTInfo.getJumpTables()[JTIdx];
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric   // The jump-table might have been optimized away.
1190b57cec5SDimitry Andric   if (JT.MBBs.empty())
1200b57cec5SDimitry Andric     return false;
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric   int MaxOffset = std::numeric_limits<int>::min(),
1230b57cec5SDimitry Andric       MinOffset = std::numeric_limits<int>::max();
1240b57cec5SDimitry Andric   MachineBasicBlock *MinBlock = nullptr;
125e8d8bef9SDimitry Andric   for (auto *Block : JT.MBBs) {
1260b57cec5SDimitry Andric     int BlockOffset = BlockInfo[Block->getNumber()];
1270b57cec5SDimitry Andric     assert(BlockOffset % 4 == 0 && "misaligned basic block");
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric     MaxOffset = std::max(MaxOffset, BlockOffset);
1300b57cec5SDimitry Andric     if (BlockOffset <= MinOffset) {
1310b57cec5SDimitry Andric       MinOffset = BlockOffset;
1320b57cec5SDimitry Andric       MinBlock = Block;
1330b57cec5SDimitry Andric     }
1340b57cec5SDimitry Andric   }
1350b57cec5SDimitry Andric   assert(MinBlock && "Failed to find minimum offset block");
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric   // The ADR instruction needed to calculate the address of the first reachable
1380b57cec5SDimitry Andric   // basic block can address +/-1MB.
1390b57cec5SDimitry Andric   if (!isInt<21>(MinOffset - Offset)) {
1400b57cec5SDimitry Andric     ++NumJT32;
1410b57cec5SDimitry Andric     return false;
1420b57cec5SDimitry Andric   }
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric   int Span = MaxOffset - MinOffset;
145e8d8bef9SDimitry Andric   auto *AFI = MF->getInfo<AArch64FunctionInfo>();
1460b57cec5SDimitry Andric   if (isUInt<8>(Span / 4)) {
1470b57cec5SDimitry Andric     AFI->setJumpTableEntryInfo(JTIdx, 1, MinBlock->getSymbol());
1480b57cec5SDimitry Andric     MI.setDesc(TII->get(AArch64::JumpTableDest8));
1490b57cec5SDimitry Andric     ++NumJT8;
1500b57cec5SDimitry Andric     return true;
151e8d8bef9SDimitry Andric   }
152e8d8bef9SDimitry Andric   if (isUInt<16>(Span / 4)) {
1530b57cec5SDimitry Andric     AFI->setJumpTableEntryInfo(JTIdx, 2, MinBlock->getSymbol());
1540b57cec5SDimitry Andric     MI.setDesc(TII->get(AArch64::JumpTableDest16));
1550b57cec5SDimitry Andric     ++NumJT16;
1560b57cec5SDimitry Andric     return true;
1570b57cec5SDimitry Andric   }
1580b57cec5SDimitry Andric 
1590b57cec5SDimitry Andric   ++NumJT32;
1600b57cec5SDimitry Andric   return false;
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric 
runOnMachineFunction(MachineFunction & MFIn)1630b57cec5SDimitry Andric bool AArch64CompressJumpTables::runOnMachineFunction(MachineFunction &MFIn) {
1640b57cec5SDimitry Andric   bool Changed = false;
1650b57cec5SDimitry Andric   MF = &MFIn;
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   const auto &ST = MF->getSubtarget<AArch64Subtarget>();
1680b57cec5SDimitry Andric   TII = ST.getInstrInfo();
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric   if (ST.force32BitJumpTables() && !MF->getFunction().hasMinSize())
1710b57cec5SDimitry Andric     return false;
1720b57cec5SDimitry Andric 
173e8d8bef9SDimitry Andric   if (!scanFunction())
174e8d8bef9SDimitry Andric     return false;
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric   for (MachineBasicBlock &MBB : *MF) {
1770b57cec5SDimitry Andric     int Offset = BlockInfo[MBB.getNumber()];
1780b57cec5SDimitry Andric     for (MachineInstr &MI : MBB) {
1790b57cec5SDimitry Andric       Changed |= compressJumpTable(MI, Offset);
1800b57cec5SDimitry Andric       Offset += TII->getInstSizeInBytes(MI);
1810b57cec5SDimitry Andric     }
1820b57cec5SDimitry Andric   }
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric   return Changed;
1850b57cec5SDimitry Andric }
1860b57cec5SDimitry Andric 
createAArch64CompressJumpTablesPass()1870b57cec5SDimitry Andric FunctionPass *llvm::createAArch64CompressJumpTablesPass() {
1880b57cec5SDimitry Andric   return new AArch64CompressJumpTables();
1890b57cec5SDimitry Andric }
190