Skip to content

Commit 8393610

Browse files
author
Greg Roth
authored
Disallow structs, arrays, and overwide vectors from typed buffers (microsoft#7130)
These never reliably generated the right results, so they are being disallowed. The check takes place in SemaHLSL and replaces a few places that checked for such things in special cases. To facilitate the check, the definition of the texture bit was expanded to include RW textures. This is consistent with the description and since the bit was never actually used before, has no other consequences. Some code that executed after Sema that either produced errors or processed resources with these now forbidden types was removed. Most of this is from CodeGen and HLOperationLower where code was generated or expanded for load/store operations on such resources. Additionally moved the check for overlarge elements in typed resources to enable keeping some tests around that tested these side by side as well as adding a new one. I changed the nature of the check a bit to limit the types to four elements and also that they be no larger than 4 32-bit elements. This means that 64-bit elements are limited to two. While there wasn't an error that caught this before, there was an assert that fired. Incidentally fixes an issue with error reporting for modified types after the first mismatch of that intrinsic is encountered. Because the insertion code changed them in the temp array and the stored function type, but printed from the temp array and the later encounters didn't add the modifiers to the temp array, later errors would incorrectly leave off the reference. By retrieving the type from the function type, the reference is always preserved. This required changing a fair number of tests that relied on this behavior including removing test cases and entire test files that existed solely for such tests. Fixes microsoft#7080
1 parent b317f1b commit 8393610

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+362
-806
lines changed

docs/ReleaseNotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ The included licenses apply to the following files:
2121

2222
Place release notes for the upcoming release below this line and remove this line upon naming this release.
2323

24+
- Typed buffers (including ROV buffers) no longer accept types other than vectors and scalars. Any other types will produce descriptive errors. This removes support for appropriately sized matrices and structs. Though it worked in some contexts, code generated from such types was unreliable.
25+
2426
### Version 1.8.2502
2527

2628
This cumulative release contains numerous bug fixes and stability improvements.

lib/HLSL/HLOperationLower.cpp

Lines changed: 9 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -7829,84 +7829,6 @@ void TranslateCBOperationsLegacy(Value *handle, Value *ptr, OP *hlslOP,
78297829

78307830
// Structured buffer.
78317831
namespace {
7832-
// Load a value from a typedef buffer with an offset.
7833-
// Typed buffer do not directly support reading at offsets
7834-
// because the whole value (e.g. float4) must be read at once.
7835-
// If we are provided a non-zero offset, we need to simulate it
7836-
// by returning the correct elements.
7837-
using ResRetValueArray = std::array<Value *, 4>;
7838-
static ResRetValueArray
7839-
GenerateTypedBufferLoad(Value *Handle, Type *BufferElemTy, Value *ElemIdx,
7840-
Value *StatusPtr, OP *HlslOP, IRBuilder<> &Builder) {
7841-
7842-
OP::OpCode OpCode = OP::OpCode::BufferLoad;
7843-
Value *LoadArgs[] = {HlslOP->GetU32Const((unsigned)OpCode), Handle, ElemIdx,
7844-
UndefValue::get(Builder.getInt32Ty())};
7845-
Function *LoadFunc = HlslOP->GetOpFunc(OpCode, BufferElemTy);
7846-
Value *Load =
7847-
Builder.CreateCall(LoadFunc, LoadArgs, OP::GetOpCodeName(OpCode));
7848-
7849-
ResRetValueArray ResultValues;
7850-
for (unsigned i = 0; i < ResultValues.size(); ++i) {
7851-
ResultValues[i] =
7852-
cast<ExtractValueInst>(Builder.CreateExtractValue(Load, {i}));
7853-
}
7854-
7855-
UpdateStatus(Load, StatusPtr, Builder, HlslOP);
7856-
return ResultValues;
7857-
}
7858-
7859-
static AllocaInst *SpillValuesToArrayAlloca(ArrayRef<Value *> Values,
7860-
IRBuilder<> &Builder) {
7861-
DXASSERT_NOMSG(!Values.empty());
7862-
IRBuilder<> AllocaBuilder(
7863-
dxilutil::FindAllocaInsertionPt(Builder.GetInsertPoint()));
7864-
AllocaInst *ArrayAlloca = AllocaBuilder.CreateAlloca(
7865-
ArrayType::get(Values[0]->getType(), Values.size()));
7866-
for (unsigned i = 0; i < Values.size(); ++i) {
7867-
Value *ArrayElemPtr = Builder.CreateGEP(
7868-
ArrayAlloca, {Builder.getInt32(0), Builder.getInt32(i)});
7869-
Builder.CreateStore(Values[i], ArrayElemPtr);
7870-
}
7871-
return ArrayAlloca;
7872-
}
7873-
7874-
static Value *ExtractFromTypedBufferLoad(const ResRetValueArray &ResRet,
7875-
Type *ResultTy, Value *Offset,
7876-
IRBuilder<> &Builder) {
7877-
unsigned ElemCount =
7878-
ResultTy->isVectorTy() ? ResultTy->getVectorNumElements() : 1;
7879-
DXASSERT_NOMSG(ElemCount < ResRet.size());
7880-
unsigned ElemSizeInBytes = ResRet[0]->getType()->getScalarSizeInBits() / 8;
7881-
7882-
SmallVector<Value *, 4> Elems;
7883-
if (ConstantInt *OffsetAsConstantInt = dyn_cast<ConstantInt>(Offset)) {
7884-
// Get all elements to be returned
7885-
uint64_t FirstElemOffset = OffsetAsConstantInt->getLimitedValue();
7886-
DXASSERT_NOMSG(FirstElemOffset % ElemSizeInBytes == 0);
7887-
uint64_t FirstElemIdx = FirstElemOffset / ElemSizeInBytes;
7888-
DXASSERT_NOMSG(FirstElemIdx <= ResRet.size() - ElemCount);
7889-
for (unsigned ElemIdx = 0; ElemIdx < ElemCount; ++ElemIdx) {
7890-
Elems.emplace_back(
7891-
ResRet[std::min<size_t>(FirstElemIdx + ElemIdx, ResRet.size() - 1)]);
7892-
}
7893-
} else {
7894-
Value *ArrayAlloca = SpillValuesToArrayAlloca(
7895-
ArrayRef<Value *>(ResRet.data(), ResRet.size()), Builder);
7896-
7897-
// Get all elements to be returned through dynamic indices
7898-
Value *FirstElemIdx =
7899-
Builder.CreateUDiv(Offset, Builder.getInt32(ElemSizeInBytes));
7900-
for (unsigned i = 0; i < ElemCount; ++i) {
7901-
Value *ElemIdx = Builder.CreateAdd(FirstElemIdx, Builder.getInt32(i));
7902-
Value *ElemPtr =
7903-
Builder.CreateGEP(ArrayAlloca, {Builder.getInt32(0), ElemIdx});
7904-
Elems.emplace_back(Builder.CreateLoad(ElemPtr));
7905-
}
7906-
}
7907-
7908-
return ScalarizeElements(ResultTy, Elems, Builder);
7909-
}
79107832

79117833
Value *GenerateRawBufLd(Value *handle, Value *bufIdx, Value *offset,
79127834
Value *status, Type *EltTy,
@@ -8378,27 +8300,16 @@ void TranslateStructBufSubscriptUser(Instruction *user, Value *handle,
83788300

83798301
if (LdInst) {
83808302
unsigned NumComponents = 0;
8381-
Value *NewLd = nullptr;
83828303
if (VectorType *VTy = dyn_cast<VectorType>(Ty))
83838304
NumComponents = VTy->getNumElements();
83848305
else
83858306
NumComponents = 1;
8386-
8387-
if (ResKind == HLResource::Kind::TypedBuffer) {
8388-
// Typed buffer cannot have offsets, they must be loaded all at once
8389-
ResRetValueArray ResRet = GenerateTypedBufferLoad(
8390-
handle, pOverloadTy, bufIdx, status, OP, Builder);
8391-
8392-
NewLd = ExtractFromTypedBufferLoad(ResRet, Ty, Offset, Builder);
8393-
} else {
8394-
Value *ResultElts[4];
8395-
Constant *Alignment =
8396-
OP->GetI32Const(DL.getTypeAllocSize(Ty->getScalarType()));
8397-
GenerateRawBufLd(handle, bufIdx, Offset, status, pOverloadTy,
8398-
ResultElts, OP, Builder, NumComponents, Alignment);
8399-
NewLd = ScalarizeElements(Ty, ResultElts, Builder);
8400-
}
8401-
8307+
Value *ResultElts[4];
8308+
Constant *Alignment =
8309+
OP->GetI32Const(DL.getTypeAllocSize(Ty->getScalarType()));
8310+
GenerateRawBufLd(handle, bufIdx, Offset, status, pOverloadTy, ResultElts,
8311+
OP, Builder, NumComponents, Alignment);
8312+
Value *NewLd = ScalarizeElements(Ty, ResultElts, Builder);
84028313
LdInst->replaceAllUsesWith(NewLd);
84038314
} else {
84048315
Value *val = StInst->getValueOperand();
@@ -8821,66 +8732,12 @@ void TranslateHLSubscript(CallInst *CI, HLSubscriptOpcode opcode,
88218732
RetTy = ObjTy->getStructElementType(0);
88228733
Translated = true;
88238734

8824-
if (DXIL::IsStructuredBuffer(RK)) {
8825-
TranslateStructBufSubscript(CI, handle, /*status*/ nullptr, hlslOP, RK,
8826-
helper.dataLayout);
8827-
} else if (RetTy->isAggregateType() &&
8828-
RK == DxilResource::Kind::TypedBuffer) {
8829-
8735+
if (DXIL::IsStructuredBuffer(RK))
88308736
TranslateStructBufSubscript(CI, handle, /*status*/ nullptr, hlslOP, RK,
88318737
helper.dataLayout);
8832-
// Clear offset for typed buf.
8833-
for (auto User = handle->user_begin(); User != handle->user_end();) {
8834-
CallInst *CI = cast<CallInst>(*(User++));
8835-
// Skip not lowered HL functions.
8836-
if (hlsl::GetHLOpcodeGroupByName(CI->getCalledFunction()) !=
8837-
HLOpcodeGroup::NotHL)
8838-
continue;
8839-
switch (hlslOP->GetDxilOpFuncCallInst(CI)) {
8840-
case DXIL::OpCode::BufferLoad: {
8841-
CI->setArgOperand(DXIL::OperandIndex::kBufferLoadCoord1OpIdx,
8842-
UndefValue::get(helper.i32Ty));
8843-
} break;
8844-
case DXIL::OpCode::BufferStore: {
8845-
CI->setArgOperand(DXIL::OperandIndex::kBufferStoreCoord1OpIdx,
8846-
UndefValue::get(helper.i32Ty));
8847-
} break;
8848-
case DXIL::OpCode::AtomicBinOp: {
8849-
CI->setArgOperand(DXIL::OperandIndex::kAtomicBinOpCoord1OpIdx,
8850-
UndefValue::get(helper.i32Ty));
8851-
} break;
8852-
case DXIL::OpCode::AtomicCompareExchange: {
8853-
CI->setArgOperand(DXIL::OperandIndex::kAtomicCmpExchangeCoord1OpIdx,
8854-
UndefValue::get(helper.i32Ty));
8855-
} break;
8856-
case DXIL::OpCode::RawBufferLoad: {
8857-
// Structured buffer inside a typed buffer must be converted to
8858-
// typed buffer load. Typed buffer load is equivalent to raw buffer
8859-
// load, except there is no mask.
8860-
StructType *STy =
8861-
cast<StructType>(CI->getFunctionType()->getReturnType());
8862-
Type *ETy = STy->getElementType(0);
8863-
SmallVector<Value *, 4> Args;
8864-
Args.emplace_back(
8865-
hlslOP->GetI32Const((unsigned)DXIL::OpCode::BufferLoad));
8866-
Args.emplace_back(CI->getArgOperand(1)); // handle
8867-
Args.emplace_back(CI->getArgOperand(2)); // index
8868-
Args.emplace_back(UndefValue::get(helper.i32Ty)); // offset
8869-
IRBuilder<> builder(CI);
8870-
Function *newFunction =
8871-
hlslOP->GetOpFunc(DXIL::OpCode::BufferLoad, ETy);
8872-
CallInst *newCall = builder.CreateCall(newFunction, Args);
8873-
CI->replaceAllUsesWith(newCall);
8874-
CI->eraseFromParent();
8875-
} break;
8876-
default:
8877-
DXASSERT(0, "Invalid operation on resource handle");
8878-
break;
8879-
}
8880-
}
8881-
} else {
8738+
else
88828739
TranslateDefaultSubscript(CI, helper, pObjHelper, Translated);
8883-
}
8740+
88848741
return;
88858742
}
88868743
}

tools/clang/include/clang/AST/HlslTypes.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,12 +370,14 @@ void AddStdIsEqualImplementation(clang::ASTContext &context, clang::Sema &sema);
370370
clang::CXXRecordDecl *DeclareTemplateTypeWithHandle(
371371
clang::ASTContext &context, llvm::StringRef name,
372372
uint8_t templateArgCount = 1,
373-
clang::TypeSourceInfo *defaultTypeArgValue = nullptr);
373+
clang::TypeSourceInfo *defaultTypeArgValue = nullptr,
374+
clang::InheritableAttr *Attr = nullptr);
374375

375376
clang::CXXRecordDecl *DeclareTemplateTypeWithHandleInDeclContext(
376377
clang::ASTContext &context, clang::DeclContext *declContext,
377378
llvm::StringRef name, uint8_t templateArgCount,
378-
clang::TypeSourceInfo *defaultTypeArgValue);
379+
clang::TypeSourceInfo *defaultTypeArgValue,
380+
clang::InheritableAttr *Attr = nullptr);
379381

380382
clang::CXXRecordDecl *DeclareUIntTemplatedTypeWithHandle(
381383
clang::ASTContext &context, llvm::StringRef typeName,

tools/clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,13 @@ def HLSLNodeTrackRWInputSharing : InheritableAttr {
992992
let Documentation = [Undocumented];
993993
}
994994

995+
def HLSLResource : InheritableAttr {
996+
let Spellings = []; // No spellings!
997+
let Args = [UnsignedArgument<"ResKind">, UnsignedArgument<"ResClass">];
998+
let Subjects = SubjectList<[CXXRecord]>;
999+
let Documentation = [Undocumented];
1000+
}
1001+
9951002
def HLSLNodeObject : InheritableAttr {
9961003
let Spellings = []; // No spellings!
9971004
let Subjects = SubjectList<[CXXRecord]>;

tools/clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7596,6 +7596,10 @@ def err_hlsl_unsupported_buffer_packoffset : Error<
75967596
"packoffset is only allowed within a constant buffer, not on the constant buffer declaration">;
75977597
def err_hlsl_unsupported_buffer_slot_target_specific : Error<
75987598
"user defined constant buffer slots cannot be target specific">;
7599+
def err_hlsl_unsupported_typedbuffer_template_parameter : Error<
7600+
"elements of typed buffers and textures must be scalars or vectors">;
7601+
def err_hlsl_unsupported_typedbuffer_template_parameter_size : Error<
7602+
"elements of typed buffers and textures must fit in four 32-bit quantities">;
75997603
def err_hlsl_unsupported_payload_access_qualifier : Error<
76007604
"payload access qualifiers are only allowed for member variables of a payload structure">;
76017605
def err_hlsl_unsupported_payload_access_qualifier_struct : Error<

tools/clang/lib/AST/ASTContextHLSL.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -903,18 +903,18 @@ void hlsl::AddStdIsEqualImplementation(clang::ASTContext &context,
903903
/// <parm name="templateArgCount">Number of template arguments (one or
904904
/// two).</param> <parm name="defaultTypeArgValue">If assigned, the default
905905
/// argument for the element template.</param>
906-
CXXRecordDecl *
907-
hlsl::DeclareTemplateTypeWithHandle(ASTContext &context, StringRef name,
908-
uint8_t templateArgCount,
909-
TypeSourceInfo *defaultTypeArgValue) {
906+
CXXRecordDecl *hlsl::DeclareTemplateTypeWithHandle(
907+
ASTContext &context, StringRef name, uint8_t templateArgCount,
908+
TypeSourceInfo *defaultTypeArgValue, InheritableAttr *Attr) {
910909
return DeclareTemplateTypeWithHandleInDeclContext(
911910
context, context.getTranslationUnitDecl(), name, templateArgCount,
912-
defaultTypeArgValue);
911+
defaultTypeArgValue, Attr);
913912
}
914913

915914
CXXRecordDecl *hlsl::DeclareTemplateTypeWithHandleInDeclContext(
916915
ASTContext &context, DeclContext *declContext, StringRef name,
917-
uint8_t templateArgCount, TypeSourceInfo *defaultTypeArgValue) {
916+
uint8_t templateArgCount, TypeSourceInfo *defaultTypeArgValue,
917+
InheritableAttr *Attr) {
918918
DXASSERT(templateArgCount != 0,
919919
"otherwise caller should be creating a class or struct");
920920
DXASSERT(templateArgCount <= 2, "otherwise the function needs to be updated "
@@ -968,6 +968,9 @@ CXXRecordDecl *hlsl::DeclareTemplateTypeWithHandleInDeclContext(
968968

969969
typeDeclBuilder.addField("h", elementType);
970970

971+
if (Attr)
972+
typeDeclBuilder.getRecordDecl()->addAttr(Attr);
973+
971974
return typeDeclBuilder.getRecordDecl();
972975
}
973976

tools/clang/lib/CodeGen/CGHLSLMS.cpp

Lines changed: 1 addition & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -3375,48 +3375,6 @@ void CGMSHLSLRuntime::CreateSubobject(
33753375
}
33763376
}
33773377

3378-
static void CollectScalarTypes(std::vector<QualType> &ScalarTys, QualType Ty) {
3379-
if (Ty->isRecordType()) {
3380-
if (hlsl::IsHLSLMatType(Ty)) {
3381-
QualType EltTy = hlsl::GetHLSLMatElementType(Ty);
3382-
unsigned row = 0;
3383-
unsigned col = 0;
3384-
hlsl::GetRowsAndCols(Ty, row, col);
3385-
unsigned size = col * row;
3386-
for (unsigned i = 0; i < size; i++) {
3387-
CollectScalarTypes(ScalarTys, EltTy);
3388-
}
3389-
} else if (hlsl::IsHLSLVecType(Ty)) {
3390-
QualType EltTy = hlsl::GetHLSLVecElementType(Ty);
3391-
unsigned row = 0;
3392-
unsigned col = 0;
3393-
hlsl::GetRowsAndColsForAny(Ty, row, col);
3394-
unsigned size = col;
3395-
for (unsigned i = 0; i < size; i++) {
3396-
CollectScalarTypes(ScalarTys, EltTy);
3397-
}
3398-
} else {
3399-
const RecordType *RT = Ty->getAs<RecordType>();
3400-
RecordDecl *RD = RT->getDecl();
3401-
for (FieldDecl *field : RD->fields())
3402-
CollectScalarTypes(ScalarTys, field->getType());
3403-
}
3404-
} else if (Ty->isArrayType()) {
3405-
const clang::ArrayType *AT = Ty->getAsArrayTypeUnsafe();
3406-
QualType EltTy = AT->getElementType();
3407-
// Set it to 5 for unsized array.
3408-
unsigned size = 5;
3409-
if (AT->isConstantArrayType()) {
3410-
size = cast<ConstantArrayType>(AT)->getSize().getLimitedValue();
3411-
}
3412-
for (unsigned i = 0; i < size; i++) {
3413-
CollectScalarTypes(ScalarTys, EltTy);
3414-
}
3415-
} else {
3416-
ScalarTys.emplace_back(Ty);
3417-
}
3418-
}
3419-
34203378
bool CGMSHLSLRuntime::SetUAVSRV(SourceLocation loc,
34213379
hlsl::DxilResourceBase::Class resClass,
34223380
DxilResource *hlslRes, QualType QualTy) {
@@ -3443,66 +3401,13 @@ bool CGMSHLSLRuntime::SetUAVSRV(SourceLocation loc,
34433401
hlslRes->SetSampleCount(sampleCount);
34443402
}
34453403

3446-
if (hlsl::DxilResource::IsAnyTexture(kind)) {
3447-
const ClassTemplateSpecializationDecl *templateDecl =
3448-
cast<ClassTemplateSpecializationDecl>(RD);
3449-
const clang::TemplateArgument &texelTyArg =
3450-
templateDecl->getTemplateArgs()[0];
3451-
llvm::Type *texelTy = CGM.getTypes().ConvertType(texelTyArg.getAsType());
3452-
if (!texelTy->isFloatingPointTy() && !texelTy->isIntegerTy() &&
3453-
!hlsl::IsHLSLVecType(texelTyArg.getAsType())) {
3454-
DiagnosticsEngine &Diags = CGM.getDiags();
3455-
unsigned DiagID = Diags.getCustomDiagID(
3456-
DiagnosticsEngine::Error,
3457-
"texture resource texel type must be scalar or vector");
3458-
Diags.Report(loc, DiagID);
3459-
return false;
3460-
}
3461-
}
3462-
34633404
QualType resultTy = hlsl::GetHLSLResourceResultType(QualTy);
34643405
if (kind != hlsl::DxilResource::Kind::StructuredBuffer &&
34653406
!resultTy.isNull()) {
34663407
QualType Ty = resultTy;
34673408
QualType EltTy = Ty;
3468-
if (hlsl::IsHLSLVecType(Ty)) {
3409+
if (hlsl::IsHLSLVecType(Ty))
34693410
EltTy = hlsl::GetHLSLVecElementType(Ty);
3470-
} else if (hlsl::IsHLSLMatType(Ty)) {
3471-
EltTy = hlsl::GetHLSLMatElementType(Ty);
3472-
} else if (hlsl::IsHLSLAggregateType(resultTy)) {
3473-
// Struct or array in a none-struct resource.
3474-
std::vector<QualType> ScalarTys;
3475-
CollectScalarTypes(ScalarTys, resultTy);
3476-
unsigned size = ScalarTys.size();
3477-
if (size == 0) {
3478-
DiagnosticsEngine &Diags = CGM.getDiags();
3479-
unsigned DiagID = Diags.getCustomDiagID(
3480-
DiagnosticsEngine::Error,
3481-
"object's templated type must have at least one element");
3482-
Diags.Report(loc, DiagID);
3483-
return false;
3484-
}
3485-
if (size > 4) {
3486-
DiagnosticsEngine &Diags = CGM.getDiags();
3487-
unsigned DiagID = Diags.getCustomDiagID(
3488-
DiagnosticsEngine::Error, "elements of typed buffers and textures "
3489-
"must fit in four 32-bit quantities");
3490-
Diags.Report(loc, DiagID);
3491-
return false;
3492-
}
3493-
3494-
EltTy = ScalarTys[0];
3495-
for (QualType ScalarTy : ScalarTys) {
3496-
if (ScalarTy != EltTy) {
3497-
DiagnosticsEngine &Diags = CGM.getDiags();
3498-
unsigned DiagID = Diags.getCustomDiagID(
3499-
DiagnosticsEngine::Error,
3500-
"all template type components must have the same type");
3501-
Diags.Report(loc, DiagID);
3502-
return false;
3503-
}
3504-
}
3505-
}
35063411

35073412
bool bSNorm = false;
35083413
bool bHasNormAttribute = hlsl::HasHLSLUNormSNorm(Ty, &bSNorm);

0 commit comments

Comments
 (0)