[spirv] add `vk::ext_result_id<T>` type (#4192)

As a part of HLSL version of GL_EXT_spirv_intrinsics, this commit adds
vk::ext_result_id<T> type. We must use it for a variable definition or
a function parameter. It means we do not consider it as a physical
storage. Instead, it will be a result id of the instruction.

Related to #3919
This commit is contained in:
Jaebaek Seo 2022-01-19 09:49:25 -05:00 коммит произвёл GitHub
Родитель 46c9735528
Коммит 19139d829d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 146 добавлений и 16 удалений

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

@ -338,6 +338,13 @@ clang::CXXRecordDecl* DeclareTemplateTypeWithHandle(
uint8_t templateArgCount,
_In_opt_ clang::TypeSourceInfo* defaultTypeArgValue);
clang::CXXRecordDecl* DeclareTemplateTypeWithHandleInDeclContext(
clang::ASTContext& context,
clang::DeclContext *declContext,
llvm::StringRef name,
uint8_t templateArgCount,
_In_opt_ clang::TypeSourceInfo* defaultTypeArgValue);
clang::CXXRecordDecl* DeclareUIntTemplatedTypeWithHandle(
clang::ASTContext& context, llvm::StringRef typeName, llvm::StringRef templateParamName);
clang::CXXRecordDecl *DeclareUIntTemplatedTypeWithHandleInDeclContext(
@ -404,6 +411,7 @@ bool IsHLSLAggregateType(clang::QualType type);
clang::QualType GetHLSLResourceResultType(clang::QualType type);
unsigned GetHLSLResourceTemplateUInt(clang::QualType type);
bool IsIncompleteHLSLResourceArrayType(clang::ASTContext& context, clang::QualType type);
clang::QualType GetHLSLResourceTemplateParamType(clang::QualType type);
clang::QualType GetHLSLInputPatchElementType(clang::QualType type);
unsigned GetHLSLInputPatchCount(clang::QualType type);
clang::QualType GetHLSLOutputPatchElementType(clang::QualType type);

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

@ -324,10 +324,16 @@ bool isOrContainsNonFpColMajorMatrix(const ASTContext &,
const SpirvCodeGenOptions &, QualType type,
const Decl *decl);
/// \bried Returns true if the given type is a String or StringLiteral type.
/// \brief Returns true if the given type is `vk::ext_result_id<T>`.
bool isExtResultIdType(QualType type);
/// \brief Returns true if the given type is defined in `vk` namespace.
bool isTypeInVkNamespace(const RecordType *type);
/// \brief Returns true if the given type is a String or StringLiteral type.
bool isStringType(QualType);
/// \bried Returns true if the given type is a bindless array of an opaque type.
/// \brief Returns true if the given type is a bindless array of an opaque type.
bool isBindlessOpaqueArray(QualType type);
/// \brief Generates the corresponding SPIR-V vector type for the given Clang

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

@ -665,6 +665,20 @@ CXXRecordDecl* hlsl::DeclareTemplateTypeWithHandle(
StringRef name,
uint8_t templateArgCount,
_In_opt_ TypeSourceInfo* defaultTypeArgValue)
{
return DeclareTemplateTypeWithHandleInDeclContext(context,
context.getTranslationUnitDecl(),
name,
templateArgCount,
defaultTypeArgValue);
}
CXXRecordDecl* hlsl::DeclareTemplateTypeWithHandleInDeclContext(
ASTContext& context,
DeclContext *declContext,
StringRef name,
uint8_t templateArgCount,
_In_opt_ TypeSourceInfo* defaultTypeArgValue)
{
DXASSERT(templateArgCount != 0, "otherwise caller should be creating a class or struct");
DXASSERT(templateArgCount <= 2, "otherwise the function needs to be updated for a different template pattern");
@ -672,7 +686,7 @@ CXXRecordDecl* hlsl::DeclareTemplateTypeWithHandle(
// Create an object template declaration in translation unit scope.
// templateArgCount=1: template<typename element> typeName { ... }
// templateArgCount=2: template<typename element, int count> typeName { ... }
BuiltinTypeDeclBuilder typeDeclBuilder(context.getTranslationUnitDecl(), name);
BuiltinTypeDeclBuilder typeDeclBuilder(declContext, name);
TemplateTypeParmDecl* elementTemplateParamDecl = typeDeclBuilder.addTypeTemplateParam("element", defaultTypeArgValue);
NonTypeTemplateParmDecl* countTemplateParamDecl = nullptr;
if (templateArgCount > 1)

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

@ -729,7 +729,8 @@ bool IsIncompleteHLSLResourceArrayType(clang::ASTContext &context,
}
return false;
}
QualType GetHLSLInputPatchElementType(QualType type) {
QualType GetHLSLResourceTemplateParamType(QualType type) {
type = type.getCanonicalType();
const RecordType *RT = cast<RecordType>(type);
const ClassTemplateSpecializationDecl *templateDecl =
@ -737,6 +738,10 @@ QualType GetHLSLInputPatchElementType(QualType type) {
const TemplateArgumentList &argList = templateDecl->getTemplateArgs();
return argList[0].getAsType();
}
QualType GetHLSLInputPatchElementType(QualType type) {
return GetHLSLResourceTemplateParamType(type);
}
unsigned GetHLSLInputPatchCount(QualType type) {
type = type.getCanonicalType();
const RecordType *RT = cast<RecordType>(type);
@ -746,12 +751,7 @@ unsigned GetHLSLInputPatchCount(QualType type) {
return argList[1].getAsIntegral().getLimitedValue();
}
clang::QualType GetHLSLOutputPatchElementType(QualType type) {
type = type.getCanonicalType();
const RecordType *RT = cast<RecordType>(type);
const ClassTemplateSpecializationDecl *templateDecl =
cast<ClassTemplateSpecializationDecl>(RT->getAsCXXRecordDecl());
const TemplateArgumentList &argList = templateDecl->getTemplateArgs();
return argList[0].getAsType();
return GetHLSLResourceTemplateParamType(type);
}
unsigned GetHLSLOutputPatchCount(QualType type) {
type = type.getCanonicalType();

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

@ -1254,6 +1254,25 @@ bool isOrContainsNonFpColMajorMatrix(const ASTContext &astContext,
return false;
}
bool isTypeInVkNamespace(const RecordType *type) {
if (const auto *nameSpaceDecl =
dyn_cast<NamespaceDecl>(type->getDecl()->getDeclContext())) {
return nameSpaceDecl->getName() == "vk";
}
return false;
}
bool isExtResultIdType(QualType type) {
if (const auto *elaboratedType = type->getAs<ElaboratedType>()) {
if (const auto *recordType = elaboratedType->getAs<RecordType>()) {
if (!isTypeInVkNamespace(recordType))
return false;
return recordType->getDecl()->getName() == "ext_result_id";
}
}
return false;
}
bool isStringType(QualType type) {
return hlsl::IsStringType(type) || hlsl::IsStringLiteralType(type);
}

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

@ -1054,6 +1054,21 @@ SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var) {
return varInstr;
}
SpirvInstruction *DeclResultIdMapper::createResultId(const VarDecl *var) {
assert(isExtResultIdType(var->getType()));
// Without initialization, we cannot generate the result id.
if (!var->hasInit()) {
emitError("Found uninitialized variable for result id.",
var->getLocation());
return nullptr;
}
SpirvInstruction *init = theEmitter.doExpr(var->getInit());
astDecls[var] = createDeclSpirvInfo(init);
return init;
}
SpirvInstruction *
DeclResultIdMapper::createOrUpdateStringVar(const VarDecl *var) {
assert(hlsl::IsStringType(var->getType()) ||

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

@ -392,6 +392,14 @@ public:
/// for it.
SpirvInstruction *createOrUpdateStringVar(const VarDecl *);
/// \brief Returns an instruction that represents the given VarDecl.
/// VarDecl must be a variable of vk::ext_result_id<Type> type.
///
/// This function inspects the VarDecl for an initialization expression. If
/// initialization expression is not found, it will emit an error because the
/// variable with result id requires an initialization.
SpirvInstruction *createResultId(const VarDecl *var);
/// \brief Creates an Enum constant.
void createEnumConstant(const EnumConstantDecl *decl);

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

@ -521,6 +521,22 @@ const SpirvType *LowerTypeVisitor::lowerType(QualType type,
return 0;
}
const SpirvType *
LowerTypeVisitor::lowerVkTypeInVkNamespace(QualType type, llvm::StringRef name,
SpirvLayoutRule rule,
SourceLocation srcLoc) {
if (name == "ext_type") {
auto typeId = hlsl::GetHLSLResourceTemplateUInt(type);
return spvContext.getCreatedSpirvIntrinsicType(typeId);
}
if (name == "ext_result_id") {
QualType realType = hlsl::GetHLSLResourceTemplateParamType(type);
return lowerType(realType, rule, llvm::None, srcLoc);
}
emitError("unknown type %0 in vk namespace", srcLoc) << type;
return nullptr;
}
const SpirvType *LowerTypeVisitor::lowerResourceType(QualType type,
SpirvLayoutRule rule,
SourceLocation srcLoc) {
@ -535,6 +551,10 @@ const SpirvType *LowerTypeVisitor::lowerResourceType(QualType type,
assert(recordType);
const llvm::StringRef name = recordType->getDecl()->getName();
if (isTypeInVkNamespace(recordType)) {
return lowerVkTypeInVkNamespace(type, name, rule, srcLoc);
}
// TODO: avoid string comparison once hlsl::IsHLSLResouceType() does that.
{ // Texture types
@ -593,11 +613,6 @@ const SpirvType *LowerTypeVisitor::lowerResourceType(QualType type,
if (name == "RayQuery")
return spvContext.getRayQueryTypeKHR();
if (name == "ext_type") {
auto typeId = hlsl::GetHLSLResourceTemplateUInt(type);
return spvContext.getCreatedSpirvIntrinsicType(typeId);
}
if (name == "StructuredBuffer" || name == "RWStructuredBuffer" ||
name == "AppendStructuredBuffer" || name == "ConsumeStructuredBuffer") {
// StructureBuffer<S> will be translated into an OpTypeStruct with one

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

@ -71,6 +71,11 @@ private:
const SpirvType *lowerResourceType(QualType type, SpirvLayoutRule rule,
SourceLocation);
/// Lowers the given type defined in vk namespace into its SPIR-V type.
const SpirvType *lowerVkTypeInVkNamespace(QualType type, llvm::StringRef name,
SpirvLayoutRule rule,
SourceLocation srcLoc);
/// For the given sampled type, returns the corresponding image format
/// that can be used to create an image object.
spv::ImageFormat translateSampledTypeToImageFormat(QualType sampledType,

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

@ -1530,6 +1530,11 @@ void SpirvEmitter::doVarDecl(const VarDecl *decl) {
const auto loc = decl->getLocation();
const auto range = decl->getSourceRange();
if (isExtResultIdType(decl->getType())) {
declIdMapper.createResultId(decl);
return;
}
// HLSL has the 'string' type which can be used for rare purposes such as
// printf (SPIR-V's DebugPrintf). SPIR-V does not have a 'char' or 'string'
// type, and therefore any variable of such type should not be created.

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

@ -184,6 +184,7 @@ enum ArBasicKind {
AR_OBJECT_VK_SUBPASS_INPUT,
AR_OBJECT_VK_SUBPASS_INPUT_MS,
AR_OBJECT_VK_SPV_INTRINSIC_TYPE,
AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID,
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV change ends
@ -476,6 +477,7 @@ const UINT g_uBasicKindProps[] =
BPROP_OBJECT | BPROP_RBUFFER, // AR_OBJECT_VK_SUBPASS_INPUT
BPROP_OBJECT | BPROP_RBUFFER, // AR_OBJECT_VK_SUBPASS_INPUT_MS
BPROP_OBJECT, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE use recordType
BPROP_OBJECT, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID use recordType
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV change ends
@ -1400,6 +1402,7 @@ const ArBasicKind g_ArBasicKindsAsTypes[] =
AR_OBJECT_VK_SUBPASS_INPUT,
AR_OBJECT_VK_SUBPASS_INPUT_MS,
AR_OBJECT_VK_SPV_INTRINSIC_TYPE,
AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID,
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV change ends
@ -1493,6 +1496,7 @@ const uint8_t g_ArBasicKindsTemplateCount[] =
1, // AR_OBJECT_VK_SUBPASS_INPUT
1, // AR_OBJECT_VK_SUBPASS_INPUT_MS,
1, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE
1, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV change ends
@ -1594,6 +1598,7 @@ const SubscriptOperatorRecord g_ArBasicKindsSubscripts[] =
{ 0, MipsFalse, SampleFalse }, // AR_OBJECT_VK_SUBPASS_INPUT (SubpassInput)
{ 0, MipsFalse, SampleFalse }, // AR_OBJECT_VK_SUBPASS_INPUT_MS (SubpassInputMS)
{ 0, MipsFalse, SampleFalse }, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE
{ 0, MipsFalse, SampleFalse }, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV change ends
@ -1714,6 +1719,7 @@ const char* g_ArBasicTypeNames[] =
"SubpassInput",
"SubpassInputMS",
"ext_type",
"ext_result_id",
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV change ends
@ -3602,6 +3608,13 @@ private:
*m_context, m_vkNSDecl, typeName, "id");
recordDecl->setImplicit(true);
}
else if (kind == AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID && m_vkNSDecl) {
recordDecl = DeclareTemplateTypeWithHandleInDeclContext(*m_context,
m_vkNSDecl,
typeName, 1,
nullptr);
recordDecl->setImplicit(true);
}
#endif
else if (templateArgCount == 0) {
recordDecl = DeclareRecordTypeWithHandle(*m_context, typeName);
@ -12924,7 +12937,8 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth,
if (!getLangOpts().SPIRV) {
if (basicKind == ArBasicKind::AR_OBJECT_VK_SUBPASS_INPUT ||
basicKind == ArBasicKind::AR_OBJECT_VK_SUBPASS_INPUT_MS ||
basicKind == ArBasicKind::AR_OBJECT_VK_SPV_INTRINSIC_TYPE) {
basicKind == ArBasicKind::AR_OBJECT_VK_SPV_INTRINSIC_TYPE ||
basicKind == ArBasicKind::AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID) {
Diag(D.getLocStart(), diag::err_hlsl_vulkan_specific_feature)
<< g_ArBasicTypeNames[basicKind];
result = false;

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

@ -0,0 +1,20 @@
// RUN: %dxc -T cs_6_0 -E main
[[vk::ext_instruction(/* OpLoad */ 61)]]
vk::ext_result_id<float> load([[vk::ext_reference]] float pointer,
[[vk::ext_literal]] int memoryOperands);
[[vk::ext_instruction(/* OpStore */ 62)]]
void store([[vk::ext_reference]] float pointer,
vk::ext_result_id<float> value,
[[vk::ext_literal]] int memoryOperands);
[numthreads(1,1,1)]
void main() {
float foo, bar;
//CHECK: [[foo_value:%\w+]] = OpLoad %float %foo None
//CHECK: OpStore %bar [[foo_value]] Volatile
vk::ext_result_id<float> foo_value = load(foo, /* None */ 0x0);
store(bar, foo_value, /* Volatile */ 0x1);
}

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

@ -1341,6 +1341,7 @@ TEST_F(FileTest, IntrinsicsVkQueueFamilyScope) {
}
TEST_F(FileTest, IntrinsicsSpirv) {
runFileTest("spv.intrinsicInstruction.hlsl");
runFileTest("spv.intrinsic.result_id.hlsl");
runFileTest("spv.intrinsicLiteral.hlsl");
runFileTest("spv.intrinsicDecorate.hlsl", Expect::Success, false);
runFileTest("spv.intrinsicExecutionMode.hlsl", Expect::Success, false);