Support multiple entries in one library.

1. Save signatures for each entry.
2. Lower signatures and strip parameters for each entry.
3. Don't save function props for patch constant function.
4. Erase value from vectorEltsMap once it is used in SROA_Parameter_HLSL::replaceCastArgument and SROA_Parameter_HLSL::replaceCastParameter.
5. Remove unused member of DxilGenerationPass.
6. Fix typo when clear semantic for cloned return annotation.
This commit is contained in:
Xiang Li 2017-05-22 17:15:44 -07:00 коммит произвёл Xiang Li
Родитель 79bb311bd9
Коммит 3f0c4e5d30
9 изменённых файлов: 367 добавлений и 33 удалений

Просмотреть файл

@ -83,6 +83,7 @@ public:
// Function props.
static const char kDxilFunctionPropertiesMDName[];
static const char kDxilEntrySignaturesMDName[];
static const unsigned kDxilEntryPointNumFields = 5;
static const unsigned kDxilEntryPointFunction = 0; // Entry point function symbol.

Просмотреть файл

@ -122,6 +122,16 @@ public:
DxilSignature &GetPatchConstantSignature();
const DxilSignature &GetPatchConstantSignature() const;
const RootSignatureHandle &GetRootSignature() const;
bool HasDxilEntrySignature(llvm::Function *F) const;
DxilEntrySignature &GetDxilEntrySignature(llvm::Function *F);
// Move DxilEntrySignature of F to NewF.
void ReplaceDxilEntrySignature(llvm::Function *F, llvm::Function *NewF);
// DxilFunctionProps.
bool HasDxilFunctionProps(llvm::Function *F) const;
DxilFunctionProps &GetDxilFunctionProps(llvm::Function *F);
// Move DxilFunctionProps of F to NewF.
void ReplaceDxilFunctionProps(llvm::Function *F, llvm::Function *NewF);
// Remove Root Signature from module metadata
void StripRootSignatureFromMetadata();
@ -155,6 +165,9 @@ public:
void ResetFunctionPropsMap(
std::unordered_map<llvm::Function *, std::unique_ptr<DxilFunctionProps>>
&&propsMap);
void ResetEntrySignatureMap(
std::unordered_map<llvm::Function *, std::unique_ptr<DxilEntrySignature>>
&&SigMap);
void StripDebugRelatedCode();
llvm::DebugInfoFinder &GetOrCreateDebugInfoFinder();
@ -411,6 +424,9 @@ private:
// Function properties for shader functions.
std::unordered_map<llvm::Function *, std::unique_ptr<DxilFunctionProps>>
m_DxilFunctionPropsMap;
// EntrySig for shader functions.
std::unordered_map<llvm::Function *, std::unique_ptr<DxilEntrySignature>>
m_DxilEntrySignatureMap;
// ViewId state.
std::unique_ptr<DxilViewIdState> m_pViewIdState;

Просмотреть файл

