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