[spirv] Revise to provide more initializer list support (#523)

Initializer lists are flexible in HLSL to the extent that as long
as the total number elements is correct, it will be accepted.
To support such flexibility, we need to conduct composite
extraction/construction and proper casting when necessary.

A new dedicated class is introduced for handling initializer lists.
It is built on top of the SPIRVEmitter class. It holds the logic
for correct initializer list handling and calls into SPIRVEmitter
for normal translating tasks. So is in a sense higher level than
the SPIRVEmitter class.

Also extended cast methods and vector type probing functions to
support ExtVectorType since we need to construct ExtVectorType
when handling initializer lists.
This commit is contained in:
Lei Zhang 2017-08-08 16:17:39 -04:00 коммит произвёл David Peixotto
Родитель c5832094a6
Коммит e4db8bda97
13 изменённых файлов: 716 добавлений и 135 удалений

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

@ -8,6 +8,7 @@ add_clang_library(clangSPIRV
DeclResultIdMapper.cpp
Decoration.cpp
EmitSPIRVAction.cpp
InitListHandler.cpp
InstBuilderAuto.cpp
InstBuilderManual.cpp
ModuleBuilder.cpp

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

@ -0,0 +1,208 @@
//===------- InitListHandler.cpp - Initializer List Handler -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
//
// This file implements an initalizer list handler that takes in an
// InitListExpr and emits the corresponding SPIR-V instructions for it.
//
//===----------------------------------------------------------------------===//
#include "InitListHandler.h"
#include "llvm/ADT/SmallVector.h"
namespace clang {
namespace spirv {
InitListHandler::InitListHandler(SPIRVEmitter &emitter)
: theEmitter(emitter), theBuilder(emitter.getModuleBuilder()),
typeTranslator(emitter.getTypeTranslator()),
diags(emitter.getDiagnosticsEngine()) {}
uint32_t InitListHandler::process(const InitListExpr *expr) {
initializers.clear();
scalars.clear();
flatten(expr);
const uint32_t init = createInitForType(expr->getType());
/// We should have consumed all initializers and scalars extracted from them.
assert(initializers.empty());
assert(scalars.empty());
return init;
}
void InitListHandler::flatten(const InitListExpr *expr) {
const auto numInits = expr->getNumInits();
for (uint32_t i = 0; i < numInits; ++i) {
const Expr *init = expr->getInit(i);
if (const auto *subInitList = dyn_cast<InitListExpr>(init)) {
flatten(subInitList);
} else if (const auto *subInitList = dyn_cast<InitListExpr>(
// Ignore constructor casts which are no-ops
// For cases like: <type>(<initializer-list>)
init->IgnoreParenNoopCasts(theEmitter.getASTContext()))) {
flatten(subInitList);
} else {
initializers.push_back(init);
}
}
}
void InitListHandler::decompose(const Expr *expr) {
const QualType type = expr->getType();
assert(!type->isBuiltinType()); // Cannot decompose builtin types
if (hlsl::IsHLSLVecType(type)) {
const uint32_t vec = theEmitter.loadIfGLValue(expr);
const QualType elemType = hlsl::GetHLSLVecElementType(type);
const auto size = hlsl::GetHLSLVecSize(type);
if (size == 1) {
// Decomposing of size-1 vector just results in the vector itself.
scalars.emplace_back(vec, elemType);
} else {
const uint32_t elemTypeId = typeTranslator.translateType(elemType);
for (uint32_t i = 0; i < size; ++i) {
const uint32_t element =
theBuilder.createCompositeExtract(elemTypeId, vec, {i});
scalars.emplace_back(element, elemType);
}
}
} else {
emitError("decomposing type %0 in initializer list unimplemented") << type;
}
}
uint32_t InitListHandler::createInitForType(QualType type) {
type = type.getCanonicalType();
if (type->isBuiltinType())
return createInitForBuiltinType(type);
if (hlsl::IsHLSLVecType(type))
return createInitForVectorType(hlsl::GetHLSLVecElementType(type),
hlsl::GetHLSLVecSize(type));
if (hlsl::IsHLSLMatType(type)) {
uint32_t rowCount = 0, colCount = 0;
hlsl::GetHLSLMatRowColCount(type, rowCount, colCount);
const QualType elemType = hlsl::GetHLSLMatElementType(type);
return createInitForMatrixType(elemType, rowCount, colCount);
}
emitError("unimplemented initializer for type '%0'") << type;
return 0;
}
uint32_t InitListHandler::createInitForBuiltinType(QualType type) {
assert(type->isBuiltinType());
if (!scalars.empty()) {
const auto init = scalars.front();
scalars.pop_front();
return theEmitter.castToType(init.first, init.second, type);
}
const Expr *init = initializers.front();
initializers.pop_front();
if (!init->getType()->isBuiltinType()) {
decompose(init);
return createInitForBuiltinType(type);
}
const uint32_t value = theEmitter.loadIfGLValue(init);
return theEmitter.castToType(value, init->getType(), type);
}
uint32_t InitListHandler::createInitForVectorType(QualType elemType,
uint32_t count) {
// If we don't have leftover scalars, we can try to see if there is a vector
// of the same size in the original initializer list so that we can use it
// directly. For all other cases, we need to construct a new vector as the
// initializer.
if (scalars.empty()) {
const Expr *init = initializers.front();
if (hlsl::IsHLSLVecType(init->getType()) &&
hlsl::GetHLSLVecSize(init->getType()) == count) {
initializers.pop_front();
/// HLSL vector types are parameterized templates and we cannot
/// construct them. So we construct an ExtVectorType here instead.
/// This is unfortunate since it means we need to handle ExtVectorType
/// in all type casting methods in SPIRVEmitter.
const auto toVecType =
theEmitter.getASTContext().getExtVectorType(elemType, count);
return theEmitter.castToType(theEmitter.loadIfGLValue(init),
init->getType(), toVecType);
}
}
if (count == 1)
return createInitForBuiltinType(elemType);
llvm::SmallVector<uint32_t, 4> elements;
for (uint32_t i = 0; i < count; ++i) {
// All elements are scalars, which should already be casted to the correct
// type if necessary.
elements.push_back(createInitForBuiltinType(elemType));
}
const uint32_t elemTypeId = typeTranslator.translateType(elemType);
const uint32_t vecType = theBuilder.getVecType(elemTypeId, count);
// TODO: use OpConstantComposite when all components are constants
return theBuilder.createCompositeConstruct(vecType, elements);
}
uint32_t InitListHandler::createInitForMatrixType(QualType elemType,
uint32_t rowCount,
uint32_t colCount) {
// Same as the vector case, first try to see if we already have a matrix at
// the beginning of the initializer queue.
if (scalars.empty()) {
const Expr *init = initializers.front();
if (hlsl::IsHLSLMatType(init->getType())) {
uint32_t initRowCount = 0, initColCount = 0;
hlsl::GetHLSLMatRowColCount(init->getType(), initRowCount, initColCount);
if (rowCount == initRowCount && colCount == initColCount) {
initializers.pop_front();
// TODO: We only support FP matrices now. Do type cast here after
// adding more matrix types.
return theEmitter.loadIfGLValue(init);
}
}
}
if (rowCount == 1)
return createInitForVectorType(elemType, colCount);
if (colCount == 1)
return createInitForVectorType(elemType, rowCount);
llvm::SmallVector<uint32_t, 4> vectors;
for (uint32_t i = 0; i < rowCount; ++i) {
// All elements are vectors, which should already be casted to the correct
// type if necessary.
vectors.push_back(createInitForVectorType(elemType, colCount));
}
const uint32_t elemTypeId = typeTranslator.translateType(elemType);
const uint32_t vecType = theBuilder.getVecType(elemTypeId, colCount);
const uint32_t matType = theBuilder.getMatType(vecType, rowCount);
// TODO: use OpConstantComposite when all components are constants
return theBuilder.createCompositeConstruct(matType, vectors);
}
} // end namespace spirv
} // end namespace clang

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

@ -0,0 +1,126 @@
//===------- InitListHandler.h - Initializer List Handler -------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
//
// This file defines an initalizer list handler that takes in an InitListExpr
// and emits the corresponding SPIR-V instructions for it.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_SPIRV_INITLISTHANDLER_H
#define LLVM_CLANG_LIB_SPIRV_INITLISTHANDLER_H
#include <deque>
#include <utility>
#include "clang/AST/Expr.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/SPIRV/ModuleBuilder.h"
#include "SPIRVEmitter.h"
#include "TypeTranslator.h"
namespace clang {
namespace spirv {
/// The class for handling initializer lists.
///
/// Initializer lists in HLSL are very flexible; as long as the initializer
/// list provides the exact number of elements required by the type of the
/// object to be initialized, it will highly likely be accepted. To handle
/// such flexibility, composite extraction, recursive composite construction,
/// and proper casting is necessary for some cases. For example:
///
/// float4x4 mat = {scalar, vec1, vec2, vec3, vec2, float2(1, 2), 3, vec4};
/// The first element vector is composed of scalar, vec1, and vec2; the second
/// element vector is composed of vec3 and the first element of vec2; etc.
///
/// The AST is designed to omit the composite extraction and construction. It
/// also does not have casts to the expected types and from lvalues to rvalues.
///
/// Resulting from the above reasons, the logic for handling initalizer lists
/// are complicated. So we have this dedicated class for it. It is built on
/// top of the SPIRVEmitter class and calls into SPIRVEmitter for normal
/// translation tasks. This gives better code structure.
///
/// The logic for handling initalizer lists is largely the following:
///
/// First we flatten() the given initalizer list recursively and put all non-
/// initializer-list AST Exprs into the initializers queue. This handles curly
/// braces of even wired forms like float2x2 mat = {{1.}, {2., {{3.}}}, 4.};
///
/// Then we construct the final SPIR-V composite from the initializer list
/// by traversing the type of the composite. This is done recursively in the
/// depth first search manner, using the type of the composite as the root.
///
/// When we reach a scalar type, we will try to decode a scalar value from the
/// front of the initializers queue. This may trigger composite extraction
/// since the front of the queue may be a vector/etc. The leftover values after
/// the extraction should be retained for the next decoding. Thus, we need
/// another queue, scalars, to keep track of leftover unused scalar values.
/// To adjust properly, when decoding values for a given type, we first try
/// the scalar queue.
///
/// When we reach a composite type, we will try to construct a composite using
/// the scalar values previously extracted and retained in the scalars queue.
/// To optimize, if we have no leftover scalars and a value of the same type at
/// the front of the initializers queue, we use the value as a whole.
class InitListHandler {
public:
/// Constructs an InitListHandler which uses the given emitter for normal
/// translation tasks. It will reuse the ModuleBuilder embedded in the given
/// emitter.
explicit InitListHandler(SPIRVEmitter &emitter);
/// Processes the given InitListExpr and returns the <result-id> for the final
/// SPIR-V value.
uint32_t process(const InitListExpr *expr);
private:
/// \brief Wrapper method to create an error message and report it
/// in the diagnostic engine associated with this consumer.
template <unsigned N> DiagnosticBuilder emitError(const char (&message)[N]) {
const auto diagId =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
return diags.Report(diagId);
}
/// Flattens the given InitListExpr and puts all non-InitListExpr AST nodes
/// into initializers.
void flatten(const InitListExpr *expr);
/// Decomposes the given Expr and puts all elements into the end of the
/// scalars queue.
void decompose(const Expr *expr);
/// Emits the necessary SPIR-V instructions to create a SPIR-V value of the
/// given type. The scalars and initializers queue will be used to fetch the
/// next value.
uint32_t createInitForType(QualType type);
uint32_t createInitForBuiltinType(QualType type);
uint32_t createInitForVectorType(QualType elemType, uint32_t count);
uint32_t createInitForMatrixType(QualType elemType, uint32_t rowCount,
uint32_t colCount);
private:
SPIRVEmitter &theEmitter;
ModuleBuilder &theBuilder;
TypeTranslator &typeTranslator;
DiagnosticsEngine &diags;
/// A queue keeping track of unused AST nodes for initializers
std::deque<const Expr *> initializers;
/// A queue keeping track of previously extracted but unused scalars.
/// Each element is a pair, with the first element as the SPIR-V <result-id>
/// and the second element as the AST type of the scalar value.
std::deque<std::pair<uint32_t, QualType>> scalars;
};
} // end namespace spirv
} // end namespace clang
#endif

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

@ -16,6 +16,8 @@
#include "dxc/HlslIntrinsicOp.h"
#include "llvm/ADT/StringExtras.h"
#include "InitListHandler.h"
namespace clang {
namespace spirv {
@ -23,34 +25,53 @@ namespace {
// TODO: Maybe we should move these type probing functions to TypeTranslator.
/// Returns true if the two types are the same scalar or vector type.
bool isSameScalarOrVecType(QualType type1, QualType type2) {
if (type1->isBuiltinType())
return type1.getCanonicalType() == type2.getCanonicalType();
QualType elemType1 = {}, elemType2 = {};
uint32_t count1 = {}, count2 = {};
if (TypeTranslator::isVectorType(type1, &elemType1, &count1) &&
TypeTranslator::isVectorType(type2, &elemType2, &count2))
return count1 == count2 &&
elemType1.getCanonicalType() == elemType2.getCanonicalType();
return false;
}
/// Returns true if the given type is a bool or vector of bool type.
bool isBoolOrVecOfBoolType(QualType type) {
QualType elemType = {};
return type->isBooleanType() ||
(hlsl::IsHLSLVecType(type) &&
hlsl::GetHLSLVecElementType(type)->isBooleanType());
(TypeTranslator::isVectorType(type, &elemType, nullptr) &&
elemType->isBooleanType());
}
/// Returns true if the given type is a signed integer or vector of signed
/// integer type.
bool isSintOrVecOfSintType(QualType type) {
QualType elemType = {};
return type->isSignedIntegerType() ||
(hlsl::IsHLSLVecType(type) &&
hlsl::GetHLSLVecElementType(type)->isSignedIntegerType());
(TypeTranslator::isVectorType(type, &elemType, nullptr) &&
elemType->isSignedIntegerType());
}
/// Returns true if the given type is an unsigned integer or vector of unsigned
/// integer type.
bool isUintOrVecOfUintType(QualType type) {
QualType elemType = {};
return type->isUnsignedIntegerType() ||
(hlsl::IsHLSLVecType(type) &&
hlsl::GetHLSLVecElementType(type)->isUnsignedIntegerType());
(TypeTranslator::isVectorType(type, &elemType, nullptr) &&
elemType->isUnsignedIntegerType());
}
/// Returns true if the given type is a float or vector of float type.
bool isFloatOrVecOfFloatType(QualType type) {
QualType elemType = {};
return type->isFloatingType() ||
(hlsl::IsHLSLVecType(type) &&
hlsl::GetHLSLVecElementType(type)->isFloatingType());
(TypeTranslator::isVectorType(type, &elemType, nullptr) &&
elemType->isFloatingType());
}
/// Returns true if the given type is a bool or vector/matrix of bool type.
@ -299,6 +320,25 @@ uint32_t SPIRVEmitter::loadIfGLValue(const Expr *expr) {
return result;
}
uint32_t SPIRVEmitter::castToType(uint32_t value, QualType fromType,
QualType toType) {
if (isFloatOrVecOfFloatType(toType))
return castToFloat(value, fromType, toType);
// Order matters here. Bool (vector) values will also be considered as uint
// (vector) values. So given a bool (vector) argument, isUintOrVecOfUintType()
// will also return true. We need to check bool before uint. The opposite is
// not true.
if (isBoolOrVecOfBoolType(toType))
return castToBool(value, fromType, toType);
if (isSintOrVecOfSintType(toType) || isUintOrVecOfUintType(toType))
return castToInt(value, fromType, toType);
emitError("casting to type %0 unimplemented") << toType;
return 0;
}
void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
curFunction = decl;
@ -783,7 +823,7 @@ uint32_t SPIRVEmitter::doCastExpr(const CastExpr *expr) {
return translateAPInt(intValue, toType);
}
return castToInt(subExpr, toType);
return castToInt(doExpr(subExpr), subExpr->getType(), toType);
}
case CastKind::CK_FloatingCast:
case CastKind::CK_IntegralToFloating:
@ -797,7 +837,7 @@ uint32_t SPIRVEmitter::doCastExpr(const CastExpr *expr) {
return translateAPFloat(evalResult.Val.getFloat(), toType);
}
return castToFloat(subExpr, toType);
return castToFloat(doExpr(subExpr), subExpr->getType(), toType);
}
case CastKind::CK_IntegralToBoolean:
case CastKind::CK_FloatingToBoolean:
@ -810,7 +850,7 @@ uint32_t SPIRVEmitter::doCastExpr(const CastExpr *expr) {
return theBuilder.getConstantBool(boolVal);
}
return castToBool(subExpr, toType);
return castToBool(doExpr(subExpr), subExpr->getType(), toType);
}
case CastKind::CK_HLSLVectorSplat: {
const size_t size = hlsl::GetHLSLVecSize(expr->getType());
@ -1129,67 +1169,7 @@ uint32_t SPIRVEmitter::doInitListExpr(const InitListExpr *expr) {
return translateAPValue(evalResult.Val, expr->getType());
}
const QualType type = expr->getType();
// InitListExpr is tricky to handle. It can have initializers of different
// types, and each initializer can itself be of a composite type.
// The front end parsing only gurantees the total number of elements in
// the initializers are the same as the one of the InitListExpr's type.
// For builtin types, we can assume the front end parsing has injected
// the necessary ImplicitCastExpr for type casting. So we just need to
// return the result of processing the only initializer.
if (type->isBuiltinType()) {
assert(expr->getNumInits() == 1);
return doExpr(expr->getInit(0));
}
// For composite types, we need to type cast the initializers if necessary.
const auto initCount = expr->getNumInits();
const uint32_t resultType = typeTranslator.translateType(type);
// For InitListExpr of vector type and having one initializer, we can avoid
// composite extraction and construction.
if (initCount == 1 && hlsl::IsHLSLVecType(type)) {
const Expr *init = expr->getInit(0);
// If the initializer already have the correct type, we don't need to
// type cast.
if (init->getType() == type) {
return doExpr(init);
}
// For the rest, we can do type cast as a whole.
const auto targetElemType = hlsl::GetHLSLVecElementType(type);
if (targetElemType->isBooleanType()) {
return castToBool(init, type);
} else if (targetElemType->isIntegerType()) {
return castToInt(init, type);
} else if (targetElemType->isFloatingType()) {
return castToFloat(init, type);
} else {
emitError("unimplemented vector InitList cases");
expr->dump();
return 0;
}
}
// Cases needing composite extraction and construction
std::vector<uint32_t> constituents;
for (size_t i = 0; i < initCount; ++i) {
const Expr *init = expr->getInit(i);
if (!init->getType()->isBuiltinType()) {
emitError("unimplemented InitList initializer type");
init->dump();
return 0;
}
constituents.push_back(doExpr(init));
}
return theBuilder.createCompositeConstruct(resultType, constituents);
return InitListHandler(*this).process(expr);
}
uint32_t SPIRVEmitter::doUnaryOperator(const UnaryOperator *expr) {
@ -1872,37 +1852,38 @@ uint32_t SPIRVEmitter::processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
return 0;
}
uint32_t SPIRVEmitter::castToBool(const Expr *expr, QualType toBoolType) {
// Converting to bool means comparing with value zero.
const uint32_t fromVal = doExpr(expr);
if (isBoolOrVecOfBoolType(expr->getType()))
uint32_t SPIRVEmitter::castToBool(const uint32_t fromVal, QualType fromType,
QualType toBoolType) {
// Semantic analysis should already checked the size
if (isBoolOrVecOfBoolType(fromType))
return fromVal;
const spv::Op spvOp = translateOp(BO_NE, expr->getType());
// Converting to bool means comparing with value zero.
const spv::Op spvOp = translateOp(BO_NE, fromType);
const uint32_t boolType = typeTranslator.translateType(toBoolType);
const uint32_t zeroVal = getValueZero(expr->getType());
const uint32_t zeroVal = getValueZero(fromType);
return theBuilder.createBinaryOp(spvOp, boolType, fromVal, zeroVal);
}
uint32_t SPIRVEmitter::castToInt(const Expr *expr, QualType toIntType) {
const QualType fromType = expr->getType();
const uint32_t intType = typeTranslator.translateType(toIntType);
const uint32_t fromVal = doExpr(expr);
uint32_t SPIRVEmitter::castToInt(const uint32_t fromVal, QualType fromType,
QualType toIntType) {
if (isSameScalarOrVecType(fromType, toIntType))
return fromVal;
const uint32_t intType = typeTranslator.translateType(toIntType);
if (isBoolOrVecOfBoolType(fromType)) {
const uint32_t one = getValueOne(toIntType);
const uint32_t zero = getValueZero(toIntType);
return theBuilder.createSelect(intType, fromVal, one, zero);
} else if (isSintOrVecOfSintType(fromType) ||
isUintOrVecOfUintType(fromType)) {
if (fromType == toIntType)
return fromVal;
}
if (isSintOrVecOfSintType(fromType) || isUintOrVecOfUintType(fromType)) {
// TODO: handle different bitwidths
return theBuilder.createUnaryOp(spv::Op::OpBitcast, intType, fromVal);
} else if (isFloatOrVecOfFloatType(fromType)) {
}
if (isFloatOrVecOfFloatType(fromType)) {
if (isSintOrVecOfSintType(toIntType)) {
return theBuilder.createUnaryOp(spv::Op::OpConvertFToS, intType, fromVal);
} else if (isUintOrVecOfUintType(toIntType)) {
@ -1914,14 +1895,15 @@ uint32_t SPIRVEmitter::castToInt(const Expr *expr, QualType toIntType) {
emitError("unimplemented casting to integer");
}
expr->dump();
return 0;
}
uint32_t SPIRVEmitter::castToFloat(const Expr *expr, QualType toFloatType) {
const QualType fromType = expr->getType();
uint32_t SPIRVEmitter::castToFloat(const uint32_t fromVal, QualType fromType,
QualType toFloatType) {
if (isSameScalarOrVecType(fromType, toFloatType))
return fromVal;
const uint32_t floatType = typeTranslator.translateType(toFloatType);
const uint32_t fromVal = doExpr(expr);
if (isBoolOrVecOfBoolType(fromType)) {
const uint32_t one = getValueOne(toFloatType);
@ -1938,11 +1920,11 @@ uint32_t SPIRVEmitter::castToFloat(const Expr *expr, QualType toFloatType) {
}
if (isFloatOrVecOfFloatType(fromType)) {
return fromVal;
emitError("casting between different fp bitwidth unimplemented");
return 0;
}
emitError("unimplemented casting to floating point");
expr->dump();
return 0;
}
@ -2064,10 +2046,11 @@ uint32_t SPIRVEmitter::processIntrinsicAllOrAny(const CallExpr *callExpr,
hlsl::IsHLSLVecType(argType) && hlsl::GetHLSLVecSize(argType) > 1;
if (!isSpirvAcceptableVecType) {
// For a scalar or vector of 1 scalar, we can simply cast to boolean.
return castToBool(arg, callExpr->getType());
return castToBool(doExpr(arg), arg->getType(), callExpr->getType());
} else {
// First cast the vector to a vector of booleans, then use OpAll
uint32_t boolVecId = castToBool(arg, callExpr->getType());
uint32_t boolVecId =
castToBool(doExpr(arg), arg->getType(), callExpr->getType());
return theBuilder.createUnaryOp(spvOp, returnType, boolVecId);
}
}
@ -2107,18 +2090,12 @@ uint32_t SPIRVEmitter::getValueZero(QualType type) {
return theBuilder.getConstantFloat32(0.0);
}
if (hlsl::IsHLSLVecType(type)) {
const QualType elemType = hlsl::GetHLSLVecElementType(type);
const uint32_t elemZeroId = getValueZero(elemType);
const size_t size = hlsl::GetHLSLVecSize(type);
if (size == 1)
return elemZeroId;
llvm::SmallVector<uint32_t, 4> elements(size, elemZeroId);
const uint32_t vecTypeId = typeTranslator.translateType(type);
return theBuilder.getConstantComposite(vecTypeId, elements);
{
QualType elemType = {};
uint32_t size = {};
if (TypeTranslator::isVectorType(type, &elemType, &size)) {
return getVecValueZero(elemType, size);
}
}
emitError("getting value 0 for type '%0' unimplemented")
@ -2126,6 +2103,19 @@ uint32_t SPIRVEmitter::getValueZero(QualType type) {
return 0;
}
uint32_t SPIRVEmitter::getVecValueZero(QualType elemType, uint32_t size) {
const uint32_t elemZeroId = getValueZero(elemType);
if (size == 1)
return elemZeroId;
llvm::SmallVector<uint32_t, 4> elements(size_t(size), elemZeroId);
const uint32_t vecType =
theBuilder.getVecType(typeTranslator.translateType(elemType), size);
return theBuilder.getConstantComposite(vecType, elements);
}
uint32_t SPIRVEmitter::getValueOne(QualType type) {
if (type->isSignedIntegerType()) {
return theBuilder.getConstantInt32(1);
@ -2139,10 +2129,12 @@ uint32_t SPIRVEmitter::getValueOne(QualType type) {
return theBuilder.getConstantFloat32(1.0);
}
if (hlsl::IsHLSLVecType(type)) {
const QualType elemType = hlsl::GetHLSLVecElementType(type);
const auto size = hlsl::GetHLSLVecSize(type);
return getVecValueOne(elemType, size);
{
QualType elemType = {};
uint32_t size = {};
if (TypeTranslator::isVectorType(type, &elemType, &size)) {
return getVecValueOne(elemType, size);
}
}
emitError("getting value 1 for type '%0' unimplemented") << type;

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

@ -43,6 +43,11 @@ public:
void HandleTranslationUnit(ASTContext &context) override;
ASTContext &getASTContext() { return astContext; }
ModuleBuilder &getModuleBuilder() { return theBuilder; }
TypeTranslator &getTypeTranslator() { return typeTranslator; }
DiagnosticsEngine &getDiagnosticsEngine() { return diags; }
void doDecl(const Decl *decl);
void doStmt(const Stmt *stmt, llvm::ArrayRef<const Attr *> attrs = {});
uint32_t doExpr(const Expr *expr);
@ -57,6 +62,10 @@ public:
/// be lvalue or rvalue.
uint32_t loadIfGLValue(const Expr *expr);
/// Casts the given value from fromType to toType. fromType and toType should
/// both be scalar or vector types of the same size.
uint32_t castToType(uint32_t value, QualType fromType, QualType toType);
private:
void doFunctionDecl(const FunctionDecl *decl);
void doVarDecl(const VarDecl *decl);
@ -181,13 +190,15 @@ private:
private:
/// Processes the given expr, casts the result into the given bool (vector)
/// type and returns the <result-id> of the casted value.
uint32_t castToBool(const Expr *expr, QualType toBoolType);
uint32_t castToBool(uint32_t value, QualType fromType, QualType toType);
/// Processes the given expr, casts the result into the given integer (vector)
/// type and returns the <result-id> of the casted value.
uint32_t castToInt(const Expr *expr, QualType toIntType);
uint32_t castToInt(uint32_t value, QualType fromType, QualType toType);
uint32_t castToFloat(const Expr *expr, QualType toFloatType);
/// Processes the given expr, casts the result into the given float (vector)
/// type and returns the <result-id> of the casted value.
uint32_t castToFloat(uint32_t value, QualType fromType, QualType toType);
private:
uint32_t processIntrinsicCallExpr(const CallExpr *callExpr);
@ -203,6 +214,10 @@ private:
/// Returns the <result-id> for constant value 0 of the given type.
uint32_t getValueZero(QualType type);
/// Returns the <result-id> for a constant zero vector of the given size and
/// element type.
uint32_t getVecValueZero(QualType elemType, uint32_t size);
/// Returns the <result-id> for constant value 1 of the given type.
uint32_t getValueOne(QualType type);

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

@ -49,15 +49,17 @@ uint32_t TypeTranslator::translateType(QualType type) {
// In AST, vector/matrix types are TypedefType of TemplateSpecializationType.
// We handle them via HLSL type inspection functions.
if (hlsl::IsHLSLVecType(type)) {
const auto elemType = hlsl::GetHLSLVecElementType(type);
const auto elemCount = hlsl::GetHLSLVecSize(type);
// In SPIR-V, vectors must have two or more elements. So translate vectors
// of size 1 into the underlying primitive types directly.
if (elemCount == 1) {
return translateType(elemType);
{
QualType elemType = {};
uint32_t elemCount = {};
if (TypeTranslator::isVectorType(type, &elemType, &elemCount)) {
// In SPIR-V, vectors must have two or more elements. So translate vectors
// of size 1 into the underlying primitive types directly.
if (elemCount == 1) {
return translateType(elemType);
}
return theBuilder.getVecType(translateType(elemType), elemCount);
}
return theBuilder.getVecType(translateType(elemType), elemCount);
}
if (hlsl::IsHLSLMatType(type)) {
@ -112,6 +114,27 @@ uint32_t TypeTranslator::translateType(QualType type) {
return 0;
}
bool TypeTranslator::isVectorType(QualType type, QualType *elemType,
uint32_t *count) {
if (hlsl::IsHLSLVecType(type)) {
if (elemType)
*elemType = hlsl::GetHLSLVecElementType(type);
if (count)
*count = hlsl::GetHLSLVecSize(type);
return true;
}
if (const auto *extVecType = dyn_cast<ExtVectorType>(type.getTypePtr())) {
if (elemType)
*elemType = extVecType->getElementType();
if (count)
*count = extVecType->getNumElements();
return true;
}
return false;
}
bool TypeTranslator::is1x1MatrixType(QualType type) {
if (!hlsl::IsHLSLMatType(type))
return false;

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

@ -37,6 +37,11 @@ public:
/// on will be generated.
uint32_t translateType(QualType type);
/// \breif Returns true if the given type is a vector type (either
/// ExtVectorType or HLSL vector type) and writes the element type and count
/// into *elementType and *count respectively if they are not nullptr.
static bool isVectorType(QualType type, QualType *elemType, uint32_t *count);
/// \brief Returns true if the givne type is a 1x1 matrix type.
static bool is1x1MatrixType(QualType type);

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

@ -1,17 +1,11 @@
// Run: %dxc -T ps_6_0 -E main
// Constants
// CHECK-DAG: %float_1 = OpConstant %float 1
// CHECK-DAG: %float_2 = OpConstant %float 2
// CHECK-DAG: %float_3 = OpConstant %float 3
// CHECK-DAG: %float_4 = OpConstant %float 4
// CHECK-DAG: %int_1 = OpConstant %int 1
// CHECK-DAG: %int_2 = OpConstant %int 2
// CHECK-DAG: [[float4constant:%\d+]] = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
// CHECK-DAG: [[int2constant:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
// 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-DAG: [[component:%\d+]] = OpVariable %_ptr_Input_float Input
// CHECK: [[component:%\d+]] = OpVariable %_ptr_Input_float Input
float4 main(float component: COLOR) : SV_TARGET {
// CHECK-LABEL: %bb_entry = OpLabel
@ -24,7 +18,7 @@ float4 main(float component: COLOR) : SV_TARGET {
// Initializer already attached to the var definition
int a = 0; // From constant
// CHECK-NEXT: [[a0:%\d+]] = OpLoad %int %a
// CHECK: [[a0:%\d+]] = OpLoad %int %a
// CHECK-NEXT: OpStore %b [[a0]]
int b = a; // From local variable
@ -57,5 +51,16 @@ float4 main(float component: COLOR) : SV_TARGET {
uint1 x = uint1(1); // Special case: vector of size 1
float2 y;
// CHECK-NEXT: [[y0:%\d+]] = OpLoad %v2float %y
// CHECK-NEXT: [[ce0:%\d+]] = OpCompositeExtract %float [[y0]] 0
// CHECK-NEXT: [[ce1:%\d+]] = OpCompositeExtract %float [[y0]] 1
// CHECK-NEXT: [[y1:%\d+]] = OpLoad %v2float %y
// CHECK-NEXT: [[ce2:%\d+]] = OpCompositeExtract %float [[y1]] 0
// CHECK-NEXT: [[ce3:%\d+]] = OpCompositeExtract %float [[y1]] 1
// CHECK-NEXT: [[zinit:%\d+]] = OpCompositeConstruct %v4float [[ce0]] [[ce1]] [[ce2]] [[ce3]]
// CHECK-NEXT: OpStore %z [[zinit]]
float4 z = {y, y};
return o;
}

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

@ -0,0 +1,21 @@
// Run: %dxc -T ps_6_0 -E main
void main() {
// CHECK-LABEL: %bb_entry = OpLabel
// CHECK: OpStore %mat1 %float_1
float1x1 mat1 = {1.};
// CHECK-NEXT: OpStore %mat2 %float_1
float1x1 mat2 = {{{1.}}};
// CHECK-NEXT: OpStore %mat3 %float_1
float1x1 mat3 = float1x1(1.);
// CHECK-NEXT: [[mat3:%\d+]] = OpLoad %float %mat3
// CHECK-NEXT: OpStore %mat4 [[mat3]]
float1x1 mat4 = float1x1(mat3);
int scalar;
// CHECK-NEXT: [[scalar:%\d+]] = OpLoad %int %scalar
// CHECK-NEXT: [[cv:%\d+]] = OpConvertSToF %float [[scalar]]
// CHECK-NEXT: OpStore %mat5 [[cv]]
float1x1 mat5 = {scalar};
}

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

@ -0,0 +1,34 @@
// Run: %dxc -T ps_6_0 -E main
void main() {
// CHECK-LABEL: %bb_entry = OpLabel
// CHECK: [[cc00:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: OpStore %mat1 [[cc00]]
float1x3 mat1 = {1., 2., 3.};
// CHECK-NEXT: [[cc01:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: OpStore %mat2 [[cc01]]
float1x3 mat2 = {1., {2., {{3.}}}};
// CHECK-NEXT: [[cc02:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: OpStore %mat3 [[cc02]]
float1x3 mat3 = float1x3(1., 2., 3.);
// CHECK-NEXT: [[mat3:%\d+]] = OpLoad %v3float %mat3
// CHECK-NEXT: OpStore %mat4 [[mat3]]
float1x3 mat4 = float1x3(mat3);
int scalar;
bool1 vec1;
uint2 vec2;
// CHECK-NEXT: [[scalar:%\d+]] = OpLoad %int %scalar
// CHECK-NEXT: [[cv0:%\d+]] = OpConvertSToF %float [[scalar]]
// CHECK-NEXT: [[vec2:%\d+]] = OpLoad %v2uint %vec2
// CHECK-NEXT: [[ce0:%\d+]] = OpCompositeExtract %uint [[vec2]] 0
// CHECK-NEXT: [[ce1:%\d+]] = OpCompositeExtract %uint [[vec2]] 1
// CHECK-NEXT: [[cv1:%\d+]] = OpConvertUToF %float [[ce0]]
// CHECK-NEXT: [[cv2:%\d+]] = OpConvertUToF %float [[ce1]]
// CHECK-NEXT: [[vec1:%\d+]] = OpLoad %bool %vec1
// CHECK-NEXT: [[cv3:%\d+]] = OpSelect %float [[vec1]] %float_1 %float_0
// CHECK-NEXT: [[cc0:%\d+]] = OpCompositeConstruct %v4float [[cv0]] [[cv1]] [[cv2]] [[cv3]]
// CHECK-NEXT: OpStore %mat5 [[cc0]]
float1x4 mat5 = {scalar, vec2, vec1};
}

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

@ -0,0 +1,34 @@
// Run: %dxc -T ps_6_0 -E main
void main() {
// CHECK-LABEL: %bb_entry = OpLabel
// CHECK: [[cc00:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: OpStore %mat1 [[cc00]]
float3x1 mat1 = {1., 2., 3.};
// CHECK-NEXT: [[cc01:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: OpStore %mat2 [[cc01]]
float3x1 mat2 = {1., {2., {{3.}}}};
// CHECK-NEXT: [[cc02:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: OpStore %mat3 [[cc02]]
float3x1 mat3 = float3x1(1., 2., 3.);
// CHECK-NEXT: [[mat3:%\d+]] = OpLoad %v3float %mat3
// CHECK-NEXT: OpStore %mat4 [[mat3]]
float3x1 mat4 = float3x1(mat3);
int scalar;
bool1 vec1;
uint2 vec2;
// CHECK-NEXT: [[scalar:%\d+]] = OpLoad %int %scalar
// CHECK-NEXT: [[cv0:%\d+]] = OpConvertSToF %float [[scalar]]
// CHECK-NEXT: [[vec2:%\d+]] = OpLoad %v2uint %vec2
// CHECK-NEXT: [[ce0:%\d+]] = OpCompositeExtract %uint [[vec2]] 0
// CHECK-NEXT: [[ce1:%\d+]] = OpCompositeExtract %uint [[vec2]] 1
// CHECK-NEXT: [[cv1:%\d+]] = OpConvertUToF %float [[ce0]]
// CHECK-NEXT: [[cv2:%\d+]] = OpConvertUToF %float [[ce1]]
// CHECK-NEXT: [[vec1:%\d+]] = OpLoad %bool %vec1
// CHECK-NEXT: [[cv3:%\d+]] = OpSelect %float [[vec1]] %float_1 %float_0
// CHECK-NEXT: [[cc0:%\d+]] = OpCompositeConstruct %v4float [[cv0]] [[cv1]] [[cv2]] [[cv3]]
// CHECK-NEXT: OpStore %mat5 [[cc0]]
float4x1 mat5 = {scalar, vec2, vec1};
}

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

@ -0,0 +1,113 @@
// Run: %dxc -T ps_6_0 -E main
// TODO: optimize to generate constant composite for suitable initializers
// TODO: decompose matrix in initializer
// CHECK: [[v3fc1:%\d+]] = OpConstantComposite %v3float %float_1 %float_1 %float_1
// CHECK-NEXT: [[v3fc0:%\d+]] = OpConstantComposite %v3float %float_0 %float_0 %float_0
void main() {
// CHECK-LABEL: %bb_entry = OpLabel
// Constructor
// CHECK: [[cc00:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: [[cc01:%\d+]] = OpCompositeConstruct %v3float %float_4 %float_5 %float_6
// CHECK-NEXT: [[cc02:%\d+]] = OpCompositeConstruct %mat2v3float [[cc00]] [[cc01]]
// CHECK-NEXT: OpStore %mat1 [[cc02]]
float2x3 mat1 = float2x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
// All elements in a single {}
// CHECK-NEXT: [[cc03:%\d+]] = OpCompositeConstruct %v2float %float_1 %float_2
// CHECK-NEXT: [[cc04:%\d+]] = OpCompositeConstruct %v2float %float_3 %float_4
// CHECK-NEXT: [[cc05:%\d+]] = OpCompositeConstruct %v2float %float_5 %float_6
// CHECK-NEXT: [[cc06:%\d+]] = OpCompositeConstruct %mat3v2float [[cc03]] [[cc04]] [[cc05]]
// CHECK-NEXT: OpStore %mat2 [[cc06]]
float3x2 mat2 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
// Each vector has its own {}
// CHECK-NEXT: [[cc07:%\d+]] = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
// CHECK-NEXT: [[cc08:%\d+]] = OpCompositeConstruct %v3float %float_4 %float_5 %float_6
// CHECK-NEXT: [[cc09:%\d+]] = OpCompositeConstruct %mat2v3float [[cc07]] [[cc08]]
// CHECK-NEXT: OpStore %mat3 [[cc09]]
float2x3 mat3 = {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}};
// Wired & complicated {}s
// CHECK-NEXT: [[cc10:%\d+]] = OpCompositeConstruct %v2float %float_1 %float_2
// CHECK-NEXT: [[cc11:%\d+]] = OpCompositeConstruct %v2float %float_3 %float_4
// CHECK-NEXT: [[cc12:%\d+]] = OpCompositeConstruct %v2float %float_5 %float_6
// CHECK-NEXT: [[cc13:%\d+]] = OpCompositeConstruct %mat3v2float [[cc10]] [[cc11]] [[cc12]]
// CHECK-NEXT: OpStore %mat4 [[cc13]]
float3x2 mat4 = {{1.0}, {2.0, 3.0}, 4.0, {{5.0}, {{6.0}}}};
float scalar;
float1 vec1;
float2 vec2;
float3 vec3;
float4 vec4;
// Mixed scalar and vector
// CHECK-NEXT: [[s:%\d+]] = OpLoad %float %scalar
// CHECK-NEXT: [[vec1:%\d+]] = OpLoad %float %vec1
// CHECK-NEXT: [[vec2:%\d+]] = OpLoad %v2float %vec2
// CHECK-NEXT: [[ce00:%\d+]] = OpCompositeExtract %float [[vec2]] 0
// CHECK-NEXT: [[ce01:%\d+]] = OpCompositeExtract %float [[vec2]] 1
// CHECK-NEXT: [[cc14:%\d+]] = OpCompositeConstruct %v4float [[s]] [[vec1]] [[ce00]] [[ce01]]
// CHECK-NEXT: [[vec3:%\d+]] = OpLoad %v3float %vec3
// CHECK-NEXT: [[ce02:%\d+]] = OpCompositeExtract %float [[vec3]] 0
// CHECK-NEXT: [[ce03:%\d+]] = OpCompositeExtract %float [[vec3]] 1
// CHECK-NEXT: [[ce04:%\d+]] = OpCompositeExtract %float [[vec3]] 2
// CHECK-NEXT: [[vec2a:%\d+]] = OpLoad %v2float %vec2
// CHECK-NEXT: [[ce05:%\d+]] = OpCompositeExtract %float [[vec2a]] 0
// CHECK-NEXT: [[ce06:%\d+]] = OpCompositeExtract %float [[vec2a]] 1
// CHECK-NEXT: [[cc15:%\d+]] = OpCompositeConstruct %v4float [[ce02]] [[ce03]] [[ce04]] [[ce05]]
// CHECK-NEXT: [[cc16:%\d+]] = OpCompositeConstruct %v4float [[ce06]] %float_1 %float_2 %float_3
// CHECK-NEXT: [[vec4:%\d+]] = OpLoad %v4float %vec4
// CHECK-NEXT: [[cc17:%\d+]] = OpCompositeConstruct %mat4v4float [[cc14]] [[cc15]] [[cc16]] [[vec4]]
// CHECK-NEXT: OpStore %mat5 [[cc17]]
float4x4 mat5 = {scalar, vec1, vec2, // [0]
vec3, vec2, // [1] + 1 scalar
float2(1., 2.), 3., // [2] - 1 scalar
vec4 // [3]
};
// From value of the same type
// CHECK-NEXT: [[mat5:%\d+]] = OpLoad %mat4v4float %mat5
// CHECK-NEXT: OpStore %mat6 [[mat5]]
float4x4 mat6 = float4x4(mat5);
int intScalar;
uint uintScalar;
bool boolScalar;
int1 intVec1;
uint2 uintVec2;
bool3 boolVec3;
// Casting
// CHECK-NEXT: [[intvec1:%\d+]] = OpLoad %int %intVec1
// CHECK-NEXT: [[convert0:%\d+]] = OpConvertSToF %float [[intvec1]]
// CHECK-NEXT: [[uintscalar:%\d+]] = OpLoad %uint %uintScalar
// CHECK-NEXT: [[convert1:%\d+]] = OpConvertUToF %float [[uintscalar]]
// CHECK-NEXT: [[uintvec2:%\d+]] = OpLoad %v2uint %uintVec2
// CHECK-NEXT: [[ce07:%\d+]] = OpCompositeExtract %uint [[uintvec2]] 0
// CHECK-NEXT: [[ce08:%\d+]] = OpCompositeExtract %uint [[uintvec2]] 1
// CHECK-NEXT: [[convert2:%\d+]] = OpConvertUToF %float [[ce07]]
// CHECK-NEXT: [[cc18:%\d+]] = OpCompositeConstruct %v3float [[convert0]] [[convert1]] [[convert2]]
// CHECK-NEXT: [[convert3:%\d+]] = OpConvertUToF %float [[ce08]]
// CHECK-NEXT: [[intscalar:%\d+]] = OpLoad %int %intScalar
// CHECK-NEXT: [[convert4:%\d+]] = OpConvertSToF %float [[intscalar]]
// CHECK-NEXT: [[boolscalar:%\d+]] = OpLoad %bool %boolScalar
// CHECK-NEXT: [[convert5:%\d+]] = OpSelect %float [[boolscalar]] %float_1 %float_0
// CHECK-NEXT: [[cc19:%\d+]] = OpCompositeConstruct %v3float [[convert3]] [[convert4]] [[convert5]]
// CHECK-NEXT: [[boolvec3:%\d+]] = OpLoad %v3bool %boolVec3
// CHECK-NEXT: [[convert6:%\d+]] = OpSelect %v3float [[boolvec3]] [[v3fc1]] [[v3fc0]]
// CHECK-NEXT: [[cc20:%\d+]] = OpCompositeConstruct %mat3v3float [[cc18]] [[cc19]] [[convert6]]
// CHECK-NEXT: OpStore %mat7 [[cc20]]
float3x3 mat7 = {intVec1, uintScalar, uintVec2, // [0] + 1 scalar
intScalar, boolScalar, // [1] - 1 scalar
boolVec3 // [2]
};
}

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

@ -44,7 +44,11 @@ TEST_F(FileTest, ScalarConstants) { runFileTest("constant.scalar.hlsl"); }
TEST_F(FileTest, VectorConstants) { runFileTest("constant.vector.hlsl"); }
// For variables
TEST_F(FileTest, VariableInitializer) { runFileTest("var.init.hlsl"); }
TEST_F(FileTest, VarInit) { runFileTest("var.init.hlsl"); }
TEST_F(FileTest, VarInitMatrixMxN) { runFileTest("var.init.matrix.mxn.hlsl"); }
TEST_F(FileTest, VarInitMatrixMx1) { runFileTest("var.init.matrix.mx1.hlsl"); }
TEST_F(FileTest, VarInitMatrix1xN) { runFileTest("var.init.matrix.1xn.hlsl"); }
TEST_F(FileTest, VarInitMatrix1x1) { runFileTest("var.init.matrix.1x1.hlsl"); }
// For prefix/postfix increment/decrement
TEST_F(FileTest, UnaryOpPrefixIncrement) {