@ -221,10 +221,27 @@ public:
std::unique_ptr<DxilEntrySignature> pSig =
llvm::make_unique<DxilEntrySignature>(SM->GetKind());
// EntrySig for shader functions.
std::unordered_map<llvm::Function *, std::unique_ptr<DxilEntrySignature>>
DxilEntrySignatureMap;
if (!SM->IsLib()) {
HLSignatureLower sigLower(m_pHLModule->GetEntryFunction(), *m_pHLModule,
*pSig);
sigLower.Run();
} else {
for (auto It = M.begin(); It != M.end();) {
Function &F = *(It++);
// Lower signature for each entry function.
if (m_pHLModule->HasDxilFunctionProps(&F)) {
DxilFunctionProps &props = m_pHLModule->GetDxilFunctionProps(&F);
std::unique_ptr<DxilEntrySignature> pSig =
llvm::make_unique<DxilEntrySignature>(props.shaderKind);
HLSignatureLower sigLower(&F, *m_pHLModule, *pSig);
sigLower.Run();
DxilEntrySignatureMap[&F] = std::move(pSig);
}
}
}
std::unordered_set<LoadInst *> UpdateCounterSet;
@ -265,6 +282,9 @@ public:
hlsl::DxilModule &DxilMod = M.GetOrCreateDxilModule(SkipInit);
InitDxilModuleFromHLModule(*m_pHLModule, DxilMod, pSig.release(),
m_HasDbgInfo);
if (SM->IsLib())
DxilMod.ResetEntrySignatureMap(std::move(DxilEntrySignatureMap));
HLModule::ClearHLMetadata(M);
M.ResetHLModule();
@ -303,21 +323,8 @@ private:
// Translate precise attribute into HL function call.
void TranslatePreciseAttribute();
// SignatureElement to Value map.
std::unordered_map<DxilSignatureElement *, Value *> m_sigValueMap;
// Set to save inout arguments.
std::unordered_set<Value *> m_inoutArgSet;
// SignatureElement which has precise attribute.
std::unordered_set<DxilSignatureElement *> m_preciseSigSet;
// Patch constant function inputs to signature element map.
std::unordered_map<unsigned, DxilSignatureElement *> m_patchConstantInputsSigMap;
// Input module is not optimized.
bool NotOptimized;
// For validation
std::unordered_map<unsigned, std::unordered_set<unsigned> > m_InputSemanticsUsed,
m_OutputSemanticsUsed[4], m_PatchConstantSemanticsUsed, m_OtherSemanticsUsed;
};
class SimplifyInst : public FunctionPass {
@ -1370,7 +1377,7 @@ Function *StripFunctionParameter(Function *F, DxilModule &DM,
}
}
Function *NewFunc = Function::Create(FT, F->getLinkage(), F->getName());
Function *NewFunc = Function::Create(FT, F->getLinkage());
M.getFunctionList().insert(F, NewFunc);
// Splice the body of the old function right into the new function.
NewFunc->getBasicBlockList().splice(NewFunc->begin(), F->getBasicBlockList());
@ -1386,6 +1393,11 @@ Function *StripFunctionParameter(Function *F, DxilModule &DM,
FunctionDIs[NewFunc] = SP;
}
NewFunc->takeName(F);
if (DM.HasDxilFunctionProps(F)) {
DM.ReplaceDxilEntrySignature(F, NewFunc);
DM.ReplaceDxilFunctionProps(F, NewFunc);
}
DM.GetTypeSystem().EraseFunctionAnnotation(F);
F->eraseFromParent();
DM.GetTypeSystem().AddFunctionAnnotation(NewFunc);
@ -1554,19 +1566,39 @@ public:
DxilModule &DM = M.GetDxilModule();
DenseMap<const Function *, DISubprogram *> FunctionDIs =
makeSubprogramMap(M);
if (Function *PatchConstantFunc = DM.GetPatchConstantFunction()) {
PatchConstantFunc =
StripFunctionParameter(PatchConstantFunc, DM, FunctionDIs);
if (PatchConstantFunc)
DM.SetPatchConstantFunction(PatchConstantFunc);
}
// Strip parameters of entry function.
if (!DM.GetShaderModel()->IsLib()) {
if (Function *PatchConstantFunc = DM.GetPatchConstantFunction()) {
PatchConstantFunc =
StripFunctionParameter(PatchConstantFunc, DM, FunctionDIs);
if (PatchConstantFunc)
DM.SetPatchConstantFunction(PatchConstantFunc);
}
if (Function *EntryFunc = DM.GetEntryFunction()) {
StringRef Name = DM.GetEntryFunctionName();
EntryFunc->setName(Name);
EntryFunc = StripFunctionParameter(EntryFunc, DM, FunctionDIs);
if (EntryFunc)
DM.SetEntryFunction(EntryFunc);
if (Function *EntryFunc = DM.GetEntryFunction()) {
StringRef Name = DM.GetEntryFunctionName();
EntryFunc->setName(Name);
EntryFunc = StripFunctionParameter(EntryFunc, DM, FunctionDIs);
if (EntryFunc)
DM.SetEntryFunction(EntryFunc);
}
} else {
std::vector<Function *> entries;
for (iplist<Function>::iterator F : M.getFunctionList()) {
if (DM.HasDxilFunctionProps(F)) {
entries.emplace_back(F);
}
}
for (Function *entry : entries) {
DxilFunctionProps &props = DM.GetDxilFunctionProps(entry);
if (props.IsHS()) {
// Strip patch constant function first.
Function *patchConstFunc = StripFunctionParameter(
props.ShaderProps.HS.patchConstantFunc, DM, FunctionDIs);
props.ShaderProps.HS.patchConstantFunc = patchConstFunc;
}
StripFunctionParameter(entry, DM, FunctionDIs);
}
}
DM.CollectShaderFlags(); // Update flags to reflect any changes.

Просмотреть файл

@ -55,6 +55,7 @@ const char DxilMDHelper::kDxilValidatorVersionMDName[] = "dx.valv
const char DxilMDHelper::kDxilRootSignatureMDName[] = "dx.rootSignature";
const char DxilMDHelper::kDxilViewIdStateMDName[] = "dx.viewIdState";
const char DxilMDHelper::kDxilFunctionPropertiesMDName[] = "dx.func.props";
const char DxilMDHelper::kDxilEntrySignaturesMDName[] = "dx.func.signatures";
static std::array<const char *, 7> DxilMDNames = {
DxilMDHelper::kDxilVersionMDName,

Просмотреть файл

@ -964,6 +964,7 @@ static void ConvertUsedResource(std::unordered_set<unsigned> &immResID,
void DxilModule::RemoveFunction(llvm::Function *F) {
DXASSERT_NOMSG(F != nullptr);
m_DxilFunctionPropsMap.erase(F);
m_DxilEntrySignatureMap.erase(F);
if (m_pTypeSystem.get()->GetFunctionAnnotation(F))
m_pTypeSystem.get()->EraseFunctionAnnotation(F);
m_pOP->RemoveFunction(F);
@ -1061,6 +1062,38 @@ const RootSignatureHandle &DxilModule::GetRootSignature() const {
return *m_RootSignature;
}
bool DxilModule::HasDxilEntrySignature(llvm::Function *F) const {
return m_DxilEntrySignatureMap.find(F) != m_DxilEntrySignatureMap.end();
}
DxilEntrySignature &DxilModule::GetDxilEntrySignature(llvm::Function *F) {
DXASSERT(m_DxilEntrySignatureMap.count(F) != 0, "cannot find F in map");
return *m_DxilEntrySignatureMap[F];
}
void DxilModule::ReplaceDxilEntrySignature(llvm::Function *F,
llvm::Function *NewF) {
DXASSERT(m_DxilEntrySignatureMap.count(F) != 0, "cannot find F in map");
std::unique_ptr<DxilEntrySignature> Sig =
std::move(m_DxilEntrySignatureMap[F]);
m_DxilEntrySignatureMap.erase(F);
m_DxilEntrySignatureMap[NewF] = std::move(Sig);
}
bool DxilModule::HasDxilFunctionProps(llvm::Function *F) const {
return m_DxilFunctionPropsMap.find(F) != m_DxilFunctionPropsMap.end();
}
DxilFunctionProps &DxilModule::GetDxilFunctionProps(llvm::Function *F) {
DXASSERT(m_DxilFunctionPropsMap.count(F) != 0, "cannot find F in map");
return *m_DxilFunctionPropsMap[F];
}
void DxilModule::ReplaceDxilFunctionProps(llvm::Function *F,
llvm::Function *NewF) {
DXASSERT(m_DxilFunctionPropsMap.count(F) != 0, "cannot find F in map");
std::unique_ptr<DxilFunctionProps> props =
std::move(m_DxilFunctionPropsMap[F]);
m_DxilFunctionPropsMap.erase(F);
m_DxilFunctionPropsMap[NewF] = std::move(props);
}
void DxilModule::StripRootSignatureFromMetadata() {
NamedMDNode *pRootSignatureNamedMD = GetModule()->getNamedMetadata(DxilMDHelper::kDxilRootSignatureMDName);
if (pRootSignatureNamedMD) {
@ -1103,6 +1136,12 @@ void DxilModule::ResetFunctionPropsMap(
m_DxilFunctionPropsMap = std::move(propsMap);
}
void DxilModule::ResetEntrySignatureMap(
std::unordered_map<llvm::Function *, std::unique_ptr<DxilEntrySignature>>
&&SigMap) {
m_DxilEntrySignatureMap = std::move(SigMap);
}
void DxilModule::EmitLLVMUsed() {
if (m_LLVMUsed.empty())
return;
@ -1175,6 +1214,16 @@ void DxilModule::EmitDxilMetadata() {
MDTuple *pProps = m_pMDHelper->EmitDxilFunctionProps(props, pair.first);
fnProps->addOperand(pProps);
}
NamedMDNode *entrySigs = m_pModule->getOrInsertNamedMetadata(
DxilMDHelper::kDxilEntrySignaturesMDName);
for (auto &&pair : m_DxilEntrySignatureMap) {
Function *F = pair.first;
DxilEntrySignature *Sig = pair.second.get();
MDTuple *pSig = m_pMDHelper->EmitDxilSignatures(*Sig);
entrySigs->addOperand(
MDTuple::get(m_Ctx, {ValueAsMetadata::get(F), pSig}));
}
}
}
@ -1227,6 +1276,28 @@ void DxilModule::LoadDxilMetadata() {
m_DxilFunctionPropsMap[F] = std::move(props);
}
NamedMDNode *entrySigs = m_pModule->getOrInsertNamedMetadata(
DxilMDHelper::kDxilEntrySignaturesMDName);
size_t sigIdx = 0;
while (sigIdx < entrySigs->getNumOperands()) {
MDTuple *pSig = dyn_cast<MDTuple>(entrySigs->getOperand(sigIdx++));
unsigned idx = 0;
Function *F = dyn_cast<Function>(
dyn_cast<ValueAsMetadata>(pSig->getOperand(idx++))->getValue());
// Entry must have props.
IFTBOOL(m_DxilFunctionPropsMap.count(F), DXC_E_INCORRECT_DXIL_METADATA);
DXIL::ShaderKind shaderKind = m_DxilFunctionPropsMap[F]->shaderKind;
std::unique_ptr<hlsl::DxilEntrySignature> Sig =
llvm::make_unique<hlsl::DxilEntrySignature>(shaderKind);
m_pMDHelper->LoadDxilSignatures(pSig->getOperand(idx), *Sig);
m_DxilEntrySignatureMap[F] = std::move(Sig);
}
}
}

Просмотреть файл

@ -4632,6 +4632,8 @@ void SROA_Parameter_HLSL::replaceCastArgument(Value *&NewArg, Value *OldArg,
elts[i] = Elt;
}
}
// Don't need elts anymore.
vectorEltsMap.erase(NewArg);
} else if (!NewTy->isPointerTy()) {
// Ptr param is cast to non-ptr param.
// Must be in param.
@ -4735,7 +4737,8 @@ void SROA_Parameter_HLSL::replaceCastParameter(
OldParam->replaceAllUsesWith(Vec);
}
}
// Don't need elts anymore.
vectorEltsMap.erase(NewParam);
} else if (!NewTy->isPointerTy()) {
// Ptr param is cast to non-ptr param.
// Must be in param.
@ -5977,6 +5980,8 @@ void SROA_Parameter_HLSL::createFlattenedFunctionCall(Function *F, Function *fla
for (Value *elt : elts) {
FlatParamList[++i] = elt;
}
// Don't need elts anymore.
vectorEltsMap.erase(flatArg);
}
}
}

