[spirv] Converted to generate wrapper for entry function (#557)

Previously for an entry function, we decompose its parameters and
return value and create stage input/output/builtin variables from
them directly. This is fine for simple shaders, but it will have
problems for shaders using structs to group stage variables.
Accessing paramters and return values (using operator. and return)
needs special handling for entry functions, which is nasty.

With the wrapper function, we can handle entry functions just like
normal functions. Stage variable handling are all done in the
wrapper funtion.
This commit is contained in:
Lei Zhang 2017-08-11 19:08:27 -04:00 коммит произвёл David Peixotto
Родитель 8c4f2db67e
Коммит d98d66564a
27 изменённых файлов: 475 добавлений и 309 удалений

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

@ -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
++++++++++++++++++

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

@ -10,6 +10,7 @@
#define LLVM_CLANG_SPIRV_MODULEBUILDER_H
#include <memory>
#include <string>
#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.
///

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

@ -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<StorageClassResolver> {
@ -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<RecordType>(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<uint32_t, 4> 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:

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

@ -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 <result-id> without generating any SPIR-V
/// instruction. The given decl will be treated as normal decl.
@ -134,10 +133,6 @@ public:
/// assigned <result-id> for it.
uint32_t getOrRegisterDeclResultId(const NamedDecl *decl);
/// \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;
/// 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 <result-id>. 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 <result-id>s.
llvm::DenseMap<const NamedDecl *, DeclSpirvInfo> remappedDecls;
/// Mapping of all normal decls to their <result-id>s.
llvm::DenseMap<const NamedDecl *, DeclSpirvInfo> normalDecls;
/// Mapping of all Clang AST decls to their <result-id>s.
llvm::DenseMap<const NamedDecl *, DeclSpirvInfo> astDecls;
/// Vector of all defined stage variables.
llvm::SmallVector<StageVar, 8> stageVars;
};

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

@ -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;
}

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

@ -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 <result-id> 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 <result-id>.
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<uint32_t, 4> 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 <result-id>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<uint32_t, 4> 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<const Stmt *> 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 <result-id>
// 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<uint32_t, 4> 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;

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

@ -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

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

@ -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
}

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

@ -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
}

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

@ -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

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

@ -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

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

@ -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

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

@ -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

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

@ -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;

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

@ -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

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

@ -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;

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

@ -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;

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

@ -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

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

@ -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;

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

@ -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;

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

@ -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

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

@ -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;
}

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

@ -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;

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

@ -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

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

@ -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

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

@ -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

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

@ -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