Check Race Condition for TGSM (#48)
This commit is contained in:
Родитель
06624f8991
Коммит
cf6f8beb4b
|
@ -2178,6 +2178,7 @@ INSTR.SAMPLERMODEFORSAMPLE sample/_l/_d/_cl_s/gather instruction requ
|
||||||
INSTR.SAMPLERMODEFORSAMPLEC sample_c_*/gather_c instructions require sampler declared in comparison mode
|
INSTR.SAMPLERMODEFORSAMPLEC sample_c_*/gather_c instructions require sampler declared in comparison mode
|
||||||
INSTR.STRUCTBITCAST Bitcast on struct types is not allowed
|
INSTR.STRUCTBITCAST Bitcast on struct types is not allowed
|
||||||
INSTR.TEXTUREOFFSET offset texture instructions must take offset which can resolve to integer literal in the range -8 to 7
|
INSTR.TEXTUREOFFSET offset texture instructions must take offset which can resolve to integer literal in the range -8 to 7
|
||||||
|
INSTR.TGSMRACECOND Race condition writing to shared memory detected, consider making this write conditional
|
||||||
INSTR.UNDEFRESULTFORGETDIMENSION GetDimensions used undef dimension %0 on %1
|
INSTR.UNDEFRESULTFORGETDIMENSION GetDimensions used undef dimension %0 on %1
|
||||||
INSTR.WRITEMASKFORTYPEDUAVSTORE store on typed uav must write to all four components of the UAV
|
INSTR.WRITEMASKFORTYPEDUAVSTORE store on typed uav must write to all four components of the UAV
|
||||||
INSTR.WRITEMASKMATCHVALUEFORUAVSTORE uav store write mask must match store value mask, write mask is %0 and store value mask is %1
|
INSTR.WRITEMASKMATCHVALUEFORUAVSTORE uav store write mask must match store value mask, write mask is %0 and store value mask is %1
|
||||||
|
|
|
@ -96,6 +96,7 @@ enum class ValidationRule : unsigned {
|
||||||
InstrSamplerModeForSample, // sample/_l/_d/_cl_s/gather instruction requires sampler declared in default mode
|
InstrSamplerModeForSample, // sample/_l/_d/_cl_s/gather instruction requires sampler declared in default mode
|
||||||
InstrSamplerModeForSampleC, // sample_c_*/gather_c instructions require sampler declared in comparison mode
|
InstrSamplerModeForSampleC, // sample_c_*/gather_c instructions require sampler declared in comparison mode
|
||||||
InstrStructBitCast, // Bitcast on struct types is not allowed
|
InstrStructBitCast, // Bitcast on struct types is not allowed
|
||||||
|
InstrTGSMRaceCond, // Race condition writing to shared memory detected, consider making this write conditional
|
||||||
InstrTextureOffset, // offset texture instructions must take offset which can resolve to integer literal in the range -8 to 7
|
InstrTextureOffset, // offset texture instructions must take offset which can resolve to integer literal in the range -8 to 7
|
||||||
InstrUndefResultForGetDimension, // GetDimensions used undef dimension %0 on %1
|
InstrUndefResultForGetDimension, // GetDimensions used undef dimension %0 on %1
|
||||||
InstrWriteMaskForTypedUAVStore, // store on typed uav must write to all four components of the UAV
|
InstrWriteMaskForTypedUAVStore, // store on typed uav must write to all four components of the UAV
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "llvm/IR/LLVMContext.h"
|
#include "llvm/IR/LLVMContext.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
#include "llvm/IR/Type.h"
|
#include "llvm/IR/Type.h"
|
||||||
|
#include "llvm/IR/Operator.h"
|
||||||
#include "llvm/IR/Instructions.h"
|
#include "llvm/IR/Instructions.h"
|
||||||
#include "llvm/IR/InstIterator.h"
|
#include "llvm/IR/InstIterator.h"
|
||||||
#include "llvm/IR/Constants.h"
|
#include "llvm/IR/Constants.h"
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include "llvm/Analysis/LoopInfo.h"
|
#include "llvm/Analysis/LoopInfo.h"
|
||||||
#include "llvm/IR/Dominators.h"
|
#include "llvm/IR/Dominators.h"
|
||||||
|
#include "llvm/Analysis/PostDominators.h"
|
||||||
#include "dxc/HLSL/DxilSpanAllocator.h"
|
#include "dxc/HLSL/DxilSpanAllocator.h"
|
||||||
#include "dxc/HLSL/DxilSignatureAllocator.h"
|
#include "dxc/HLSL/DxilSignatureAllocator.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -158,6 +160,7 @@ const char *hlsl::GetValidationRuleText(ValidationRule value) {
|
||||||
case hlsl::ValidationRule::InstrCBufferClassForCBufferHandle: return "Expect Cbuffer for CBufferLoad handle";
|
case hlsl::ValidationRule::InstrCBufferClassForCBufferHandle: return "Expect Cbuffer for CBufferLoad handle";
|
||||||
case hlsl::ValidationRule::InstrFailToResloveTGSMPointer: return "TGSM pointers must originate from an unambiguous TGSM global variable.";
|
case hlsl::ValidationRule::InstrFailToResloveTGSMPointer: return "TGSM pointers must originate from an unambiguous TGSM global variable.";
|
||||||
case hlsl::ValidationRule::InstrExtractValue: return "ExtractValue should only be used on dxil struct types and cmpxchg";
|
case hlsl::ValidationRule::InstrExtractValue: return "ExtractValue should only be used on dxil struct types and cmpxchg";
|
||||||
|
case hlsl::ValidationRule::InstrTGSMRaceCond: return "Race condition writing to shared memory detected, consider making this write conditional";
|
||||||
case hlsl::ValidationRule::TypesNoVector: return "Vector type '%0' is not allowed";
|
case hlsl::ValidationRule::TypesNoVector: return "Vector type '%0' is not allowed";
|
||||||
case hlsl::ValidationRule::TypesDefined: return "Type '%0' is not defined on DXIL primitives";
|
case hlsl::ValidationRule::TypesDefined: return "Type '%0' is not defined on DXIL primitives";
|
||||||
case hlsl::ValidationRule::TypesIntWidth: return "Int type '%0' has an invalid width";
|
case hlsl::ValidationRule::TypesIntWidth: return "Int type '%0' has an invalid width";
|
||||||
|
@ -2551,15 +2554,57 @@ static void ValidateGlobalVariable(GlobalVariable &GV,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
CollectFixAddressAccess(Value *V, std::vector<Instruction *> &fixAddrTGSMList) {
|
||||||
|
for (User *U : V->users()) {
|
||||||
|
if (GEPOperator *GEP = dyn_cast<GEPOperator>(U)) {
|
||||||
|
if (isa<ConstantExpr>(GEP) || GEP->hasAllConstantIndices()) {
|
||||||
|
CollectFixAddressAccess(GEP, fixAddrTGSMList);
|
||||||
|
}
|
||||||
|
} else if (isa<StoreInst>(U)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ValidateTGSMRaceCondition(std::vector<Instruction *> &fixAddrTGSMList,
|
||||||
|
ValidationContext &ValCtx) {
|
||||||
|
std::unordered_set<Function *> fixAddrTGSMFuncSet;
|
||||||
|
for (Instruction *I : fixAddrTGSMList) {
|
||||||
|
BasicBlock *BB = I->getParent();
|
||||||
|
fixAddrTGSMFuncSet.insert(BB->getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &F : ValCtx.DxilMod.GetModule()->functions()) {
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PostDominatorTree PDT;
|
||||||
|
PDT.runOnFunction(F);
|
||||||
|
|
||||||
|
BasicBlock *Entry = &F.getEntryBlock();
|
||||||
|
|
||||||
|
for (Instruction *I : fixAddrTGSMList) {
|
||||||
|
BasicBlock *BB = I->getParent();
|
||||||
|
if (BB->getParent() == &F) {
|
||||||
|
if (PDT.dominates(BB, Entry)) {
|
||||||
|
ValCtx.EmitInstrError(I, ValidationRule::InstrTGSMRaceCond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ValidateGlobalVariables(ValidationContext &ValCtx) {
|
static void ValidateGlobalVariables(ValidationContext &ValCtx) {
|
||||||
DxilModule &M = ValCtx.DxilMod;
|
DxilModule &M = ValCtx.DxilMod;
|
||||||
|
|
||||||
unsigned TGSMSize = 0;
|
unsigned TGSMSize = 0;
|
||||||
|
std::vector<Instruction*> fixAddrTGSMList;
|
||||||
const DataLayout &DL = M.GetModule()->getDataLayout();
|
const DataLayout &DL = M.GetModule()->getDataLayout();
|
||||||
for (GlobalVariable &GV : M.GetModule()->globals()) {
|
for (GlobalVariable &GV : M.GetModule()->globals()) {
|
||||||
ValidateGlobalVariable(GV, ValCtx);
|
ValidateGlobalVariable(GV, ValCtx);
|
||||||
if (GV.getType()->getAddressSpace() == DXIL::kTGSMAddrSpace) {
|
if (GV.getType()->getAddressSpace() == DXIL::kTGSMAddrSpace) {
|
||||||
TGSMSize += DL.getTypeAllocSize(GV.getType()->getElementType());
|
TGSMSize += DL.getTypeAllocSize(GV.getType()->getElementType());
|
||||||
|
CollectFixAddressAccess(&GV, fixAddrTGSMList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2568,6 +2613,9 @@ static void ValidateGlobalVariables(ValidationContext &ValCtx) {
|
||||||
{std::to_string(TGSMSize).c_str(),
|
{std::to_string(TGSMSize).c_str(),
|
||||||
std::to_string(DXIL::kMaxTGSMSize).c_str()});
|
std::to_string(DXIL::kMaxTGSMSize).c_str()});
|
||||||
}
|
}
|
||||||
|
if (!fixAddrTGSMList.empty()) {
|
||||||
|
ValidateTGSMRaceCondition(fixAddrTGSMList, ValCtx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ValidateValidatorVersion(ValidationContext &ValCtx) {
|
static void ValidateValidatorVersion(ValidationContext &ValCtx) {
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// RUN: %dxc -E main -T cs_6_0 %s | FileCheck %s
|
||||||
|
|
||||||
|
// CHECK: Race condition writing to shared memory detected, consider making this write conditional
|
||||||
|
|
||||||
|
RWBuffer< int > g_Intensities : register(u1);
|
||||||
|
|
||||||
|
groupshared int sharedData;
|
||||||
|
|
||||||
|
[ numthreads( 64, 2, 2 ) ]
|
||||||
|
void main( uint GI : SV_GroupIndex)
|
||||||
|
{
|
||||||
|
sharedData = 0;
|
||||||
|
InterlockedAdd(sharedData, g_Intensities[GI]);
|
||||||
|
g_Intensities[GI] = sharedData;
|
||||||
|
}
|
|
@ -15,7 +15,9 @@ groupshared Foo sharedData;
|
||||||
[ numthreads( 64, 2, 2 ) ]
|
[ numthreads( 64, 2, 2 ) ]
|
||||||
void main( uint GI : SV_GroupIndex)
|
void main( uint GI : SV_GroupIndex)
|
||||||
{
|
{
|
||||||
|
if (GI==0)
|
||||||
sharedData = inputs[GI];
|
sharedData = inputs[GI];
|
||||||
|
|
||||||
int rtn;
|
int rtn;
|
||||||
InterlockedAdd(sharedData.d, g_Intensities[GI], rtn);
|
InterlockedAdd(sharedData.d, g_Intensities[GI], rtn);
|
||||||
g_Intensities[GI] = rtn + sharedData.d;
|
g_Intensities[GI] = rtn + sharedData.d;
|
||||||
|
|
|
@ -13,6 +13,7 @@ groupshared Foo sharedData;
|
||||||
[ numthreads( 64, 2, 2 ) ]
|
[ numthreads( 64, 2, 2 ) ]
|
||||||
void main( uint GI : SV_GroupIndex)
|
void main( uint GI : SV_GroupIndex)
|
||||||
{
|
{
|
||||||
|
if (GI==0)
|
||||||
sharedData = inputs[GI];
|
sharedData = inputs[GI];
|
||||||
int rtn;
|
int rtn;
|
||||||
InterlockedAdd(sharedData.d[0], g_Intensities[GI], rtn);
|
InterlockedAdd(sharedData.d[0], g_Intensities[GI], rtn);
|
||||||
|
|
|
@ -14,12 +14,10 @@ RWBuffer< int > g_Intensities : register(u1);
|
||||||
|
|
||||||
groupshared Foo sharedData;
|
groupshared Foo sharedData;
|
||||||
|
|
||||||
#ifdef DX12
|
|
||||||
[RootSignature("DescriptorTable(UAV(u1, numDescriptors=1), SRV(t1), visibility=SHADER_VISIBILITY_ALL)")]
|
|
||||||
#endif
|
|
||||||
[ numthreads( 64, 2, 2 ) ]
|
[ numthreads( 64, 2, 2 ) ]
|
||||||
void main( uint GI : SV_GroupIndex)
|
void main( uint GI : SV_GroupIndex)
|
||||||
{
|
{
|
||||||
|
if(GI==0)
|
||||||
sharedData = inputs[GI];
|
sharedData = inputs[GI];
|
||||||
int rtn;
|
int rtn;
|
||||||
InterlockedAdd(sharedData.d, g_Intensities[GI], rtn);
|
InterlockedAdd(sharedData.d, g_Intensities[GI], rtn);
|
||||||
|
|
|
@ -125,6 +125,8 @@ public:
|
||||||
TEST_METHOD(I8Type)
|
TEST_METHOD(I8Type)
|
||||||
TEST_METHOD(EmptyStructInBuffer)
|
TEST_METHOD(EmptyStructInBuffer)
|
||||||
TEST_METHOD(BigStructInBuffer)
|
TEST_METHOD(BigStructInBuffer)
|
||||||
|
TEST_METHOD(TGSMRaceCond)
|
||||||
|
TEST_METHOD(TGSMRaceCond2)
|
||||||
TEST_METHOD(AddUint64Odd)
|
TEST_METHOD(AddUint64Odd)
|
||||||
|
|
||||||
TEST_METHOD(ClipCullMaxComponents)
|
TEST_METHOD(ClipCullMaxComponents)
|
||||||
|
@ -1309,6 +1311,18 @@ TEST_F(ValidationTest, BigStructInBuffer) {
|
||||||
TestCheck(L"..\\CodeGenHLSL\\BigStructInBuffer.hlsl");
|
TestCheck(L"..\\CodeGenHLSL\\BigStructInBuffer.hlsl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidationTest, TGSMRaceCond) {
|
||||||
|
TestCheck(L"..\\CodeGenHLSL\\RaceCond.hlsl");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidationTest, TGSMRaceCond2) {
|
||||||
|
RewriteAssemblyCheckMsg(L"..\\CodeGenHLSL\\structInBuffer.hlsl", "cs_6_0",
|
||||||
|
"ret void",
|
||||||
|
"store i32 0, i32 addrspace(3)* @\"\\01?sharedData@@3UFoo@@A.3\", align 4\n"
|
||||||
|
"ret void",
|
||||||
|
"Race condition writing to shared memory detected, consider making this write conditional");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ValidationTest, AddUint64Odd) {
|
TEST_F(ValidationTest, AddUint64Odd) {
|
||||||
TestCheck(L"..\\CodeGenHLSL\\AddUint64Odd.hlsl");
|
TestCheck(L"..\\CodeGenHLSL\\AddUint64Odd.hlsl");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1562,6 +1562,7 @@ class db_dxil(object):
|
||||||
self.add_valrule("Instr.CBufferClassForCBufferHandle", "Expect Cbuffer for CBufferLoad handle")
|
self.add_valrule("Instr.CBufferClassForCBufferHandle", "Expect Cbuffer for CBufferLoad handle")
|
||||||
self.add_valrule("Instr.FailToResloveTGSMPointer", "TGSM pointers must originate from an unambiguous TGSM global variable.")
|
self.add_valrule("Instr.FailToResloveTGSMPointer", "TGSM pointers must originate from an unambiguous TGSM global variable.")
|
||||||
self.add_valrule("Instr.ExtractValue", "ExtractValue should only be used on dxil struct types and cmpxchg")
|
self.add_valrule("Instr.ExtractValue", "ExtractValue should only be used on dxil struct types and cmpxchg")
|
||||||
|
self.add_valrule("Instr.TGSMRaceCond", "Race condition writing to shared memory detected, consider making this write conditional")
|
||||||
|
|
||||||
# Some legacy rules:
|
# Some legacy rules:
|
||||||
# - space is only supported for shader targets 5.1 and higher
|
# - space is only supported for shader targets 5.1 and higher
|
||||||
|
|
Загрузка…
Ссылка в новой задаче