//==- AArch64PromoteConstant.cpp - Promote constant to global for AArch64 --==// // // 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 implements the AArch64PromoteConstant pass which promotes constants // to global variables when this is likely to be more efficient. Currently only // types related to constant vector (i.e., constant vector, array of constant // vectors, constant structure with a constant vector field, etc.) are promoted // to global variables. Constant vectors are likely to be lowered in target // constant pool during instruction selection already; therefore, the access // will remain the same (memory load), but the structure types are not split // into different constant pool accesses for each field. A bonus side effect is // that created globals may be merged by the global merge pass. // // FIXME: This pass may be useful for other targets too. //===----------------------------------------------------------------------===// #include "AArch64.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> #include <utility> usingnamespacellvm; #define DEBUG_TYPE … // Stress testing mode - disable heuristics. static cl::opt<bool> Stress("aarch64-stress-promote-const", cl::Hidden, cl::desc("Promote all vector constants")); STATISTIC(NumPromoted, "Number of promoted constants"); STATISTIC(NumPromotedUses, "Number of promoted constants uses"); //===----------------------------------------------------------------------===// // AArch64PromoteConstant //===----------------------------------------------------------------------===// namespace { /// Promotes interesting constant into global variables. /// The motivating example is: /// static const uint16_t TableA[32] = { /// 41944, 40330, 38837, 37450, 36158, 34953, 33826, 32768, /// 31776, 30841, 29960, 29128, 28340, 27595, 26887, 26215, /// 25576, 24967, 24386, 23832, 23302, 22796, 22311, 21846, /// 21400, 20972, 20561, 20165, 19785, 19419, 19066, 18725, /// }; /// /// uint8x16x4_t LoadStatic(void) { /// uint8x16x4_t ret; /// ret.val[0] = vld1q_u16(TableA + 0); /// ret.val[1] = vld1q_u16(TableA + 8); /// ret.val[2] = vld1q_u16(TableA + 16); /// ret.val[3] = vld1q_u16(TableA + 24); /// return ret; /// } /// /// The constants in this example are folded into the uses. Thus, 4 different /// constants are created. /// /// As their type is vector the cheapest way to create them is to load them /// for the memory. /// /// Therefore the final assembly final has 4 different loads. With this pass /// enabled, only one load is issued for the constants. class AArch64PromoteConstant : public ModulePass { … }; } // end anonymous namespace char AArch64PromoteConstant::ID = …; INITIALIZE_PASS_BEGIN(AArch64PromoteConstant, "aarch64-promote-const", "AArch64 Promote Constant Pass", false, false) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) INITIALIZE_PASS_END(AArch64PromoteConstant, "aarch64-promote-const", "AArch64 Promote Constant Pass", false, false) ModulePass *llvm::createAArch64PromoteConstantPass() { … } /// Check if the given type uses a vector type. static bool isConstantUsingVectorTy(const Type *CstTy) { … } // Returns true if \p C contains only ConstantData leafs and no global values, // block addresses or constant expressions. Traverses ConstantAggregates. static bool containsOnlyConstantData(const Constant *C) { … } /// Check if the given use (Instruction + OpIdx) of Cst should be converted into /// a load of a global variable initialized with Cst. /// A use should be converted if it is legal to do so. /// For instance, it is not legal to turn the mask operand of a shuffle vector /// into a load of a global variable. static bool shouldConvertUse(const Constant *Cst, const Instruction *Instr, unsigned OpIdx) { … } /// Check if the given Cst should be converted into /// a load of a global variable initialized with Cst. /// A constant should be converted if it is likely that the materialization of /// the constant will be tricky. Thus, we give up on zero or undef values. /// /// \todo Currently, accept only vector related types. /// Also we give up on all simple vector type to keep the existing /// behavior. Otherwise, we should push here all the check of the lowering of /// BUILD_VECTOR. By giving up, we lose the potential benefit of merging /// constant via global merge and the fact that the same constant is stored /// only once with this method (versus, as many function that uses the constant /// for the regular approach, even for float). /// Again, the simplest solution would be to promote every /// constant and rematerialize them when they are actually cheap to create. static bool shouldConvertImpl(const Constant *Cst) { … } static bool shouldConvert(Constant &C, AArch64PromoteConstant::PromotionCacheTy &PromotionCache) { … } Instruction *AArch64PromoteConstant::findInsertionPoint(Instruction &User, unsigned OpNo) { … } bool AArch64PromoteConstant::isDominated(Instruction *NewPt, Instruction *User, unsigned OpNo, InsertionPoints &InsertPts) { … } bool AArch64PromoteConstant::tryAndMerge(Instruction *NewPt, Instruction *User, unsigned OpNo, InsertionPoints &InsertPts) { … } void AArch64PromoteConstant::computeInsertionPoint( Instruction *User, unsigned OpNo, InsertionPoints &InsertPts) { … } static void ensurePromotedGV(Function &F, Constant &C, AArch64PromoteConstant::PromotedConstant &PC) { … } void AArch64PromoteConstant::insertDefinitions(Function &F, GlobalVariable &PromotedGV, InsertionPoints &InsertPts) { … } void AArch64PromoteConstant::promoteConstants( Function &F, SmallVectorImpl<UpdateRecord> &Updates, PromotionCacheTy &PromotionCache) { … } bool AArch64PromoteConstant::runOnFunction(Function &F, PromotionCacheTy &PromotionCache) { … }