|
@@ -8,12 +8,12 @@
|
|
//===----------------------------------------------------------------------===//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
#include "clang/SPIRV/EmitSPIRVAction.h"
|
|
#include "clang/SPIRV/EmitSPIRVAction.h"
|
|
|
|
+
|
|
#include "clang/AST/AST.h"
|
|
#include "clang/AST/AST.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/HlslTypes.h"
|
|
#include "clang/AST/HlslTypes.h"
|
|
#include "clang/AST/RecordLayout.h"
|
|
#include "clang/AST/RecordLayout.h"
|
|
-#include "clang/AST/RecursiveASTVisitor.h"
|
|
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
@@ -25,41 +25,79 @@
|
|
namespace clang {
|
|
namespace clang {
|
|
namespace {
|
|
namespace {
|
|
|
|
|
|
-/// \brief Generates the corresponding SPIR-V type for the given Clang frontend
|
|
|
|
-/// type and returns the <result-id>.
|
|
|
|
|
|
+/// The class responsible to translate Clang frontend types into SPIR-V type
|
|
|
|
+/// instructions.
|
|
///
|
|
///
|
|
-/// The translation is recursive; all the types that the target type depends on
|
|
|
|
-/// will be generated.
|
|
|
|
-uint32_t translateType(QualType type, spirv::ModuleBuilder &theBuilder) {
|
|
|
|
- // In AST, vector types are TypedefType of TemplateSpecializationType,
|
|
|
|
- // which is nested deeply. So we do fast track check here.
|
|
|
|
- const auto symbol = type.getAsString();
|
|
|
|
- if (symbol == "float4") {
|
|
|
|
- const uint32_t floatType = theBuilder.getFloatType();
|
|
|
|
- return theBuilder.getVec4Type(floatType);
|
|
|
|
- } else if (symbol == "float3") {
|
|
|
|
- const uint32_t floatType = theBuilder.getFloatType();
|
|
|
|
- return theBuilder.getVec3Type(floatType);
|
|
|
|
- } else if (symbol == "float2") {
|
|
|
|
- const uint32_t floatType = theBuilder.getFloatType();
|
|
|
|
- return theBuilder.getVec2Type(floatType);
|
|
|
|
- } else if (auto *builtinType = dyn_cast<BuiltinType>(type.getTypePtr())) {
|
|
|
|
- switch (builtinType->getKind()) {
|
|
|
|
- case BuiltinType::Void:
|
|
|
|
- return theBuilder.getVoidType();
|
|
|
|
- case BuiltinType::Float:
|
|
|
|
- return theBuilder.getFloatType();
|
|
|
|
- default:
|
|
|
|
- // TODO: handle other primitive types
|
|
|
|
- assert(false && "unhandled builtin type");
|
|
|
|
- break;
|
|
|
|
|
|
+/// SPIR-V type instructions generated during translation will be emitted to
|
|
|
|
+/// the SPIR-V module builder passed into the constructor.
|
|
|
|
+/// Warnings and errors during the translation will be reported to the
|
|
|
|
+/// DiagnosticEngine passed into the constructor.
|
|
|
|
+class TypeTranslator {
|
|
|
|
+public:
|
|
|
|
+ TypeTranslator(spirv::ModuleBuilder &builder, DiagnosticsEngine &diag)
|
|
|
|
+ : theBuilder(builder), diags(diag) {}
|
|
|
|
+
|
|
|
|
+ /// \brief Generates the corresponding SPIR-V type for the given Clang
|
|
|
|
+ /// frontend type and returns the type's <result-id>. On failure, reports
|
|
|
|
+ /// the error and returns 0.
|
|
|
|
+ ///
|
|
|
|
+ /// The translation is recursive; all the types that the target type depends
|
|
|
|
+ /// on will be generated.
|
|
|
|
+ uint32_t translateType(QualType type) {
|
|
|
|
+ const auto *typePtr = type.getTypePtr();
|
|
|
|
+
|
|
|
|
+ // Primitive types
|
|
|
|
+ if (const auto *builtinType = dyn_cast<BuiltinType>(typePtr)) {
|
|
|
|
+ switch (builtinType->getKind()) {
|
|
|
|
+ case BuiltinType::Void:
|
|
|
|
+ return theBuilder.getVoidType();
|
|
|
|
+ case BuiltinType::Float:
|
|
|
|
+ return theBuilder.getFloatType();
|
|
|
|
+ default:
|
|
|
|
+ emitError("Primitive type '%0' is not supported yet.")
|
|
|
|
+ << builtinType->getTypeClassName();
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // In AST, vector types are TypedefType of TemplateSpecializationType.
|
|
|
|
+ // We handle them via HLSL type inspection functions.
|
|
|
|
+ if (hlsl::IsHLSLVecType(type)) {
|
|
|
|
+ const auto elemType = hlsl::GetHLSLVecElementType(type);
|
|
|
|
+ const auto elemCount = hlsl::GetHLSLVecSize(type);
|
|
|
|
+ return theBuilder.getVecType(translateType(elemType), elemCount);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Struct type
|
|
|
|
+ if (const auto *structType = dyn_cast<RecordType>(typePtr)) {
|
|
|
|
+ const auto *decl = structType->getDecl();
|
|
|
|
+
|
|
|
|
+ // Collect all fields' types.
|
|
|
|
+ std::vector<uint32_t> fieldTypes;
|
|
|
|
+ for (const auto *field : decl->fields()) {
|
|
|
|
+ fieldTypes.push_back(translateType(field->getType()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return theBuilder.getStructType(fieldTypes);
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- // TODO: handle other types
|
|
|
|
- assert(false && "unhandled clang type");
|
|
|
|
|
|
+
|
|
|
|
+ emitError("Type '%0' is not supported yet.") << type->getTypeClassName();
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
|
|
+
|
|
|
|
+private:
|
|
|
|
+ /// \brief Wrapper method to create an error message and report it
|
|
|
|
+ /// in the diagnostic engine associated with this consumer.
|
|
|
|
+ template <unsigned N> DiagnosticBuilder emitError(const char (&message)[N]) {
|
|
|
|
+ const auto diagId =
|
|
|
|
+ diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
|
|
|
|
+ return diags.Report(diagId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+private:
|
|
|
|
+ spirv::ModuleBuilder &theBuilder;
|
|
|
|
+ DiagnosticsEngine &diags;
|
|
|
|
+};
|
|
|
|
|
|
/// \brief The class containing mappings from Clang frontend Decls to their
|
|
/// \brief The class containing mappings from Clang frontend Decls to their
|
|
/// corresponding SPIR-V <result-id>s.
|
|
/// corresponding SPIR-V <result-id>s.
|
|
@@ -70,84 +108,40 @@ uint32_t translateType(QualType type, spirv::ModuleBuilder &theBuilder) {
|
|
/// will be used to generate all SPIR-V instructions required.
|
|
/// will be used to generate all SPIR-V instructions required.
|
|
///
|
|
///
|
|
/// This class acts as a middle layer to handle the mapping between HLSL
|
|
/// This class acts as a middle layer to handle the mapping between HLSL
|
|
-/// semantics and Vulkan interface (stage builtin/input/output) variables.
|
|
|
|
-/// Such mapping is required because of the semantic differences between DirectX
|
|
|
|
-/// and Vulkan and the essence of HLSL as the front-end language for DirectX.
|
|
|
|
|
|
+/// semantics and Vulkan stage (builtin/input/output) variables. Such mapping
|
|
|
|
+/// is required because of the semantic differences between DirectX and
|
|
|
|
+/// Vulkan and the essence of HLSL as the front-end language for DirectX.
|
|
/// A normal variable attached with some semantic will be translated into a
|
|
/// A normal variable attached with some semantic will be translated into a
|
|
-/// single interface variables if it is of non-struct type. If it is of struct
|
|
|
|
|
|
+/// single stage variables if it is of non-struct type. If it is of struct
|
|
/// type, the fields with attached semantics will need to be translated into
|
|
/// type, the fields with attached semantics will need to be translated into
|
|
-/// interface variables per Vulkan's requirements.
|
|
|
|
|
|
+/// stage variables per Vulkan's requirements.
|
|
///
|
|
///
|
|
-/// In the following class, we call a Decl or symbol as *remapped* when it is
|
|
|
|
-/// translated into an interface variable; otherwise, we call it as *normal*.
|
|
|
|
|
|
+/// In the following class, we call a Decl as *remapped* when it is translated
|
|
|
|
+/// into a stage variable; otherwise, we call it as *normal*. Remapped decls
|
|
|
|
+/// include:
|
|
|
|
+/// * FunctionDecl if the return value is attached with a semantic
|
|
|
|
+/// * ParmVarDecl if the parameter is attached with a semantic
|
|
|
|
+/// * FieldDecl if the field is attached with a semantic.
|
|
class DeclResultIdMapper {
|
|
class DeclResultIdMapper {
|
|
public:
|
|
public:
|
|
- DeclResultIdMapper(spirv::ModuleBuilder *builder) : theBuilder(*builder) {}
|
|
|
|
|
|
+ DeclResultIdMapper(spv::ExecutionModel stage, spirv::ModuleBuilder &builder,
|
|
|
|
+ DiagnosticsEngine &diag)
|
|
|
|
+ : shaderStage(stage), theBuilder(builder), typeTranslator(builder, diag) {
|
|
|
|
+ }
|
|
|
|
|
|
- /// \brief Defines a function return value in this mapper and returns the
|
|
|
|
- /// <result-id> for the final return type.
|
|
|
|
- ///
|
|
|
|
- /// The final return type is the "residual" type after "stripping" all
|
|
|
|
- /// subtypes with attached semantics. For exmaple, stripping "float4 :
|
|
|
|
- /// SV_Target" will result in "void", and stripping "struct { float4 :
|
|
|
|
- /// SV_Target, float4 }" will result in "struct { float4 }".
|
|
|
|
- ///
|
|
|
|
- /// Proper SPIR-V instructions will be generated to create the corresponding
|
|
|
|
- /// interface variable if stripping happens.
|
|
|
|
- uint32_t defineFnReturn(FunctionDecl *funcDecl) {
|
|
|
|
|
|
+ /// \brief Creates the stage variables by parsing the semantics attached to
|
|
|
|
+ /// the given function's return value.
|
|
|
|
+ void createStageVarFromFnReturn(FunctionDecl *funcDecl) {
|
|
// SemanticDecl for the return value is attached to the FunctionDecl.
|
|
// SemanticDecl for the return value is attached to the FunctionDecl.
|
|
- const auto sk = getInterfaceVarSemanticAndKind(funcDecl);
|
|
|
|
- if (sk.second != InterfaceVariableKind::None) {
|
|
|
|
- // Found return value with semantic attached. This means we need to map
|
|
|
|
- // the return value to a single interface variable.
|
|
|
|
- const uint32_t retTypeId =
|
|
|
|
- translateType(funcDecl->getReturnType(), theBuilder);
|
|
|
|
- // TODO: Change to the correct interface variable kind here.
|
|
|
|
- const uint32_t varId = theBuilder.addStageIOVariable(
|
|
|
|
- retTypeId, spv::StorageClass::Output, llvm::None);
|
|
|
|
-
|
|
|
|
- stageOutputs.push_back(std::make_pair(varId, sk.first));
|
|
|
|
- remappedDecls[funcDecl] = varId;
|
|
|
|
- interfaceVars.insert(varId);
|
|
|
|
-
|
|
|
|
- return theBuilder.getVoidType();
|
|
|
|
- } else {
|
|
|
|
- // TODO: We need to handle struct return types here.
|
|
|
|
- return translateType(funcDecl->getReturnType(), theBuilder);
|
|
|
|
- }
|
|
|
|
|
|
+ createStageVariables(funcDecl, false);
|
|
}
|
|
}
|
|
|
|
|
|
- /// \brief Defines a function parameter in this mapper and returns the
|
|
|
|
- /// <result-id> for the final parameter type. Returns 0 if the final type
|
|
|
|
- /// is void.
|
|
|
|
- ///
|
|
|
|
- /// The final parameter type is the "residual" type after "stripping" all
|
|
|
|
- /// subtypes will attached semantics. For exmaple, stripping "float4 :
|
|
|
|
- /// SV_Target" will result in "void", and stripping "struct { float4 :
|
|
|
|
- /// SV_Target, float4 }" will result in "struct { float4 }".
|
|
|
|
- ///
|
|
|
|
- /// Proper SPIR-V instructions will be generated to create the corresponding
|
|
|
|
- /// interface variable if stripping happens.
|
|
|
|
- uint32_t defineFnParam(ParmVarDecl *paramDecl) {
|
|
|
|
- const auto sk = getInterfaceVarSemanticAndKind(paramDecl);
|
|
|
|
- if (sk.second != InterfaceVariableKind::None) {
|
|
|
|
- // Found parameter with semantic attached. This means we need to map the
|
|
|
|
- // parameter to a single interface variable.
|
|
|
|
- const uint32_t paramTypeId =
|
|
|
|
- translateType(paramDecl->getType(), theBuilder);
|
|
|
|
- // TODO: Change to the correct interface variable kind here.
|
|
|
|
- const uint32_t varId = theBuilder.addStageIOVariable(
|
|
|
|
- paramTypeId, spv::StorageClass::Input, llvm::None);
|
|
|
|
-
|
|
|
|
- stageInputs.push_back(std::make_pair(varId, sk.first));
|
|
|
|
- remappedDecls[paramDecl] = varId;
|
|
|
|
- interfaceVars.insert(varId);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
- } else {
|
|
|
|
- // TODO: We need to handle struct parameter types here.
|
|
|
|
- return translateType(paramDecl->getType(), theBuilder);
|
|
|
|
- }
|
|
|
|
|
|
+ /// \brief Creates the stage variables by parsing the semantics attached to
|
|
|
|
+ /// the given function's parameter.
|
|
|
|
+ void createStageVarFromFnParam(ParmVarDecl *paramDecl) {
|
|
|
|
+ // TODO: We cannot treat all parameters as stage inputs because of
|
|
|
|
+ // out/input modifiers.
|
|
|
|
+ createStageVariables(paramDecl, true);
|
|
}
|
|
}
|
|
|
|
|
|
/// \brief Registers a Decl's <result-id> without generating any SPIR-V
|
|
/// \brief Registers a Decl's <result-id> without generating any SPIR-V
|
|
@@ -156,9 +150,9 @@ public:
|
|
normalDecls[symbol] = resultId;
|
|
normalDecls[symbol] = resultId;
|
|
}
|
|
}
|
|
|
|
|
|
- /// \brief Returns true if the given <result-id> is for an interface variable.
|
|
|
|
- bool isInterfaceVariable(uint32_t varId) const {
|
|
|
|
- return interfaceVars.count(varId) != 0;
|
|
|
|
|
|
+ /// \brief Returns true if the given <result-id> is for a stage variable.
|
|
|
|
+ bool isStageVariable(uint32_t varId) const {
|
|
|
|
+ return stageVars.count(varId) != 0;
|
|
}
|
|
}
|
|
|
|
|
|
/// \brief Returns the <result-id> for the given Decl.
|
|
/// \brief Returns the <result-id> for the given Decl.
|
|
@@ -190,18 +184,22 @@ public:
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- /// \brief Returns all defined stage input and ouput variables in this mapper.
|
|
|
|
- std::vector<uint32_t> collectStageIOVariables() {
|
|
|
|
- std::vector<uint32_t> stageIOVars;
|
|
|
|
|
|
+ /// \brief Returns all defined stage (builtin/input/ouput) variables in this
|
|
|
|
+ /// mapper.
|
|
|
|
+ std::vector<uint32_t> collectStageVariables() const {
|
|
|
|
+ std::vector<uint32_t> stageVars;
|
|
|
|
|
|
|
|
+ for (const auto &builtin : stageBuiltins) {
|
|
|
|
+ stageVars.push_back(builtin.first);
|
|
|
|
+ }
|
|
for (const auto &input : stageInputs) {
|
|
for (const auto &input : stageInputs) {
|
|
- stageIOVars.push_back(input.first);
|
|
|
|
|
|
+ stageVars.push_back(input.first);
|
|
}
|
|
}
|
|
for (const auto &output : stageOutputs) {
|
|
for (const auto &output : stageOutputs) {
|
|
- stageIOVars.push_back(output.first);
|
|
|
|
|
|
+ stageVars.push_back(output.first);
|
|
}
|
|
}
|
|
|
|
|
|
- return stageIOVars;
|
|
|
|
|
|
+ return stageVars;
|
|
}
|
|
}
|
|
|
|
|
|
/// \brief Decorates all stage input and output variables with proper
|
|
/// \brief Decorates all stage input and output variables with proper
|
|
@@ -224,87 +222,151 @@ public:
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
private:
|
|
- /// \brief Interface variable kind.
|
|
|
|
|
|
+ /// \brief Stage variable kind.
|
|
///
|
|
///
|
|
- /// By interface variable, I mean all stage builtin, input, and output
|
|
|
|
- /// variables. They participate in interface matching in Vulkan pipelines.
|
|
|
|
- enum class InterfaceVariableKind {
|
|
|
|
- None, ///< Not an interface variable
|
|
|
|
- Input, ///< Stage input variable
|
|
|
|
- Output, ///< Stage output variable
|
|
|
|
- IO, ///< Interface variable that can act as both stage input or stage output
|
|
|
|
- // TODO: builtins
|
|
|
|
|
|
+ /// Stage variables include builtin, input, and output variables.
|
|
|
|
+ /// They participate in interface matching in Vulkan pipelines.
|
|
|
|
+ enum class StageVarKind {
|
|
|
|
+ None,
|
|
|
|
+ Arbitary,
|
|
|
|
+ Position,
|
|
|
|
+ Color,
|
|
|
|
+ Target,
|
|
|
|
+ // TODO: other possible kinds
|
|
};
|
|
};
|
|
|
|
|
|
- using InterfaceVarIdSemanticPair = std::pair<uint32_t, llvm::StringRef>;
|
|
|
|
|
|
+ using StageVarIdSemanticPair = std::pair<uint32_t, std::string>;
|
|
|
|
+
|
|
|
|
+ /// Returns the type of the given decl. If the given decl is a FunctionDecl,
|
|
|
|
+ /// returns its result type.
|
|
|
|
+ QualType getFnParamOrRetType(const DeclaratorDecl *decl) const {
|
|
|
|
+ if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
|
|
|
|
+ return funcDecl->getReturnType();
|
|
|
|
+ }
|
|
|
|
+ return decl->getType();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Creates all the stage variables mapped from semantics on the given decl.
|
|
|
|
+ ///
|
|
|
|
+ /// Assumes the decl has semantic attached to itself or to its fields.
|
|
|
|
+ void createStageVariables(const DeclaratorDecl *decl, bool actAsInput) {
|
|
|
|
+ QualType type = getFnParamOrRetType(decl);
|
|
|
|
+
|
|
|
|
+ if (type->isVoidType()) {
|
|
|
|
+ // No stage variables will be created for void type.
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const std::string semantic = getStageVarSemantic(decl);
|
|
|
|
+ if (!semantic.empty()) {
|
|
|
|
+ // Found semantic attached directly to this Decl. This means we need to
|
|
|
|
+ // map this decl to a single stage variable.
|
|
|
|
+ const uint32_t typeId = typeTranslator.translateType(type);
|
|
|
|
+ const auto kind = getStageVarKind(semantic);
|
|
|
|
+
|
|
|
|
+ if (actAsInput) {
|
|
|
|
+ // Stage (builtin) input variable cases
|
|
|
|
+ const uint32_t varId =
|
|
|
|
+ theBuilder.addStageIOVariable(typeId, spv::StorageClass::Input);
|
|
|
|
+
|
|
|
|
+ stageInputs.push_back(std::make_pair(varId, semantic));
|
|
|
|
+ remappedDecls[decl] = varId;
|
|
|
|
+ stageVars.insert(varId);
|
|
|
|
+ } else {
|
|
|
|
+ // Handle output builtin variables first
|
|
|
|
+ if (shaderStage == spv::ExecutionModel::Vertex &&
|
|
|
|
+ kind == StageVarKind::Position) {
|
|
|
|
+ const uint32_t varId = theBuilder.addStageBuiltinVariable(
|
|
|
|
+ typeId, spv::BuiltIn::Position);
|
|
|
|
+
|
|
|
|
+ stageBuiltins.push_back(std::make_pair(varId, semantic));
|
|
|
|
+ remappedDecls[decl] = varId;
|
|
|
|
+ stageVars.insert(varId);
|
|
|
|
+ } else {
|
|
|
|
+ // The rest are normal stage output variables
|
|
|
|
+ const uint32_t varId =
|
|
|
|
+ theBuilder.addStageIOVariable(typeId, spv::StorageClass::Output);
|
|
|
|
+
|
|
|
|
+ stageOutputs.push_back(std::make_pair(varId, semantic));
|
|
|
|
+ remappedDecls[decl] = varId;
|
|
|
|
+ stageVars.insert(varId);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // If the decl itself doesn't have semantic, it should be a struct having
|
|
|
|
+ // all its fields with semantics.
|
|
|
|
+ assert(type->isStructureType() &&
|
|
|
|
+ "found non-struct decls without semantics");
|
|
|
|
|
|
- /// \brief Returns the interface variable's semantic and kind for the given
|
|
|
|
- /// Decl.
|
|
|
|
- std::pair<llvm::StringRef, InterfaceVariableKind>
|
|
|
|
- getInterfaceVarSemanticAndKind(NamedDecl *decl) const {
|
|
|
|
|
|
+ const auto *structDecl = cast<RecordType>(type.getTypePtr())->getDecl();
|
|
|
|
+
|
|
|
|
+ // Recursively handle all the fields.
|
|
|
|
+ for (const auto *field : structDecl->fields()) {
|
|
|
|
+ createStageVariables(field, actAsInput);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// \brief Returns the stage variable's kind for the given semantic.
|
|
|
|
+ StageVarKind getStageVarKind(llvm::StringRef semantic) const {
|
|
|
|
+ return llvm::StringSwitch<StageVarKind>(semantic)
|
|
|
|
+ .Case("", StageVarKind::None)
|
|
|
|
+ .StartsWith("COLOR", StageVarKind::Color)
|
|
|
|
+ .StartsWith("POSITION", StageVarKind::Position)
|
|
|
|
+ .StartsWith("SV_POSITION", StageVarKind::Position)
|
|
|
|
+ .StartsWith("SV_TARGET", StageVarKind::Target)
|
|
|
|
+ .Default(StageVarKind::Arbitary);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// \brief Returns the stage variable's semantic for the given Decl.
|
|
|
|
+ std::string getStageVarSemantic(const NamedDecl *decl) const {
|
|
for (auto *annotation : decl->getUnusualAnnotations()) {
|
|
for (auto *annotation : decl->getUnusualAnnotations()) {
|
|
if (auto *semantic = dyn_cast<hlsl::SemanticDecl>(annotation)) {
|
|
if (auto *semantic = dyn_cast<hlsl::SemanticDecl>(annotation)) {
|
|
- const llvm::StringRef name = semantic->SemanticName;
|
|
|
|
- // TODO: We should check the semantic name ends with a number.
|
|
|
|
- if (name.startswith("SV_TARGET")) {
|
|
|
|
- return std::make_pair(name, InterfaceVariableKind::Output);
|
|
|
|
- }
|
|
|
|
- if (name.startswith("COLOR")) {
|
|
|
|
- return std::make_pair(name, InterfaceVariableKind::IO);
|
|
|
|
- }
|
|
|
|
|
|
+ return semantic->SemanticName.upper();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- return std::make_pair("", InterfaceVariableKind::None);
|
|
|
|
|
|
+ return "";
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
private:
|
|
|
|
+ const spv::ExecutionModel shaderStage;
|
|
spirv::ModuleBuilder &theBuilder;
|
|
spirv::ModuleBuilder &theBuilder;
|
|
|
|
+ TypeTranslator typeTranslator;
|
|
|
|
|
|
/// Mapping of all remapped decls to their <result-id>s.
|
|
/// Mapping of all remapped decls to their <result-id>s.
|
|
llvm::DenseMap<const NamedDecl *, uint32_t> remappedDecls;
|
|
llvm::DenseMap<const NamedDecl *, uint32_t> remappedDecls;
|
|
/// Mapping of all normal decls to their <result-id>s.
|
|
/// Mapping of all normal decls to their <result-id>s.
|
|
llvm::DenseMap<const NamedDecl *, uint32_t> normalDecls;
|
|
llvm::DenseMap<const NamedDecl *, uint32_t> normalDecls;
|
|
- /// <result-id>s of all defined interface variables.
|
|
|
|
|
|
+ /// <result-id>s of all defined stage variables.
|
|
///
|
|
///
|
|
/// We need to keep a separate list here to avoid looping through the
|
|
/// We need to keep a separate list here to avoid looping through the
|
|
- /// remappedDecls to find whether an <result-id> is for interface variable.
|
|
|
|
- llvm::SmallSet<uint32_t, 16> interfaceVars;
|
|
|
|
|
|
+ /// remappedDecls to find whether an <result-id> is for a stage variable.
|
|
|
|
+ llvm::SmallSet<uint32_t, 16> stageVars;
|
|
|
|
|
|
/// Stage input/oupt/builtin variables and their kinds.
|
|
/// Stage input/oupt/builtin variables and their kinds.
|
|
///
|
|
///
|
|
/// We need to keep a separate list here in order to sort them at the end
|
|
/// We need to keep a separate list here in order to sort them at the end
|
|
/// of the module building.
|
|
/// of the module building.
|
|
- llvm::SmallVector<InterfaceVarIdSemanticPair, 8> stageInputs;
|
|
|
|
- llvm::SmallVector<InterfaceVarIdSemanticPair, 8> stageOutputs;
|
|
|
|
- llvm::SmallVector<InterfaceVarIdSemanticPair, 8> stageBuiltins;
|
|
|
|
|
|
+ llvm::SmallVector<StageVarIdSemanticPair, 8> stageInputs;
|
|
|
|
+ llvm::SmallVector<StageVarIdSemanticPair, 8> stageOutputs;
|
|
|
|
+ llvm::SmallVector<StageVarIdSemanticPair, 8> stageBuiltins;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/// SPIR-V emitter class. It consumes the HLSL AST and emits SPIR-V words.
|
|
|
|
+///
|
|
|
|
+/// This class only overrides the HandleTranslationUnit() method; Traversing
|
|
|
|
+/// through the AST is done manually instead of using ASTConsumer's harness.
|
|
class SPIRVEmitter : public ASTConsumer {
|
|
class SPIRVEmitter : public ASTConsumer {
|
|
public:
|
|
public:
|
|
explicit SPIRVEmitter(CompilerInstance &ci)
|
|
explicit SPIRVEmitter(CompilerInstance &ci)
|
|
- : theCompilerInstance(ci), diags(ci.getDiagnostics()), theContext(),
|
|
|
|
- theBuilder(&theContext), declIdMapper(&theBuilder),
|
|
|
|
|
|
+ : theCompilerInstance(ci), diags(ci.getDiagnostics()),
|
|
entryFunctionName(ci.getCodeGenOpts().HLSLEntryFunction),
|
|
entryFunctionName(ci.getCodeGenOpts().HLSLEntryFunction),
|
|
shaderStage(getSpirvShaderStageFromHlslProfile(
|
|
shaderStage(getSpirvShaderStageFromHlslProfile(
|
|
ci.getCodeGenOpts().HLSLProfile.c_str())),
|
|
ci.getCodeGenOpts().HLSLProfile.c_str())),
|
|
- entryFunctionId(0), curFunction(nullptr) {}
|
|
|
|
-
|
|
|
|
- /// \brief Wrapper method to create an error message and report it
|
|
|
|
- /// in the diagnostic engine associated with this consumer
|
|
|
|
- template <unsigned N> DiagnosticBuilder emitError(const char (&message)[N]) {
|
|
|
|
- const auto diagId =
|
|
|
|
- diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
|
|
|
|
- return diags.Report(diagId);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \brief Wrapper method to create a warning message and report it
|
|
|
|
- /// in the diagnostic engine associated with this consumer
|
|
|
|
- template <unsigned N>
|
|
|
|
- DiagnosticBuilder emitWarning(const char (&message)[N]) {
|
|
|
|
- const auto diagId =
|
|
|
|
- diags.getCustomDiagID(clang::DiagnosticsEngine::Warning, message);
|
|
|
|
- return diags.Report(diagId);
|
|
|
|
- }
|
|
|
|
|
|
+ theContext(), theBuilder(&theContext),
|
|
|
|
+ declIdMapper(shaderStage, theBuilder, diags),
|
|
|
|
+ typeTranslator(theBuilder, diags), entryFunctionId(0),
|
|
|
|
+ curFunction(nullptr) {}
|
|
|
|
|
|
spv::ExecutionModel getSpirvShaderStageFromHlslProfile(const char *profile) {
|
|
spv::ExecutionModel getSpirvShaderStageFromHlslProfile(const char *profile) {
|
|
assert(profile && "nullptr passed as HLSL profile.");
|
|
assert(profile && "nullptr passed as HLSL profile.");
|
|
@@ -379,13 +441,27 @@ public:
|
|
|
|
|
|
TranslationUnitDecl *tu = context.getTranslationUnitDecl();
|
|
TranslationUnitDecl *tu = context.getTranslationUnitDecl();
|
|
|
|
|
|
- // Process all top level Decls.
|
|
|
|
|
|
+ // A queue of functions we need to translate.
|
|
|
|
+ std::deque<FunctionDecl *> workQueue;
|
|
|
|
+
|
|
|
|
+ // The entry function is the seed of the queue.
|
|
for (auto *decl : tu->decls()) {
|
|
for (auto *decl : tu->decls()) {
|
|
- doDecl(decl);
|
|
|
|
|
|
+ if (auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
|
|
|
|
+ if (funcDecl->getName() == entryFunctionName) {
|
|
|
|
+ workQueue.push_back(funcDecl);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // TODO: enlarge the queue upon seeing a function call.
|
|
|
|
+
|
|
|
|
+ // Translate all functions reachable from the entry function.
|
|
|
|
+ while (!workQueue.empty()) {
|
|
|
|
+ doFunctionDecl(workQueue.front());
|
|
|
|
+ workQueue.pop_front();
|
|
}
|
|
}
|
|
|
|
|
|
theBuilder.addEntryPoint(shaderStage, entryFunctionId, entryFunctionName,
|
|
theBuilder.addEntryPoint(shaderStage, entryFunctionId, entryFunctionName,
|
|
- declIdMapper.collectStageIOVariables());
|
|
|
|
|
|
+ declIdMapper.collectStageVariables());
|
|
|
|
|
|
AddExecutionModeForEntryPoint(shaderStage, entryFunctionId);
|
|
AddExecutionModeForEntryPoint(shaderStage, entryFunctionId);
|
|
|
|
|
|
@@ -399,92 +475,150 @@ public:
|
|
}
|
|
}
|
|
|
|
|
|
void doDecl(Decl *decl) {
|
|
void doDecl(Decl *decl) {
|
|
- if (auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
|
|
|
|
- doFunctionDecl(funcDecl);
|
|
|
|
|
|
+ if (auto *varDecl = dyn_cast<VarDecl>(decl)) {
|
|
|
|
+ doVarDecl(varDecl);
|
|
} else {
|
|
} else {
|
|
- emitWarning("Translation is not implemented for this decl type: %0")
|
|
|
|
- << std::string(decl->getDeclKindName());
|
|
|
|
// TODO: Implement handling of other Decl types.
|
|
// TODO: Implement handling of other Decl types.
|
|
|
|
+ emitWarning("Decl type '%0' is not supported yet.")
|
|
|
|
+ << std::string(decl->getDeclKindName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void doFunctionDecl(FunctionDecl *decl) {
|
|
void doFunctionDecl(FunctionDecl *decl) {
|
|
curFunction = decl;
|
|
curFunction = decl;
|
|
|
|
|
|
- uint32_t retType = declIdMapper.defineFnReturn(decl);
|
|
|
|
-
|
|
|
|
- // Process function parameters. We need to strip parameters mapping to stage
|
|
|
|
- // builtin/input/output variables and use what's left in the function type.
|
|
|
|
- std::vector<uint32_t> residualParamTypes;
|
|
|
|
- std::vector<ParmVarDecl *> residualParams;
|
|
|
|
-
|
|
|
|
- for (auto *param : decl->params()) {
|
|
|
|
- // Get the "residual" parameter type. If nothing left after stripping,
|
|
|
|
- // zero will be returned.
|
|
|
|
- const uint32_t paramType = declIdMapper.defineFnParam(param);
|
|
|
|
- if (paramType) {
|
|
|
|
- residualParamTypes.push_back(paramType);
|
|
|
|
- residualParams.push_back(param);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const uint32_t funcType =
|
|
|
|
- theBuilder.getFunctionType(retType, residualParamTypes);
|
|
|
|
- const std::string funcName = decl->getNameInfo().getAsString();
|
|
|
|
- const uint32_t funcId =
|
|
|
|
- theBuilder.beginFunction(funcType, retType, funcName);
|
|
|
|
-
|
|
|
|
- // Register all the "residual" parameters into the mapper.
|
|
|
|
- for (uint32_t i = 0; i < residualParams.size(); ++i) {
|
|
|
|
- declIdMapper.registerDeclResultId(
|
|
|
|
- residualParams[i], theBuilder.addFnParameter(residualParamTypes[i]));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (decl->hasBody()) {
|
|
|
|
- const uint32_t entryLabel = theBuilder.createBasicBlock();
|
|
|
|
- theBuilder.setInsertPoint(entryLabel);
|
|
|
|
-
|
|
|
|
- // Process all statments in the body.
|
|
|
|
- for (Stmt *stmt : cast<CompoundStmt>(decl->getBody())->body())
|
|
|
|
- doStmt(stmt);
|
|
|
|
-
|
|
|
|
- // We have processed all Stmts in this function and now in the last basic
|
|
|
|
- // block. Make sure we have OpReturn if this is a void(...) function.
|
|
|
|
- if (retType == theBuilder.getVoidType() &&
|
|
|
|
- !theBuilder.isCurrentBasicBlockTerminated()) {
|
|
|
|
- theBuilder.createReturn();
|
|
|
|
|
|
+ const llvm::StringRef funcName = decl->getName();
|
|
|
|
+
|
|
|
|
+ if (funcName == entryFunctionName) {
|
|
|
|
+ // First create stage variables for the entry point.
|
|
|
|
+ declIdMapper.createStageVarFromFnReturn(decl);
|
|
|
|
+ for (auto *param : decl->params())
|
|
|
|
+ declIdMapper.createStageVarFromFnParam(param);
|
|
|
|
+
|
|
|
|
+ // Construct the function signature.
|
|
|
|
+ const uint32_t voidType = theBuilder.getVoidType();
|
|
|
|
+ const uint32_t funcType = theBuilder.getFunctionType(voidType, {});
|
|
|
|
+ const uint32_t funcId =
|
|
|
|
+ theBuilder.beginFunction(funcType, voidType, funcName);
|
|
|
|
+
|
|
|
|
+ if (decl->hasBody()) {
|
|
|
|
+ // The entry basic block.
|
|
|
|
+ const uint32_t entryLabel = theBuilder.createBasicBlock();
|
|
|
|
+ theBuilder.setInsertPoint(entryLabel);
|
|
|
|
+
|
|
|
|
+ // Process all statments in the body.
|
|
|
|
+ for (Stmt *stmt : cast<CompoundStmt>(decl->getBody())->body())
|
|
|
|
+ doStmt(stmt);
|
|
|
|
+
|
|
|
|
+ // We have processed all Stmts in this function and now in the last
|
|
|
|
+ // basic block. Make sure we have OpReturn if missing.
|
|
|
|
+ if (!theBuilder.isCurrentBasicBlockTerminated()) {
|
|
|
|
+ theBuilder.createReturn();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
theBuilder.endFunction();
|
|
theBuilder.endFunction();
|
|
- }
|
|
|
|
|
|
|
|
- // Record the entry function's <result-id>.
|
|
|
|
- if (entryFunctionName == funcName) {
|
|
|
|
|
|
+ // Record the entry function's <result-id>.
|
|
entryFunctionId = funcId;
|
|
entryFunctionId = funcId;
|
|
|
|
+ } else {
|
|
|
|
+ emitError("Non-entry functions are not supported yet.");
|
|
}
|
|
}
|
|
|
|
|
|
curFunction = nullptr;
|
|
curFunction = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ void doVarDecl(VarDecl *decl) {
|
|
|
|
+ if (decl->isLocalVarDecl()) {
|
|
|
|
+ const uint32_t ptrType = theBuilder.getPointerType(
|
|
|
|
+ typeTranslator.translateType(decl->getType()),
|
|
|
|
+ spv::StorageClass::Function);
|
|
|
|
+ const uint32_t varId = theBuilder.addFnVariable(ptrType);
|
|
|
|
+ declIdMapper.registerDeclResultId(decl, varId);
|
|
|
|
+ } else {
|
|
|
|
+ // TODO: handle global variables
|
|
|
|
+ emitError("Global variables are not supported yet.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
void doStmt(Stmt *stmt) {
|
|
void doStmt(Stmt *stmt) {
|
|
if (auto *retStmt = dyn_cast<ReturnStmt>(stmt)) {
|
|
if (auto *retStmt = dyn_cast<ReturnStmt>(stmt)) {
|
|
doReturnStmt(retStmt);
|
|
doReturnStmt(retStmt);
|
|
|
|
+ } else if (auto *declStmt = dyn_cast<DeclStmt>(stmt)) {
|
|
|
|
+ for (auto *decl : declStmt->decls()) {
|
|
|
|
+ doDecl(decl);
|
|
|
|
+ }
|
|
|
|
+ } else if (auto *binOp = dyn_cast<BinaryOperator>(stmt)) {
|
|
|
|
+ const auto opcode = binOp->getOpcode();
|
|
|
|
+ const uint32_t lhs = doExpr(binOp->getLHS());
|
|
|
|
+ const uint32_t rhs = doExpr(binOp->getRHS());
|
|
|
|
+
|
|
|
|
+ doBinaryOperator(opcode, lhs, rhs);
|
|
|
|
+ } else {
|
|
|
|
+ emitError("Stmt '%0' is not supported yet.") << stmt->getStmtClassName();
|
|
}
|
|
}
|
|
// TODO: handle other statements
|
|
// TODO: handle other statements
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ void doBinaryOperator(BinaryOperatorKind opcode, uint32_t lhs, uint32_t rhs) {
|
|
|
|
+ if (opcode == BO_Assign) {
|
|
|
|
+ theBuilder.createStore(lhs, rhs);
|
|
|
|
+ } else {
|
|
|
|
+ emitError("BinaryOperator '%0' is not supported yet.") << opcode;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
void doReturnStmt(ReturnStmt *stmt) {
|
|
void doReturnStmt(ReturnStmt *stmt) {
|
|
|
|
+ // First get the <result-id> of the value we want to return.
|
|
const uint32_t retValue = doExpr(stmt->getRetValue());
|
|
const uint32_t retValue = doExpr(stmt->getRetValue());
|
|
- const uint32_t interfaceVarId =
|
|
|
|
|
|
+
|
|
|
|
+ if (curFunction->getName() != entryFunctionName) {
|
|
|
|
+ theBuilder.createReturnValue(retValue);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // SPIR-V requires the signature of entry functions to be void(), while
|
|
|
|
+ // in HLSL we can have non-void parameter and return types for entry points.
|
|
|
|
+ // So we should treat the ReturnStmt in entry functions specially.
|
|
|
|
+ //
|
|
|
|
+ // We need to walk through the return type, and for each subtype attached
|
|
|
|
+ // with semantics, write out the value to the corresponding stage variable
|
|
|
|
+ // mapped to the semantic.
|
|
|
|
+ const uint32_t stageVarId =
|
|
declIdMapper.getRemappedDeclResultId(curFunction);
|
|
declIdMapper.getRemappedDeclResultId(curFunction);
|
|
|
|
|
|
- if (interfaceVarId) {
|
|
|
|
- // The return value is mapped to an interface variable. We need to store
|
|
|
|
- // the value into the interface variable instead.
|
|
|
|
- theBuilder.createStore(interfaceVarId, retValue);
|
|
|
|
|
|
+ if (stageVarId) {
|
|
|
|
+ // The return value is mapped to a single stage variable. We just need
|
|
|
|
+ // to store the value into the stage variable instead.
|
|
|
|
+ theBuilder.createStore(stageVarId, retValue);
|
|
theBuilder.createReturn();
|
|
theBuilder.createReturn();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ QualType retType = stmt->getRetValue()->getType();
|
|
|
|
+
|
|
|
|
+ if (const auto *structType = retType->getAsStructureType()) {
|
|
|
|
+ // We are trying to return a value of struct type. Go through all fields.
|
|
|
|
+ uint32_t fieldIndex = 0;
|
|
|
|
+ for (const auto *field : structType->getDecl()->fields()) {
|
|
|
|
+ // Load the value from the current field.
|
|
|
|
+ const uint32_t valueType =
|
|
|
|
+ typeTranslator.translateType(field->getType());
|
|
|
|
+ // TODO: We may need to change the storage class accordingly.
|
|
|
|
+ const uint32_t ptrType = theBuilder.getPointerType(
|
|
|
|
+ typeTranslator.translateType(field->getType()),
|
|
|
|
+ spv::StorageClass::Function);
|
|
|
|
+ const uint32_t indexId = theBuilder.getInt32Value(fieldIndex++);
|
|
|
|
+ const uint32_t valuePtr =
|
|
|
|
+ theBuilder.createAccessChain(ptrType, retValue, {indexId});
|
|
|
|
+ const uint32_t value = theBuilder.createLoad(valueType, valuePtr);
|
|
|
|
+ // Store it to the corresponding stage variable.
|
|
|
|
+ const uint32_t targetVar = declIdMapper.getDeclResultId(field);
|
|
|
|
+ theBuilder.createStore(targetVar, value);
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
- theBuilder.createReturnValue(retValue);
|
|
|
|
|
|
+ emitError("Return type '%0' for entry function is not supported yet.")
|
|
|
|
+ << retType->getTypeClassName();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -497,32 +631,67 @@ public:
|
|
} else if (auto *castExpr = dyn_cast<ImplicitCastExpr>(expr)) {
|
|
} else if (auto *castExpr = dyn_cast<ImplicitCastExpr>(expr)) {
|
|
const uint32_t fromValue = doExpr(castExpr->getSubExpr());
|
|
const uint32_t fromValue = doExpr(castExpr->getSubExpr());
|
|
// Using lvalue as rvalue will result in a ImplicitCast in Clang AST.
|
|
// Using lvalue as rvalue will result in a ImplicitCast in Clang AST.
|
|
- // This place gives us a place to inject the code for handling interface
|
|
|
|
- // variables. Since using the <result-id> of an interface variable as
|
|
|
|
|
|
+ // This place gives us a place to inject the code for handling stage
|
|
|
|
+ // variables. Since using the <result-id> of a stage variable as
|
|
// rvalue means OpLoad it first. For normal values, it is not required.
|
|
// rvalue means OpLoad it first. For normal values, it is not required.
|
|
- if (declIdMapper.isInterfaceVariable(fromValue)) {
|
|
|
|
|
|
+ if (declIdMapper.isStageVariable(fromValue)) {
|
|
const uint32_t resultType =
|
|
const uint32_t resultType =
|
|
- translateType(castExpr->getType(), theBuilder);
|
|
|
|
|
|
+ typeTranslator.translateType(castExpr->getType());
|
|
return theBuilder.createLoad(resultType, fromValue);
|
|
return theBuilder.createLoad(resultType, fromValue);
|
|
}
|
|
}
|
|
return fromValue;
|
|
return fromValue;
|
|
|
|
+ } else if (auto *memberExpr = dyn_cast<MemberExpr>(expr)) {
|
|
|
|
+ const uint32_t base = doExpr(memberExpr->getBase());
|
|
|
|
+ auto *memberDecl = memberExpr->getMemberDecl();
|
|
|
|
+ if (auto *fieldDecl = dyn_cast<FieldDecl>(memberDecl)) {
|
|
|
|
+ const auto index = theBuilder.getInt32Value(fieldDecl->getFieldIndex());
|
|
|
|
+ const uint32_t fieldType =
|
|
|
|
+ typeTranslator.translateType(fieldDecl->getType());
|
|
|
|
+ const uint32_t ptrType =
|
|
|
|
+ theBuilder.getPointerType(fieldType, spv::StorageClass::Function);
|
|
|
|
+ return theBuilder.createAccessChain(ptrType, base, {index});
|
|
|
|
+ } else {
|
|
|
|
+ emitError("Decl '%0' in MemberExpr is not supported yet.")
|
|
|
|
+ << memberDecl->getDeclKindName();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ emitError("Expr '%0' is not supported yet.") << expr->getStmtClassName();
|
|
// TODO: handle other expressions
|
|
// TODO: handle other expressions
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+private:
|
|
|
|
+ /// \brief Wrapper method to create an error message and report it
|
|
|
|
+ /// in the diagnostic engine associated with this consumer.
|
|
|
|
+ template <unsigned N> DiagnosticBuilder emitError(const char (&message)[N]) {
|
|
|
|
+ const auto diagId =
|
|
|
|
+ diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
|
|
|
|
+ return diags.Report(diagId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// \brief Wrapper method to create a warning message and report it
|
|
|
|
+ /// in the diagnostic engine associated with this consumer
|
|
|
|
+ template <unsigned N>
|
|
|
|
+ DiagnosticBuilder emitWarning(const char (&message)[N]) {
|
|
|
|
+ const auto diagId =
|
|
|
|
+ diags.getCustomDiagID(clang::DiagnosticsEngine::Warning, message);
|
|
|
|
+ return diags.Report(diagId);
|
|
|
|
+ }
|
|
|
|
+
|
|
private:
|
|
private:
|
|
CompilerInstance &theCompilerInstance;
|
|
CompilerInstance &theCompilerInstance;
|
|
DiagnosticsEngine &diags;
|
|
DiagnosticsEngine &diags;
|
|
- spirv::SPIRVContext theContext;
|
|
|
|
- spirv::ModuleBuilder theBuilder;
|
|
|
|
- DeclResultIdMapper declIdMapper;
|
|
|
|
|
|
|
|
/// Entry function name and shader stage. Both of them are derived from the
|
|
/// Entry function name and shader stage. Both of them are derived from the
|
|
/// command line and should be const.
|
|
/// command line and should be const.
|
|
const llvm::StringRef entryFunctionName;
|
|
const llvm::StringRef entryFunctionName;
|
|
const spv::ExecutionModel shaderStage;
|
|
const spv::ExecutionModel shaderStage;
|
|
|
|
|
|
|
|
+ spirv::SPIRVContext theContext;
|
|
|
|
+ spirv::ModuleBuilder theBuilder;
|
|
|
|
+ DeclResultIdMapper declIdMapper;
|
|
|
|
+ TypeTranslator typeTranslator;
|
|
|
|
+
|
|
/// <result-id> for the entry function. Initially it is zero and will be reset
|
|
/// <result-id> for the entry function. Initially it is zero and will be reset
|
|
/// when starting to translate the entry function.
|
|
/// when starting to translate the entry function.
|
|
uint32_t entryFunctionId;
|
|
uint32_t entryFunctionId;
|