|
@@ -29,7 +29,8 @@ namespace spirv {
|
|
|
class SPIRVEmitter : public ASTConsumer {
|
|
class SPIRVEmitter : public ASTConsumer {
|
|
|
public:
|
|
public:
|
|
|
explicit SPIRVEmitter(CompilerInstance &ci)
|
|
explicit SPIRVEmitter(CompilerInstance &ci)
|
|
|
- : theCompilerInstance(ci), diags(ci.getDiagnostics()),
|
|
|
|
|
|
|
+ : theCompilerInstance(ci), astContext(ci.getASTContext()),
|
|
|
|
|
+ diags(ci.getDiagnostics()),
|
|
|
entryFunctionName(ci.getCodeGenOpts().HLSLEntryFunction),
|
|
entryFunctionName(ci.getCodeGenOpts().HLSLEntryFunction),
|
|
|
shaderStage(getSpirvShaderStageFromHlslProfile(
|
|
shaderStage(getSpirvShaderStageFromHlslProfile(
|
|
|
ci.getCodeGenOpts().HLSLProfile.c_str())),
|
|
ci.getCodeGenOpts().HLSLProfile.c_str())),
|
|
@@ -203,7 +204,25 @@ public:
|
|
|
const uint32_t ptrType = theBuilder.getPointerType(
|
|
const uint32_t ptrType = theBuilder.getPointerType(
|
|
|
typeTranslator.translateType(decl->getType()),
|
|
typeTranslator.translateType(decl->getType()),
|
|
|
spv::StorageClass::Function);
|
|
spv::StorageClass::Function);
|
|
|
- const uint32_t varId = theBuilder.addFnVariable(ptrType, decl->getName());
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Handle initializer. SPIR-V requires that "initializer must be an <id>
|
|
|
|
|
+ // from a constant instruction or a global (module scope) OpVariable
|
|
|
|
|
+ // instruction."
|
|
|
|
|
+ llvm::Optional<uint32_t> init = llvm::None;
|
|
|
|
|
+ if (decl->hasInit()) {
|
|
|
|
|
+ const Expr *declInit = decl->getInit();
|
|
|
|
|
+ if (declInit->isConstantInitializer(astContext, /*ForRef*/ false)) {
|
|
|
|
|
+ APValue evalResult;
|
|
|
|
|
+ llvm::SmallVector<PartialDiagnosticAt, 0> notes;
|
|
|
|
|
+ declInit->EvaluateAsInitializer(evalResult, astContext, decl, notes);
|
|
|
|
|
+ init = llvm::Optional<uint32_t>(
|
|
|
|
|
+ translateAPValue(evalResult, decl->getType()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const uint32_t varId =
|
|
|
|
|
+ theBuilder.addFnVariable(ptrType, decl->getName(), init);
|
|
|
|
|
+
|
|
|
declIdMapper.registerDeclResultId(decl, varId);
|
|
declIdMapper.registerDeclResultId(decl, varId);
|
|
|
} else {
|
|
} else {
|
|
|
// TODO: handle global variables
|
|
// TODO: handle global variables
|
|
@@ -218,32 +237,18 @@ public:
|
|
|
for (auto *decl : declStmt->decls()) {
|
|
for (auto *decl : declStmt->decls()) {
|
|
|
doDecl(decl);
|
|
doDecl(decl);
|
|
|
}
|
|
}
|
|
|
- } else if (auto *binOp = dyn_cast<BinaryOperator>(stmt)) {
|
|
|
|
|
- const auto opcode = binOp->getOpcode();
|
|
|
|
|
- const uint32_t lhs = doExpr(binOp->getLHS());
|
|
|
|
|
- const uint32_t rhs = doExpr(binOp->getRHS());
|
|
|
|
|
-
|
|
|
|
|
- doBinaryOperator(opcode, lhs, rhs);
|
|
|
|
|
|
|
+ } else if (auto *expr = dyn_cast<Expr>(stmt)) {
|
|
|
|
|
+ // All cases for expressions used as statements
|
|
|
|
|
+ doExpr(expr);
|
|
|
} else {
|
|
} else {
|
|
|
emitError("Stmt '%0' is not supported yet.") << stmt->getStmtClassName();
|
|
emitError("Stmt '%0' is not supported yet.") << stmt->getStmtClassName();
|
|
|
}
|
|
}
|
|
|
- // TODO: handle other statements
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void doBinaryOperator(BinaryOperatorKind opcode, uint32_t lhs, uint32_t rhs) {
|
|
|
|
|
- if (opcode == BO_Assign) {
|
|
|
|
|
- theBuilder.createStore(lhs, rhs);
|
|
|
|
|
- } else {
|
|
|
|
|
- emitError("BinaryOperator '%0' is not supported yet.") << opcode;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void doReturnStmt(ReturnStmt *stmt) {
|
|
void doReturnStmt(ReturnStmt *stmt) {
|
|
|
- // First get the <result-id> of the value we want to return.
|
|
|
|
|
- const uint32_t retValue = doExpr(stmt->getRetValue());
|
|
|
|
|
-
|
|
|
|
|
|
|
+ // For normal functions, just return in the normal way.
|
|
|
if (curFunction->getName() != entryFunctionName) {
|
|
if (curFunction->getName() != entryFunctionName) {
|
|
|
- theBuilder.createReturnValue(retValue);
|
|
|
|
|
|
|
+ theBuilder.createReturnValue(doExpr(stmt->getRetValue()));
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -254,13 +259,14 @@ public:
|
|
|
// We need to walk through the return type, and for each subtype attached
|
|
// We need to walk through the return type, and for each subtype attached
|
|
|
// with semantics, write out the value to the corresponding stage variable
|
|
// with semantics, write out the value to the corresponding stage variable
|
|
|
// mapped to the semantic.
|
|
// mapped to the semantic.
|
|
|
|
|
+
|
|
|
const uint32_t stageVarId =
|
|
const uint32_t stageVarId =
|
|
|
declIdMapper.getRemappedDeclResultId(curFunction);
|
|
declIdMapper.getRemappedDeclResultId(curFunction);
|
|
|
|
|
|
|
|
if (stageVarId) {
|
|
if (stageVarId) {
|
|
|
// The return value is mapped to a single stage variable. We just need
|
|
// The return value is mapped to a single stage variable. We just need
|
|
|
// to store the value into the stage variable instead.
|
|
// to store the value into the stage variable instead.
|
|
|
- theBuilder.createStore(stageVarId, retValue);
|
|
|
|
|
|
|
+ theBuilder.createStore(stageVarId, doExpr(stmt->getRetValue()));
|
|
|
theBuilder.createReturn();
|
|
theBuilder.createReturn();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -268,7 +274,16 @@ public:
|
|
|
QualType retType = stmt->getRetValue()->getType();
|
|
QualType retType = stmt->getRetValue()->getType();
|
|
|
|
|
|
|
|
if (const auto *structType = retType->getAsStructureType()) {
|
|
if (const auto *structType = retType->getAsStructureType()) {
|
|
|
- // We are trying to return a value of struct type. Go through all fields.
|
|
|
|
|
|
|
+ // 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 uint32_t retValue =
|
|
|
|
|
+ doExpr(stmt->getRetValue()->IgnoreParenLValueCasts());
|
|
|
|
|
+
|
|
|
|
|
+ // Then go through all fields.
|
|
|
uint32_t fieldIndex = 0;
|
|
uint32_t fieldIndex = 0;
|
|
|
for (const auto *field : structType->getDecl()->fields()) {
|
|
for (const auto *field : structType->getDecl()->fields()) {
|
|
|
// Load the value from the current field.
|
|
// Load the value from the current field.
|
|
@@ -292,24 +307,12 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- uint32_t doExpr(Expr *expr) {
|
|
|
|
|
|
|
+ uint32_t doExpr(const Expr *expr) {
|
|
|
if (auto *delRefExpr = dyn_cast<DeclRefExpr>(expr)) {
|
|
if (auto *delRefExpr = dyn_cast<DeclRefExpr>(expr)) {
|
|
|
// Returns the <result-id> of the referenced Decl.
|
|
// Returns the <result-id> of the referenced Decl.
|
|
|
const NamedDecl *referredDecl = delRefExpr->getFoundDecl();
|
|
const NamedDecl *referredDecl = delRefExpr->getFoundDecl();
|
|
|
assert(referredDecl && "found non-NamedDecl referenced");
|
|
assert(referredDecl && "found non-NamedDecl referenced");
|
|
|
return declIdMapper.getDeclResultId(referredDecl);
|
|
return declIdMapper.getDeclResultId(referredDecl);
|
|
|
- } else if (auto *castExpr = dyn_cast<ImplicitCastExpr>(expr)) {
|
|
|
|
|
- const uint32_t fromValue = doExpr(castExpr->getSubExpr());
|
|
|
|
|
- // Using lvalue as rvalue will result in a ImplicitCast in Clang AST.
|
|
|
|
|
- // This place gives us a place to inject the code for handling stage
|
|
|
|
|
- // variables. Since using the <result-id> of a stage variable as
|
|
|
|
|
- // rvalue means OpLoad it first. For normal values, it is not required.
|
|
|
|
|
- if (declIdMapper.isStageVariable(fromValue)) {
|
|
|
|
|
- const uint32_t resultType =
|
|
|
|
|
- typeTranslator.translateType(castExpr->getType());
|
|
|
|
|
- return theBuilder.createLoad(resultType, fromValue);
|
|
|
|
|
- }
|
|
|
|
|
- return fromValue;
|
|
|
|
|
} else if (auto *memberExpr = dyn_cast<MemberExpr>(expr)) {
|
|
} else if (auto *memberExpr = dyn_cast<MemberExpr>(expr)) {
|
|
|
const uint32_t base = doExpr(memberExpr->getBase());
|
|
const uint32_t base = doExpr(memberExpr->getBase());
|
|
|
auto *memberDecl = memberExpr->getMemberDecl();
|
|
auto *memberDecl = memberExpr->getMemberDecl();
|
|
@@ -325,41 +328,163 @@ public:
|
|
|
emitError("Decl '%0' in MemberExpr is not supported yet.")
|
|
emitError("Decl '%0' in MemberExpr is not supported yet.")
|
|
|
<< memberDecl->getDeclKindName();
|
|
<< memberDecl->getDeclKindName();
|
|
|
}
|
|
}
|
|
|
|
|
+ } else if (auto *castExpr = dyn_cast<ImplicitCastExpr>(expr)) {
|
|
|
|
|
+ return doImplicitCastExpr(castExpr);
|
|
|
} else if (auto *cxxFunctionalCastExpr =
|
|
} else if (auto *cxxFunctionalCastExpr =
|
|
|
dyn_cast<CXXFunctionalCastExpr>(expr)) {
|
|
dyn_cast<CXXFunctionalCastExpr>(expr)) {
|
|
|
// Explicit cast is a NO-OP (e.g. vector<float, 4> -> float4)
|
|
// Explicit cast is a NO-OP (e.g. vector<float, 4> -> float4)
|
|
|
if (cxxFunctionalCastExpr->getCastKind() == CK_NoOp) {
|
|
if (cxxFunctionalCastExpr->getCastKind() == CK_NoOp) {
|
|
|
return doExpr(cxxFunctionalCastExpr->getSubExpr());
|
|
return doExpr(cxxFunctionalCastExpr->getSubExpr());
|
|
|
- } else {
|
|
|
|
|
- emitError("Found unhandled CXXFunctionalCastExpr cast type: %0")
|
|
|
|
|
- << cxxFunctionalCastExpr->getCastKindName();
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ emitError("Found unhandled CXXFunctionalCastExpr cast type: %0")
|
|
|
|
|
+ << cxxFunctionalCastExpr->getCastKindName();
|
|
|
|
|
+ return 0;
|
|
|
} else if (auto *initListExpr = dyn_cast<InitListExpr>(expr)) {
|
|
} else if (auto *initListExpr = dyn_cast<InitListExpr>(expr)) {
|
|
|
- const bool isConstantInitializer = expr->isConstantInitializer(
|
|
|
|
|
- theCompilerInstance.getASTContext(), false);
|
|
|
|
|
const uint32_t resultType =
|
|
const uint32_t resultType =
|
|
|
typeTranslator.translateType(initListExpr->getType());
|
|
typeTranslator.translateType(initListExpr->getType());
|
|
|
|
|
+
|
|
|
std::vector<uint32_t> constituents;
|
|
std::vector<uint32_t> constituents;
|
|
|
for (size_t i = 0; i < initListExpr->getNumInits(); ++i) {
|
|
for (size_t i = 0; i < initListExpr->getNumInits(); ++i) {
|
|
|
constituents.push_back(doExpr(initListExpr->getInit(i)));
|
|
constituents.push_back(doExpr(initListExpr->getInit(i)));
|
|
|
}
|
|
}
|
|
|
- if (isConstantInitializer) {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (expr->isConstantInitializer(astContext, false)) {
|
|
|
return theBuilder.getConstantComposite(resultType, constituents);
|
|
return theBuilder.getConstantComposite(resultType, constituents);
|
|
|
- } else {
|
|
|
|
|
- // TODO: use OpCompositeConstruct if it is not a constant initializer
|
|
|
|
|
- // list.
|
|
|
|
|
- emitError("Non-const initializer lists are currently not supported.");
|
|
|
|
|
}
|
|
}
|
|
|
- } else if (auto *floatingLiteral = dyn_cast<FloatingLiteral>(expr)) {
|
|
|
|
|
- // TODO: use floatingLiteral->getType() to also handle float64 cases.
|
|
|
|
|
- const float value = floatingLiteral->getValue().convertToFloat();
|
|
|
|
|
- return theBuilder.getConstantFloat32(value);
|
|
|
|
|
|
|
+ // TODO: use OpCompositeConstruct for non-constant initializer lists.
|
|
|
|
|
+ emitError("Non-const initializer lists are currently not supported.");
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ } else if (auto *boolLiteral = dyn_cast<CXXBoolLiteralExpr>(expr)) {
|
|
|
|
|
+ const bool value = boolLiteral->getValue();
|
|
|
|
|
+ return theBuilder.getConstantBool(value);
|
|
|
|
|
+ } else if (auto *intLiteral = dyn_cast<IntegerLiteral>(expr)) {
|
|
|
|
|
+ return translateAPInt(intLiteral->getValue(), expr->getType());
|
|
|
|
|
+ } else if (auto *floatLiteral = dyn_cast<FloatingLiteral>(expr)) {
|
|
|
|
|
+ return translateAPFloat(floatLiteral->getValue(), expr->getType());
|
|
|
|
|
+ } else if (auto *binOp = dyn_cast<BinaryOperator>(expr)) {
|
|
|
|
|
+ return doBinaryOperator(binOp);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
emitError("Expr '%0' is not supported yet.") << expr->getStmtClassName();
|
|
emitError("Expr '%0' is not supported yet.") << expr->getStmtClassName();
|
|
|
// TODO: handle other expressions
|
|
// TODO: handle other expressions
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ uint32_t doBinaryOperator(const BinaryOperator *expr) {
|
|
|
|
|
+ const auto opcode = expr->getOpcode();
|
|
|
|
|
+ const uint32_t rhs = doExpr(expr->getRHS());
|
|
|
|
|
+ const uint32_t lhs = doExpr(expr->getLHS());
|
|
|
|
|
+
|
|
|
|
|
+ switch (opcode) {
|
|
|
|
|
+ case BO_Assign:
|
|
|
|
|
+ theBuilder.createStore(lhs, rhs);
|
|
|
|
|
+ // Assignment returns a rvalue.
|
|
|
|
|
+ return rhs;
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ emitError("BinaryOperator '%0' is not supported yet.") << opcode;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t doImplicitCastExpr(const ImplicitCastExpr *expr) {
|
|
|
|
|
+ const Expr *subExpr = expr->getSubExpr();
|
|
|
|
|
+ const QualType toType = expr->getType();
|
|
|
|
|
+
|
|
|
|
|
+ switch (expr->getCastKind()) {
|
|
|
|
|
+ // Integer literals in the AST are represented using 64bit APInt
|
|
|
|
|
+ // themselves and then implicitly casted into the expected bitwidth.
|
|
|
|
|
+ // We need special treatment of integer literals here because generating
|
|
|
|
|
+ // a 64bit constant and then explicit casting in SPIR-V requires Int64
|
|
|
|
|
+ // capability. We should avoid introducing unnecessary capabilities to
|
|
|
|
|
+ // our best.
|
|
|
|
|
+ case CastKind::CK_IntegralCast: {
|
|
|
|
|
+ llvm::APSInt intValue;
|
|
|
|
|
+ if (expr->EvaluateAsInt(intValue, astContext, Expr::SE_NoSideEffects)) {
|
|
|
|
|
+ return translateAPInt(intValue, toType);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ emitError("Integral cast is not supported yet");
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ case CastKind::CK_LValueToRValue: {
|
|
|
|
|
+ const uint32_t fromValue = doExpr(subExpr);
|
|
|
|
|
+ // Using lvalue as rvalue means we need to OpLoad the contents from
|
|
|
|
|
+ // the parameter/variable first.
|
|
|
|
|
+ const uint32_t resultType = typeTranslator.translateType(toType);
|
|
|
|
|
+ return theBuilder.createLoad(resultType, fromValue);
|
|
|
|
|
+ }
|
|
|
|
|
+ default:
|
|
|
|
|
+ emitError("ImplictCast Kind '%0' is not supported yet.")
|
|
|
|
|
+ << expr->getCastKind();
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t translateAPValue(const APValue &value, const QualType targetType) {
|
|
|
|
|
+ if (targetType->isBooleanType()) {
|
|
|
|
|
+ const bool boolValue = value.getInt().getBoolValue();
|
|
|
|
|
+ return theBuilder.getConstantBool(boolValue);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (targetType->isIntegerType()) {
|
|
|
|
|
+ const llvm::APInt &intValue = value.getInt();
|
|
|
|
|
+ return translateAPInt(intValue, targetType);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (targetType->isFloatingType()) {
|
|
|
|
|
+ const llvm::APFloat &floatValue = value.getFloat();
|
|
|
|
|
+ return translateAPFloat(floatValue, targetType);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ emitError("APValue of type '%0' is not supported yet.") << value.getKind();
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t translateAPInt(const llvm::APInt &intValue, QualType targetType) {
|
|
|
|
|
+ const auto bitwidth = astContext.getIntWidth(targetType);
|
|
|
|
|
+
|
|
|
|
|
+ if (targetType->isSignedIntegerType()) {
|
|
|
|
|
+ const int64_t value = intValue.getSExtValue();
|
|
|
|
|
+ switch (bitwidth) {
|
|
|
|
|
+ case 32:
|
|
|
|
|
+ return theBuilder.getConstantInt32(static_cast<int32_t>(value));
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const uint64_t value = intValue.getZExtValue();
|
|
|
|
|
+ switch (bitwidth) {
|
|
|
|
|
+ case 32:
|
|
|
|
|
+ return theBuilder.getConstantUint32(static_cast<uint32_t>(value));
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ emitError("APInt for target bitwidth '%0' is not supported yet.")
|
|
|
|
|
+ << bitwidth;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t translateAPFloat(const llvm::APFloat &floatValue,
|
|
|
|
|
+ QualType targetType) {
|
|
|
|
|
+ const auto &semantics = astContext.getFloatTypeSemantics(targetType);
|
|
|
|
|
+ const auto bitwidth = llvm::APFloat::getSizeInBits(semantics);
|
|
|
|
|
+
|
|
|
|
|
+ switch (bitwidth) {
|
|
|
|
|
+ case 32:
|
|
|
|
|
+ return theBuilder.getConstantFloat32(floatValue.convertToFloat());
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ emitError("APFloat for target bitwidth '%0' is not supported yet.")
|
|
|
|
|
+ << bitwidth;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private:
|
|
private:
|
|
|
/// \brief Wrapper method to create an error message and report it
|
|
/// \brief Wrapper method to create an error message and report it
|
|
|
/// in the diagnostic engine associated with this consumer.
|
|
/// in the diagnostic engine associated with this consumer.
|
|
@@ -380,6 +505,7 @@ private:
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
CompilerInstance &theCompilerInstance;
|
|
CompilerInstance &theCompilerInstance;
|
|
|
|
|
+ ASTContext &astContext;
|
|
|
DiagnosticsEngine &diags;
|
|
DiagnosticsEngine &diags;
|
|
|
|
|
|
|
|
/// Entry function name and shader stage. Both of them are derived from the
|
|
/// Entry function name and shader stage. Both of them are derived from the
|