|
@@ -12,345 +12,15 @@
|
|
#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/RecordLayout.h"
|
|
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/Diagnostic.h"
|
|
-#include "clang/Basic/FileManager.h"
|
|
|
|
-#include "clang/Basic/SourceManager.h"
|
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
|
|
+#include "clang/SPIRV/DeclResultIdMapper.h"
|
|
#include "clang/SPIRV/ModuleBuilder.h"
|
|
#include "clang/SPIRV/ModuleBuilder.h"
|
|
-#include "llvm/Support/Path.h"
|
|
|
|
-#include "llvm/Support/raw_ostream.h"
|
|
|
|
|
|
+#include "clang/SPIRV/TypeTranslator.h"
|
|
|
|
+#include "llvm/ADT/STLExtras.h"
|
|
|
|
|
|
namespace clang {
|
|
namespace clang {
|
|
-namespace {
|
|
|
|
-
|
|
|
|
-/// The class responsible to translate Clang frontend types into SPIR-V type
|
|
|
|
-/// instructions.
|
|
|
|
-///
|
|
|
|
-/// 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);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- emitError("Type '%0' is not supported yet.") << type->getTypeClassName();
|
|
|
|
- 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
|
|
|
|
-/// corresponding SPIR-V <result-id>s.
|
|
|
|
-///
|
|
|
|
-/// All symbols defined in the AST should be "defined" or registered in this
|
|
|
|
-/// class and have their <result-id>s queried from this class. In the process
|
|
|
|
-/// of defining a Decl, the SPIR-V module builder passed into the constructor
|
|
|
|
-/// will be used to generate all SPIR-V instructions required.
|
|
|
|
-///
|
|
|
|
-/// This class acts as a middle layer to handle the mapping between HLSL
|
|
|
|
-/// 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
|
|
|
|
-/// 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
|
|
|
|
-/// stage variables per Vulkan's requirements.
|
|
|
|
-///
|
|
|
|
-/// 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 {
|
|
|
|
-public:
|
|
|
|
- DeclResultIdMapper(spv::ExecutionModel stage, spirv::ModuleBuilder &builder,
|
|
|
|
- DiagnosticsEngine &diag)
|
|
|
|
- : shaderStage(stage), theBuilder(builder), typeTranslator(builder, diag) {
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \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.
|
|
|
|
- createStageVariables(funcDecl, false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \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
|
|
|
|
- /// instruction.
|
|
|
|
- void registerDeclResultId(const NamedDecl *symbol, uint32_t resultId) {
|
|
|
|
- normalDecls[symbol] = resultId;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \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.
|
|
|
|
- uint32_t getDeclResultId(const NamedDecl *decl) const {
|
|
|
|
- if (const uint32_t id = getRemappedDeclResultId(decl))
|
|
|
|
- return id;
|
|
|
|
- if (const uint32_t id = getNormalDeclResultId(decl))
|
|
|
|
- return id;
|
|
|
|
-
|
|
|
|
- assert(false && "found unregistered Decl in DeclResultIdMapper");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \brief Returns the <result-id> for the given remapped Decl. Returns zero
|
|
|
|
- /// if it is not a registered remapped Decl.
|
|
|
|
- uint32_t getRemappedDeclResultId(const NamedDecl *decl) const {
|
|
|
|
- auto it = remappedDecls.find(decl);
|
|
|
|
- if (it != remappedDecls.end())
|
|
|
|
- return it->second;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \brief Returns the <result-id> for the given normal Decl. Returns zero if
|
|
|
|
- /// it is not a registered normal Decl.
|
|
|
|
- uint32_t getNormalDeclResultId(const NamedDecl *decl) const {
|
|
|
|
- auto it = normalDecls.find(decl);
|
|
|
|
- if (it != normalDecls.end())
|
|
|
|
- return it->second;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \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) {
|
|
|
|
- stageVars.push_back(input.first);
|
|
|
|
- }
|
|
|
|
- for (const auto &output : stageOutputs) {
|
|
|
|
- stageVars.push_back(output.first);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return stageVars;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// \brief Decorates all stage input and output variables with proper
|
|
|
|
- /// location.
|
|
|
|
- ///
|
|
|
|
- /// This method will writes the location assignment into the module under
|
|
|
|
- /// construction.
|
|
|
|
- void finalizeStageIOLocations() {
|
|
|
|
- uint32_t nextInputLocation = 0;
|
|
|
|
- uint32_t nextOutputLocation = 0;
|
|
|
|
-
|
|
|
|
- // TODO: sort the variables according to some criteria first, e.g.,
|
|
|
|
- // alphabetical order of semantic names.
|
|
|
|
- for (const auto &input : stageInputs) {
|
|
|
|
- theBuilder.decorateLocation(input.first, nextInputLocation++);
|
|
|
|
- }
|
|
|
|
- for (const auto &output : stageOutputs) {
|
|
|
|
- theBuilder.decorateLocation(output.first, nextOutputLocation++);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-private:
|
|
|
|
- /// \brief Stage variable kind.
|
|
|
|
- ///
|
|
|
|
- /// 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 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");
|
|
|
|
-
|
|
|
|
- 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()) {
|
|
|
|
- if (auto *semantic = dyn_cast<hlsl::SemanticDecl>(annotation)) {
|
|
|
|
- return semantic->SemanticName.upper();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return "";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-private:
|
|
|
|
- const spv::ExecutionModel shaderStage;
|
|
|
|
- spirv::ModuleBuilder &theBuilder;
|
|
|
|
- TypeTranslator typeTranslator;
|
|
|
|
-
|
|
|
|
- /// Mapping of all remapped decls to their <result-id>s.
|
|
|
|
- llvm::DenseMap<const NamedDecl *, uint32_t> remappedDecls;
|
|
|
|
- /// Mapping of all normal decls to their <result-id>s.
|
|
|
|
- llvm::DenseMap<const NamedDecl *, uint32_t> normalDecls;
|
|
|
|
- /// <result-id>s of all defined stage variables.
|
|
|
|
- ///
|
|
|
|
- /// We need to keep a separate list here to avoid looping through the
|
|
|
|
- /// 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.
|
|
|
|
- ///
|
|
|
|
- /// We need to keep a separate list here in order to sort them at the end
|
|
|
|
- /// of the module building.
|
|
|
|
- llvm::SmallVector<StageVarIdSemanticPair, 8> stageInputs;
|
|
|
|
- llvm::SmallVector<StageVarIdSemanticPair, 8> stageOutputs;
|
|
|
|
- llvm::SmallVector<StageVarIdSemanticPair, 8> stageBuiltins;
|
|
|
|
-};
|
|
|
|
|
|
+namespace spirv {
|
|
|
|
|
|
/// SPIR-V emitter class. It consumes the HLSL AST and emits SPIR-V words.
|
|
/// SPIR-V emitter class. It consumes the HLSL AST and emits SPIR-V words.
|
|
///
|
|
///
|
|
@@ -717,8 +387,8 @@ private:
|
|
const llvm::StringRef entryFunctionName;
|
|
const llvm::StringRef entryFunctionName;
|
|
const spv::ExecutionModel shaderStage;
|
|
const spv::ExecutionModel shaderStage;
|
|
|
|
|
|
- spirv::SPIRVContext theContext;
|
|
|
|
- spirv::ModuleBuilder theBuilder;
|
|
|
|
|
|
+ SPIRVContext theContext;
|
|
|
|
+ ModuleBuilder theBuilder;
|
|
DeclResultIdMapper declIdMapper;
|
|
DeclResultIdMapper declIdMapper;
|
|
TypeTranslator typeTranslator;
|
|
TypeTranslator typeTranslator;
|
|
|
|
|
|
@@ -729,10 +399,10 @@ private:
|
|
const FunctionDecl *curFunction;
|
|
const FunctionDecl *curFunction;
|
|
};
|
|
};
|
|
|
|
|
|
-} // namespace
|
|
|
|
|
|
+} // end namespace spirv
|
|
|
|
|
|
std::unique_ptr<ASTConsumer>
|
|
std::unique_ptr<ASTConsumer>
|
|
EmitSPIRVAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
EmitSPIRVAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
- return llvm::make_unique<SPIRVEmitter>(CI);
|
|
|
|
|
|
+ return llvm::make_unique<spirv::SPIRVEmitter>(CI);
|
|
}
|
|
}
|
|
} // end namespace clang
|
|
} // end namespace clang
|