123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- ///////////////////////////////////////////////////////////////////////////////
- // //
- // HLLegalizeParameter.cpp //
- // Copyright (C) Microsoft Corporation. All rights reserved. //
- // This file is distributed under the University of Illinois Open Source //
- // License. See LICENSE.TXT for details. //
- // //
- // Legalize in parameter has write and out parameter has read. //
- // Must be call before inline pass. //
- ///////////////////////////////////////////////////////////////////////////////
- #include "dxc/HLSL/HLModule.h"
- #include "dxc/DXIL/DxilOperations.h"
- #include "dxc/DXIL/DxilUtil.h"
- #include "dxc/HLSL/DxilGenerationPass.h"
- #include "dxc/HLSL/HLUtil.h"
- #include "dxc/DXIL/DxilTypeSystem.h"
- #include "llvm/IR/IntrinsicInst.h"
- #include "dxc/Support/Global.h"
- #include "llvm/Pass.h"
- #include "llvm/ADT/ArrayRef.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/IR/Constant.h"
- #include "llvm/IR/Constants.h"
- #include "llvm/IR/Function.h"
- #include "llvm/IR/Instruction.h"
- #include "llvm/IR/Instructions.h"
- #include "llvm/IR/IRBuilder.h"
- #include "llvm/IR/Module.h"
- #include "llvm/Support/Casting.h"
- #include <vector>
- using namespace llvm;
- using namespace hlsl;
- // For parameter need to legalize, create alloca to replace all uses of it, and copy between the alloca and the parameter.
- namespace {
- class HLLegalizeParameter : public ModulePass {
- public:
- static char ID;
- explicit HLLegalizeParameter() : ModulePass(ID) {}
- bool runOnModule(Module &M) override;
- private:
- void patchWriteOnInParam(Function &F, Argument &Arg, const DataLayout &DL);
- void patchReadOnOutParam(Function &F, Argument &Arg, const DataLayout &DL);
- };
- AllocaInst *createAllocaForPatch(Function &F, Type *Ty) {
- IRBuilder<> Builder(F.getEntryBlock().getFirstInsertionPt());
- return Builder.CreateAlloca(Ty);
- }
- void copyIn(AllocaInst *temp, Value *arg, CallInst *CI, unsigned size) {
- if (size == 0)
- return;
- // Copy arg to temp before CI.
- IRBuilder<> Builder(CI);
- Builder.CreateMemCpy(temp, arg, size, 1);
- }
- void copyOut(AllocaInst *temp, Value *arg, CallInst *CI, unsigned size) {
- if (size == 0)
- return;
- // Copy temp to arg after CI.
- IRBuilder<> Builder(CI->getNextNode());
- Builder.CreateMemCpy(arg, temp, size, 1);
- }
- bool isPointerNeedToLower(Value *V, Type *HandleTy) {
- // CBuffer, Buffer, Texture....
- // Anything related to dxil op.
- // hl.subscript.
- // Got to root of GEP.
- while (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
- V = GEP->getPointerOperand();
- }
- CallInst *CI = dyn_cast<CallInst>(V);
- if (!CI) {
- // If array of vector, we need a copy to handle vector to array in LowerTypePasses.
- Type *Ty = V->getType();
- if (Ty->isPointerTy())
- Ty = Ty->getPointerElementType();
- if (!Ty->isArrayTy())
- return false;
- while (Ty->isArrayTy()) {
- Ty = Ty->getArrayElementType();
- }
- return Ty->isVectorTy();
- }
- HLOpcodeGroup group = GetHLOpcodeGroup(CI->getCalledFunction());
- if (group != HLOpcodeGroup::HLSubscript)
- return false;
- Value *Ptr = CI->getArgOperand(HLOperandIndex::kSubscriptObjectOpIdx);
- // Ptr from resource handle.
- if (Ptr->getType() == HandleTy)
- return true;
- unsigned Opcode = GetHLOpcode(CI);
- // Ptr from cbuffer.
- if (Opcode == (unsigned)HLSubscriptOpcode::CBufferSubscript)
- return true;
- return isPointerNeedToLower(Ptr, HandleTy);
- }
- bool mayAliasWithGlobal(Value *V, CallInst *CallSite, std::vector<GlobalVariable *> &staticGVs) {
- // The unsafe case need copy-in copy-out will be global variable alias with
- // parameter. Then global variable is updated in the function, the parameter
- // will be updated silently.
- // Currently add copy for all non-const static global in
- // CGMSHLSLRuntime::EmitHLSLOutParamConversionInit.
- //So here just return false and do nothing.
- // For case like
- // struct T {
- // float4 a[10];
- //};
- // static T g;
- // void foo(inout T t) {
- // // modify g
- //}
- // void bar() {
- // T t = g;
- // // Not copy because t is local.
- // // But optimizations will change t to g later.
- // foo(t);
- //}
- // Optimizations which remove the copy should not replace foo(t) into foo(g)
- // when g could be modified.
- // TODO: remove copy for global in
- // CGMSHLSLRuntime::EmitHLSLOutParamConversionInit, do analysis to check alias
- // only generate copy when there's alias.
- return false;
- }
- struct CopyData {
- CallInst *CallSite;
- Value *Arg;
- bool bCopyIn;
- bool bCopyOut;
- };
- void ParameterCopyInCopyOut(hlsl::HLModule &HLM) {
- Module &M = *HLM.GetModule();
- Type *HandleTy = HLM.GetOP()->GetHandleType();
- const DataLayout &DL = M.getDataLayout();
- std::vector<GlobalVariable *> staticGVs;
- for (GlobalVariable &GV : M.globals()) {
- if (dxilutil::IsStaticGlobal(&GV) && !GV.isConstant()) {
- staticGVs.emplace_back(&GV);
- }
- }
- SmallVector<CopyData, 4> WorkList;
- for (Function &F : M) {
- if (F.user_empty())
- continue;
- DxilFunctionAnnotation *Annot = HLM.GetFunctionAnnotation(&F);
- // Skip functions don't have annotation, include llvm intrinsic and HLOp
- // functions.
- if (!Annot)
- continue;
- bool bNoInline = F.hasFnAttribute(llvm::Attribute::NoInline) || F.isDeclaration();
- for (User *U : F.users()) {
- CallInst *CI = dyn_cast<CallInst>(U);
- if (!CI)
- continue;
- for (unsigned i = 0; i < CI->getNumArgOperands(); i++) {
- Value *arg = CI->getArgOperand(i);
- if (!arg->getType()->isPointerTy())
- continue;
- DxilParameterAnnotation &ParamAnnot = Annot->GetParameterAnnotation(i);
- bool bCopyIn = false;
- bool bCopyOut = false;
- switch (ParamAnnot.GetParamInputQual()) {
- default:
- break;
- case DxilParamInputQual::In: {
- bCopyIn = true;
- } break;
- case DxilParamInputQual::Out: {
- bCopyOut = true;
- } break;
- case DxilParamInputQual::Inout: {
- bCopyIn = true;
- bCopyOut = true;
- } break;
- }
- if (!bCopyIn && !bCopyOut)
- continue;
- // When use ptr from cbuffer/buffer, need copy to avoid lower on user
- // function.
- bool bNeedCopy = mayAliasWithGlobal(arg, CI, staticGVs);
- if (bNoInline)
- bNeedCopy |= isPointerNeedToLower(arg, HandleTy);
- if (!bNeedCopy)
- continue;
- CopyData data = {CI, arg, bCopyIn, bCopyOut};
- WorkList.emplace_back(data);
- }
- }
- }
- for (CopyData &data : WorkList) {
- CallInst *CI = data.CallSite;
- Value *arg = data.Arg;
- Type *Ty = arg->getType()->getPointerElementType();
- Type *EltTy = dxilutil::GetArrayEltTy(Ty);
- // Skip on object type and resource type.
- if (dxilutil::IsHLSLObjectType(EltTy) ||
- dxilutil::IsHLSLResourceType(EltTy))
- continue;
- unsigned size = DL.getTypeAllocSize(Ty);
- AllocaInst *temp = createAllocaForPatch(*CI->getParent()->getParent(), Ty);
- // TODO: Adding lifetime intrinsics isn't easy here, have to analyze uses.
- if (data.bCopyIn)
- copyIn(temp, arg, CI, size);
- if (data.bCopyOut)
- copyOut(temp, arg, CI, size);
- CI->replaceUsesOfWith(arg, temp);
- }
- }
- } // namespace
- bool HLLegalizeParameter::runOnModule(Module &M) {
- HLModule &HLM = M.GetOrCreateHLModule();
- auto &typeSys = HLM.GetTypeSystem();
- const DataLayout &DL = M.getDataLayout();
- for (Function &F : M) {
- if (F.isDeclaration())
- continue;
- DxilFunctionAnnotation *Annot = HLM.GetFunctionAnnotation(&F);
- if (!Annot)
- continue;
- for (Argument &Arg : F.args()) {
- if (!Arg.getType()->isPointerTy())
- continue;
- Type *EltTy = dxilutil::GetArrayEltTy(Arg.getType());
- if (dxilutil::IsHLSLObjectType(EltTy) ||
- dxilutil::IsHLSLResourceType(EltTy))
- continue;
- DxilParameterAnnotation &ParamAnnot =
- Annot->GetParameterAnnotation(Arg.getArgNo());
- switch (ParamAnnot.GetParamInputQual()) {
- default:
- break;
- case DxilParamInputQual::In: {
- hlutil::PointerStatus PS(&Arg, 0, /*bLdStOnly*/ true);
- PS.analyze(typeSys, /*bStructElt*/ false);
- if (PS.HasStored()) {
- patchWriteOnInParam(F, Arg, DL);
- }
- } break;
- case DxilParamInputQual::Out: {
- hlutil::PointerStatus PS(&Arg, 0, /*bLdStOnly*/ true);
- PS.analyze(typeSys, /*bStructElt*/false);
- if (PS.HasLoaded()) {
- patchReadOnOutParam(F, Arg, DL);
- }
- }
- }
- }
- }
- // Copy-in copy-out for ptr arg when need.
- ParameterCopyInCopyOut(HLM);
- return true;
- }
- void HLLegalizeParameter::patchWriteOnInParam(Function &F, Argument &Arg,
- const DataLayout &DL) {
- // TODO: Adding lifetime intrinsics isn't easy here, have to analyze uses.
- Type *Ty = Arg.getType()->getPointerElementType();
- AllocaInst *temp = createAllocaForPatch(F, Ty);
- Arg.replaceAllUsesWith(temp);
- IRBuilder<> Builder(temp->getNextNode());
- unsigned size = DL.getTypeAllocSize(Ty);
- // copy arg to temp at beginning of function.
- Builder.CreateMemCpy(temp, &Arg, size, 1);
- }
- void HLLegalizeParameter::patchReadOnOutParam(Function &F, Argument &Arg,
- const DataLayout &DL) {
- // TODO: Adding lifetime intrinsics isn't easy here, have to analyze uses.
- Type *Ty = Arg.getType()->getPointerElementType();
- AllocaInst *temp = createAllocaForPatch(F, Ty);
- Arg.replaceAllUsesWith(temp);
- unsigned size = DL.getTypeAllocSize(Ty);
- for (auto &BB : F.getBasicBlockList()) {
- // copy temp to arg before every return.
- if (ReturnInst *RI = dyn_cast<ReturnInst>(BB.getTerminator())) {
- IRBuilder<> RetBuilder(RI);
- RetBuilder.CreateMemCpy(&Arg, temp, size, 1);
- }
- }
- }
- char HLLegalizeParameter::ID = 0;
- ModulePass *llvm::createHLLegalizeParameter() {
- return new HLLegalizeParameter();
- }
- INITIALIZE_PASS(HLLegalizeParameter, "hl-legalize-parameter",
- "Legalize parameter", false, false)
|