diff --git a/docs/SPIR-V.rst b/docs/SPIR-V.rst index 88ff3d0d3..07ec89c0e 100644 --- a/docs/SPIR-V.rst +++ b/docs/SPIR-V.rst @@ -240,7 +240,7 @@ Normal local variables (without any modifier) will be placed in the ``Function`` ``static`` ~~~~~~~~~~ -- Global variables with ``static`` modifier will be placed in the ``Private`` SPIR-V storage class. Initalizers of such global variables will be translated into SPIR-V ``OpVariable`` initializers if possible; otherwise, they will be initialized at the very beginning of the entry function using SPIR-V ``OpStore``. +- Global variables with ``static`` modifier will be placed in the ``Private`` SPIR-V storage class. Initalizers of such global variables will be translated into SPIR-V ``OpVariable`` initializers if possible; otherwise, they will be initialized at the very beginning of the entry function wrapper using SPIR-V ``OpStore``. - Local variables with ``static`` modifier will also be placed in the ``Private`` SPIR-V storage class. initializers of such local variables will also be translated into SPIR-V ``OpVariable`` initializers if possible; otherwise, they will be initialized at the very beginning of the enclosing function. To make sure that such a local variable is only initialized once, a second boolean variable of the ``Private`` SPIR-V storage class will be generated to mark its initialization status. Type modifier @@ -421,6 +421,83 @@ Functions All functions reachable from the entry-point function will be translated into SPIR-V code. Functions not reachable from the entry-point function will be ignored. +Entry function wrapper +++++++++++++++++++++++ + +HLSL entry functions takes in parameters and returns values. These parameters and return values can have semantics attached or if they are struct type, the struct fields can have semantics attached. However, in Vulkan, the entry function must be of the ``void(void)`` signature. To handle this difference, for a given entry function ``main``, we will emit a wrapper function for it. + +The wrapper function will take the name of the source code entry function, while the source code entry function will have its name prefixed with "src.". The wrapper function reads in stage input/builtin variables created according to semantics and groups them into composites meeting the requirements of the source code entry point. Then the wrapper calls the source code entry point. The return value is extracted and components of it will be written to stage output/builtin variables created according to semantics. For example:: + + + // HLSL source code + + struct S { + bool a : A; + uint2 b: B; + float2x3 c: C; + }; + + struct T { + S x; + int y: D; + }; + + T main(T input) { + return input; + } + + // SPIR-V code + + %in_var_A = OpVariable %_ptr_Input_bool Input + %in_var_B = OpVariable %_ptr_Input_v2uint Input + %in_var_C = OpVariable %_ptr_Input_mat2v3float Input + %in_var_D = OpVariable %_ptr_Input_int Input + + %out_var_A = OpVariable %_ptr_Output_bool Output + %out_var_B = OpVariable %_ptr_Output_v2uint Output + %out_var_C = OpVariable %_ptr_Output_mat2v3float Output + %out_var_D = OpVariable %_ptr_Output_int Output + + ; Wrapper function starts + + %main = OpFunction %void None {{%\d+}} + {{%\d+}} = OpLabel + + %param_var_input = OpVariable %_ptr_Function_T Function + + ; Load stage input variables and group into the expected composite + + [[inA:%\d+]] = OpLoad %bool %in_var_A + [[inB:%\d+]] = OpLoad %v2uint %in_var_B + [[inC:%\d+]] = OpLoad %mat2v3float %in_var_C + [[inS:%\d+]] = OpCompositeConstruct %S [[inA]] [[inB]] [[inC]] + [[inD:%\d+]] = OpLoad %int %in_var_D + [[inT:%\d+]] = OpCompositeConstruct %T [[inS]] [[inD]] + OpStore %param_var_input [[inT]] + + [[ret:%\d+]] = OpFunctionCall %T %src_main %param_var_input + + ; Extract component values from the composite and store into stage output variables + + [[outS:%\d+]] = OpCompositeExtract %S [[ret]] 0 + [[outA:%\d+]] = OpCompositeExtract %bool [[outS]] 0 + OpStore %out_var_A [[outA]] + [[outB:%\d+]] = OpCompositeExtract %v2uint [[outS]] 1 + OpStore %out_var_B [[outB]] + [[outC:%\d+]] = OpCompositeExtract %mat2v3float [[outS]] 2 + OpStore %out_var_C [[outC]] + [[outD:%\d+]] = OpCompositeExtract %int [[ret]] 1 + OpStore %out_var_D [[outD]] + + OpReturn + OpFunctionEnd + + ; Source code entry point starts + + %src_main = OpFunction %T None ... + +In this way, we can concentrate all stage input/output/builtin variable manipulation in the wrapper function and handle the source code entry function just like other nomal functions. + Function parameter ++++++++++++++++++ diff --git a/tools/clang/include/clang/SPIRV/ModuleBuilder.h b/tools/clang/include/clang/SPIRV/ModuleBuilder.h index d655f54ba..910b02322 100644 --- a/tools/clang/include/clang/SPIRV/ModuleBuilder.h +++ b/tools/clang/include/clang/SPIRV/ModuleBuilder.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_SPIRV_MODULEBUILDER_H #include +#include #include "clang/SPIRV/InstBuilder.h" #include "clang/SPIRV/SPIRVContext.h" @@ -216,7 +217,8 @@ public: /// /// The corresponding pointer type of the given type will be constructed in /// this method for the variable itself. - uint32_t addStageIOVar(uint32_t type, spv::StorageClass storageClass); + uint32_t addStageIOVar(uint32_t type, spv::StorageClass storageClass, + std::string name); /// \brief Adds a stage builtin variable whose value is of the given type. /// diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 474e4fe4e..5cff2ef90 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -18,15 +18,14 @@ namespace clang { namespace spirv { -bool DeclResultIdMapper::createStageVarFromFnReturn( - const FunctionDecl *funcDecl) { - // SemanticDecl for the return value is attached to the FunctionDecl. - return createStageVars(funcDecl, true); +bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl, + uint32_t storedValue) { + return createStageVars(decl, &storedValue, false, "out.var"); } -bool DeclResultIdMapper::createStageVarFromFnParam( - const ParmVarDecl *paramDecl) { - return createStageVars(paramDecl, false); +bool DeclResultIdMapper::createStageInputVar(const ParmVarDecl *paramDecl, + uint32_t *loadedValue) { + return createStageVars(paramDecl, loadedValue, true, "in.var"); } void DeclResultIdMapper::registerDeclResultId(const NamedDecl *symbol, @@ -43,17 +42,13 @@ void DeclResultIdMapper::registerDeclResultId(const NamedDecl *symbol, sc = spv::StorageClass::Private; } } - normalDecls[symbol] = {resultId, sc}; + astDecls[symbol] = {resultId, sc}; } const DeclResultIdMapper::DeclSpirvInfo * DeclResultIdMapper::getDeclSpirvInfo(const NamedDecl *decl) const { - auto it = remappedDecls.find(decl); - if (it != remappedDecls.end()) - return &it->second; - - it = normalDecls.find(decl); - if (it != normalDecls.end()) + auto it = astDecls.find(decl); + if (it != astDecls.end()) return &it->second; return nullptr; @@ -77,14 +72,6 @@ uint32_t DeclResultIdMapper::getOrRegisterDeclResultId(const NamedDecl *decl) { return id; } -uint32_t -DeclResultIdMapper::getRemappedDeclResultId(const NamedDecl *decl) const { - auto it = remappedDecls.find(decl); - if (it != remappedDecls.end()) - return it->second.resultId; - return 0; -} - namespace { /// A class for resolving the storage class of a given Decl or Expr. class StorageClassResolver : public RecursiveASTVisitor { @@ -179,23 +166,22 @@ DeclResultIdMapper::getFnParamOrRetType(const DeclaratorDecl *decl) const { } bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl, - bool forRet) { + uint32_t *value, bool asInput, + const llvm::Twine &namePrefix) { QualType type = getFnParamOrRetType(decl); - if (type->isVoidType()) { // No stage variables will be created for void type. return true; } + const uint32_t typeId = typeTranslator.translateType(type); const llvm::StringRef semanticStr = getStageVarSemantic(decl); if (!semanticStr.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); - // TODO: fix this when supporting parameter in/out qualifiers const hlsl::DxilParamInputQual qual = - forRet ? hlsl::DxilParamInputQual::Out : hlsl::DxilParamInputQual::In; + asInput ? hlsl::DxilParamInputQual::In : hlsl::DxilParamInputQual::Out; // TODO: use the correct isPC value when supporting patch constant function // in hull shader @@ -216,11 +202,20 @@ bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl, } StageVar stageVar(sigPoint, semantic, typeId); - const uint32_t varId = createSpirvStageVar(&stageVar); + llvm::Twine name = hlsl::Semantic::HasSVPrefix(semanticStr) + // Canonicalize SV semantics + ? namePrefix + "." + semantic->GetName() + : namePrefix + "." + semanticStr; + const uint32_t varId = createSpirvStageVar(&stageVar, name); stageVar.setSpirvId(varId); stageVars.push_back(stageVar); - remappedDecls[decl] = {varId, stageVar.getStorageClass()}; + + if (asInput) { + *value = theBuilder.createLoad(typeId, varId); + } else { + theBuilder.createStore(varId, *value); + } } else { // If the decl itself doesn't have semantic, it should be a struct having // all its fields with semantics. @@ -229,17 +224,36 @@ bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl, const auto *structDecl = cast(type.getTypePtr())->getDecl(); - // Recursively handle all the fields. - for (const auto *field : structDecl->fields()) { - if (!createStageVars(field, forRet)) - return false; + if (asInput) { + // If this decl translates into multiple stage input variables, we need to + // load their values into a composite. + llvm::SmallVector subValues; + for (const auto *field : structDecl->fields()) { + uint32_t subValue = 0; + if (!createStageVars(field, &subValue, true, namePrefix)) + return false; + subValues.push_back(subValue); + } + *value = theBuilder.createCompositeConstruct(typeId, subValues); + } else { + // If this decl translates into multiple stage output variables, we need + // to store the value components into them. + for (const auto *field : structDecl->fields()) { + const uint32_t fieldType = + typeTranslator.translateType(field->getType()); + uint32_t subValue = theBuilder.createCompositeExtract( + fieldType, *value, {field->getFieldIndex()}); + if (!createStageVars(field, &subValue, false, namePrefix)) + return false; + } } } return true; } -uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) { +uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar, + const llvm::Twine &name) { using spv::BuiltIn; const auto semanticKind = stageVar->getSemantic()->GetKind(); @@ -264,7 +278,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) { case hlsl::Semantic::Kind::Position: { switch (sigPointKind) { case hlsl::SigPoint::Kind::VSIn: - return theBuilder.addStageIOVar(type, sc); + return theBuilder.addStageIOVar(type, sc, name.str()); case hlsl::SigPoint::Kind::VSOut: stageVar->setIsSpirvBuiltin(); return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::Position); @@ -290,9 +304,9 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) { stageVar->setIsSpirvBuiltin(); return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::InstanceIndex); case hlsl::SigPoint::Kind::VSOut: - return theBuilder.addStageIOVar(type, sc); + return theBuilder.addStageIOVar(type, sc, name.str()); case hlsl::SigPoint::Kind::PSIn: - return theBuilder.addStageIOVar(type, sc); + return theBuilder.addStageIOVar(type, sc, name.str()); default: emitError("semantic InstanceID for SigPoint %0 unimplemented yet") << stageVar->getSigPoint()->GetName(); @@ -310,7 +324,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) { // An arbitrary semantic is defined by users. Generate normal Vulkan stage // input/output variables. case hlsl::Semantic::Kind::Arbitrary: { - return theBuilder.addStageIOVar(type, sc); + return theBuilder.addStageIOVar(type, sc, name.str()); // TODO: patch constant function in hull shader } default: diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.h b/tools/clang/lib/SPIRV/DeclResultIdMapper.h index a33d51c97..eb06f0117 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.h +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.h @@ -20,6 +20,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Twine.h" #include "TypeTranslator.h" @@ -89,25 +90,23 @@ StageVar::StageVar(const hlsl::SigPoint *sig, const hlsl::Semantic *sema, /// 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: inline DeclResultIdMapper(const hlsl::ShaderModel &stage, ModuleBuilder &builder, DiagnosticsEngine &diag); - /// \brief Creates the stage variables by parsing the semantics attached to - /// the given function's return value and returns true on success. - bool createStageVarFromFnReturn(const FunctionDecl *funcDecl); + /// \brief Creates the stage output variables by parsing the semantics + /// attached to the given function's parameter or return value and returns + /// true on success. SPIR-V instructions will also be generated to update the + /// contents of the output variables by extracting sub-values from the given + /// storedValue. + bool createStageOutputVar(const DeclaratorDecl *decl, uint32_t storedValue); - /// \brief Creates the stage variables by parsing the semantics attached to - /// the given function's parameter and returns true on success. - bool createStageVarFromFnParam(const ParmVarDecl *paramDecl); + /// \brief Creates the stage input variables by parsing the semantics attached + /// to the given function's parameter and returns true on success. SPIR-V + /// instructions will also be generated to load the contents from the input + /// variables and composite them into one and write to *loadedValue. + bool createStageInputVar(const ParmVarDecl *paramDecl, uint32_t *loadedValue); /// \brief Registers a decl's without generating any SPIR-V /// instruction. The given decl will be treated as normal decl. @@ -134,10 +133,6 @@ public: /// assigned for it. uint32_t getOrRegisterDeclResultId(const NamedDecl *decl); - /// \brief Returns the for the given remapped decl. Returns zero - /// if it is not a registered remapped decl. - uint32_t getRemappedDeclResultId(const NamedDecl *decl) const; - /// Returns the storage class for the given expression. The expression is /// expected to be an lvalue. Otherwise this method may panic. spv::StorageClass resolveStorageClass(const Expr *expr) const; @@ -171,12 +166,14 @@ private: /// and returns true on success. /// /// Assumes the decl has semantic attached to itself or to its fields. - bool createStageVars(const DeclaratorDecl *decl, bool forReturnValue); + bool createStageVars(const DeclaratorDecl *decl, uint32_t *value, + bool asInput, const llvm::Twine &namePrefix); /// Creates the SPIR-V variable instruction for the given StageVar and returns /// the . Also sets whether the StageVar is a SPIR-V builtin and - /// its storage class accordingly. - uint32_t createSpirvStageVar(StageVar *); + /// its storage class accordingly. name will be used as the debug name when + /// creating a stage input/output variable. + uint32_t createSpirvStageVar(StageVar *, const llvm::Twine &name); /// \brief Returns the stage variable's semantic for the given Decl. static llvm::StringRef getStageVarSemantic(const NamedDecl *decl); @@ -187,10 +184,8 @@ private: TypeTranslator typeTranslator; DiagnosticsEngine &diags; - /// Mapping of all remapped decls to their s. - llvm::DenseMap remappedDecls; - /// Mapping of all normal decls to their s. - llvm::DenseMap normalDecls; + /// Mapping of all Clang AST decls to their s. + llvm::DenseMap astDecls; /// Vector of all defined stage variables. llvm::SmallVector stageVars; }; diff --git a/tools/clang/lib/SPIRV/ModuleBuilder.cpp b/tools/clang/lib/SPIRV/ModuleBuilder.cpp index b6e48ae58..44d67d7ea 100644 --- a/tools/clang/lib/SPIRV/ModuleBuilder.cpp +++ b/tools/clang/lib/SPIRV/ModuleBuilder.cpp @@ -327,11 +327,13 @@ uint32_t ModuleBuilder::getGLSLExtInstSet() { } uint32_t ModuleBuilder::addStageIOVar(uint32_t type, - spv::StorageClass storageClass) { + spv::StorageClass storageClass, + std::string name) { const uint32_t pointerType = getPointerType(type, storageClass); const uint32_t varId = theContext.takeNextId(); instBuilder.opVariable(pointerType, varId, storageClass, llvm::None).x(); theModule.addVariable(std::move(constructSite)); + theModule.addDebugName(varId, name); return varId; } diff --git a/tools/clang/lib/SPIRV/SPIRVEmitter.cpp b/tools/clang/lib/SPIRV/SPIRVEmitter.cpp index d2a44c075..9c7774703 100644 --- a/tools/clang/lib/SPIRV/SPIRVEmitter.cpp +++ b/tools/clang/lib/SPIRV/SPIRVEmitter.cpp @@ -392,57 +392,48 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) { curFunction = decl; - const llvm::StringRef funcName = decl->getName(); + std::string funcName = decl->getName(); uint32_t funcId = 0; if (funcName == entryFunctionName) { - // First create stage variables for the entry point. - if (!declIdMapper.createStageVarFromFnReturn(decl)) - return; - - for (const auto *param : decl->params()) - if (!declIdMapper.createStageVarFromFnParam(param)) - return; - - // Construct the function signature. - const uint32_t voidType = theBuilder.getVoidType(); - const uint32_t funcType = theBuilder.getFunctionType(voidType, {}); - // The entry function surely does not have pre-assigned for // it like other functions that got added to the work queue following // function calls. - funcId = theBuilder.beginFunction(funcType, voidType, funcName); + funcId = theContext.takeNextId(); + funcName = "src." + funcName; - // Record the entry function's . - entryFunctionId = funcId; + // Create wrapper for the entry function + entryFunctionId = emitEntryFunctionWrapper(decl, funcId); + if (entryFunctionId == 0) + return; } else { - const uint32_t retType = - typeTranslator.translateType(decl->getReturnType()); - - // Construct the function signature. - llvm::SmallVector paramTypes; - for (const auto *param : decl->params()) { - const uint32_t valueType = typeTranslator.translateType(param->getType()); - const uint32_t ptrType = - theBuilder.getPointerType(valueType, spv::StorageClass::Function); - paramTypes.push_back(ptrType); - } - const uint32_t funcType = theBuilder.getFunctionType(retType, paramTypes); - // Non-entry functions are added to the work queue following function // calls. We have already assigned s for it when translating // its call site. Query it here. funcId = declIdMapper.getDeclResultId(decl); - theBuilder.beginFunction(funcType, retType, funcName, funcId); + } - // Create all parameters. - for (uint32_t i = 0; i < decl->getNumParams(); ++i) { - const ParmVarDecl *paramDecl = decl->getParamDecl(i); - const uint32_t paramId = - theBuilder.addFnParam(paramTypes[i], paramDecl->getName()); - declIdMapper.registerDeclResultId(paramDecl, paramId); - } + const uint32_t retType = typeTranslator.translateType(decl->getReturnType()); + + // Construct the function signature. + llvm::SmallVector paramTypes; + for (const auto *param : decl->params()) { + const uint32_t valueType = typeTranslator.translateType(param->getType()); + const uint32_t ptrType = + theBuilder.getPointerType(valueType, spv::StorageClass::Function); + paramTypes.push_back(ptrType); + } + + const uint32_t funcType = theBuilder.getFunctionType(retType, paramTypes); + theBuilder.beginFunction(funcType, retType, funcName, funcId); + + // Create all parameters. + for (uint32_t i = 0; i < decl->getNumParams(); ++i) { + const ParmVarDecl *paramDecl = decl->getParamDecl(i); + const uint32_t paramId = + theBuilder.addFnParam(paramTypes[i], paramDecl->getName()); + declIdMapper.registerDeclResultId(paramDecl, paramId); } if (decl->hasBody()) { @@ -450,12 +441,6 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) { const uint32_t entryLabel = theBuilder.createBasicBlock("bb.entry"); theBuilder.setInsertPoint(entryLabel); - // Initialize all global variables at the beginning of the entry function - if (funcId == entryFunctionId) - for (const VarDecl *varDecl : toInitGloalVars) - theBuilder.createStore(declIdMapper.getDeclResultId(varDecl), - doExpr(varDecl->getInit())); - // Process all statments in the body. doStmt(decl->getBody()); @@ -951,7 +936,10 @@ void SPIRVEmitter::doIfStmt(const IfStmt *ifStmt) { } void SPIRVEmitter::doReturnStmt(const ReturnStmt *stmt) { - processReturnStmt(stmt); + if (const auto *retVal = stmt->getRetValue()) + theBuilder.createReturnValue(doExpr(retVal)); + else + theBuilder.createReturn(); // Handle early returns if (!isLastStmtBeforeControlFlowBranching(astContext, stmt)) { @@ -961,70 +949,6 @@ void SPIRVEmitter::doReturnStmt(const ReturnStmt *stmt) { } } -void SPIRVEmitter::processReturnStmt(const ReturnStmt *stmt) { - // For normal functions, just return in the normal way. - if (curFunction->getName() != entryFunctionName) { - theBuilder.createReturnValue(doExpr(stmt->getRetValue())); - 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); - - 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, doExpr(stmt->getRetValue())); - theBuilder.createReturn(); - return; - } - - // RetValue is nullptr when "return;" is used for a void function. - if (!stmt->getRetValue()) { - theBuilder.createReturn(); - return; - } - - QualType retType = stmt->getRetValue()->getType(); - - if (const auto *structType = retType->getAsStructureType()) { - // We are trying to return a value of struct type. - - // First get the return value. Clang AST will use an LValueToRValue cast - // for returning a struct variable. We need to ignore the cast to avoid - // creating OpLoad instruction since we need the pointer to the variable - // for creating access chain later. - const Expr *retValue = stmt->getRetValue()->IgnoreParenLValueCasts(); - - // Then 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()); - const uint32_t ptrType = theBuilder.getPointerType( - typeTranslator.translateType(field->getType()), - declIdMapper.resolveStorageClass(retValue)); - const uint32_t indexId = theBuilder.getConstantInt32(fieldIndex++); - const uint32_t valuePtr = - theBuilder.createAccessChain(ptrType, doExpr(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 { - emitError("Return type '%0' for entry function is not supported yet.") - << retType->getTypeClassName(); - } -} - bool SPIRVEmitter::breakStmtIsLastStmtInCaseStmt(const BreakStmt *breakStmt, const SwitchStmt *switchStmt) { std::vector flatSwitch; @@ -1596,6 +1520,7 @@ uint32_t SPIRVEmitter::doMemberExpr(const MemberExpr *expr) { const uint32_t fieldType = typeTranslator.translateType(expr->getType()); const uint32_t ptrType = theBuilder.getPointerType( fieldType, declIdMapper.resolveStorageClass(baseExpr)); + return theBuilder.createAccessChain(ptrType, base, indices); } @@ -3176,6 +3101,63 @@ void SPIRVEmitter::AddExecutionModeForEntryPoint(uint32_t entryPointId) { } } +uint32_t SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, + uint32_t entryFuncId) { + // Construct the wrapper function signature. + const uint32_t voidType = theBuilder.getVoidType(); + const uint32_t funcType = theBuilder.getFunctionType(voidType, {}); + + // The wrapper entry function surely does not have pre-assigned + // for it like other functions that got added to the work queue following + // function calls. + const uint32_t funcId = + theBuilder.beginFunction(funcType, voidType, decl->getName()); + + // The entry basic block. + const uint32_t entryLabel = theBuilder.createBasicBlock(); + theBuilder.setInsertPoint(entryLabel); + + // Initialize all global variables at the beginning of the entry function + for (const VarDecl *varDecl : toInitGloalVars) + theBuilder.createStore(declIdMapper.getDeclResultId(varDecl), + doExpr(varDecl->getInit())); + + // Create and read stage input variables + // TODO: handle out/inout modifier + llvm::SmallVector params; + for (const auto *param : decl->params()) { + // Create stage variable(s) from this parameter and load the composite value + uint32_t loadedValue = 0; + if (!declIdMapper.createStageInputVar(param, &loadedValue)) + return 0; + + // Create a temporary variable to hold the composite value of the stage + // variable + const uint32_t typeId = typeTranslator.translateType(param->getType()); + std::string tempVarName = "param.var." + param->getNameAsString(); + const uint32_t tempVar = theBuilder.addFnVar(typeId, tempVarName); + theBuilder.createStore(tempVar, loadedValue); + + params.push_back(tempVar); + } + + // Call the original entry function + const uint32_t retType = typeTranslator.translateType(decl->getReturnType()); + const uint32_t retVal = + theBuilder.createFunctionCall(retType, entryFuncId, params); + + // Create and write stage output variables + // TODO: handle out/inout modifier + + if (!declIdMapper.createStageOutputVar(decl, retVal)) + return 0; + + theBuilder.createReturn(); + theBuilder.endFunction(); + + return funcId; +} + bool SPIRVEmitter::allSwitchCasesAreIntegerLiterals(const Stmt *root) { if (!root) return false; diff --git a/tools/clang/lib/SPIRV/SPIRVEmitter.h b/tools/clang/lib/SPIRV/SPIRVEmitter.h index cd3fb83ba..2a3de1ffa 100644 --- a/tools/clang/lib/SPIRV/SPIRVEmitter.h +++ b/tools/clang/lib/SPIRV/SPIRVEmitter.h @@ -310,6 +310,19 @@ private: /// shader model. void AddExecutionModeForEntryPoint(uint32_t entryPointId); + /// \brief Emits a wrapper function for the entry function. + /// + /// The wrapper function loads the values of all stage input variables and + /// creates composites as expected by the source code entry function. It then + /// calls the source code entry point and writes out stage output variables + /// by extracting sub-values from the return value. In this way, we can handle + /// the source code entry point as a normal function. + /// + /// The wrapper function is also responsible for initializing global static + /// variables for some cases. + uint32_t emitEntryFunctionWrapper(const FunctionDecl *entryFunction, + uint32_t entryFuncId); + private: /// \brief Returns true iff *all* the case values in the given switch /// statement are integer literals. In such cases OpSwitch can be used to diff --git a/tools/clang/test/CodeGenSPIRV/break-stmt.mixed.hlsl b/tools/clang/test/CodeGenSPIRV/break-stmt.mixed.hlsl index 8397570eb..cfa424c98 100644 --- a/tools/clang/test/CodeGenSPIRV/break-stmt.mixed.hlsl +++ b/tools/clang/test/CodeGenSPIRV/break-stmt.mixed.hlsl @@ -42,8 +42,7 @@ void main() { // CHECK-NEXT: OpBranch %for_check default: // CHECK-NEXT: %for_check = OpLabel - // CHECK-NEXT: {{%\d+}} = OpLoad %int %i - // CHECK-NEXT: [[i_lt_10:%\d+]] = OpSLessThan %bool %37 %int_10 + // CHECK: [[i_lt_10:%\d+]] = OpSLessThan %bool {{%\d+}} %int_10 // CHECK-NEXT: OpLoopMerge %for_merge %for_continue None // CHECK-NEXT: OpBranchConditional [[i_lt_10]] %for_body %for_merge for (int i=0; i<10; ++i) { @@ -79,7 +78,7 @@ void main() { // CHECK-NEXT: OpBranch %while_merge break; // Break the while loop. - + // CHECK-NEXT: %while_continue = OpLabel // CHECK-NEXT: OpBranch %while_check } diff --git a/tools/clang/test/CodeGenSPIRV/cf.if.for.hlsl b/tools/clang/test/CodeGenSPIRV/cf.if.for.hlsl index ef60d1acf..67f7e721b 100644 --- a/tools/clang/test/CodeGenSPIRV/cf.if.for.hlsl +++ b/tools/clang/test/CodeGenSPIRV/cf.if.for.hlsl @@ -1,9 +1,5 @@ // Run: %dxc -T ps_6_0 -E main -// Stage IO variables -// CHECK-DAG: [[color:%\d+]] = OpVariable %_ptr_Input_float Input -// CHECK-DAG: [[target:%\d+]] = OpVariable %_ptr_Output_v4float Output - float4 main(float color: COLOR) : SV_TARGET { // CHECK-LABEL: %bb_entry = OpLabel @@ -13,7 +9,7 @@ float4 main(float color: COLOR) : SV_TARGET { // CHECK-NEXT: %j = OpVariable %_ptr_Function_int Function %int_0 // CHECK-NEXT: %k = OpVariable %_ptr_Function_int Function %int_0 -// CHECK-NEXT: [[color0:%\d+]] = OpLoad %float [[color]] +// CHECK-NEXT: [[color0:%\d+]] = OpLoad %float %color // CHECK-NEXT: [[lt0:%\d+]] = OpFOrdLessThan %bool [[color0]] %float_0_3 // CHECK-NEXT: OpSelectionMerge %if_merge None // CHECK-NEXT: OpBranchConditional [[lt0]] %if_true %if_merge @@ -34,15 +30,13 @@ float4 main(float color: COLOR) : SV_TARGET { // CHECK-NEXT: OpBranchConditional [[lt1]] %for_body %for_merge for (int i = 0; i < 10; ++i) { // CHECK-LABEL: %for_body = OpLabel -// CHECK-NEXT: [[color1:%\d+]] = OpLoad %float [[color]] +// CHECK-NEXT: [[color1:%\d+]] = OpLoad %float %color // CHECK-NEXT: [[lt2:%\d+]] = OpFOrdLessThan %bool [[color1]] %float_0_5 // CHECK-NEXT: OpSelectionMerge %if_merge_0 None // CHECK-NEXT: OpBranchConditional [[lt2]] %if_true_0 %if_merge_0 if (color < 0.5) { // if-stmt nested in for-stmt // CHECK-LABEL: %if_true_0 = OpLabel -// CHECK-NEXT: [[val0:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[add1:%\d+]] = OpFAdd %float [[val0]] %float_1 -// CHECK-NEXT: OpStore %val [[add1]] +// CHECK: OpStore %val val = val + 1.; // CHECK-NEXT: OpBranch %for_check_0 @@ -53,9 +47,7 @@ float4 main(float color: COLOR) : SV_TARGET { // CHECK-NEXT: OpBranchConditional [[lt3]] %for_body_0 %for_merge_0 for (int j = 0; j < 15; ++j) { // for-stmt deeply nested in if-then // CHECK-LABEL: %for_body_0 = OpLabel -// CHECK-NEXT: [[val1:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[mul2:%\d+]] = OpFMul %float [[val1]] %float_2 -// CHECK-NEXT: OpStore %val [[mul2]] +// CHECK: OpStore %val val = val * 2.; // CHECK-NEXT: OpBranch %for_continue_0 @@ -66,24 +58,20 @@ float4 main(float color: COLOR) : SV_TARGET { // CHECK-NEXT: OpBranch %for_check_0 } // end for (int j // CHECK-LABEL: %for_merge_0 = OpLabel -// CHECK-NEXT: [[val2:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[add3:%\d+]] = OpFAdd %float [[val2]] %float_3 -// CHECK-NEXT: OpStore %val [[add3]] +// CHECK: OpStore %val val = val + 3.; // CHECK-NEXT: OpBranch %if_merge_0 } // CHECK-LABEL: %if_merge_0 = OpLabel -// CHECK-NEXT: [[color2:%\d+]] = OpLoad %float [[color]] +// CHECK-NEXT: [[color2:%\d+]] = OpLoad %float %color // CHECK-NEXT: [[lt4:%\d+]] = OpFOrdLessThan %bool [[color2]] %float_0_8 // CHECK-NEXT: OpSelectionMerge %if_merge_1 None // CHECK-NEXT: OpBranchConditional [[lt4]] %if_true_1 %if_false if (color < 0.8) { // if-stmt following if-stmt // CHECK-LABEL: %if_true_1 = OpLabel -// CHECK-NEXT: [[val3:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[mul4:%\d+]] = OpFMul %float [[val3]] %float_4 -// CHECK-NEXT: OpStore %val [[mul4]] +// CHECK: OpStore %val val = val * 4.; // CHECK-NEXT: OpBranch %if_merge_1 } else { @@ -97,9 +85,7 @@ float4 main(float color: COLOR) : SV_TARGET { // CHECK-NEXT: OpBranchConditional [[lt5]] %for_body_1 %for_merge_1 for (int k = 0; k < 20; ++k) { // for-stmt deeply nested in if-else // CHECK-LABEL: %for_body_1 = OpLabel -// CHECK-NEXT: [[val4:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[sub5:%\d+]] = OpFSub %float [[val4]] %float_5 -// CHECK-NEXT: OpStore %val [[sub5]] +// CHECK: OpStore %val val = val - 5.; // CHECK-NEXT: [[val5:%\d+]] = OpLoad %float %val @@ -108,9 +94,7 @@ float4 main(float color: COLOR) : SV_TARGET { // CHECK-NEXT: OpBranchConditional [[lt6]] %if_true_2 %if_merge_2 if (val < 0.) { // deeply nested if-stmt // CHECK-LABEL: %if_true_2 = OpLabel -// CHECK-NEXT: [[val6:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[add100:%\d+]] = OpFAdd %float [[val6]] %float_100 -// CHECK-NEXT: OpStore %val [[add100]] +// CHECK: OpStore %val val = val + 100.; // CHECK-NEXT: OpBranch %if_merge_2 } @@ -138,27 +122,19 @@ float4 main(float color: COLOR) : SV_TARGET { // CHECK-LABEL: %for_merge = OpLabel // if-stmt following for-stmt -// CHECK-NEXT: [[color3:%\d+]] = OpLoad %float [[color]] +// CHECK-NEXT: [[color3:%\d+]] = OpLoad %float %color // CHECK-NEXT: [[lt7:%\d+]] = OpFOrdLessThan %bool [[color3]] %float_0_9 // CHECK-NEXT: OpSelectionMerge %if_merge_3 None // CHECK-NEXT: OpBranchConditional [[lt7]] %if_true_3 %if_merge_3 if (color < 0.9) { // CHECK-LABEL: %if_true_3 = OpLabel -// CHECK-NEXT: [[val7:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[add6:%\d+]] = OpFAdd %float [[val7]] %float_6 -// CHECK-NEXT: OpStore %val [[add6]] +// CHECK: OpStore %val val = val + 6.; // CHECK-NEXT: OpBranch %if_merge_3 } // CHECK-LABEL: %if_merge_3 = OpLabel -// CHECK-NEXT: [[comp0:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[comp1:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[comp2:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[comp3:%\d+]] = OpLoad %float %val -// CHECK-NEXT: [[ret:%\d+]] = OpCompositeConstruct %v4float [[comp0]] [[comp1]] [[comp2]] [[comp3]] -// CHECK-NEXT: OpStore [[target]] [[ret]] -// CHECK-NEXT: OpReturn +// CHECK: OpReturnValue return float4(val, val, val, val); // CHECK-NEXT: OpFunctionEnd } diff --git a/tools/clang/test/CodeGenSPIRV/constant-ps.hlsl2spv b/tools/clang/test/CodeGenSPIRV/constant-ps.hlsl2spv index 9746e9e31..cff122786 100644 --- a/tools/clang/test/CodeGenSPIRV/constant-ps.hlsl2spv +++ b/tools/clang/test/CodeGenSPIRV/constant-ps.hlsl2spv @@ -8,28 +8,36 @@ float4 main(): SV_TARGET // ; SPIR-V // ; Version: 1.0 // ; Generator: Google spiregg; 0 -// ; Bound: 14 +// ; Bound: 18 // ; Schema: 0 // OpCapability Shader // OpMemoryModel Logical GLSL450 -// OpEntryPoint Fragment %main "main" %4 +// OpEntryPoint Fragment %main "main" %out_var_SV_Target // OpExecutionMode %main OriginUpperLeft // OpName %main "main" +// OpName %out_var_SV_Target "out.var.SV_Target" +// OpName %src_main "src.main" // OpName %bb_entry "bb.entry" -// OpDecorate %4 Location 0 +// OpDecorate %out_var_SV_Target Location 0 +// %void = OpTypeVoid +// %3 = OpTypeFunction %void // %float = OpTypeFloat 32 // %v4float = OpTypeVector %float 4 // %_ptr_Output_v4float = OpTypePointer Output %v4float -// %void = OpTypeVoid -// %6 = OpTypeFunction %void +// %11 = OpTypeFunction %v4float // %float_1 = OpConstant %float 1 // %float_2 = OpConstant %float 2 // %float_3_5 = OpConstant %float 3.5 // %float_4_7 = OpConstant %float 4.7 -// %13 = OpConstantComposite %v4float %float_1 %float_2 %float_3_5 %float_4_7 -// %4 = OpVariable %_ptr_Output_v4float Output -// %main = OpFunction %void None %6 -// %bb_entry = OpLabel -// OpStore %4 %13 +// %17 = OpConstantComposite %v4float %float_1 %float_2 %float_3_5 %float_4_7 +// %out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +// %main = OpFunction %void None %3 +// %5 = OpLabel +// %8 = OpFunctionCall %v4float %src_main +// OpStore %out_var_SV_Target %8 // OpReturn +// OpFunctionEnd +// %src_main = OpFunction %v4float None %11 +// %bb_entry = OpLabel +// OpReturnValue %17 // OpFunctionEnd \ No newline at end of file diff --git a/tools/clang/test/CodeGenSPIRV/empty-void-main.hlsl2spv b/tools/clang/test/CodeGenSPIRV/empty-void-main.hlsl2spv index 3644a1a87..e52db3652 100644 --- a/tools/clang/test/CodeGenSPIRV/empty-void-main.hlsl2spv +++ b/tools/clang/test/CodeGenSPIRV/empty-void-main.hlsl2spv @@ -8,17 +8,23 @@ void main() // ; SPIR-V // ; Version: 1.0 // ; Generator: Google spiregg; 0 -// ; Bound: 5 +// ; Bound: 8 // ; Schema: 0 // OpCapability Shader // OpMemoryModel Logical GLSL450 // OpEntryPoint Fragment %main "main" // OpExecutionMode %main OriginUpperLeft // OpName %main "main" +// OpName %src_main "src.main" // OpName %bb_entry "bb.entry" // %void = OpTypeVoid -// %2 = OpTypeFunction %void -// %main = OpFunction %void None %2 +// %3 = OpTypeFunction %void +// %main = OpFunction %void None %3 +// %5 = OpLabel +// %6 = OpFunctionCall %void %src_main +// OpReturn +// OpFunctionEnd +// %src_main = OpFunction %void None %3 // %bb_entry = OpLabel // OpReturn // OpFunctionEnd \ No newline at end of file diff --git a/tools/clang/test/CodeGenSPIRV/passthru-ps.hlsl2spv b/tools/clang/test/CodeGenSPIRV/passthru-ps.hlsl2spv index fc2bcf8a6..8bc27be02 100644 --- a/tools/clang/test/CodeGenSPIRV/passthru-ps.hlsl2spv +++ b/tools/clang/test/CodeGenSPIRV/passthru-ps.hlsl2spv @@ -9,27 +9,43 @@ float4 main(float4 input: COLOR): SV_TARGET // ; SPIR-V // ; Version: 1.0 // ; Generator: Google spiregg; 0 -// ; Bound: 12 +// ; Bound: 20 // ; Schema: 0 // OpCapability Shader // OpMemoryModel Logical GLSL450 -// OpEntryPoint Fragment %main "main" %4 %6 +// OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_Target // OpExecutionMode %main OriginUpperLeft // OpName %main "main" +// OpName %in_var_COLOR "in.var.COLOR" +// OpName %param_var_input "param.var.input" +// OpName %out_var_SV_Target "out.var.SV_Target" +// OpName %src_main "src.main" +// OpName %input "input" // OpName %bb_entry "bb.entry" -// OpDecorate %4 Location 0 -// OpDecorate %6 Location 0 +// OpDecorate %in_var_COLOR Location 0 +// OpDecorate %out_var_SV_Target Location 0 +// %void = OpTypeVoid +// %3 = OpTypeFunction %void // %float = OpTypeFloat 32 // %v4float = OpTypeVector %float 4 -// %_ptr_Output_v4float = OpTypePointer Output %v4float // %_ptr_Input_v4float = OpTypePointer Input %v4float -// %void = OpTypeVoid -// %8 = OpTypeFunction %void -// %4 = OpVariable %_ptr_Output_v4float Output -// %6 = OpVariable %_ptr_Input_v4float Input -// %main = OpFunction %void None %8 -// %bb_entry = OpLabel -// %11 = OpLoad %v4float %6 -// OpStore %4 %11 +// %_ptr_Function_v4float = OpTypePointer Function %v4float +// %_ptr_Output_v4float = OpTypePointer Output %v4float +// %16 = OpTypeFunction %v4float %_ptr_Function_v4float +// %in_var_COLOR = OpVariable %_ptr_Input_v4float Input +// %out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +// %main = OpFunction %void None %3 +// %5 = OpLabel +// %param_var_input = OpVariable %_ptr_Function_v4float Function +// %10 = OpLoad %v4float %in_var_COLOR +// OpStore %param_var_input %10 +// %13 = OpFunctionCall %v4float %src_main %param_var_input +// OpStore %out_var_SV_Target %13 // OpReturn +// OpFunctionEnd +// %src_main = OpFunction %v4float None %16 +// %input = OpFunctionParameter %_ptr_Function_v4float +// %bb_entry = OpLabel +// %19 = OpLoad %v4float %input +// OpReturnValue %19 // OpFunctionEnd \ No newline at end of file diff --git a/tools/clang/test/CodeGenSPIRV/passthru-vs.hlsl2spv b/tools/clang/test/CodeGenSPIRV/passthru-vs.hlsl2spv index d2feccee9..536c8070b 100644 --- a/tools/clang/test/CodeGenSPIRV/passthru-vs.hlsl2spv +++ b/tools/clang/test/CodeGenSPIRV/passthru-vs.hlsl2spv @@ -17,51 +17,72 @@ PSInput VSmain(float4 position: POSITION, float4 color: COLOR) { // ; SPIR-V // ; Version: 1.0 // ; Generator: Google spiregg; 0 -// ; Bound: 28 +// ; Bound: 37 // ; Schema: 0 // OpCapability Shader // OpMemoryModel Logical GLSL450 -// OpEntryPoint Vertex %VSmain "VSmain" %gl_Position %5 %7 %8 +// OpEntryPoint Vertex %VSmain "VSmain" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR // OpName %VSmain "VSmain" -// OpName %bb_entry "bb.entry" +// OpName %in_var_POSITION "in.var.POSITION" +// OpName %param_var_position "param.var.position" +// OpName %in_var_COLOR "in.var.COLOR" +// OpName %param_var_color "param.var.color" // OpName %PSInput "PSInput" // OpMemberName %PSInput 0 "position" // OpMemberName %PSInput 1 "color" +// OpName %out_var_COLOR "out.var.COLOR" +// OpName %src_VSmain "src.VSmain" +// OpName %position "position" +// OpName %color "color" +// OpName %bb_entry "bb.entry" // OpName %result "result" // OpDecorate %gl_Position BuiltIn Position -// OpDecorate %5 Location 0 -// OpDecorate %7 Location 0 -// OpDecorate %8 Location 1 +// OpDecorate %in_var_POSITION Location 0 +// OpDecorate %in_var_COLOR Location 1 +// OpDecorate %out_var_COLOR Location 0 // %int = OpTypeInt 32 1 +// %void = OpTypeVoid +// %3 = OpTypeFunction %void // %float = OpTypeFloat 32 // %v4float = OpTypeVector %float 4 -// %_ptr_Output_v4float = OpTypePointer Output %v4float // %_ptr_Input_v4float = OpTypePointer Input %v4float -// %void = OpTypeVoid -// %10 = OpTypeFunction %void -// %PSInput = OpTypeStruct %v4float %v4float -// %_ptr_Function_PSInput = OpTypePointer Function %PSInput // %_ptr_Function_v4float = OpTypePointer Function %v4float +// %PSInput = OpTypeStruct %v4float %v4float +// %_ptr_Output_v4float = OpTypePointer Output %v4float +// %23 = OpTypeFunction %PSInput %_ptr_Function_v4float %_ptr_Function_v4float +// %_ptr_Function_PSInput = OpTypePointer Function %PSInput // %int_0 = OpConstant %int 0 // %int_1 = OpConstant %int 1 +// %in_var_POSITION = OpVariable %_ptr_Input_v4float Input +// %in_var_COLOR = OpVariable %_ptr_Input_v4float Input // %gl_Position = OpVariable %_ptr_Output_v4float Output -// %5 = OpVariable %_ptr_Output_v4float Output -// %7 = OpVariable %_ptr_Input_v4float Input -// %8 = OpVariable %_ptr_Input_v4float Input -// %VSmain = OpFunction %void None %10 +// %out_var_COLOR = OpVariable %_ptr_Output_v4float Output +// %VSmain = OpFunction %void None %3 +// %5 = OpLabel +// %param_var_position = OpVariable %_ptr_Function_v4float Function +// %param_var_color = OpVariable %_ptr_Function_v4float Function +// %10 = OpLoad %v4float %in_var_POSITION +// OpStore %param_var_position %10 +// %14 = OpLoad %v4float %in_var_COLOR +// OpStore %param_var_color %14 +// %17 = OpFunctionCall %PSInput %src_VSmain %param_var_position %param_var_color +// %18 = OpCompositeExtract %v4float %17 0 +// OpStore %gl_Position %18 +// %21 = OpCompositeExtract %v4float %17 1 +// OpStore %out_var_COLOR %21 +// OpReturn +// OpFunctionEnd +// %src_VSmain = OpFunction %PSInput None %23 +// %position = OpFunctionParameter %_ptr_Function_v4float +// %color = OpFunctionParameter %_ptr_Function_v4float // %bb_entry = OpLabel // %result = OpVariable %_ptr_Function_PSInput Function -// %16 = OpLoad %v4float %7 -// %20 = OpAccessChain %_ptr_Function_v4float %result %int_0 -// OpStore %20 %16 -// %21 = OpLoad %v4float %8 -// %23 = OpAccessChain %_ptr_Function_v4float %result %int_1 -// OpStore %23 %21 -// %24 = OpAccessChain %_ptr_Function_v4float %result %int_0 -// %25 = OpLoad %v4float %24 -// OpStore %gl_Position %25 -// %26 = OpAccessChain %_ptr_Function_v4float %result %int_1 -// %27 = OpLoad %v4float %26 -// OpStore %5 %27 -// OpReturn +// %29 = OpLoad %v4float %position +// %32 = OpAccessChain %_ptr_Function_v4float %result %int_0 +// OpStore %32 %29 +// %33 = OpLoad %v4float %color +// %35 = OpAccessChain %_ptr_Function_v4float %result %int_1 +// OpStore %35 %33 +// %36 = OpLoad %PSInput %result +// OpReturnValue %36 // OpFunctionEnd \ No newline at end of file diff --git a/tools/clang/test/CodeGenSPIRV/semantic.arbitrary.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.arbitrary.hlsl index 8cc5f4af3..5ebdf55ba 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.arbitrary.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.arbitrary.hlsl @@ -1,16 +1,16 @@ // Run: %dxc -T vs_6_0 -E main -// CHECK: OpEntryPoint Vertex %main "main" [[out:%\d+]] [[a:%\d+]] [[b:%\d+]] [[c:%\d+]] +// CHECK: OpEntryPoint Vertex %main "main" %in_var_AAA %in_var_B %in_var_CC %out_var_DDDD -// CHECK: OpDecorate [[out]] Location 0 -// CHECK: OpDecorate [[a]] Location 0 -// CHECK: OpDecorate [[b]] Location 1 -// CHECK: OpDecorate [[c]] Location 2 +// CHECK: OpDecorate %in_var_AAA Location 0 +// CHECK: OpDecorate %in_var_B Location 1 +// CHECK: OpDecorate %in_var_CC Location 2 +// CHECK: OpDecorate %out_var_DDDD Location 0 -// CHECK: [[out]] = OpVariable %_ptr_Output_float Output -// CHECK: [[a]] = OpVariable %_ptr_Input_v4float Input -// CHECK: [[b]] = OpVariable %_ptr_Input_int Input -// CHECK: [[c]] = OpVariable %_ptr_Input_mat2v3float Input +// CHECK: %in_var_AAA = OpVariable %_ptr_Input_v4float Input +// CHECK: %in_var_B = OpVariable %_ptr_Input_int Input +// CHECK: %in_var_CC = OpVariable %_ptr_Input_mat2v3float Input +// CHECK: %out_var_DDDD = OpVariable %_ptr_Output_float Output float main(float4 a: AAA, int b: B, float2x3 c: CC) : DDDD { return 1.0; diff --git a/tools/clang/test/CodeGenSPIRV/semantic.depth.ps.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.depth.ps.hlsl index f8945c909..6921335b2 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.depth.ps.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.depth.ps.hlsl @@ -1,6 +1,6 @@ // Run: %dxc -T ps_6_0 -E main -// CHECK: OpEntryPoint Fragment %main "main" %gl_FragDepth {{%\d+}} +// CHECK: OpEntryPoint Fragment %main "main" %in_var_A %gl_FragDepth // CHECK: OpDecorate %gl_FragDepth BuiltIn FragDepth diff --git a/tools/clang/test/CodeGenSPIRV/semantic.instance-id.ps.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.instance-id.ps.hlsl index ebf97f58f..e948101dc 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.instance-id.ps.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.instance-id.ps.hlsl @@ -1,10 +1,10 @@ // Run: %dxc -T ps_6_0 -E main -// CHECK: OpEntryPoint Fragment %main "main" {{%\d+}} [[input:%\d+]] +// CHECK: OpEntryPoint Fragment %main "main" %in_var_SV_InstanceID %out_var_SV_Target -// CHECK: OpDecorate [[input]] Location 0 +// CHECK: OpDecorate %in_var_SV_InstanceID Location 0 -// CHECK: [[input]] = OpVariable %_ptr_Input_int Input +// CHECK: %in_var_SV_InstanceID = OpVariable %_ptr_Input_int Input float4 main(int input: SV_InstanceID) : SV_Target { return input; diff --git a/tools/clang/test/CodeGenSPIRV/semantic.instance-id.vs.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.instance-id.vs.hlsl index 43aa4bd05..13c6bc088 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.instance-id.vs.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.instance-id.vs.hlsl @@ -1,12 +1,12 @@ // Run: %dxc -T vs_6_0 -E main -// CHECK: OpEntryPoint Vertex %main "main" [[output:%\d+]] %gl_InstanceIndex +// CHECK: OpEntryPoint Vertex %main "main" %gl_InstanceIndex %out_var_SV_InstanceID // CHECK: OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex -// CHECK: OpDecorate [[output]] Location 0 +// CHECK: OpDecorate %out_var_SV_InstanceID Location 0 -// CHECK: [[output]] = OpVariable %_ptr_Output_int Output // CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_int Input +// CHECK: %out_var_SV_InstanceID = OpVariable %_ptr_Output_int Output int main(int input: SV_InstanceID) : SV_InstanceID { return input; diff --git a/tools/clang/test/CodeGenSPIRV/semantic.position.ps.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.position.ps.hlsl index 687922673..04ee92fe5 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.position.ps.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.position.ps.hlsl @@ -1,6 +1,6 @@ // Run: %dxc -T ps_6_0 -E main -// CHECK: OpEntryPoint Fragment %main "main" {{%\d+}} %gl_FragCoord +// CHECK: OpEntryPoint Fragment %main "main" %gl_FragCoord %out_var_SV_Target // CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord diff --git a/tools/clang/test/CodeGenSPIRV/semantic.position.vs.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.position.vs.hlsl index bebc32cca..ce83d1682 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.position.vs.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.position.vs.hlsl @@ -1,12 +1,12 @@ // Run: %dxc -T vs_6_0 -E main -// CHECK: OpEntryPoint Vertex %main "main" %gl_Position [[input:%\d+]] +// CHECK: OpEntryPoint Vertex %main "main" %in_var_SV_Position %gl_Position // CHECK: OpDecorate %gl_Position BuiltIn Position -// CHECK: OpDecorate [[input]] Location 0 +// CHECK: OpDecorate %in_var_SV_Position Location 0 +// CHECK: %in_var_SV_Position = OpVariable %_ptr_Input_v4float Input // CHECK: %gl_Position = OpVariable %_ptr_Output_v4float Output -// CHECK: [[input]] = OpVariable %_ptr_Input_v4float Input float4 main(float4 input: SV_Position) : SV_Position { return input; diff --git a/tools/clang/test/CodeGenSPIRV/semantic.target.ps.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.target.ps.hlsl index 3bf4dcfe1..1fc61118c 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.target.ps.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.target.ps.hlsl @@ -1,10 +1,10 @@ // Run: %dxc -T ps_6_0 -E main -// CHECK: OpEntryPoint Fragment %main "main" [[target:%\d+]] {{%\d+}} +// CHECK: OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_Target -// CHECK: OpDecorate [[target]] Location 0 +// CHECK: OpDecorate %out_var_SV_Target Location 0 -// CHECK: [[target]] = OpVariable %_ptr_Output_v4float Output +// CHECK: %out_var_SV_Target = OpVariable %_ptr_Output_v4float Output float4 main(float4 input: A) : SV_Target { return input; diff --git a/tools/clang/test/CodeGenSPIRV/semantic.vertex-id.vs.hlsl b/tools/clang/test/CodeGenSPIRV/semantic.vertex-id.vs.hlsl index 02f6572e4..2fb47af50 100644 --- a/tools/clang/test/CodeGenSPIRV/semantic.vertex-id.vs.hlsl +++ b/tools/clang/test/CodeGenSPIRV/semantic.vertex-id.vs.hlsl @@ -1,6 +1,6 @@ // Run: %dxc -T vs_6_0 -E main -// CHECK: OpEntryPoint Vertex %main "main" {{%\d+}} %gl_VertexIndex +// CHECK: OpEntryPoint Vertex %main "main" %gl_VertexIndex %out_var_A // CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex diff --git a/tools/clang/test/CodeGenSPIRV/spirv.entry-function.wrapper.hlsl b/tools/clang/test/CodeGenSPIRV/spirv.entry-function.wrapper.hlsl new file mode 100644 index 000000000..4807307d3 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/spirv.entry-function.wrapper.hlsl @@ -0,0 +1,51 @@ +// Run: %dxc -T vs_6_0 -E main + +struct S { + bool a : A; + uint2 b: B; + float2x3 c: C; +}; + +struct T { + S x; + int y: D; +}; + +// CHECK: %in_var_A = OpVariable %_ptr_Input_bool Input +// CHECK-NEXT: %in_var_B = OpVariable %_ptr_Input_v2uint Input +// CHECK-NEXT: %in_var_C = OpVariable %_ptr_Input_mat2v3float Input +// CHECK-NEXT: %in_var_D = OpVariable %_ptr_Input_int Input + +// CHECK-NEXT: %out_var_A = OpVariable %_ptr_Output_bool Output +// CHECK-NEXT: %out_var_B = OpVariable %_ptr_Output_v2uint Output +// CHECK-NEXT: %out_var_C = OpVariable %_ptr_Output_mat2v3float Output +// CHECK-NEXT: %out_var_D = OpVariable %_ptr_Output_int Output + +// CHECK-NEXT: %main = OpFunction %void None {{%\d+}} +// CHECK-NEXT: {{%\d+}} = OpLabel + +// CHECK-NEXT: %param_var_input = OpVariable %_ptr_Function_T Function +// CHECK-NEXT: [[inA:%\d+]] = OpLoad %bool %in_var_A +// CHECK-NEXT: [[inB:%\d+]] = OpLoad %v2uint %in_var_B +// CHECK-NEXT: [[inC:%\d+]] = OpLoad %mat2v3float %in_var_C +// CHECK-NEXT: [[inS:%\d+]] = OpCompositeConstruct %S [[inA]] [[inB]] [[inC]] +// CHECK-NEXT: [[inD:%\d+]] = OpLoad %int %in_var_D +// CHECK-NEXT: [[inT:%\d+]] = OpCompositeConstruct %T [[inS]] [[inD]] +// CHECK-NEXT: OpStore %param_var_input [[inT]] + +// CHECK-NEXT: [[ret:%\d+]] = OpFunctionCall %T %src_main %param_var_input +// CHECK-NEXT: [[outS:%\d+]] = OpCompositeExtract %S [[ret]] 0 +// CHECK-NEXT: [[outA:%\d+]] = OpCompositeExtract %bool [[outS]] 0 +// CHECK-NEXT: OpStore %out_var_A [[outA]] +// CHECK-NEXT: [[outB:%\d+]] = OpCompositeExtract %v2uint [[outS]] 1 +// CHECK-NEXT: OpStore %out_var_B [[outB]] +// CHECK-NEXT: [[outC:%\d+]] = OpCompositeExtract %mat2v3float [[outS]] 2 +// CHECK-NEXT: OpStore %out_var_C [[outC]] +// CHECK-NEXT: [[outD:%\d+]] = OpCompositeExtract %int [[ret]] 1 +// CHECK-NEXT: OpStore %out_var_D [[outD]] + +// CHECK-NEXT: OpReturn +// CHECK-NEXT: OpFunctionEnd +T main(T input) { + return input; +} diff --git a/tools/clang/test/CodeGenSPIRV/spirv.storage-class.hlsl b/tools/clang/test/CodeGenSPIRV/spirv.storage-class.hlsl index 85bb487a8..e01d69ab8 100644 --- a/tools/clang/test/CodeGenSPIRV/spirv.storage-class.hlsl +++ b/tools/clang/test/CodeGenSPIRV/spirv.storage-class.hlsl @@ -5,20 +5,23 @@ struct VSOut { float4 out2: D; }; -static float4 sgVar; // Private +static float4 sgVar; // Privte -// CHECK: [[input:%\d+]] = OpVariable %_ptr_Input_v4float Input +// Note: The entry function in the source code is treated as a normal function. +// Another wrapper function take care of handling stage input/output variables. +// and calling the source code entry function. So there are no Input/Ouput +// storage class involved in the following. -VSOut main(float4 input: A /* Input */, uint index: B /* Input */) { +VSOut main(float4 input: A /* Function */, uint index: B /* Function */) { static float4 slVar; // Private VSOut ret; // Function -// CHECK: {{%\d+}} = OpAccessChain %_ptr_Input_float [[input]] {{%\d+}} -// CHECK: {{%\d+}} = OpAccessChain %_ptr_Private_float %sgVar {{%\d+}} -// CHECK: {{%\d+}} = OpAccessChain %_ptr_Private_float %slVar {{%\d+}} +// CHECK: OpAccessChain %_ptr_Function_float %input +// CHECK: OpAccessChain %_ptr_Private_float %sgVar +// CHECK: OpAccessChain %_ptr_Private_float %slVar // CHECK: [[lhs:%\d+]] = OpAccessChain %_ptr_Function_v4float %ret %int_0 -// CHECK-NEXT: {{%\d+}} = OpAccessChain %_ptr_Function_float [[lhs]] {{%\d+}} +// CHECK-NEXT: OpAccessChain %_ptr_Function_float [[lhs]] ret.out1[index] = input[index] + sgVar[index] + slVar[index]; return ret; diff --git a/tools/clang/test/CodeGenSPIRV/var.init.hlsl b/tools/clang/test/CodeGenSPIRV/var.init.hlsl index 8b27c75df..e21cce26c 100644 --- a/tools/clang/test/CodeGenSPIRV/var.init.hlsl +++ b/tools/clang/test/CodeGenSPIRV/var.init.hlsl @@ -4,9 +4,6 @@ // CHECK: [[float4constant:%\d+]] = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 // CHECK: [[int2constant:%\d+]] = OpConstantComposite %v2int %int_1 %int_2 -// Stage IO variables -// CHECK: [[component:%\d+]] = OpVariable %_ptr_Input_float Input - float4 main(float component: COLOR) : SV_TARGET { // CHECK-LABEL: %bb_entry = OpLabel @@ -24,7 +21,7 @@ float4 main(float component: COLOR) : SV_TARGET { // Initializer already attached to the var definition float i = 1. + 2.; // From const expr -// CHECK-NEXT: [[component0:%\d+]] = OpLoad %float [[component]] +// CHECK-NEXT: [[component0:%\d+]] = OpLoad %float %component // CHECK-NEXT: OpStore %j [[component0]] float j = component; // From stage variable diff --git a/tools/clang/test/CodeGenSPIRV/var.init.struct.hlsl b/tools/clang/test/CodeGenSPIRV/var.init.struct.hlsl index fcc9bc2a6..acca7303d 100644 --- a/tools/clang/test/CodeGenSPIRV/var.init.struct.hlsl +++ b/tools/clang/test/CodeGenSPIRV/var.init.struct.hlsl @@ -80,7 +80,7 @@ void main() { // CHECK-NEXT: [[n:%\d+]] = OpBitcast %int [[s2bv]] // CHECK-NEXT: [[s2c:%\d+]] = OpAccessChain %_ptr_Function_mat2v2float %s2 %int_2 -// CHECK-NEXT: [[o:%\d+]] = OpLoad %mat2v2float %65 +// CHECK-NEXT: [[o:%\d+]] = OpLoad %mat2v2float [[s2c]] // CHECK-NEXT: {{%\d+}} = OpCompositeConstruct %T [[h]] [[i]] [[j]] [[k]] [[l]] [[m]] [[n]] [[o]] T t = {s1, // Decomposing struct diff --git a/tools/clang/test/CodeGenSPIRV/var.static.hlsl b/tools/clang/test/CodeGenSPIRV/var.static.hlsl index 93ce96f0d..8e27072eb 100644 --- a/tools/clang/test/CodeGenSPIRV/var.static.hlsl +++ b/tools/clang/test/CodeGenSPIRV/var.static.hlsl @@ -15,22 +15,23 @@ static bool3 gb; // InitListHandler. static float2x2 gc = {1, 2, 3, 4}; -// CHECK: [[input:%\d+]] = OpVariable %_ptr_Input_int Input - // CHECK: %a = OpVariable %_ptr_Private_uint Private %uint_5 // CHECK: %b = OpVariable %_ptr_Private_v4float Private [[v4f0]] // CHECK: %c = OpVariable %_ptr_Private_int Private // CHECK: %init_done_c = OpVariable %_ptr_Private_bool Private %false -int main(int input: A) : B { -// CHECK-LABEL: %bb_entry = OpLabel - - // initialization of gc + // initialization of gc appears at the beginning of the entry function wrapper +// CHECK-LABEL: OpLabel // CHECK: [[v2f12:%\d+]] = OpCompositeConstruct %v2float %float_1 %float_2 // CHECK-NEXT: [[v2f34:%\d+]] = OpCompositeConstruct %v2float %float_3 %float_4 // CHECK-NEXT: [[mat1234:%\d+]] = OpCompositeConstruct %mat2v2float [[v2f12]] [[v2f34]] // CHECK-NEXT: OpStore %gc [[mat1234]] +// CHECK: OpFunctionCall %int %src_main +// CHECK-LABEL: OpFunctionEnd + +int main(int input: A) : B { +// CHECK-LABEL: %bb_entry = OpLabel static uint a = 5; // const init static float4 b; // no init @@ -39,7 +40,7 @@ int main(int input: A) : B { // CHECK-NEXT: OpSelectionMerge %if_merge None // CHECK-NEXT: OpBranchConditional [[initdonec]] %if_true %if_merge // CHECK-NEXT: %if_true = OpLabel -// CHECK-NEXT: [[initc:%\d+]] = OpLoad %int [[input]] +// CHECK-NEXT: [[initc:%\d+]] = OpLoad %int %input // CHECK-NEXT: OpStore %c [[initc]] // CHECK-NEXT: OpStore %init_done_c %true // CHECK-NEXT: OpBranch %if_merge diff --git a/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp b/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp index 9b8996b93..7df086d51 100644 --- a/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp +++ b/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp @@ -312,5 +312,8 @@ TEST_F(FileTest, IntrinsicsAtan) { runFileTest("intrinsics.atan.hlsl"); } // SPIR-V specific TEST_F(FileTest, SpirvStorageClass) { runFileTest("spirv.storage-class.hlsl"); } +TEST_F(FileTest, SpirvEntryFunctionWrapper) { + runFileTest("spirv.entry-function.wrapper.hlsl"); +} } // namespace