Path: blob/main/contrib/llvm-project/llvm/lib/Target/Lanai/LanaiMemAluCombiner.cpp
35271 views
//===-- LanaiMemAluCombiner.cpp - Pass to combine memory & ALU operations -===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7// Simple pass to combine memory and ALU operations8//9// The Lanai ISA supports instructions where a load/store modifies the base10// register used in the load/store operation. This pass finds suitable11// load/store and ALU instructions and combines them into one instruction.12//13// For example,14// ld [ %r6 -- ], %r1215// is a supported instruction that is not currently generated by the instruction16// selection pass of this backend. This pass generates these instructions by17// merging18// add %r6, -4, %r619// followed by20// ld [ %r6 ], %r1221// in the same machine basic block into one machine instruction.22//===----------------------------------------------------------------------===//2324#include "LanaiAluCode.h"25#include "LanaiTargetMachine.h"26#include "llvm/ADT/Statistic.h"27#include "llvm/CodeGen/MachineFunctionPass.h"28#include "llvm/CodeGen/MachineInstrBuilder.h"29#include "llvm/CodeGen/RegisterScavenging.h"30#include "llvm/CodeGen/TargetInstrInfo.h"31#include "llvm/Support/CommandLine.h"32using namespace llvm;3334#define GET_INSTRMAP_INFO35#include "LanaiGenInstrInfo.inc"3637#define DEBUG_TYPE "lanai-mem-alu-combiner"3839STATISTIC(NumLdStAluCombined, "Number of memory and ALU instructions combined");4041static llvm::cl::opt<bool> DisableMemAluCombiner(42"disable-lanai-mem-alu-combiner", llvm::cl::init(false),43llvm::cl::desc("Do not combine ALU and memory operators"),44llvm::cl::Hidden);4546namespace llvm {47void initializeLanaiMemAluCombinerPass(PassRegistry &);48} // namespace llvm4950namespace {51typedef MachineBasicBlock::iterator MbbIterator;52typedef MachineFunction::iterator MfIterator;5354class LanaiMemAluCombiner : public MachineFunctionPass {55public:56static char ID;57explicit LanaiMemAluCombiner() : MachineFunctionPass(ID) {58initializeLanaiMemAluCombinerPass(*PassRegistry::getPassRegistry());59}6061StringRef getPassName() const override {62return "Lanai load / store optimization pass";63}6465bool runOnMachineFunction(MachineFunction &F) override;6667MachineFunctionProperties getRequiredProperties() const override {68return MachineFunctionProperties().set(69MachineFunctionProperties::Property::NoVRegs);70}7172private:73MbbIterator findClosestSuitableAluInstr(MachineBasicBlock *BB,74const MbbIterator &MemInstr,75bool Decrement);76void insertMergedInstruction(MachineBasicBlock *BB,77const MbbIterator &MemInstr,78const MbbIterator &AluInstr, bool Before);79bool combineMemAluInBasicBlock(MachineBasicBlock *BB);8081// Target machine description which we query for register names, data82// layout, etc.83const TargetInstrInfo *TII;84};85} // namespace8687char LanaiMemAluCombiner::ID = 0;8889INITIALIZE_PASS(LanaiMemAluCombiner, DEBUG_TYPE,90"Lanai memory ALU combiner pass", false, false)9192namespace {93bool isSpls(uint16_t Opcode) { return Lanai::splsIdempotent(Opcode) == Opcode; }9495// Determine the opcode for the merged instruction created by considering the96// old memory operation's opcode and whether the merged opcode will have an97// immediate offset.98unsigned mergedOpcode(unsigned OldOpcode, bool ImmediateOffset) {99switch (OldOpcode) {100case Lanai::LDW_RI:101case Lanai::LDW_RR:102if (ImmediateOffset)103return Lanai::LDW_RI;104return Lanai::LDW_RR;105case Lanai::LDHs_RI:106case Lanai::LDHs_RR:107if (ImmediateOffset)108return Lanai::LDHs_RI;109return Lanai::LDHs_RR;110case Lanai::LDHz_RI:111case Lanai::LDHz_RR:112if (ImmediateOffset)113return Lanai::LDHz_RI;114return Lanai::LDHz_RR;115case Lanai::LDBs_RI:116case Lanai::LDBs_RR:117if (ImmediateOffset)118return Lanai::LDBs_RI;119return Lanai::LDBs_RR;120case Lanai::LDBz_RI:121case Lanai::LDBz_RR:122if (ImmediateOffset)123return Lanai::LDBz_RI;124return Lanai::LDBz_RR;125case Lanai::SW_RI:126case Lanai::SW_RR:127if (ImmediateOffset)128return Lanai::SW_RI;129return Lanai::SW_RR;130case Lanai::STB_RI:131case Lanai::STB_RR:132if (ImmediateOffset)133return Lanai::STB_RI;134return Lanai::STB_RR;135case Lanai::STH_RI:136case Lanai::STH_RR:137if (ImmediateOffset)138return Lanai::STH_RI;139return Lanai::STH_RR;140default:141return 0;142}143}144145// Check if the machine instruction has non-volatile memory operands of the type146// supported for combining with ALU instructions.147bool isNonVolatileMemoryOp(const MachineInstr &MI) {148if (!MI.hasOneMemOperand())149return false;150151// Determine if the machine instruction is a supported memory operation by152// testing if the computed merge opcode is a valid memory operation opcode.153if (mergedOpcode(MI.getOpcode(), false) == 0)154return false;155156const MachineMemOperand *MemOperand = *MI.memoperands_begin();157158// Don't move volatile memory accesses159// TODO: unclear if we need to be as conservative about atomics160if (MemOperand->isVolatile() || MemOperand->isAtomic())161return false;162163return true;164}165166// Test to see if two machine operands are of the same type. This test is less167// strict than the MachineOperand::isIdenticalTo function.168bool isSameOperand(const MachineOperand &Op1, const MachineOperand &Op2) {169if (Op1.getType() != Op2.getType())170return false;171172switch (Op1.getType()) {173case MachineOperand::MO_Register:174return Op1.getReg() == Op2.getReg();175case MachineOperand::MO_Immediate:176return Op1.getImm() == Op2.getImm();177default:178return false;179}180}181182bool isZeroOperand(const MachineOperand &Op) {183return ((Op.isReg() && Op.getReg() == Lanai::R0) ||184(Op.isImm() && Op.getImm() == 0));185}186187// Determines whether a register is used by an instruction.188bool InstrUsesReg(const MbbIterator &Instr, const MachineOperand *Reg) {189for (MachineInstr::const_mop_iterator Mop = Instr->operands_begin();190Mop != Instr->operands_end(); ++Mop) {191if (isSameOperand(*Mop, *Reg))192return true;193}194return false;195}196197// Converts between machine opcode and AluCode.198// Flag using/modifying ALU operations should not be considered for merging and199// are omitted from this list.200LPAC::AluCode mergedAluCode(unsigned AluOpcode) {201switch (AluOpcode) {202case Lanai::ADD_I_LO:203case Lanai::ADD_R:204return LPAC::ADD;205case Lanai::SUB_I_LO:206case Lanai::SUB_R:207return LPAC::SUB;208case Lanai::AND_I_LO:209case Lanai::AND_R:210return LPAC::AND;211case Lanai::OR_I_LO:212case Lanai::OR_R:213return LPAC::OR;214case Lanai::XOR_I_LO:215case Lanai::XOR_R:216return LPAC::XOR;217case Lanai::SHL_R:218return LPAC::SHL;219case Lanai::SRL_R:220return LPAC::SRL;221case Lanai::SRA_R:222return LPAC::SRA;223case Lanai::SA_I:224case Lanai::SL_I:225default:226return LPAC::UNKNOWN;227}228}229230// Insert a new combined memory and ALU operation instruction.231//232// This function builds a new machine instruction using the MachineInstrBuilder233// class and inserts it before the memory instruction.234void LanaiMemAluCombiner::insertMergedInstruction(MachineBasicBlock *BB,235const MbbIterator &MemInstr,236const MbbIterator &AluInstr,237bool Before) {238// Insert new combined load/store + alu operation239MachineOperand Dest = MemInstr->getOperand(0);240MachineOperand Base = MemInstr->getOperand(1);241MachineOperand MemOffset = MemInstr->getOperand(2);242MachineOperand AluOffset = AluInstr->getOperand(2);243244// Abort if ALU offset is not a register or immediate245assert((AluOffset.isReg() || AluOffset.isImm()) &&246"Unsupported operand type in merge");247248// Determined merged instructions opcode and ALU code249LPAC::AluCode AluOpcode = mergedAluCode(AluInstr->getOpcode());250unsigned NewOpc = mergedOpcode(MemInstr->getOpcode(), AluOffset.isImm());251252assert(AluOpcode != LPAC::UNKNOWN && "Unknown ALU code in merging");253assert(NewOpc != 0 && "Unknown merged node opcode");254255// Build and insert new machine instruction256MachineInstrBuilder InstrBuilder =257BuildMI(*BB, MemInstr, MemInstr->getDebugLoc(), TII->get(NewOpc));258InstrBuilder.addReg(Dest.getReg(), getDefRegState(true));259InstrBuilder.addReg(Base.getReg(), getKillRegState(true));260261// Add offset to machine instruction262if (AluOffset.isReg())263InstrBuilder.addReg(AluOffset.getReg());264else if (AluOffset.isImm())265InstrBuilder.addImm(AluOffset.getImm());266else267llvm_unreachable("Unsupported ld/st ALU merge.");268269// Create a pre-op if the ALU operation preceded the memory operation or the270// MemOffset is non-zero (i.e. the memory value should be adjusted before271// accessing it), else create a post-op.272if (Before || !isZeroOperand(MemOffset))273InstrBuilder.addImm(LPAC::makePreOp(AluOpcode));274else275InstrBuilder.addImm(LPAC::makePostOp(AluOpcode));276277// Transfer memory operands.278InstrBuilder.setMemRefs(MemInstr->memoperands());279}280281// Function determines if ALU operation (in alu_iter) can be combined with282// a load/store with base and offset.283bool isSuitableAluInstr(bool IsSpls, const MbbIterator &AluIter,284const MachineOperand &Base,285const MachineOperand &Offset) {286// ALU operations have 3 operands287if (AluIter->getNumOperands() != 3)288return false;289290MachineOperand &Dest = AluIter->getOperand(0);291MachineOperand &Op1 = AluIter->getOperand(1);292MachineOperand &Op2 = AluIter->getOperand(2);293294// Only match instructions using the base register as destination and with the295// base and first operand equal296if (!isSameOperand(Dest, Base) || !isSameOperand(Dest, Op1))297return false;298299if (Op2.isImm()) {300// It is not a match if the 2nd operand in the ALU operation is an301// immediate but the ALU operation is not an addition.302if (AluIter->getOpcode() != Lanai::ADD_I_LO)303return false;304305if (Offset.isReg() && Offset.getReg() == Lanai::R0)306return true;307308if (Offset.isImm() &&309((Offset.getImm() == 0 &&310// Check that the Op2 would fit in the immediate field of the311// memory operation.312((IsSpls && isInt<10>(Op2.getImm())) ||313(!IsSpls && isInt<16>(Op2.getImm())))) ||314Offset.getImm() == Op2.getImm()))315return true;316} else if (Op2.isReg()) {317// The Offset and 2nd operand are both registers and equal318if (Offset.isReg() && Op2.getReg() == Offset.getReg())319return true;320} else321// Only consider operations with register or immediate values322return false;323324return false;325}326327MbbIterator LanaiMemAluCombiner::findClosestSuitableAluInstr(328MachineBasicBlock *BB, const MbbIterator &MemInstr, const bool Decrement) {329MachineOperand *Base = &MemInstr->getOperand(1);330MachineOperand *Offset = &MemInstr->getOperand(2);331bool IsSpls = isSpls(MemInstr->getOpcode());332333MbbIterator First = MemInstr;334MbbIterator Last = Decrement ? BB->begin() : BB->end();335336while (First != Last) {337Decrement ? --First : ++First;338339if (First == Last)340break;341342// Skip over debug instructions343if (First->isDebugInstr())344continue;345346if (isSuitableAluInstr(IsSpls, First, *Base, *Offset)) {347return First;348}349350// Usage of the base or offset register is not a form suitable for merging.351if (First != Last) {352if (InstrUsesReg(First, Base))353break;354if (Offset->isReg() && InstrUsesReg(First, Offset))355break;356}357}358359return MemInstr;360}361362bool LanaiMemAluCombiner::combineMemAluInBasicBlock(MachineBasicBlock *BB) {363bool Modified = false;364365MbbIterator MBBIter = BB->begin(), End = BB->end();366while (MBBIter != End) {367bool IsMemOp = isNonVolatileMemoryOp(*MBBIter);368369if (IsMemOp) {370MachineOperand AluOperand = MBBIter->getOperand(3);371unsigned int DestReg = MBBIter->getOperand(0).getReg(),372BaseReg = MBBIter->getOperand(1).getReg();373assert(AluOperand.isImm() && "Unexpected memory operator type");374LPAC::AluCode AluOpcode = static_cast<LPAC::AluCode>(AluOperand.getImm());375376// Skip memory operations that already modify the base register or if377// the destination and base register are the same378if (!LPAC::modifiesOp(AluOpcode) && DestReg != BaseReg) {379for (int Inc = 0; Inc <= 1; ++Inc) {380MbbIterator AluIter =381findClosestSuitableAluInstr(BB, MBBIter, Inc == 0);382if (AluIter != MBBIter) {383insertMergedInstruction(BB, MBBIter, AluIter, Inc == 0);384385++NumLdStAluCombined;386Modified = true;387388// Erase the matching ALU instruction389BB->erase(AluIter);390// Erase old load/store instruction391BB->erase(MBBIter++);392break;393}394}395}396}397if (MBBIter == End)398break;399++MBBIter;400}401402return Modified;403}404405// Driver function that iterates over the machine basic building blocks of a406// machine function407bool LanaiMemAluCombiner::runOnMachineFunction(MachineFunction &MF) {408if (DisableMemAluCombiner)409return false;410411TII = MF.getSubtarget<LanaiSubtarget>().getInstrInfo();412bool Modified = false;413for (MachineBasicBlock &MBB : MF)414Modified |= combineMemAluInBasicBlock(&MBB);415return Modified;416}417} // namespace418419FunctionPass *llvm::createLanaiMemAluCombinerPass() {420return new LanaiMemAluCombiner();421}422423424