258 lines · cpp
1//===-- SPIRVRegularizer.cpp - regularize IR for SPIR-V ---------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This pass implements regularization of LLVM IR for SPIR-V. The prototype of10// the pass was taken from SPIRV-LLVM translator.11//12//===----------------------------------------------------------------------===//13 14#include "SPIRV.h"15#include "llvm/Demangle/Demangle.h"16#include "llvm/IR/InstIterator.h"17#include "llvm/IR/InstVisitor.h"18#include "llvm/IR/PassManager.h"19#include "llvm/Transforms/Utils/Cloning.h"20 21#include <list>22 23#define DEBUG_TYPE "spirv-regularizer"24 25using namespace llvm;26 27namespace {28struct SPIRVRegularizer : public FunctionPass, InstVisitor<SPIRVRegularizer> {29 DenseMap<Function *, Function *> Old2NewFuncs;30 31public:32 static char ID;33 SPIRVRegularizer() : FunctionPass(ID) {}34 bool runOnFunction(Function &F) override;35 StringRef getPassName() const override { return "SPIR-V Regularizer"; }36 37 void getAnalysisUsage(AnalysisUsage &AU) const override {38 FunctionPass::getAnalysisUsage(AU);39 }40 void visitCallInst(CallInst &CI);41 42private:43 void visitCallScalToVec(CallInst *CI, StringRef MangledName,44 StringRef DemangledName);45 void runLowerConstExpr(Function &F);46};47} // namespace48 49char SPIRVRegularizer::ID = 0;50 51INITIALIZE_PASS(SPIRVRegularizer, DEBUG_TYPE, "SPIR-V Regularizer", false,52 false)53 54// Since SPIR-V cannot represent constant expression, constant expressions55// in LLVM IR need to be lowered to instructions. For each function,56// the constant expressions used by instructions of the function are replaced57// by instructions placed in the entry block since it dominates all other BBs.58// Each constant expression only needs to be lowered once in each function59// and all uses of it by instructions in that function are replaced by60// one instruction.61// TODO: remove redundant instructions for common subexpression.62void SPIRVRegularizer::runLowerConstExpr(Function &F) {63 LLVMContext &Ctx = F.getContext();64 std::list<Instruction *> WorkList;65 for (auto &II : instructions(F))66 WorkList.push_back(&II);67 68 auto FBegin = F.begin();69 while (!WorkList.empty()) {70 Instruction *II = WorkList.front();71 72 auto LowerOp = [&II, &FBegin, &F](Value *V) -> Value * {73 if (isa<Function>(V))74 return V;75 auto *CE = cast<ConstantExpr>(V);76 LLVM_DEBUG(dbgs() << "[lowerConstantExpressions] " << *CE);77 auto ReplInst = CE->getAsInstruction();78 auto InsPoint = II->getParent() == &*FBegin ? II : &FBegin->back();79 ReplInst->insertBefore(InsPoint->getIterator());80 LLVM_DEBUG(dbgs() << " -> " << *ReplInst << '\n');81 std::vector<Instruction *> Users;82 // Do not replace use during iteration of use. Do it in another loop.83 for (auto U : CE->users()) {84 LLVM_DEBUG(dbgs() << "[lowerConstantExpressions] Use: " << *U << '\n');85 auto InstUser = dyn_cast<Instruction>(U);86 // Only replace users in scope of current function.87 if (InstUser && InstUser->getParent()->getParent() == &F)88 Users.push_back(InstUser);89 }90 for (auto &User : Users) {91 if (ReplInst->getParent() == User->getParent() &&92 User->comesBefore(ReplInst))93 ReplInst->moveBefore(User->getIterator());94 User->replaceUsesOfWith(CE, ReplInst);95 }96 return ReplInst;97 };98 99 WorkList.pop_front();100 auto LowerConstantVec = [&II, &LowerOp, &WorkList,101 &Ctx](ConstantVector *Vec,102 unsigned NumOfOp) -> Value * {103 if (std::all_of(Vec->op_begin(), Vec->op_end(), [](Value *V) {104 return isa<ConstantExpr>(V) || isa<Function>(V);105 })) {106 // Expand a vector of constexprs and construct it back with107 // series of insertelement instructions.108 std::list<Value *> OpList;109 std::transform(Vec->op_begin(), Vec->op_end(),110 std::back_inserter(OpList),111 [LowerOp](Value *V) { return LowerOp(V); });112 Value *Repl = nullptr;113 unsigned Idx = 0;114 auto *PhiII = dyn_cast<PHINode>(II);115 Instruction *InsPoint =116 PhiII ? &PhiII->getIncomingBlock(NumOfOp)->back() : II;117 std::list<Instruction *> ReplList;118 for (auto V : OpList) {119 if (auto *Inst = dyn_cast<Instruction>(V))120 ReplList.push_back(Inst);121 Repl = InsertElementInst::Create(122 (Repl ? Repl : PoisonValue::get(Vec->getType())), V,123 ConstantInt::get(Type::getInt32Ty(Ctx), Idx++), "",124 InsPoint->getIterator());125 }126 WorkList.splice(WorkList.begin(), ReplList);127 return Repl;128 }129 return nullptr;130 };131 for (unsigned OI = 0, OE = II->getNumOperands(); OI != OE; ++OI) {132 auto *Op = II->getOperand(OI);133 if (auto *Vec = dyn_cast<ConstantVector>(Op)) {134 Value *ReplInst = LowerConstantVec(Vec, OI);135 if (ReplInst)136 II->replaceUsesOfWith(Op, ReplInst);137 } else if (auto CE = dyn_cast<ConstantExpr>(Op)) {138 WorkList.push_front(cast<Instruction>(LowerOp(CE)));139 } else if (auto MDAsVal = dyn_cast<MetadataAsValue>(Op)) {140 auto ConstMD = dyn_cast<ConstantAsMetadata>(MDAsVal->getMetadata());141 if (!ConstMD)142 continue;143 Constant *C = ConstMD->getValue();144 Value *ReplInst = nullptr;145 if (auto *Vec = dyn_cast<ConstantVector>(C))146 ReplInst = LowerConstantVec(Vec, OI);147 if (auto *CE = dyn_cast<ConstantExpr>(C))148 ReplInst = LowerOp(CE);149 if (!ReplInst)150 continue;151 Metadata *RepMD = ValueAsMetadata::get(ReplInst);152 Value *RepMDVal = MetadataAsValue::get(Ctx, RepMD);153 II->setOperand(OI, RepMDVal);154 WorkList.push_front(cast<Instruction>(ReplInst));155 }156 }157 }158}159 160// It fixes calls to OCL builtins that accept vector arguments and one of them161// is actually a scalar splat.162void SPIRVRegularizer::visitCallInst(CallInst &CI) {163 auto F = CI.getCalledFunction();164 if (!F)165 return;166 167 auto MangledName = F->getName();168 char *NameStr = itaniumDemangle(F->getName().data());169 if (!NameStr)170 return;171 StringRef DemangledName(NameStr);172 173 // TODO: add support for other builtins.174 if (DemangledName.starts_with("fmin") || DemangledName.starts_with("fmax") ||175 DemangledName.starts_with("min") || DemangledName.starts_with("max"))176 visitCallScalToVec(&CI, MangledName, DemangledName);177 free(NameStr);178}179 180void SPIRVRegularizer::visitCallScalToVec(CallInst *CI, StringRef MangledName,181 StringRef DemangledName) {182 // Check if all arguments have the same type - it's simple case.183 auto Uniform = true;184 Type *Arg0Ty = CI->getOperand(0)->getType();185 auto IsArg0Vector = isa<VectorType>(Arg0Ty);186 for (unsigned I = 1, E = CI->arg_size(); Uniform && (I != E); ++I)187 Uniform = isa<VectorType>(CI->getOperand(I)->getType()) == IsArg0Vector;188 if (Uniform)189 return;190 191 auto *OldF = CI->getCalledFunction();192 Function *NewF = nullptr;193 auto [It, Inserted] = Old2NewFuncs.try_emplace(OldF);194 if (Inserted) {195 AttributeList Attrs = CI->getCalledFunction()->getAttributes();196 SmallVector<Type *, 2> ArgTypes = {OldF->getArg(0)->getType(), Arg0Ty};197 auto *NewFTy =198 FunctionType::get(OldF->getReturnType(), ArgTypes, OldF->isVarArg());199 NewF = Function::Create(NewFTy, OldF->getLinkage(), OldF->getName(),200 *OldF->getParent());201 ValueToValueMapTy VMap;202 auto NewFArgIt = NewF->arg_begin();203 for (auto &Arg : OldF->args()) {204 auto ArgName = Arg.getName();205 NewFArgIt->setName(ArgName);206 VMap[&Arg] = &(*NewFArgIt++);207 }208 SmallVector<ReturnInst *, 8> Returns;209 CloneFunctionInto(NewF, OldF, VMap,210 CloneFunctionChangeType::LocalChangesOnly, Returns);211 NewF->setAttributes(Attrs);212 It->second = NewF;213 } else {214 NewF = It->second;215 }216 assert(NewF);217 218 // This produces an instruction sequence that implements a splat of219 // CI->getOperand(1) to a vector Arg0Ty. However, we use InsertElementInst220 // and ShuffleVectorInst to generate the same code as the SPIR-V translator.221 // For instance (transcoding/OpMin.ll), this call222 // call spir_func <2 x i32> @_Z3minDv2_ii(<2 x i32> <i32 1, i32 10>, i32 5)223 // is translated to224 // %8 = OpUndef %v2uint225 // %14 = OpConstantComposite %v2uint %uint_1 %uint_10226 // ...227 // %10 = OpCompositeInsert %v2uint %uint_5 %8 0228 // %11 = OpVectorShuffle %v2uint %10 %8 0 0229 // %call = OpExtInst %v2uint %1 s_min %14 %11230 auto ConstInt = ConstantInt::get(IntegerType::get(CI->getContext(), 32), 0);231 PoisonValue *PVal = PoisonValue::get(Arg0Ty);232 Instruction *Inst = InsertElementInst::Create(233 PVal, CI->getOperand(1), ConstInt, "", CI->getIterator());234 ElementCount VecElemCount = cast<VectorType>(Arg0Ty)->getElementCount();235 Constant *ConstVec = ConstantVector::getSplat(VecElemCount, ConstInt);236 Value *NewVec =237 new ShuffleVectorInst(Inst, PVal, ConstVec, "", CI->getIterator());238 CI->setOperand(1, NewVec);239 CI->replaceUsesOfWith(OldF, NewF);240 CI->mutateFunctionType(NewF->getFunctionType());241}242 243bool SPIRVRegularizer::runOnFunction(Function &F) {244 runLowerConstExpr(F);245 visit(F);246 for (auto &OldNew : Old2NewFuncs) {247 Function *OldF = OldNew.first;248 Function *NewF = OldNew.second;249 NewF->takeName(OldF);250 OldF->eraseFromParent();251 }252 return true;253}254 255FunctionPass *llvm::createSPIRVRegularizerPass() {256 return new SPIRVRegularizer();257}258