//===----- ARMCodeGenPrepare.cpp ------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // /// \file /// This pass inserts intrinsics to handle small types that would otherwise be /// promoted during legalization. Here we can manually promote types or insert /// intrinsics which can handle narrow types that aren't supported by the /// register classes. // //===----------------------------------------------------------------------===// #include "ARM.h" #include "ARMSubtarget.h" #include "ARMTargetMachine.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Constants.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/IR/Verifier.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #define DEBUG_TYPE "arm-codegenprepare" using namespace llvm; static cl::opt DisableCGP("arm-disable-cgp", cl::Hidden, cl::init(true), cl::desc("Disable ARM specific CodeGenPrepare pass")); static cl::opt EnableDSP("arm-enable-scalar-dsp", cl::Hidden, cl::init(false), cl::desc("Use DSP instructions for scalar operations")); static cl::opt EnableDSPWithImms("arm-enable-scalar-dsp-imms", cl::Hidden, cl::init(false), cl::desc("Use DSP instructions for scalar operations\ with immediate operands")); namespace { class IRPromoter { SmallPtrSet NewInsts; SmallVector InstsToRemove; Module *M = nullptr; LLVMContext &Ctx; public: IRPromoter(Module *M) : M(M), Ctx(M->getContext()) { } void Cleanup() { for (auto *I : InstsToRemove) { LLVM_DEBUG(dbgs() << "ARM CGP: Removing " << *I << "\n"); I->dropAllReferences(); I->eraseFromParent(); } InstsToRemove.clear(); NewInsts.clear(); } void Mutate(Type *OrigTy, SmallPtrSetImpl &Visited, SmallPtrSetImpl &Leaves, SmallPtrSetImpl &Roots); }; class ARMCodeGenPrepare : public FunctionPass { const ARMSubtarget *ST = nullptr; IRPromoter *Promoter = nullptr; std::set AllVisited; Type *OrigTy = nullptr; unsigned TypeSize = 0; bool isNarrowInstSupported(Instruction *I); bool isSupportedValue(Value *V); bool isLegalToPromote(Value *V); bool TryToPromote(Value *V); public: static char ID; ARMCodeGenPrepare() : FunctionPass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); } StringRef getPassName() const override { return "ARM IR optimizations"; } bool doInitialization(Module &M) override; bool runOnFunction(Function &F) override; bool doFinalization(Module &M) override; }; } /// Can the given value generate sign bits. static bool isSigned(Value *V) { if (!isa(V)) return false; unsigned Opc = cast(V)->getOpcode(); return Opc == Instruction::AShr || Opc == Instruction::SDiv || Opc == Instruction::SRem; } /// Some instructions can use 8- and 16-bit operands, and we don't need to /// promote anything larger. We disallow booleans to make life easier when /// dealing with icmps but allow any other integer that is <= 16 bits. Void /// types are accepted so we can handle switches. static bool isSupportedType(Value *V) { if (V->getType()->isVoidTy()) return true; const IntegerType *IntTy = dyn_cast(V->getType()); if (!IntTy) return false; // Don't try to promote boolean values. if (IntTy->getBitWidth() == 1) return false; if (auto *ZExt = dyn_cast(V)) return isSupportedType(ZExt->getOperand(0)); return IntTy->getBitWidth() <= 16; } /// Return true if V will require any promoted values to be truncated for the /// use to be valid. static bool isSink(Value *V) { auto UsesNarrowValue = [](Value *V) { return V->getType()->getScalarSizeInBits() <= 32; }; if (auto *Store = dyn_cast(V)) return UsesNarrowValue(Store->getValueOperand()); if (auto *Return = dyn_cast(V)) return UsesNarrowValue(Return->getReturnValue()); return isa(V); } /// Return true if the given value is a leaf that will need to be zext'd. static bool isSource(Value *V) { if (isa(V) && isSupportedType(V)) return true; else if (isa(V)) return true; else if (auto *ZExt = dyn_cast(V)) // ZExt can be a leaf if its the only user of a load. return isa(ZExt->getOperand(0)) && ZExt->getOperand(0)->hasOneUse(); else if (auto *Call = dyn_cast(V)) return Call->hasRetAttr(Attribute::AttrKind::ZExt); else if (auto *Load = dyn_cast(V)) { if (!isa(Load->getType())) return false; // A load is a leaf, unless its already just being zext'd. if (Load->hasOneUse() && isa(*Load->use_begin())) return false; return true; } return false; } /// Return whether the instruction can be promoted within any modifications to /// it's operands or result. static bool isSafeOverflow(Instruction *I) { if (isa(I) && I->hasNoUnsignedWrap()) return true; unsigned Opc = I->getOpcode(); if (Opc == Instruction::Add || Opc == Instruction::Sub) { // We don't care if the add or sub could wrap if the value is decreasing // and is only being used by an unsigned compare. if (!I->hasOneUse() || !isa(*I->user_begin()) || !isa(I->getOperand(1))) return false; auto *CI = cast(*I->user_begin()); if (CI->isSigned()) return false; bool NegImm = cast(I->getOperand(1))->isNegative(); bool IsDecreasing = ((Opc == Instruction::Sub) && !NegImm) || ((Opc == Instruction::Add) && NegImm); if (!IsDecreasing) return false; LLVM_DEBUG(dbgs() << "ARM CGP: Allowing safe overflow for " << *I << "\n"); return true; } // Otherwise, if an instruction is using a negative immediate we will need // to fix it up during the promotion. for (auto &Op : I->operands()) { if (auto *Const = dyn_cast(Op)) if (Const->isNegative()) return false; } return false; } static bool shouldPromote(Value *V) { auto *I = dyn_cast(V); if (!I) return false; if (!isa(V->getType())) return false; if (isa(I) || isa(I) || isa(I) || isa(I)) return false; if (auto *ZExt = dyn_cast(I)) return !ZExt->getDestTy()->isIntegerTy(32); return true; } /// Return whether we can safely mutate V's type to ExtTy without having to be /// concerned with zero extending or truncation. static bool isPromotedResultSafe(Value *V) { if (!isa(V)) return true; if (isSigned(V)) return false; // If I is only being used by something that will require its value to be // truncated, then we don't care about the promoted result. auto *I = cast(V); if (I->hasOneUse() && isSink(*I->use_begin())) return true; if (isa(I)) return isSafeOverflow(I); return true; } /// Return the intrinsic for the instruction that can perform the same /// operation but on a narrow type. This is using the parallel dsp intrinsics /// on scalar values. static Intrinsic::ID getNarrowIntrinsic(Instruction *I, unsigned TypeSize) { // Whether we use the signed or unsigned versions of these intrinsics // doesn't matter because we're not using the GE bits that they set in // the APSR. switch(I->getOpcode()) { default: break; case Instruction::Add: return TypeSize == 16 ? Intrinsic::arm_uadd16 : Intrinsic::arm_uadd8; case Instruction::Sub: return TypeSize == 16 ? Intrinsic::arm_usub16 : Intrinsic::arm_usub8; } llvm_unreachable("unhandled opcode for narrow intrinsic"); } void IRPromoter::Mutate(Type *OrigTy, SmallPtrSetImpl &Visited, SmallPtrSetImpl &Leaves, SmallPtrSetImpl &Roots) { IRBuilder<> Builder{Ctx}; Type *ExtTy = Type::getInt32Ty(M->getContext()); unsigned TypeSize = OrigTy->getPrimitiveSizeInBits(); SmallPtrSet Promoted; LLVM_DEBUG(dbgs() << "ARM CGP: Promoting use-def chains to from " << TypeSize << " to 32-bits\n"); auto ReplaceAllUsersOfWith = [&](Value *From, Value *To) { SmallVector Users; Instruction *InstTo = dyn_cast(To); for (Use &U : From->uses()) { auto *User = cast(U.getUser()); if (InstTo && User->isIdenticalTo(InstTo)) continue; Users.push_back(User); } for (auto &U : Users) U->replaceUsesOfWith(From, To); }; auto FixConst = [&](ConstantInt *Const, Instruction *I) { Constant *NewConst = nullptr; if (isSafeOverflow(I)) { NewConst = (Const->isNegative()) ? ConstantExpr::getSExt(Const, ExtTy) : ConstantExpr::getZExt(Const, ExtTy); } else { uint64_t NewVal = *Const->getValue().getRawData(); if (Const->getType() == Type::getInt16Ty(Ctx)) NewVal &= 0xFFFF; else NewVal &= 0xFF; NewConst = ConstantInt::get(ExtTy, NewVal); } I->replaceUsesOfWith(Const, NewConst); }; auto InsertDSPIntrinsic = [&](Instruction *I) { LLVM_DEBUG(dbgs() << "ARM CGP: Inserting DSP intrinsic for " << *I << "\n"); Function *DSPInst = Intrinsic::getDeclaration(M, getNarrowIntrinsic(I, TypeSize)); Builder.SetInsertPoint(I); Builder.SetCurrentDebugLocation(I->getDebugLoc()); Value *Args[] = { I->getOperand(0), I->getOperand(1) }; CallInst *Call = Builder.CreateCall(DSPInst, Args); ReplaceAllUsersOfWith(I, Call); InstsToRemove.push_back(I); NewInsts.insert(Call); }; auto InsertZExt = [&](Value *V, Instruction *InsertPt) { LLVM_DEBUG(dbgs() << "ARM CGP: Inserting ZExt for " << *V << "\n"); Builder.SetInsertPoint(InsertPt); if (auto *I = dyn_cast(V)) Builder.SetCurrentDebugLocation(I->getDebugLoc()); auto *ZExt = cast(Builder.CreateZExt(V, ExtTy)); if (isa(V)) ZExt->moveBefore(InsertPt); else ZExt->moveAfter(InsertPt); ReplaceAllUsersOfWith(V, ZExt); NewInsts.insert(ZExt); }; // First, insert extending instructions between the leaves and their users. LLVM_DEBUG(dbgs() << "ARM CGP: Promoting leaves:\n"); for (auto V : Leaves) { LLVM_DEBUG(dbgs() << " - " << *V << "\n"); if (auto *ZExt = dyn_cast(V)) ZExt->mutateType(ExtTy); else if (auto *I = dyn_cast(V)) InsertZExt(I, I); else if (auto *Arg = dyn_cast(V)) { BasicBlock &BB = Arg->getParent()->front(); InsertZExt(Arg, &*BB.getFirstInsertionPt()); } else { llvm_unreachable("unhandled leaf that needs extending"); } Promoted.insert(V); } LLVM_DEBUG(dbgs() << "ARM CGP: Mutating the tree..\n"); // Then mutate the types of the instructions within the tree. Here we handle // constant operands. for (auto *V : Visited) { if (Leaves.count(V)) continue; if (!isa(V)) continue; auto *I = cast(V); if (Roots.count(I)) continue; for (auto &U : I->operands()) { if ((U->getType() == ExtTy) || !isSupportedType(&*U)) continue; if (auto *Const = dyn_cast(&*U)) FixConst(Const, I); else if (isa(&*U)) U->mutateType(ExtTy); } if (shouldPromote(I)) { I->mutateType(ExtTy); Promoted.insert(I); } } // Now we need to remove any zexts that have become unnecessary, as well // as insert any intrinsics. for (auto *V : Visited) { if (Leaves.count(V)) continue; if (auto *ZExt = dyn_cast(V)) { if (ZExt->getDestTy() != ExtTy) { ZExt->mutateType(ExtTy); Promoted.insert(ZExt); } else if (ZExt->getSrcTy() == ExtTy) { ReplaceAllUsersOfWith(V, ZExt->getOperand(0)); InstsToRemove.push_back(ZExt); } continue; } if (!shouldPromote(V) || isPromotedResultSafe(V)) continue; // Replace unsafe instructions with appropriate intrinsic calls. InsertDSPIntrinsic(cast(V)); } LLVM_DEBUG(dbgs() << "ARM CGP: Fixing up the roots:\n"); // Fix up any stores or returns that use the results of the promoted // chain. for (auto I : Roots) { LLVM_DEBUG(dbgs() << " - " << *I << "\n"); Type *TruncTy = OrigTy; if (auto *Store = dyn_cast(I)) { auto *PtrTy = cast(Store->getPointerOperandType()); TruncTy = PtrTy->getElementType(); } else if (isa(I)) { Function *F = I->getParent()->getParent(); TruncTy = F->getFunctionType()->getReturnType(); } for (unsigned i = 0; i < I->getNumOperands(); ++i) { Value *V = I->getOperand(i); if (Promoted.count(V) || NewInsts.count(V)) { if (auto *Op = dyn_cast(V)) { if (auto *Call = dyn_cast(I)) TruncTy = Call->getFunctionType()->getParamType(i); if (TruncTy == ExtTy) continue; LLVM_DEBUG(dbgs() << "ARM CGP: Creating " << *TruncTy << " Trunc for " << *Op << "\n"); Builder.SetInsertPoint(Op); auto *Trunc = cast(Builder.CreateTrunc(Op, TruncTy)); Trunc->moveBefore(I); I->setOperand(i, Trunc); NewInsts.insert(Trunc); } } } } LLVM_DEBUG(dbgs() << "ARM CGP: Mutation complete.\n"); } bool ARMCodeGenPrepare::isNarrowInstSupported(Instruction *I) { if (!ST->hasDSP() || !EnableDSP || !isSupportedType(I)) return false; if (ST->isThumb() && !ST->hasThumb2()) return false; if (I->getOpcode() != Instruction::Add && I->getOpcode() != Instruction::Sub) return false; // TODO // Would it be profitable? For Thumb code, these parallel DSP instructions // are only Thumb-2, so we wouldn't be able to dual issue on Cortex-M33. For // Cortex-A, specifically Cortex-A72, the latency is double and throughput is // halved. They also do not take immediates as operands. for (auto &Op : I->operands()) { if (isa(Op)) { if (!EnableDSPWithImms) return false; } } return true; } /// We accept most instructions, as well as Arguments and ConstantInsts. We /// Disallow casts other than zext and truncs and only allow calls if their /// return value is zeroext. We don't allow opcodes that can introduce sign /// bits. bool ARMCodeGenPrepare::isSupportedValue(Value *V) { LLVM_DEBUG(dbgs() << "ARM CGP: Is " << *V << " supported?\n"); // Non-instruction values that we can handle. if (isa(V) || isa(V)) return true; // Memory instructions if (isa(V) || isa(V) || isa(V)) return true; // Branches and targets. if (auto *ICmp = dyn_cast(V)) return ICmp->isEquality() || !ICmp->isSigned(); if( isa(V) || isa(V) || isa(V)) return true; if (isa(V) || isa(V) || isa(V)) return true; // Special cases for calls as we need to check for zeroext // TODO We should accept calls even if they don't have zeroext, as they can // still be roots. if (auto *Call = dyn_cast(V)) return Call->hasRetAttr(Attribute::AttrKind::ZExt); else if (auto *Cast = dyn_cast(V)) { if (isa(Cast)) return Cast->getDestTy()->getScalarSizeInBits() <= 32; else if (auto *Trunc = dyn_cast(V)) return Trunc->getDestTy()->getScalarSizeInBits() <= TypeSize; else { LLVM_DEBUG(dbgs() << "ARM CGP: No, unsupported cast.\n"); return false; } } else if (!isa(V)) { LLVM_DEBUG(dbgs() << "ARM CGP: No, not a binary operator.\n"); return false; } bool res = !isSigned(V); if (!res) LLVM_DEBUG(dbgs() << "ARM CGP: No, it's a signed instruction.\n"); return res; } /// Check that the type of V would be promoted and that the original type is /// smaller than the targeted promoted type. Check that we're not trying to /// promote something larger than our base 'TypeSize' type. bool ARMCodeGenPrepare::isLegalToPromote(Value *V) { if (!isSupportedType(V)) return false; unsigned VSize = 0; if (auto *Ld = dyn_cast(V)) { auto *PtrTy = cast(Ld->getPointerOperandType()); VSize = PtrTy->getElementType()->getPrimitiveSizeInBits(); } else if (auto *ZExt = dyn_cast(V)) { VSize = ZExt->getOperand(0)->getType()->getPrimitiveSizeInBits(); } else { VSize = V->getType()->getPrimitiveSizeInBits(); } if (VSize > TypeSize) return false; if (isPromotedResultSafe(V)) return true; if (auto *I = dyn_cast(V)) return isNarrowInstSupported(I); return false; } bool ARMCodeGenPrepare::TryToPromote(Value *V) { OrigTy = V->getType(); TypeSize = OrigTy->getPrimitiveSizeInBits(); if (!isSupportedValue(V) || !shouldPromote(V) || !isLegalToPromote(V)) return false; LLVM_DEBUG(dbgs() << "ARM CGP: TryToPromote: " << *V << "\n"); SetVector WorkList; SmallPtrSet Leaves; SmallPtrSet Roots; WorkList.insert(V); SmallPtrSet CurrentVisited; CurrentVisited.clear(); // Return true if the given value can, or has been, visited. Add V to the // worklist if needed. auto AddLegalInst = [&](Value *V) { if (CurrentVisited.count(V)) return true; if (!isSupportedValue(V) || (shouldPromote(V) && !isLegalToPromote(V))) { LLVM_DEBUG(dbgs() << "ARM CGP: Can't handle: " << *V << "\n"); return false; } WorkList.insert(V); return true; }; // Iterate through, and add to, a tree of operands and users in the use-def. while (!WorkList.empty()) { Value *V = WorkList.back(); WorkList.pop_back(); if (CurrentVisited.count(V)) continue; if (!isa(V) && !isSource(V)) continue; // If we've already visited this value from somewhere, bail now because // the tree has already been explored. // TODO: This could limit the transform, ie if we try to promote something // from an i8 and fail first, before trying an i16. if (AllVisited.count(V)) { LLVM_DEBUG(dbgs() << "ARM CGP: Already visited this: " << *V << "\n"); return false; } CurrentVisited.insert(V); AllVisited.insert(V); // Calls can be both sources and sinks. if (isSink(V)) Roots.insert(cast(V)); if (isSource(V)) Leaves.insert(V); else if (auto *I = dyn_cast(V)) { // Visit operands of any instruction visited. for (auto &U : I->operands()) { if (!AddLegalInst(U)) return false; } } // Don't visit users of a node which isn't going to be mutated unless its a // source. if (isSource(V) || shouldPromote(V)) { for (Use &U : V->uses()) { if (!AddLegalInst(U.getUser())) return false; } } } unsigned NumToPromote = 0; unsigned Cost = 0; for (auto *V : CurrentVisited) { // Truncs will cause a uxt and no zeroext arguments will often require // a uxt somewhere. if (isa(V)) ++Cost; else if (auto *Arg = dyn_cast(V)) { if (!Arg->hasZExtAttr()) ++Cost; } // Mem ops can automatically be extended/truncated and non-instructions // don't need anything done. if (Leaves.count(V) || isa(V) || !isa(V)) continue; // Will need to truncate calls args and returns. if (Roots.count(cast(V))) { ++Cost; continue; } if (shouldPromote(V)) ++NumToPromote; } LLVM_DEBUG(dbgs() << "ARM CGP: Visited nodes:\n"; for (auto *I : CurrentVisited) I->dump(); ); LLVM_DEBUG(dbgs() << "ARM CGP: Cost of promoting " << NumToPromote << " instructions = " << Cost << "\n"); if (Cost > NumToPromote || (NumToPromote == 0)) return false; Promoter->Mutate(OrigTy, CurrentVisited, Leaves, Roots); return true; } bool ARMCodeGenPrepare::doInitialization(Module &M) { Promoter = new IRPromoter(&M); return false; } bool ARMCodeGenPrepare::runOnFunction(Function &F) { if (skipFunction(F) || DisableCGP) return false; auto *TPC = &getAnalysis(); if (!TPC) return false; const TargetMachine &TM = TPC->getTM(); ST = &TM.getSubtarget(F); bool MadeChange = false; LLVM_DEBUG(dbgs() << "ARM CGP: Running on " << F.getName() << "\n"); // Search up from icmps to try to promote their operands. for (BasicBlock &BB : F) { auto &Insts = BB.getInstList(); for (auto &I : Insts) { if (AllVisited.count(&I)) continue; if (isa(I)) { auto &CI = cast(I); // Skip signed or pointer compares if (CI.isSigned() || !isa(CI.getOperand(0)->getType())) continue; LLVM_DEBUG(dbgs() << "ARM CGP: Searching from: " << CI << "\n"); for (auto &Op : CI.operands()) { if (auto *I = dyn_cast(Op)) { if (isa(I)) MadeChange |= TryToPromote(I->getOperand(0)); else MadeChange |= TryToPromote(I); } } } } Promoter->Cleanup(); LLVM_DEBUG(if (verifyFunction(F, &dbgs())) { dbgs(); report_fatal_error("Broken function after type promotion"); }); } if (MadeChange) LLVM_DEBUG(dbgs() << "After ARMCodeGenPrepare: " << F << "\n"); return MadeChange; } bool ARMCodeGenPrepare::doFinalization(Module &M) { delete Promoter; return false; } INITIALIZE_PASS_BEGIN(ARMCodeGenPrepare, DEBUG_TYPE, "ARM IR optimizations", false, false) INITIALIZE_PASS_END(ARMCodeGenPrepare, DEBUG_TYPE, "ARM IR optimizations", false, false) char ARMCodeGenPrepare::ID = 0; FunctionPass *llvm::createARMCodeGenPreparePass() { return new ARMCodeGenPrepare(); }