212 строки
7.2 KiB
C++
212 строки
7.2 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// DxilSignature.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. //
|
||
|
// //
|
||
|
// DxilLegalizeSampleOffsetPass implementation. //
|
||
|
// //
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "dxc/HLSL/DxilGenerationPass.h"
|
||
|
#include "dxc/HLSL/DxilModule.h"
|
||
|
#include "dxc/HLSL/DxilOperations.h"
|
||
|
|
||
|
#include "llvm/Analysis/InstructionSimplify.h"
|
||
|
#include "llvm/Analysis/LoopInfo.h"
|
||
|
#include "llvm/IR/Constants.h"
|
||
|
#include "llvm/IR/Dominators.h"
|
||
|
#include "llvm/IR/Instructions.h"
|
||
|
#include "llvm/IR/LLVMContext.h"
|
||
|
#include "llvm/IR/LegacyPassManager.h"
|
||
|
#include "llvm/IR/PassManager.h"
|
||
|
#include "llvm/Pass.h"
|
||
|
#include "llvm/Transforms/Scalar.h"
|
||
|
|
||
|
#include <unordered_set>
|
||
|
|
||
|
using std::vector;
|
||
|
using std::unique_ptr;
|
||
|
using namespace llvm;
|
||
|
using namespace hlsl;
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Legalize Sample offset.
|
||
|
|
||
|
namespace {
|
||
|
// When optimizations are disabled, try to legalize sample offset.
|
||
|
class DxilLegalizeSampleOffsetPass : public FunctionPass {
|
||
|
|
||
|
public:
|
||
|
static char ID; // Pass identification, replacement for typeid
|
||
|
explicit DxilLegalizeSampleOffsetPass() : FunctionPass(ID) {}
|
||
|
|
||
|
const char *getPassName() const override {
|
||
|
return "DXIL legalize sample offset";
|
||
|
}
|
||
|
|
||
|
bool runOnFunction(Function &F) override {
|
||
|
DxilModule &DM = F.getParent()->GetOrCreateDxilModule();
|
||
|
hlsl::OP *hlslOP = DM.GetOP();
|
||
|
|
||
|
std::vector<Instruction *> illegalOffsets;
|
||
|
|
||
|
CollectIllegalOffsets(illegalOffsets, F, hlslOP);
|
||
|
|
||
|
if (illegalOffsets.empty())
|
||
|
return false;
|
||
|
|
||
|
// Loop unroll if has offset inside loop.
|
||
|
TryUnrollLoop(illegalOffsets, F);
|
||
|
|
||
|
// Collect offset again after mem2reg.
|
||
|
std::vector<Instruction *> ssaIllegalOffsets;
|
||
|
CollectIllegalOffsets(ssaIllegalOffsets, F, hlslOP);
|
||
|
|
||
|
// Run simple optimization to legalize offsets.
|
||
|
LegalizeOffsets(ssaIllegalOffsets);
|
||
|
|
||
|
// Remove PHINodes to keep code shape.
|
||
|
legacy::FunctionPassManager PM(F.getParent());
|
||
|
PM.add(createDemoteRegisterToMemoryHlslPass());
|
||
|
PM.run(F);
|
||
|
|
||
|
FinalCheck(illegalOffsets, F, hlslOP);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void TryUnrollLoop(std::vector<Instruction *> &illegalOffsets, Function &F);
|
||
|
void CollectIllegalOffsets(std::vector<Instruction *> &illegalOffsets,
|
||
|
Function &F, hlsl::OP *hlslOP);
|
||
|
void CollectIllegalOffsets(std::vector<Instruction *> &illegalOffsets,
|
||
|
Function &F, DXIL::OpCode opcode,
|
||
|
hlsl::OP *hlslOP);
|
||
|
void LegalizeOffsets(const std::vector<Instruction *> &illegalOffsets);
|
||
|
void FinalCheck(std::vector<Instruction *> &illegalOffsets, Function &F,
|
||
|
hlsl::OP *hlslOP);
|
||
|
};
|
||
|
|
||
|
char DxilLegalizeSampleOffsetPass::ID = 0;
|
||
|
|
||
|
bool HasIllegalOffsetInLoop(std::vector<Instruction *> &illegalOffsets,
|
||
|
Function &F) {
|
||
|
DominatorTreeAnalysis DTA;
|
||
|
DominatorTree DT = DTA.run(F);
|
||
|
LoopInfo LI;
|
||
|
LI.Analyze(DT);
|
||
|
|
||
|
bool findOffset = false;
|
||
|
|
||
|
for (Instruction *I : illegalOffsets) {
|
||
|
BasicBlock *BB = I->getParent();
|
||
|
if (LI.getLoopFor(BB)) {
|
||
|
findOffset = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return findOffset;
|
||
|
}
|
||
|
|
||
|
void CollectIllegalOffset(CallInst *CI,
|
||
|
std::vector<Instruction *> &illegalOffsets) {
|
||
|
Value *offset0 =
|
||
|
CI->getArgOperand(DXIL::OperandIndex::kTextureSampleOffset0OpIdx);
|
||
|
// No offset.
|
||
|
if (isa<UndefValue>(offset0))
|
||
|
return;
|
||
|
|
||
|
for (unsigned i = DXIL::OperandIndex::kTextureSampleOffset0OpIdx;
|
||
|
i <= DXIL::OperandIndex::kTextureSampleOffset2OpIdx; i++) {
|
||
|
Value *offset = CI->getArgOperand(i);
|
||
|
if (Instruction *I = dyn_cast<Instruction>(offset))
|
||
|
illegalOffsets.emplace_back(I);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DxilLegalizeSampleOffsetPass::FinalCheck(
|
||
|
std::vector<Instruction *> &illegalOffsets, Function &F, hlsl::OP *hlslOP) {
|
||
|
// Collect offset to make sure no illegal offsets.
|
||
|
std::vector<Instruction *> finalIllegalOffsets;
|
||
|
CollectIllegalOffsets(finalIllegalOffsets, F, hlslOP);
|
||
|
|
||
|
if (!finalIllegalOffsets.empty()) {
|
||
|
const StringRef kIllegalOffsetError =
|
||
|
"Offsets for Sample* must be immediated value. "
|
||
|
"Consider unroll the loop manually and use O3, it may help in some "
|
||
|
"cases\n";
|
||
|
std::string errorMsg;
|
||
|
raw_string_ostream errorStr(errorMsg);
|
||
|
for (Instruction *offset : finalIllegalOffsets) {
|
||
|
if (const DebugLoc &L = offset->getDebugLoc())
|
||
|
L.print(errorStr);
|
||
|
errorStr << " " << kIllegalOffsetError;
|
||
|
}
|
||
|
errorStr.flush();
|
||
|
F.getContext().emitError(errorMsg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DxilLegalizeSampleOffsetPass::TryUnrollLoop(
|
||
|
std::vector<Instruction *> &illegalOffsets, Function &F) {
|
||
|
legacy::FunctionPassManager PM(F.getParent());
|
||
|
// Always need mem2reg for simplify illegal offsets.
|
||
|
PM.add(createPromoteMemoryToRegisterPass());
|
||
|
|
||
|
if (HasIllegalOffsetInLoop(illegalOffsets, F)) {
|
||
|
PM.add(createCFGSimplificationPass());
|
||
|
PM.add(createLCSSAPass());
|
||
|
PM.add(createLoopSimplifyPass());
|
||
|
PM.add(createLoopRotatePass());
|
||
|
PM.add(createLoopUnrollPass(-2, -1, 0, 0));
|
||
|
}
|
||
|
PM.run(F);
|
||
|
}
|
||
|
|
||
|
void DxilLegalizeSampleOffsetPass::CollectIllegalOffsets(
|
||
|
std::vector<Instruction *> &illegalOffsets, Function &CurF,
|
||
|
hlsl::OP *hlslOP) {
|
||
|
CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::Sample, hlslOP);
|
||
|
CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleBias, hlslOP);
|
||
|
CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleCmp, hlslOP);
|
||
|
CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleCmpLevelZero,
|
||
|
hlslOP);
|
||
|
CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleGrad, hlslOP);
|
||
|
CollectIllegalOffsets(illegalOffsets, CurF, DXIL::OpCode::SampleLevel,
|
||
|
hlslOP);
|
||
|
}
|
||
|
|
||
|
void DxilLegalizeSampleOffsetPass::CollectIllegalOffsets(
|
||
|
std::vector<Instruction *> &illegalOffsets, Function &CurF,
|
||
|
DXIL::OpCode opcode, hlsl::OP *hlslOP) {
|
||
|
ArrayRef<Function *> intrFuncList = hlslOP->GetOpFuncList(opcode);
|
||
|
for (Function *intrFunc : intrFuncList) {
|
||
|
if (!intrFunc)
|
||
|
continue;
|
||
|
for (User *U : intrFunc->users()) {
|
||
|
CallInst *CI = cast<CallInst>(U);
|
||
|
// Skip inst not in current function.
|
||
|
if (CI->getParent()->getParent() != &CurF)
|
||
|
continue;
|
||
|
|
||
|
CollectIllegalOffset(CI, illegalOffsets);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DxilLegalizeSampleOffsetPass::LegalizeOffsets(
|
||
|
const std::vector<Instruction *> &illegalOffsets) {
|
||
|
for (Instruction *I : illegalOffsets)
|
||
|
llvm::recursivelySimplifyInstruction(I);
|
||
|
}
|
||
|
|
||
|
FunctionPass *llvm::createDxilLegalizeSampleOffsetPass() {
|
||
|
return new DxilLegalizeSampleOffsetPass();
|
||
|
}
|
||
|
|
||
|
INITIALIZE_PASS(DxilLegalizeSampleOffsetPass, "dxil-legalize-sample-offset",
|
||
|
"DXIL legalize sample offset", false, false)
|