Merge branch 'master' into hlsl-frontend
This commit is contained in:
@@ -92,7 +92,7 @@ public:
|
||||
case EPrefixInternalError: append("INTERNAL ERROR: "); break;
|
||||
case EPrefixUnimplemented: append("UNIMPLEMENTED: "); break;
|
||||
case EPrefixNote: append("NOTE: "); break;
|
||||
default: append("UNKOWN ERROR: "); break;
|
||||
default: append("UNKNOWN ERROR: "); break;
|
||||
}
|
||||
}
|
||||
void location(const TSourceLoc& loc) {
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
|
||||
void checkAllocList() const;
|
||||
|
||||
// Return total size needed to accomodate user buffer of 'size',
|
||||
// Return total size needed to accommodate user buffer of 'size',
|
||||
// plus our tracking data.
|
||||
inline static size_t allocationSize(size_t size) {
|
||||
return size + 2 * guardBlockSize + headerSize();
|
||||
@@ -241,8 +241,8 @@ protected:
|
||||
int numCalls; // just an interesting statistic
|
||||
size_t totalBytes; // just an interesting statistic
|
||||
private:
|
||||
TPoolAllocator& operator=(const TPoolAllocator&); // dont allow assignment operator
|
||||
TPoolAllocator(const TPoolAllocator&); // dont allow default copy constructor
|
||||
TPoolAllocator& operator=(const TPoolAllocator&); // don't allow assignment operator
|
||||
TPoolAllocator(const TPoolAllocator&); // don't allow default copy constructor
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -411,6 +411,19 @@ public:
|
||||
clearLayout();
|
||||
}
|
||||
|
||||
// Drop just the storage qualification, which perhaps should
|
||||
// never be done, as it is fundamentally inconsistent, but need to
|
||||
// explore what downstream consumers need.
|
||||
// E.g., in a deference, it is an inconsistency between:
|
||||
// A) partially dereferenced resource is still in the storage class it started in
|
||||
// B) partially dereferenced resource is a new temporary object
|
||||
// If A, then nothing should change, if B, then everything should change, but this is half way.
|
||||
void makePartialTemporary()
|
||||
{
|
||||
storage = EvqTemporary;
|
||||
specConstant = false;
|
||||
}
|
||||
|
||||
TStorageQualifier storage : 6;
|
||||
TBuiltInVariable builtIn : 8;
|
||||
TPrecisionQualifier precision : 3;
|
||||
@@ -727,6 +740,7 @@ public:
|
||||
}
|
||||
void makeSpecConstant()
|
||||
{
|
||||
storage = EvqConst;
|
||||
specConstant = true;
|
||||
}
|
||||
static const char* getLayoutPackingString(TLayoutPacking packing)
|
||||
@@ -1209,6 +1223,7 @@ public:
|
||||
virtual int getMatrixCols() const { return matrixCols; }
|
||||
virtual int getMatrixRows() const { return matrixRows; }
|
||||
virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); }
|
||||
virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); }
|
||||
virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); }
|
||||
virtual bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; }
|
||||
virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); }
|
||||
|
||||
@@ -612,25 +612,29 @@ public:
|
||||
// if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from
|
||||
// per process threadPoolAllocator, then it causes increased memory usage per compile
|
||||
// it is essential to use "symbol = sym" to assign to symbol
|
||||
TIntermSymbol(int i, const TString& n, const TType& t) :
|
||||
TIntermTyped(t), id(i) { name = n;}
|
||||
TIntermSymbol(int i, const TString& n, const TType& t)
|
||||
: TIntermTyped(t), id(i), constSubtree(nullptr)
|
||||
{ name = n; }
|
||||
virtual int getId() const { return id; }
|
||||
virtual const TString& getName() const { return name; }
|
||||
virtual void traverse(TIntermTraverser*);
|
||||
virtual TIntermSymbol* getAsSymbolNode() { return this; }
|
||||
virtual const TIntermSymbol* getAsSymbolNode() const { return this; }
|
||||
void setConstArray(const TConstUnionArray& c) { unionArray = c; }
|
||||
const TConstUnionArray& getConstArray() const { return unionArray; }
|
||||
void setConstArray(const TConstUnionArray& c) { constArray = c; }
|
||||
const TConstUnionArray& getConstArray() const { return constArray; }
|
||||
void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; }
|
||||
TIntermTyped* getConstSubtree() const { return constSubtree; }
|
||||
protected:
|
||||
int id; // the unique id of the symbol this node represents
|
||||
TString name; // the name of the symbol this node represents
|
||||
TConstUnionArray unionArray; // if the symbol is a front-end compile-time constant, this is its value
|
||||
TConstUnionArray constArray; // if the symbol is a front-end compile-time constant, this is its value
|
||||
TIntermTyped* constSubtree;
|
||||
};
|
||||
|
||||
class TIntermConstantUnion : public TIntermTyped {
|
||||
public:
|
||||
TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), unionArray(ua), literal(false) { }
|
||||
const TConstUnionArray& getConstArray() const { return unionArray; }
|
||||
TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), constArray(ua), literal(false) { }
|
||||
const TConstUnionArray& getConstArray() const { return constArray; }
|
||||
virtual TIntermConstantUnion* getAsConstantUnion() { return this; }
|
||||
virtual const TIntermConstantUnion* getAsConstantUnion() const { return this; }
|
||||
virtual void traverse(TIntermTraverser*);
|
||||
@@ -640,7 +644,7 @@ public:
|
||||
void setExpression() { literal = false; }
|
||||
bool isLiteral() const { return literal; }
|
||||
protected:
|
||||
const TConstUnionArray unionArray;
|
||||
const TConstUnionArray constArray;
|
||||
bool literal; // true if node represents a literal in the source code
|
||||
};
|
||||
|
||||
|
||||
@@ -61,25 +61,35 @@ namespace glslang {
|
||||
// Returns the added node.
|
||||
//
|
||||
|
||||
TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TSourceLoc& loc)
|
||||
TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray,
|
||||
TIntermTyped* constSubtree, const TSourceLoc& loc)
|
||||
{
|
||||
TIntermSymbol* node = new TIntermSymbol(id, name, type);
|
||||
node->setLoc(loc);
|
||||
node->setConstArray(constArray);
|
||||
node->setConstSubtree(constSubtree);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, const TSourceLoc& loc)
|
||||
TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable)
|
||||
{
|
||||
TIntermSymbol* node = addSymbol(id, name, type, loc);
|
||||
node->setConstArray(constArray);
|
||||
glslang::TSourceLoc loc; // just a null location
|
||||
loc.init();
|
||||
|
||||
return node;
|
||||
return addSymbol(variable, loc);
|
||||
}
|
||||
|
||||
TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc)
|
||||
{
|
||||
return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), loc);
|
||||
return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc);
|
||||
}
|
||||
|
||||
TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc)
|
||||
{
|
||||
TConstUnionArray unionArray; // just a null constant
|
||||
|
||||
return addSymbol(0, "", type, unionArray, nullptr, loc);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -252,6 +262,7 @@ TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, TSo
|
||||
|
||||
//
|
||||
// For constructors, we are now done, it was all in the conversion.
|
||||
// TODO: but, did this bypass constant folding?
|
||||
//
|
||||
switch (op) {
|
||||
case EOpConstructInt:
|
||||
@@ -281,7 +292,7 @@ TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, TSo
|
||||
if (child->getAsConstantUnion())
|
||||
return child->getAsConstantUnion()->fold(op, node->getType());
|
||||
|
||||
// If it's a specialiation constant, the result is too.
|
||||
// If it's a specialization constant, the result is too.
|
||||
if (child->getType().getQualifier().isSpecConstant())
|
||||
node->getWritableType().getQualifier().makeSpecConstant();
|
||||
|
||||
@@ -474,6 +485,7 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt
|
||||
case EOpSub:
|
||||
case EOpMul:
|
||||
case EOpDiv:
|
||||
case EOpMod:
|
||||
|
||||
case EOpVectorTimesScalar:
|
||||
case EOpVectorTimesMatrix:
|
||||
@@ -605,6 +617,12 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt
|
||||
newNode->setLoc(node->getLoc());
|
||||
newNode->setOperand(node);
|
||||
|
||||
// TODO: it seems that some unary folding operations should occur here, but are not
|
||||
|
||||
// Propagate specialization-constant-ness.
|
||||
if (node->getType().getQualifier().isSpecConstant())
|
||||
newNode->getWritableType().getQualifier().makeSpecConstant();
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
@@ -813,7 +831,7 @@ TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* true
|
||||
// Make a selection node.
|
||||
//
|
||||
TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
|
||||
node->getQualifier().storage = EvqTemporary;
|
||||
node->getQualifier().makeTemporary();
|
||||
node->setLoc(loc);
|
||||
node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision);
|
||||
|
||||
@@ -872,7 +890,6 @@ TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseT
|
||||
|
||||
TIntermTyped* TIntermediate::addSwizzle(TVectorFields& fields, const TSourceLoc& loc)
|
||||
{
|
||||
|
||||
TIntermAggregate* node = new TIntermAggregate(EOpSequence);
|
||||
|
||||
node->setLoc(loc);
|
||||
@@ -1017,8 +1034,7 @@ void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymb
|
||||
const TAnonMember* anon = symbol.getAsAnonMember();
|
||||
variable = &anon->getAnonContainer();
|
||||
}
|
||||
TIntermSymbol* node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType());
|
||||
node->setConstArray(variable->getConstArray());
|
||||
TIntermSymbol* node = addSymbol(*variable);
|
||||
linkage = growAggregate(linkage, node);
|
||||
}
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
|
||||
TIntermTyped* result = nullptr;
|
||||
|
||||
int indexValue = 0;
|
||||
if (index->getQualifier().storage == EvqConst) {
|
||||
if (index->getQualifier().isFrontEndConstant()) {
|
||||
indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
|
||||
checkIndex(loc, base->getType(), indexValue);
|
||||
}
|
||||
@@ -495,7 +495,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
|
||||
error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), "");
|
||||
else
|
||||
error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
|
||||
} else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
|
||||
} else if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant())
|
||||
return intermediate.foldDereference(base, indexValue, loc);
|
||||
else {
|
||||
// at least one of base and index is variable...
|
||||
@@ -503,7 +503,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
|
||||
if (base->getAsSymbolNode() && isIoResizeArray(base->getType()))
|
||||
handleIoResizeArrayAccess(loc, base);
|
||||
|
||||
if (index->getQualifier().storage == EvqConst) {
|
||||
if (index->getQualifier().isFrontEndConstant()) {
|
||||
if (base->getType().isImplicitlySizedArray())
|
||||
updateImplicitArraySize(loc, base, indexValue);
|
||||
result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
|
||||
@@ -541,10 +541,15 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn
|
||||
} else {
|
||||
// Insert valid dereferenced result
|
||||
TType newType(base->getType(), 0); // dereferenced type
|
||||
if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
|
||||
if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) {
|
||||
newType.getQualifier().storage = EvqConst;
|
||||
else
|
||||
newType.getQualifier().storage = EvqTemporary;
|
||||
// If base or index is a specialization constant, the result should also be a specialization constant.
|
||||
if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) {
|
||||
newType.getQualifier().makeSpecConstant();
|
||||
}
|
||||
} else {
|
||||
newType.getQualifier().makePartialTemporary();
|
||||
}
|
||||
result->setType(newType);
|
||||
|
||||
if (anyIndexLimits)
|
||||
@@ -587,7 +592,7 @@ void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* b
|
||||
(! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniformOrBuffer() &&
|
||||
! base->getType().getQualifier().isPipeInput() &&
|
||||
! base->getType().getQualifier().isPipeOutput() &&
|
||||
base->getType().getQualifier().storage != EvqConst) ||
|
||||
! base->getType().getQualifier().isConstant()) ||
|
||||
(! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() ||
|
||||
base->getType().getQualifier().isPipeOutput()))) {
|
||||
// it's too early to know what the inductive variables are, save it for post processing
|
||||
@@ -820,6 +825,9 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm
|
||||
return result;
|
||||
else {
|
||||
TType type(base->getBasicType(), EvqTemporary, fields.num);
|
||||
// Swizzle operations propagate specialization-constantness
|
||||
if (base->getQualifier().isSpecConstant())
|
||||
type.getQualifier().makeSpecConstant();
|
||||
return addConstructor(loc, base, type, mapTypeToConstructorOp(type));
|
||||
}
|
||||
}
|
||||
@@ -837,6 +845,9 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm
|
||||
result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
|
||||
result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, (int) vectorString.size()));
|
||||
}
|
||||
// Swizzle operations propagate specialization-constantness
|
||||
if (base->getType().getQualifier().isSpecConstant())
|
||||
result->getWritableType().getQualifier().makeSpecConstant();
|
||||
}
|
||||
} else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
|
||||
const TTypeList* fields = base->getType().getStruct();
|
||||
@@ -849,7 +860,7 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm
|
||||
}
|
||||
}
|
||||
if (fieldFound) {
|
||||
if (base->getType().getQualifier().storage == EvqConst)
|
||||
if (base->getType().getQualifier().isFrontEndConstant())
|
||||
result = intermediate.foldDereference(base, member, loc);
|
||||
else {
|
||||
blockMemberExtensionCheck(loc, base, field);
|
||||
@@ -1017,7 +1028,7 @@ TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc,
|
||||
loc);
|
||||
}
|
||||
} else
|
||||
paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(0, "", *param.type, loc), loc);
|
||||
paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
|
||||
}
|
||||
intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
|
||||
loopNestingLevel = 0;
|
||||
@@ -1220,6 +1231,11 @@ TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction
|
||||
else
|
||||
error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method");
|
||||
}
|
||||
} else if (type.getOuterArrayNode()) {
|
||||
// If the array's outer size is specified by an intermediate node, it means the array's length
|
||||
// was specified by a specialization constant. In such a case, we should return the node of the
|
||||
// specialization constants to represent the length.
|
||||
return type.getOuterArrayNode();
|
||||
} else
|
||||
length = type.getOuterArraySize();
|
||||
} else if (type.isMatrix())
|
||||
@@ -2039,7 +2055,7 @@ void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TInt
|
||||
//
|
||||
void TParseContext::constantValueCheck(TIntermTyped* node, const char* token)
|
||||
{
|
||||
if (node->getQualifier().storage != EvqConst)
|
||||
if (! node->getQualifier().isConstant())
|
||||
error(node->getLoc(), "constant expression required", token, "");
|
||||
}
|
||||
|
||||
@@ -2204,6 +2220,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T
|
||||
|
||||
int size = 0;
|
||||
bool constType = true;
|
||||
bool specConstType = false; // value is only valid if constType is true
|
||||
bool full = false;
|
||||
bool overFull = false;
|
||||
bool matrixInMatrix = false;
|
||||
@@ -2232,12 +2249,18 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T
|
||||
if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
|
||||
full = true;
|
||||
|
||||
if (function[arg].type->getQualifier().storage != EvqConst)
|
||||
if (! function[arg].type->getQualifier().isConstant())
|
||||
constType = false;
|
||||
if (function[arg].type->getQualifier().isSpecConstant())
|
||||
specConstType = true;
|
||||
}
|
||||
|
||||
if (constType)
|
||||
type.getQualifier().storage = EvqConst;
|
||||
if (constType) {
|
||||
if (specConstType)
|
||||
type.getQualifier().makeSpecConstant();
|
||||
else
|
||||
type.getQualifier().storage = EvqConst;
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
if (function.getParamCount() == 0) {
|
||||
@@ -3099,16 +3122,27 @@ void TParseContext::updateImplicitArraySize(const TSourceLoc& loc, TIntermNode *
|
||||
// This has to be the result of a block dereference, unless it's bad shader code
|
||||
// If it's a uniform block, then an error will be issued elsewhere, but
|
||||
// return early now to avoid crashing later in this function.
|
||||
if (! deref->getLeft()->getAsSymbolNode() || deref->getLeft()->getBasicType() != EbtBlock ||
|
||||
if (deref->getLeft()->getBasicType() != EbtBlock ||
|
||||
deref->getLeft()->getType().getQualifier().storage == EvqUniform ||
|
||||
deref->getRight()->getAsConstantUnion() == nullptr)
|
||||
return;
|
||||
|
||||
blockIndex = deref->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
|
||||
const TIntermTyped* left = deref->getLeft();
|
||||
const TIntermTyped* right = deref->getRight();
|
||||
|
||||
lookupName = &deref->getLeft()->getAsSymbolNode()->getName();
|
||||
if (left->getAsBinaryNode()) {
|
||||
left = left->getAsBinaryNode()->getLeft(); // Block array access
|
||||
assert(left->isArray());
|
||||
}
|
||||
|
||||
if (! left->getAsSymbolNode())
|
||||
return;
|
||||
|
||||
blockIndex = right->getAsConstantUnion()->getConstArray()[0].getIConst();
|
||||
|
||||
lookupName = &left->getAsSymbolNode()->getName();
|
||||
if (IsAnonymous(*lookupName))
|
||||
lookupName = &(*deref->getLeft()->getType().getStruct())[blockIndex].type->getFieldName();
|
||||
lookupName = &(*left->getType().getStruct())[blockIndex].type->getFieldName();
|
||||
}
|
||||
|
||||
// Lookup the symbol, should only fail if shader code is incorrect
|
||||
@@ -3121,7 +3155,10 @@ void TParseContext::updateImplicitArraySize(const TSourceLoc& loc, TIntermNode *
|
||||
return;
|
||||
}
|
||||
|
||||
symbol->getWritableType().setImplicitArraySize(index + 1);
|
||||
if (symbol->getType().isStruct() && blockIndex != -1)
|
||||
(*symbol->getWritableType().getStruct())[blockIndex].type->setImplicitArraySize(index + 1);
|
||||
else
|
||||
symbol->getWritableType().setImplicitArraySize(index + 1);
|
||||
}
|
||||
|
||||
// Returns true if the first argument to the #line directive is the line number for the next line.
|
||||
@@ -3148,7 +3185,7 @@ void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier
|
||||
//
|
||||
if (type.getQualifier().storage == EvqConst ||
|
||||
type.getQualifier().storage == EvqConstReadOnly) {
|
||||
type.getQualifier().storage = EvqTemporary;
|
||||
type.getQualifier().makeTemporary();
|
||||
error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), "");
|
||||
}
|
||||
}
|
||||
@@ -4849,7 +4886,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
|
||||
if (! initializer) {
|
||||
// error recovery; don't leave const without constant values
|
||||
if (qualifier == EvqConst)
|
||||
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
||||
variable->getWritableType().getQualifier().makeTemporary();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -4869,21 +4906,22 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
|
||||
}
|
||||
}
|
||||
|
||||
// Uniform and global consts require a constant initializer
|
||||
if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
|
||||
// Uniforms require a compile-time constant initializer
|
||||
if (qualifier == EvqUniform && ! initializer->getType().getQualifier().isFrontEndConstant()) {
|
||||
error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
|
||||
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
||||
variable->getWritableType().getQualifier().makeTemporary();
|
||||
return nullptr;
|
||||
}
|
||||
if (qualifier == EvqConst && symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) {
|
||||
// Global consts require a constant initializer (specialization constant is okay)
|
||||
if (qualifier == EvqConst && symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) {
|
||||
error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
|
||||
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
||||
variable->getWritableType().getQualifier().makeTemporary();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Const variables require a constant initializer, depending on version
|
||||
if (qualifier == EvqConst) {
|
||||
if (initializer->getType().getQualifier().storage != EvqConst) {
|
||||
if (! initializer->getType().getQualifier().isConstant()) {
|
||||
const char* initFeature = "non-constant initializer";
|
||||
requireProfile(loc, ~EEsProfile, initFeature);
|
||||
profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature);
|
||||
@@ -4895,7 +4933,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
|
||||
//
|
||||
// "In declarations of global variables with no storage qualifier or with a const
|
||||
// qualifier any initializer must be a constant expression."
|
||||
if (symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) {
|
||||
if (symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) {
|
||||
const char* initFeature = "non-constant global initializer";
|
||||
if (relaxedErrors())
|
||||
warn(loc, "not allowed in this version", initFeature, "");
|
||||
@@ -4908,14 +4946,27 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
|
||||
// Compile-time tagging of the variable with its constant value...
|
||||
|
||||
initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
|
||||
if (! initializer || ! initializer->getAsConstantUnion() || variable->getType() != initializer->getType()) {
|
||||
if (! initializer || ! initializer->getType().getQualifier().isConstant() || variable->getType() != initializer->getType()) {
|
||||
error(loc, "non-matching or non-convertible constant type for const initializer",
|
||||
variable->getType().getStorageQualifierString(), "");
|
||||
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
||||
variable->getWritableType().getQualifier().makeTemporary();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
|
||||
// We either have a folded constant in getAsConstantUnion, or we have to use
|
||||
// the initializer's subtree in the AST to represent the computation of a
|
||||
// specialization constant.
|
||||
assert(initializer->getAsConstantUnion() || initializer->getType().getQualifier().isSpecConstant());
|
||||
if (initializer->getAsConstantUnion())
|
||||
variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
|
||||
else {
|
||||
// It's a specialization constant.
|
||||
variable->getWritableType().getQualifier().makeSpecConstant();
|
||||
|
||||
// Keep the subtree that computes the specialization constant with the variable.
|
||||
// Later, a symbol node will adopt the subtree from the variable.
|
||||
variable->setConstSubtree(initializer);
|
||||
}
|
||||
} else {
|
||||
// normal assigning of a value to a variable...
|
||||
specializationCheck(loc, initializer->getType(), "initializer");
|
||||
@@ -5076,12 +5127,17 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode*
|
||||
// if the structure constructor contains more than one parameter, then construct
|
||||
// each parameter
|
||||
|
||||
int paramCount = 0; // keeps a track of the constructor parameter number being checked
|
||||
int paramCount = 0; // keeps track of the constructor parameter number being checked
|
||||
|
||||
// for each parameter to the constructor call, check to see if the right type is passed or convert them
|
||||
// to the right type if possible (and allowed).
|
||||
// for structure constructors, just check if the right type is passed, no conversion is allowed.
|
||||
|
||||
// We don't know "top down" whether type is a specialization constant,
|
||||
// but a const becomes a specialization constant if any of its children are.
|
||||
bool hasSpecConst = false;
|
||||
bool isConstConstrutor = true;
|
||||
|
||||
for (TIntermSequence::iterator p = sequenceVector.begin();
|
||||
p != sequenceVector.end(); p++, paramCount++) {
|
||||
if (type.isArray())
|
||||
@@ -5091,13 +5147,19 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode*
|
||||
else
|
||||
newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
|
||||
|
||||
if (newNode)
|
||||
if (newNode) {
|
||||
*p = newNode;
|
||||
else
|
||||
if (! newNode->getType().getQualifier().isConstant())
|
||||
isConstConstrutor = false;
|
||||
if (newNode->getType().getQualifier().isSpecConstant())
|
||||
hasSpecConst = true;
|
||||
} else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
|
||||
if (isConstConstrutor && hasSpecConst)
|
||||
constructor->getWritableType().getQualifier().makeSpecConstant();
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
//POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
#include "../Include/PoolAlloc.h"
|
||||
#include "../Include/Common.h"
|
||||
#include "../Include/PoolAlloc.h"
|
||||
|
||||
#include "../Include/InitializeGlobals.h"
|
||||
#include "../OSDependent/osinclude.h"
|
||||
|
||||
@@ -51,10 +51,10 @@ const int EndOfInput = -1;
|
||||
//
|
||||
class TInputScanner {
|
||||
public:
|
||||
TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr, int b = 0, int f = 0) :
|
||||
TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr, int b = 0, int f = 0, bool single = false) :
|
||||
numSources(n),
|
||||
sources(reinterpret_cast<const unsigned char* const *>(s)), // up to this point, common usage is "char*", but now we need positive 8-bit characters
|
||||
lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f)
|
||||
lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f), singleLogical(single)
|
||||
{
|
||||
loc = new TSourceLoc[numSources];
|
||||
for (int i = 0; i < numSources; ++i) {
|
||||
@@ -67,6 +67,10 @@ public:
|
||||
loc[currentSource].string = -stringBias;
|
||||
loc[currentSource].line = 1;
|
||||
loc[currentSource].column = 0;
|
||||
logicalSourceLoc.string = 0;
|
||||
logicalSourceLoc.line = 1;
|
||||
logicalSourceLoc.column = 0;
|
||||
logicalSourceLoc.name = loc[0].name;
|
||||
}
|
||||
|
||||
virtual ~TInputScanner()
|
||||
@@ -82,8 +86,11 @@ public:
|
||||
|
||||
int ret = peek();
|
||||
++loc[currentSource].column;
|
||||
++logicalSourceLoc.column;
|
||||
if (ret == '\n') {
|
||||
++loc[currentSource].line;
|
||||
++logicalSourceLoc.line;
|
||||
logicalSourceLoc.column = 0;
|
||||
loc[currentSource].column = 0;
|
||||
}
|
||||
advance();
|
||||
@@ -118,6 +125,7 @@ public:
|
||||
if (currentChar > 0) {
|
||||
--currentChar;
|
||||
--loc[currentSource].column;
|
||||
--logicalSourceLoc.column;
|
||||
if (loc[currentSource].column < 0) {
|
||||
// We've moved back past a new line. Find the
|
||||
// previous newline (or start of the file) to compute
|
||||
@@ -129,6 +137,7 @@ public:
|
||||
}
|
||||
--chIndex;
|
||||
}
|
||||
logicalSourceLoc.column = (int)(currentChar - chIndex);
|
||||
loc[currentSource].column = (int)(currentChar - chIndex);
|
||||
}
|
||||
} else {
|
||||
@@ -141,23 +150,57 @@ public:
|
||||
} else
|
||||
currentChar = lengths[currentSource] - 1;
|
||||
}
|
||||
if (peek() == '\n')
|
||||
if (peek() == '\n') {
|
||||
--loc[currentSource].line;
|
||||
--logicalSourceLoc.line;
|
||||
}
|
||||
}
|
||||
|
||||
// for #line override
|
||||
void setLine(int newLine) { loc[getLastValidSourceIndex()].line = newLine; }
|
||||
void setFile(const char* filename) { loc[getLastValidSourceIndex()].name = filename; }
|
||||
void setLine(int newLine)
|
||||
{
|
||||
logicalSourceLoc.line = newLine;
|
||||
loc[getLastValidSourceIndex()].line = newLine;
|
||||
}
|
||||
|
||||
// for #line override in filename based parsing
|
||||
void setFile(const char* filename)
|
||||
{
|
||||
logicalSourceLoc.name = filename;
|
||||
loc[getLastValidSourceIndex()].name = filename;
|
||||
}
|
||||
|
||||
void setFile(const char* filename, size_t i)
|
||||
{
|
||||
if (i == getLastValidSourceIndex()) {
|
||||
logicalSourceLoc.name = filename;
|
||||
}
|
||||
loc[i].name = filename;
|
||||
}
|
||||
|
||||
void setString(int newString)
|
||||
{
|
||||
logicalSourceLoc.string = newString;
|
||||
loc[getLastValidSourceIndex()].string = newString;
|
||||
logicalSourceLoc.name = nullptr;
|
||||
loc[getLastValidSourceIndex()].name = nullptr;
|
||||
}
|
||||
|
||||
// for #include content indentation
|
||||
void setColumn(int col) { loc[getLastValidSourceIndex()].column = col; }
|
||||
void setColumn(int col)
|
||||
{
|
||||
logicalSourceLoc.column = col;
|
||||
loc[getLastValidSourceIndex()].column = col;
|
||||
}
|
||||
|
||||
const TSourceLoc& getSourceLoc() const { return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; }
|
||||
const TSourceLoc& getSourceLoc() const
|
||||
{
|
||||
if (singleLogical) {
|
||||
return logicalSourceLoc;
|
||||
} else {
|
||||
return loc[std::max(0, std::min(currentSource, numSources - finale - 1))];
|
||||
}
|
||||
}
|
||||
// Returns the index (starting from 0) of the most recent valid source string we are reading from.
|
||||
int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); }
|
||||
|
||||
@@ -204,6 +247,10 @@ protected:
|
||||
|
||||
int stringBias; // the first string that is the user's string number 0
|
||||
int finale; // number of internal strings after user's last string
|
||||
|
||||
TSourceLoc logicalSourceLoc;
|
||||
bool singleLogical; // treats the strings as a single logical string.
|
||||
// locations will be reported from the first string.
|
||||
};
|
||||
|
||||
} // end namespace glslang
|
||||
|
||||
@@ -132,7 +132,8 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil
|
||||
TIntermediate intermediate(language, version, profile);
|
||||
|
||||
TParseContext parseContext(symbolTable, intermediate, true, version, profile, spv, vulkan, language, infoSink);
|
||||
TPpContext ppContext(parseContext, TShader::ForbidInclude());
|
||||
TShader::ForbidInclude includer;
|
||||
TPpContext ppContext(parseContext, "", includer);
|
||||
TScanContext scanContext(parseContext);
|
||||
parseContext.setScanContext(&scanContext);
|
||||
parseContext.setPpContext(&ppContext);
|
||||
@@ -491,7 +492,7 @@ bool ProcessDeferred(
|
||||
TIntermediate& intermediate, // returned tree, etc.
|
||||
ProcessingContext& processingContext,
|
||||
bool requireNonempty,
|
||||
const TShader::Includer& includer
|
||||
TShader::Includer& includer
|
||||
)
|
||||
{
|
||||
if (! InitThread())
|
||||
@@ -559,7 +560,6 @@ bool ProcessDeferred(
|
||||
version = defaultVersion;
|
||||
profile = defaultProfile;
|
||||
}
|
||||
|
||||
int spv = (messages & EShMsgSpvRules) ? 100 : 0; // TODO find path to get real version number here, for now non-0 is what matters
|
||||
EShSource source = (messages & EShMsgReadHlsl) ? EShSourceHlsl : EShSourceGlsl;
|
||||
bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), versionNotFirst, defaultVersion, source, version, profile, spv);
|
||||
@@ -609,7 +609,7 @@ bool ProcessDeferred(
|
||||
parseContext = new TParseContext(symbolTable, intermediate, false, version, profile, spv, vulkan,
|
||||
compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages);
|
||||
}
|
||||
TPpContext ppContext(*parseContext, includer);
|
||||
TPpContext ppContext(*parseContext, names[numPre]? names[numPre]: "", includer);
|
||||
|
||||
// only GLSL (bison triggered, really) needs an externally set scan context
|
||||
glslang::TScanContext scanContext(*parseContext);
|
||||
@@ -886,7 +886,7 @@ bool PreprocessDeferred(
|
||||
bool forceDefaultVersionAndProfile,
|
||||
bool forwardCompatible, // give errors for use of deprecated features
|
||||
EShMessages messages, // warnings/errors/AST; things to print out
|
||||
const TShader::Includer& includer,
|
||||
TShader::Includer& includer,
|
||||
TIntermediate& intermediate, // returned tree, etc.
|
||||
std::string* outputString)
|
||||
{
|
||||
@@ -925,7 +925,7 @@ bool CompileDeferred(
|
||||
bool forwardCompatible, // give errors for use of deprecated features
|
||||
EShMessages messages, // warnings/errors/AST; things to print out
|
||||
TIntermediate& intermediate,// returned tree, etc.
|
||||
const TShader::Includer& includer)
|
||||
TShader::Includer& includer)
|
||||
{
|
||||
DoFullParse parser;
|
||||
return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
|
||||
@@ -1074,9 +1074,10 @@ int ShCompile(
|
||||
compiler->infoSink.debug.erase();
|
||||
|
||||
TIntermediate intermediate(compiler->getLanguage());
|
||||
TShader::ForbidInclude includer;
|
||||
bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr,
|
||||
"", optLevel, resources, defaultVersion, ENoProfile, false,
|
||||
forwardCompatible, messages, intermediate, TShader::ForbidInclude());
|
||||
forwardCompatible, messages, intermediate, includer);
|
||||
|
||||
//
|
||||
// Call the machine dependent compiler
|
||||
@@ -1389,7 +1390,7 @@ void TShader::setEntryPoint(const char* entryPoint)
|
||||
// Returns true for success.
|
||||
//
|
||||
bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
|
||||
bool forwardCompatible, EShMessages messages, const Includer& includer)
|
||||
bool forwardCompatible, EShMessages messages, Includer& includer)
|
||||
{
|
||||
if (! InitThread())
|
||||
return false;
|
||||
@@ -1417,7 +1418,7 @@ bool TShader::preprocess(const TBuiltInResource* builtInResources,
|
||||
bool forceDefaultVersionAndProfile,
|
||||
bool forwardCompatible, EShMessages message,
|
||||
std::string* output_string,
|
||||
const TShader::Includer& includer)
|
||||
Includer& includer)
|
||||
{
|
||||
if (! InitThread())
|
||||
return false;
|
||||
|
||||
@@ -252,11 +252,14 @@ TVariable::TVariable(const TVariable& copyOf) : TSymbol(copyOf)
|
||||
if (copyOf.numExtensions != 0)
|
||||
setExtensions(copyOf.numExtensions, copyOf.extensions);
|
||||
|
||||
if (! copyOf.unionArray.empty()) {
|
||||
if (! copyOf.constArray.empty()) {
|
||||
assert(! copyOf.type.isStruct());
|
||||
TConstUnionArray newArray(copyOf.unionArray, 0, copyOf.unionArray.size());
|
||||
unionArray = newArray;
|
||||
TConstUnionArray newArray(copyOf.constArray, 0, copyOf.constArray.size());
|
||||
constArray = newArray;
|
||||
}
|
||||
|
||||
// don't support specialization-constant subtrees in cloned tables
|
||||
constSubtree = nullptr;
|
||||
}
|
||||
|
||||
TVariable* TVariable::clone() const
|
||||
|
||||
@@ -135,7 +135,7 @@ protected:
|
||||
//
|
||||
// Variable class, meaning a symbol that's not a function.
|
||||
//
|
||||
// There could be a separate class heirarchy for Constant variables;
|
||||
// There could be a separate class hierarchy for Constant variables;
|
||||
// Only one of int, bool, or float, (or none) is correct for
|
||||
// any particular use, but it's easy to do this way, and doesn't
|
||||
// seem worth having separate classes, and "getConst" can't simply return
|
||||
@@ -144,7 +144,10 @@ protected:
|
||||
//
|
||||
class TVariable : public TSymbol {
|
||||
public:
|
||||
TVariable(const TString *name, const TType& t, bool uT = false ) : TSymbol(name), userType(uT) { type.shallowCopy(t); }
|
||||
TVariable(const TString *name, const TType& t, bool uT = false )
|
||||
: TSymbol(name),
|
||||
userType(uT),
|
||||
constSubtree(nullptr) { type.shallowCopy(t); }
|
||||
virtual TVariable* clone() const;
|
||||
virtual ~TVariable() { }
|
||||
|
||||
@@ -153,9 +156,11 @@ public:
|
||||
virtual const TType& getType() const { return type; }
|
||||
virtual TType& getWritableType() { assert(writable); return type; }
|
||||
virtual bool isUserType() const { return userType; }
|
||||
virtual const TConstUnionArray& getConstArray() const { return unionArray; }
|
||||
virtual TConstUnionArray& getWritableConstArray() { assert(writable); return unionArray; }
|
||||
virtual void setConstArray(const TConstUnionArray& constArray) { unionArray = constArray; }
|
||||
virtual const TConstUnionArray& getConstArray() const { return constArray; }
|
||||
virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; }
|
||||
virtual void setConstArray(const TConstUnionArray& array) { constArray = array; }
|
||||
virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; }
|
||||
virtual TIntermTyped* getConstSubtree() const { return constSubtree; }
|
||||
|
||||
virtual void dump(TInfoSink &infoSink) const;
|
||||
|
||||
@@ -167,7 +172,12 @@ protected:
|
||||
bool userType;
|
||||
// we are assuming that Pool Allocator will free the memory allocated to unionArray
|
||||
// when this object is destroyed
|
||||
TConstUnionArray unionArray;
|
||||
|
||||
// TODO: these two should be a union
|
||||
// A variable could be a compile-time constant, or a specialization
|
||||
// constant, or neither, but never both.
|
||||
TConstUnionArray constArray; // for compile-time constant value
|
||||
TIntermTyped* constSubtree; // for specialization constant computation
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace {
|
||||
bool is_positive_infinity(double x) {
|
||||
#ifdef _MSC_VER
|
||||
return _fpclass(x) == _FPCLASS_PINF;
|
||||
#elif defined __ANDROID__ || defined __linux__
|
||||
#elif defined __ANDROID__ || defined __linux__ || __MINGW32__ || __MINGW64__
|
||||
return std::isinf(x) && (x >= 0);
|
||||
#else
|
||||
return isinf(x) && (x >= 0);
|
||||
@@ -605,6 +605,11 @@ void TOutputTraverser::visitSymbol(TIntermSymbol* node)
|
||||
|
||||
if (! node->getConstArray().empty())
|
||||
OutputConstantUnion(infoSink, node, node->getConstArray(), depth + 1);
|
||||
else if (node->getConstSubtree()) {
|
||||
incrementDepth(node);
|
||||
node->getConstSubtree()->traverse(this);
|
||||
decrementDepth();
|
||||
}
|
||||
}
|
||||
|
||||
bool TOutputTraverser::visitLoop(TVisit /* visit */, TIntermLoop* node)
|
||||
|
||||
@@ -167,10 +167,10 @@ public:
|
||||
int getNumErrors() const { return numErrors; }
|
||||
void addPushConstantCount() { ++numPushConstants; }
|
||||
bool isRecursive() const { return recursive; }
|
||||
|
||||
TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, const TSourceLoc&);
|
||||
TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TSourceLoc&);
|
||||
|
||||
TIntermSymbol* addSymbol(const TVariable&);
|
||||
TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&);
|
||||
TIntermSymbol* addSymbol(const TType&, const TSourceLoc&);
|
||||
TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*) const;
|
||||
TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, TSourceLoc);
|
||||
TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc);
|
||||
@@ -331,6 +331,7 @@ public:
|
||||
static int getBaseAlignment(const TType&, int& size, int& stride, bool std140, bool rowMajor);
|
||||
|
||||
protected:
|
||||
TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&);
|
||||
void error(TInfoSink& infoSink, const char*);
|
||||
void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals);
|
||||
void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects);
|
||||
|
||||
@@ -611,24 +611,28 @@ int TPpContext::CPPinclude(TPpToken* ppToken)
|
||||
if (token != '\n' && token != EndOfInput) {
|
||||
parseContext.ppError(ppToken->loc, "extra content after file designation", "#include", "");
|
||||
} else {
|
||||
auto include = includer.include(filename.c_str());
|
||||
std::string sourceName = include.first;
|
||||
std::string replacement = include.second;
|
||||
if (!sourceName.empty()) {
|
||||
if (!replacement.empty()) {
|
||||
TShader::Includer::IncludeResult* res = includer.include(filename.c_str(), TShader::Includer::EIncludeRelative, currentSourceFile.c_str(), includeStack.size() + 1);
|
||||
if (res && !res->file_name.empty()) {
|
||||
if (res->file_data && res->file_length) {
|
||||
const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine();
|
||||
std::ostringstream content;
|
||||
content << "#line " << forNextLine << " " << "\"" << sourceName << "\"\n";
|
||||
content << replacement << (replacement.back() == '\n' ? "" : "\n");
|
||||
content << "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n";
|
||||
pushInput(new TokenizableString(directiveLoc, content.str(), this));
|
||||
std::ostringstream prologue;
|
||||
std::ostringstream epilogue;
|
||||
prologue << "#line " << forNextLine << " " << "\"" << res->file_name << "\"\n";
|
||||
epilogue << (res->file_data[res->file_length - 1] == '\n'? "" : "\n") << "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n";
|
||||
pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.str(), this));
|
||||
}
|
||||
// At EOF, there's no "current" location anymore.
|
||||
if (token != EndOfInput) parseContext.setCurrentColumn(0);
|
||||
// Don't accidentally return EndOfInput, which will end all preprocessing.
|
||||
return '\n';
|
||||
} else {
|
||||
parseContext.ppError(directiveLoc, replacement.c_str(), "#include", "");
|
||||
std::string message =
|
||||
res ? std::string(res->file_data, res->file_length)
|
||||
: std::string("Could not process include directive");
|
||||
parseContext.ppError(directiveLoc, message.c_str(), "#include", "");
|
||||
if (res) {
|
||||
includer.releaseInclude(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +83,10 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace glslang {
|
||||
|
||||
TPpContext::TPpContext(TParseContextBase& pc, const TShader::Includer& inclr) :
|
||||
preamble(0), strings(0), parseContext(pc), includer(inclr), inComment(false)
|
||||
TPpContext::TPpContext(TParseContextBase& pc, const std::string& rootFileName, TShader::Includer& inclr) :
|
||||
preamble(0), strings(0), parseContext(pc), includer(inclr), inComment(false),
|
||||
rootFileName(rootFileName),
|
||||
currentSourceFile(rootFileName)
|
||||
{
|
||||
InitAtomTable();
|
||||
InitScanner();
|
||||
|
||||
@@ -78,6 +78,7 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef PPCONTEXT_H
|
||||
#define PPCONTEXT_H
|
||||
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../ParseHelper.h"
|
||||
@@ -121,7 +122,7 @@ class TInputScanner;
|
||||
// Don't expect too much in terms of OO design.
|
||||
class TPpContext {
|
||||
public:
|
||||
TPpContext(TParseContextBase&, const TShader::Includer&);
|
||||
TPpContext(TParseContextBase&, const std::string& rootFileName, TShader::Includer&);
|
||||
virtual ~TPpContext();
|
||||
|
||||
void setPreamble(const char* preamble, size_t length);
|
||||
@@ -290,7 +291,7 @@ protected:
|
||||
//
|
||||
|
||||
// Used to obtain #include content.
|
||||
const TShader::Includer& includer;
|
||||
TShader::Includer& includer;
|
||||
|
||||
int InitCPP();
|
||||
int CPPdefine(TPpToken * ppToken);
|
||||
@@ -430,21 +431,40 @@ protected:
|
||||
TInputScanner* input;
|
||||
};
|
||||
|
||||
// Holds a string that can be tokenized via the tInput interface.
|
||||
class TokenizableString : public tInput {
|
||||
// Holds a reference to included file data, as well as a
|
||||
// prologue and an epilogue string. This can be scanned using the tInput
|
||||
// interface and acts as a single source string.
|
||||
class TokenizableIncludeFile : public tInput {
|
||||
public:
|
||||
// Copies str, which must be non-empty.
|
||||
TokenizableString(const TSourceLoc& startLoc, const std::string& str, TPpContext* pp)
|
||||
// Copies prologue and epilogue. The includedFile must remain valid
|
||||
// until this TokenizableIncludeFile is no longer used.
|
||||
TokenizableIncludeFile(const TSourceLoc& startLoc,
|
||||
const std::string& prologue,
|
||||
TShader::Includer::IncludeResult* includedFile,
|
||||
const std::string& epilogue,
|
||||
TPpContext* pp)
|
||||
: tInput(pp),
|
||||
str_(str),
|
||||
strings(str_.data()),
|
||||
length(str_.size()),
|
||||
scanner(1, &strings, &length),
|
||||
prologue_(prologue),
|
||||
includedFile_(includedFile),
|
||||
epilogue_(epilogue),
|
||||
scanner(3, strings, lengths, names, 0, 0, true),
|
||||
prevScanner(nullptr),
|
||||
stringInput(pp, scanner) {
|
||||
scanner.setLine(startLoc.line);
|
||||
scanner.setString(startLoc.string);
|
||||
scanner.setFile(startLoc.name);
|
||||
stringInput(pp, scanner)
|
||||
{
|
||||
strings[0] = prologue_.data();
|
||||
strings[1] = includedFile_->file_data;
|
||||
strings[2] = epilogue_.data();
|
||||
|
||||
lengths[0] = prologue_.size();
|
||||
lengths[1] = includedFile_->file_length;
|
||||
lengths[2] = epilogue_.size();
|
||||
|
||||
scanner.setLine(startLoc.line);
|
||||
scanner.setString(startLoc.string);
|
||||
|
||||
scanner.setFile(startLoc.name, 0);
|
||||
scanner.setFile(startLoc.name, 1);
|
||||
scanner.setFile(startLoc.name, 2);
|
||||
}
|
||||
|
||||
// tInput methods:
|
||||
@@ -456,16 +476,34 @@ protected:
|
||||
{
|
||||
prevScanner = pp->parseContext.getScanner();
|
||||
pp->parseContext.setScanner(&scanner);
|
||||
pp->push_include(includedFile_);
|
||||
}
|
||||
|
||||
void notifyDeleted() override
|
||||
{
|
||||
pp->parseContext.setScanner(prevScanner);
|
||||
pp->pop_include();
|
||||
}
|
||||
void notifyDeleted() override { pp->parseContext.setScanner(prevScanner); }
|
||||
|
||||
private:
|
||||
// Stores the titular string.
|
||||
const std::string str_;
|
||||
// Will point to str_[0] and be passed to scanner constructor.
|
||||
const char* const strings;
|
||||
// Stores the prologue for this string.
|
||||
const std::string prologue_;
|
||||
|
||||
// Stores the epilogue for this string.
|
||||
const std::string epilogue_;
|
||||
|
||||
// Points to the IncludeResult that this TokenizableIncludeFile represents.
|
||||
TShader::Includer::IncludeResult* includedFile_;
|
||||
|
||||
// Will point to prologue_, includedFile_->file_data and epilogue_
|
||||
// This is passed to scanner constructor.
|
||||
// These do not own the storage and it must remain valid until this
|
||||
// object has been destroyed.
|
||||
const char* strings[3];
|
||||
// Length of str_, passed to scanner constructor.
|
||||
size_t length;
|
||||
size_t lengths[3];
|
||||
// String names
|
||||
const char* names[3];
|
||||
// Scans over str_.
|
||||
TInputScanner scanner;
|
||||
// The previous effective scanner before the scanner in this instance
|
||||
@@ -480,6 +518,24 @@ protected:
|
||||
void missingEndifCheck();
|
||||
int lFloatConst(int len, int ch, TPpToken* ppToken);
|
||||
|
||||
void push_include(TShader::Includer::IncludeResult* result)
|
||||
{
|
||||
currentSourceFile = result->file_name;
|
||||
includeStack.push(result);
|
||||
}
|
||||
|
||||
void pop_include()
|
||||
{
|
||||
TShader::Includer::IncludeResult* include = includeStack.top();
|
||||
includeStack.pop();
|
||||
includer.releaseInclude(include);
|
||||
if (includeStack.empty()) {
|
||||
currentSourceFile = rootFileName;
|
||||
} else {
|
||||
currentSourceFile = includeStack.top()->file_name;
|
||||
}
|
||||
}
|
||||
|
||||
bool inComment;
|
||||
|
||||
//
|
||||
@@ -487,8 +543,12 @@ protected:
|
||||
//
|
||||
typedef TUnorderedMap<TString, int> TAtomMap;
|
||||
typedef TVector<const TString*> TStringMap;
|
||||
|
||||
TAtomMap atomMap;
|
||||
TStringMap stringMap;
|
||||
std::stack<TShader::Includer::IncludeResult*> includeStack;
|
||||
std::string currentSourceFile;
|
||||
std::string rootFileName;
|
||||
int nextAtom;
|
||||
void InitAtomTable();
|
||||
void AddAtomFixed(const char* s, int atom);
|
||||
|
||||
@@ -61,7 +61,7 @@ static void DetachThreadLinux(void *)
|
||||
|
||||
|
||||
//
|
||||
// Registers cleanup handler, sets cancel type and state, and excecutes the thread specific
|
||||
// Registers cleanup handler, sets cancel type and state, and executes the thread specific
|
||||
// cleanup handler. This function will be called in the Standalone.cpp for regression
|
||||
// testing. When OpenGL applications are run with the driver code, Linux OS does the
|
||||
// thread cleanup.
|
||||
|
||||
@@ -4,6 +4,12 @@ set(SOURCES ossource.cpp ../osinclude.h)
|
||||
|
||||
add_library(OSDependent STATIC ${SOURCES})
|
||||
|
||||
# MinGW GCC complains about function pointer casts to void*.
|
||||
# Turn that off with -fpermissive.
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||
target_compile_options(OSDependent PRIVATE -fpermissive)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
source_group("Source" FILES ${SOURCES})
|
||||
endif(WIN32)
|
||||
|
||||
@@ -300,25 +300,90 @@ public:
|
||||
void setEntryPoint(const char* entryPoint);
|
||||
|
||||
// Interface to #include handlers.
|
||||
//
|
||||
// To support #include, a client of Glslang does the following:
|
||||
// 1. Call setStringsWithNames to set the source strings and associated
|
||||
// names. For example, the names could be the names of the files
|
||||
// containing the shader sources.
|
||||
// 2. Call parse with an Includer.
|
||||
//
|
||||
// When the Glslang parser encounters an #include directive, it calls
|
||||
// the Includer's include method with the the requested include name
|
||||
// together with the current string name. The returned IncludeResult
|
||||
// contains the fully resolved name of the included source, together
|
||||
// with the source text that should replace the #include directive
|
||||
// in the source stream. After parsing that source, Glslang will
|
||||
// release the IncludeResult object.
|
||||
class Includer {
|
||||
public:
|
||||
// On success, returns the full path and content of the file with the given
|
||||
// filename that replaces "#include filename". On failure, returns an empty
|
||||
// string and an error message.
|
||||
virtual std::pair<std::string, std::string> include(const char* filename) const = 0;
|
||||
typedef enum {
|
||||
EIncludeRelative, // For #include "something"
|
||||
EIncludeStandard // Reserved. For #include <something>
|
||||
} IncludeType;
|
||||
|
||||
// An IncludeResult contains the resolved name and content of a source
|
||||
// inclusion.
|
||||
struct IncludeResult {
|
||||
// For a successful inclusion, the fully resolved name of the requested
|
||||
// include. For example, in a filesystem-based includer, full resolution
|
||||
// should convert a relative path name into an absolute path name.
|
||||
// For a failed inclusion, this is an empty string.
|
||||
std::string file_name;
|
||||
// The content and byte length of the requested inclusion. The
|
||||
// Includer producing this IncludeResult retains ownership of the
|
||||
// storage.
|
||||
// For a failed inclusion, the file_data
|
||||
// field points to a string containing error details.
|
||||
const char* file_data;
|
||||
const size_t file_length;
|
||||
// Include resolver's context.
|
||||
void* user_data;
|
||||
};
|
||||
|
||||
// Resolves an inclusion request by name, type, current source name,
|
||||
// and include depth.
|
||||
// On success, returns an IncludeResult containing the resolved name
|
||||
// and content of the include. On failure, returns an IncludeResult
|
||||
// with an empty string for the file_name and error details in the
|
||||
// file_data field. The Includer retains ownership of the contents
|
||||
// of the returned IncludeResult value, and those contents must
|
||||
// remain valid until the releaseInclude method is called on that
|
||||
// IncludeResult object.
|
||||
virtual IncludeResult* include(const char* requested_source,
|
||||
IncludeType type,
|
||||
const char* requesting_source,
|
||||
size_t inclusion_depth) = 0;
|
||||
// Signals that the parser will no longer use the contents of the
|
||||
// specified IncludeResult.
|
||||
virtual void releaseInclude(IncludeResult* result) = 0;
|
||||
};
|
||||
|
||||
// Returns an error message for any #include directive.
|
||||
class ForbidInclude : public Includer {
|
||||
public:
|
||||
std::pair<std::string, std::string> include(const char* /*filename*/) const override
|
||||
IncludeResult* include(const char*, IncludeType, const char*, size_t) override
|
||||
{
|
||||
return std::make_pair<std::string, std::string>("", "unexpected include directive");
|
||||
static const char unexpected_include[] =
|
||||
"unexpected include directive";
|
||||
static const IncludeResult unexpectedIncludeResult =
|
||||
{"", unexpected_include, sizeof(unexpected_include) - 1, nullptr};
|
||||
return new IncludeResult(unexpectedIncludeResult);
|
||||
}
|
||||
virtual void releaseInclude(IncludeResult* result) override
|
||||
{
|
||||
delete result;
|
||||
}
|
||||
};
|
||||
|
||||
bool parse(const TBuiltInResource* res, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
|
||||
bool forwardCompatible, EShMessages messages)
|
||||
{
|
||||
TShader::ForbidInclude includer;
|
||||
return parse(res, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, includer);
|
||||
}
|
||||
|
||||
bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
|
||||
bool forwardCompatible, EShMessages, const Includer& = ForbidInclude());
|
||||
bool forwardCompatible, EShMessages, Includer&);
|
||||
|
||||
// Equivalent to parse() without a default profile and without forcing defaults.
|
||||
// Provided for backwards compatibility.
|
||||
@@ -326,7 +391,7 @@ public:
|
||||
bool preprocess(const TBuiltInResource* builtInResources,
|
||||
int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
|
||||
bool forwardCompatible, EShMessages message, std::string* outputString,
|
||||
const TShader::Includer& includer);
|
||||
Includer& includer);
|
||||
|
||||
const char* getInfoLog();
|
||||
const char* getInfoDebugLog();
|
||||
|
||||
Reference in New Issue
Block a user