Просмотреть файл

@ -114,6 +114,8 @@ private:
// Map to save patch constant functions
StringMap<Function *> patchConstantFunctionMap;
std::unordered_map<Function *, std::unique_ptr<DxilFunctionProps>>
patchConstantFunctionPropsMap;
bool IsPatchConstantFunction(const Function *F);
// Map to save entry functions.
@ -1172,7 +1174,7 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
if (patchConstantFunctionMap.count(funcName) == 1) {
Function *patchConstFunc = patchConstantFunctionMap[funcName];
funcProps->ShaderProps.HS.patchConstantFunc = patchConstFunc;
DXASSERT_NOMSG(m_pHLModule->HasDxilFunctionProps(patchConstFunc));
DXASSERT_NOMSG(patchConstantFunctionPropsMap.count(patchConstFunc));
// Check no inout parameter for patch constant function.
DxilFunctionAnnotation *patchConstFuncAnnotation =
m_pHLModule->GetFunctionAnnotation(patchConstFunc);
@ -1604,9 +1606,9 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
if (isHS) {
// Check
Function *patchConstFunc = funcProps->ShaderProps.HS.patchConstantFunc;
if (m_pHLModule->HasDxilFunctionProps(patchConstFunc)) {
if (patchConstantFunctionPropsMap.count(patchConstFunc)) {
DxilFunctionProps &patchProps =
m_pHLModule->GetDxilFunctionProps(patchConstFunc);
*patchConstantFunctionPropsMap[patchConstFunc];
if (patchProps.ShaderProps.HS.outputControlPoints != 0 &&
patchProps.ShaderProps.HS.outputControlPoints !=
funcProps->ShaderProps.HS.outputControlPoints) {
@ -1633,8 +1635,11 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
}
// Only add functionProps when exist.
if (profileAttributes || isPatchConstantFunction || isEntry)
if (profileAttributes || isEntry)
m_pHLModule->AddDxilFunctionProps(F, funcProps);
if (isPatchConstantFunction)
patchConstantFunctionPropsMap[F] = std::move(funcProps);
// Save F to entry map.
if (profileAttributes) {
if (entryFunctionMap.count(FD->getName())) {
@ -3818,8 +3823,8 @@ static void CloneShaderEntry(Function *ShaderF, StringRef EntryName,
DxilParameterAnnotation &cloneRetAnnot = annot->GetRetTypeAnnotation();
cloneRetAnnot = retAnnot;
// Clear semantic for cloned one.
retAnnot.SetSemanticString("");
retAnnot.SetSemanticIndexVec({});
cloneRetAnnot.SetSemanticString("");
cloneRetAnnot.SetSemanticIndexVec({});
for (unsigned i = 0; i < shaderAnnot->GetNumParameters(); i++) {
DxilParameterAnnotation &cloneParamAnnot = annot->GetParameterAnnotation(i);
DxilParameterAnnotation &paramAnnot =

Просмотреть файл

@ -0,0 +1,198 @@
// RUN: %dxc -T lib_6_1 %s | FileCheck %s
// Make sure entry function exist.
// CHECK: @cs_main()
// Make sure signatures are lowered.
// CHECK: dx.op.threadId
// CHECK: dx.op.groupId
// Make sure entry function exist.
// CHECK: @gs_main()
// Make sure signatures are lowered.
// CHECK: dx.op.loadInput
// CHECK: dx.op.storeOutput
// CHECK: dx.op.emitStream
// CHECK: dx.op.cutStream
// Make sure entry function exist.
// CHECK: @ds_main()
// Make sure signatures are lowered.
// CHECK: dx.op.loadPatchConstant
// CHECK: dx.op.domainLocation
// CHECK: dx.op.loadInput
// CHECK: dx.op.storeOutput
// Make sure patch constant function exist.
// CHECK: HSPerPatchFunc
// Make sure signatures are lowered.
// CHECK: dx.op.storePatchConstant
// Make sure entry function exist.
// CHECK: @hs_main()
// Make sure signatures are lowered.
// CHECK: dx.op.outputControlPointID
// CHECK: dx.op.loadInput
// CHECK: dx.op.storeOutput
// Make sure entry function exist.
// CHECK: @vs_main()
// Make sure signatures are lowered.
// CHECK: dx.op.loadInput
// Dot4 for clipplane
// CHECK: dx.op.dot4
// CHECK: dx.op.storeOutput
// Make sure entry function exist.
// CHECK: @ps_main()
// Make sure signatures are lowered.
// CHECK: dx.op.loadInput
// CHECK: dx.op.storeOutput
// Finish ps_main
// CHECK: ret void
// Make sure cloned function signatures are not lowered.
// CHECK-NOT: call float @dx.op.loadInput
// CHECK-NOT: call void @dx.op.storeOutput
// Make sure cloned function exist.
// CHECK: @"\01?ps_main
// Make sure function entrys exist.
// CHECK: dx.func.signatures
// Make sure cs don't have signature.
// CHECK: @cs_main, null
void StoreCSOutput(uint2 tid, uint2 gid);
[numthreads(8,8,1)]
void cs_main( uint2 tid : SV_DispatchThreadID, uint2 gid : SV_GroupID, uint2 gtid : SV_GroupThreadID, uint gidx : SV_GroupIndex )
{
StoreCSOutput(tid, gid);
}
// GS
struct GSOut {
float2 uv : TEXCOORD0;
float4 pos : SV_Position;
};
// geometry shader that outputs 3 vertices from a point
[maxvertexcount(3)]
[instance(24)]
void gs_main(InputPatch<GSOut, 2>points, inout PointStream<GSOut> stream) {
stream.Append(points[0]);
stream.RestartStrip();
}
// DS
struct PSSceneIn {
float4 pos : SV_Position;
float2 tex : TEXCOORD0;
float3 norm : NORMAL;
uint RTIndex : SV_RenderTargetArrayIndex;
};
struct HSPerVertexData {
// This is just the original vertex verbatim. In many real life cases this would be a
// control point instead
PSSceneIn v;
};
struct HSPerPatchData {
// We at least have to specify tess factors per patch
// As we're tesselating triangles, there will be 4 tess factors
// In real life case this might contain face normal, for example
float edges[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
};
// domain shader that actually outputs the triangle vertices
[domain("tri")] PSSceneIn ds_main(const float3 bary
: SV_DomainLocation,
const OutputPatch<HSPerVertexData, 3> patch,
const HSPerPatchData perPatchData) {
PSSceneIn v;
// Compute interpolated coordinates
v.pos = patch[0].v.pos * bary.x + patch[1].v.pos * bary.y + patch[2].v.pos * bary.z + perPatchData.edges[1];
v.tex = patch[0].v.tex * bary.x + patch[1].v.tex * bary.y + patch[2].v.tex * bary.z + perPatchData.edges[0];
v.norm = patch[0].v.norm * bary.x + patch[1].v.norm * bary.y + patch[2].v.norm * bary.z + perPatchData.inside;
v.RTIndex = 0;
return v;
}
// HS
HSPerPatchData HSPerPatchFunc( const InputPatch< PSSceneIn, 3 > points, OutputPatch<HSPerVertexData, 3> outp)
{
HSPerPatchData d;
d.edges[ 0 ] = 1;
d.edges[ 1 ] = 1;
d.edges[ 2 ] = 1;
d.inside = 1;
return d;
}
// hull per-control point shader
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[patchconstantfunc("HSPerPatchFunc")]
[outputcontrolpoints(3)]
HSPerVertexData hs_main( const uint id : SV_OutputControlPointID,
const InputPatch< PSSceneIn, 3 > points)
{
HSPerVertexData v;
// Just forward the vertex
v.v = points[ id ];
return v;
}
// VS
struct VS_INPUT
{
float3 vPosition : POSITION;
float3 vNormal : NORMAL;
float2 vTexcoord : TEXCOORD0;
};
struct VS_OUTPUT
{
float3 vNormal : NORMAL;
float2 vTexcoord : TEXCOORD0;
float4 vPosition : SV_POSITION;
};
cbuffer VSCB {
float4 plane;
}
[clipplanes(plane)]
VS_OUTPUT vs_main(VS_INPUT Input)
{
VS_OUTPUT Output;
Output.vPosition = float4( Input.vPosition, 1.0 );
Output.vNormal = Input.vNormal;
Output.vTexcoord = Input.vTexcoord;
return Output;
}
// PS
[earlydepthstencil]
float4 ps_main(float4 a : A) : SV_TARGET
{
return a;
}

Просмотреть файл

@ -504,6 +504,7 @@ public:
TEST_METHOD(CodeGenLibCsEntry)
TEST_METHOD(CodeGenLibCsEntry2)
TEST_METHOD(CodeGenLibCsEntry3)
TEST_METHOD(CodeGenLibEntries)
TEST_METHOD(CodeGenLibResource)
TEST_METHOD(CodeGenLibUnusedFunc)
TEST_METHOD(CodeGenLitInParen)
@ -2788,6 +2789,10 @@ TEST_F(CompilerTest, CodeGenLibCsEntry3) {
CodeGenTestCheck(L"..\\CodeGenHLSL\\lib_cs_entry3.hlsl");
}
TEST_F(CompilerTest, CodeGenLibEntries) {
CodeGenTestCheck(L"..\\CodeGenHLSL\\lib_entries.hlsl");
}
TEST_F(CompilerTest, CodeGenLibResource) {
CodeGenTestCheck(L"..\\CodeGenHLSL\\lib_resource.hlsl");
}