A bunch of semantic checks were missing for binary arithmetic operations. Refactor the "promote" logic to fix these.

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@21784 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-05-30 19:15:35 +00:00
parent 0f359f0e35
commit 69762564de
5 changed files with 154 additions and 122 deletions

View File

@ -563,6 +563,7 @@ public:
void setArrayInformationType(TType* t) { arrayInformationType = t; } void setArrayInformationType(TType* t) { arrayInformationType = t; }
TType* getArrayInformationType() { return arrayInformationType; } TType* getArrayInformationType() { return arrayInformationType; }
virtual bool isVector() const { return vectorSize > 1; } virtual bool isVector() const { return vectorSize > 1; }
virtual bool isScalar() const { return vectorSize == 1; }
const char* getBasicString() const { const char* getBasicString() const {
return TType::getBasicString(basicType); return TType::getBasicString(basicType);
} }

View File

@ -360,6 +360,7 @@ public:
virtual bool isMatrix() const { return type.isMatrix(); } virtual bool isMatrix() const { return type.isMatrix(); }
virtual bool isArray() const { return type.isArray(); } virtual bool isArray() const { return type.isArray(); }
virtual bool isVector() const { return type.isVector(); } virtual bool isVector() const { return type.isVector(); }
virtual bool isScalar() const { return type.isScalar(); }
TString getCompleteString() const { return type.getCompleteString(); } TString getCompleteString() const { return type.getCompleteString(); }
protected: protected:

View File

@ -71,37 +71,11 @@ TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType
// //
TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line) TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line)
{ {
// No operations work on blocks
if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock)
return 0; return 0;
switch (op) { // Try converting the children's base types to compatible types.
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
if (left->getType().isMatrix() || left->getType().isArray() || left->getType().isVector() || left->getType().getBasicType() == EbtStruct) {
return 0;
}
break;
case EOpLogicalOr:
case EOpLogicalXor:
case EOpLogicalAnd:
if (left->getType().getBasicType() != EbtBool || left->getType().isMatrix() || left->getType().isArray() || left->getType().isVector()) {
return 0;
}
break;
case EOpAdd:
case EOpSub:
case EOpDiv:
case EOpMul:
if (left->getType().getBasicType() == EbtStruct || left->getType().getBasicType() == EbtBool || left->getType().isArray())
return 0;
default: break; // some compilers want this
}
//
// First try converting the children to compatible types.
//
TIntermTyped* child = addConversion(op, left->getType(), right); TIntermTyped* child = addConversion(op, left->getType(), right);
if (child) if (child)
right = child; right = child;
@ -153,6 +127,10 @@ TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIn
// //
TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line) TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line)
{ {
// No block assignment
if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock)
return 0;
// //
// Like adding binary math, except the conversion can only go // Like adding binary math, except the conversion can only go
// from right to left. // from right to left.
@ -991,111 +969,166 @@ void TIntermUnary::updatePrecision()
// //
bool TIntermBinary::promote(TInfoSink& infoSink) bool TIntermBinary::promote(TInfoSink& infoSink)
{ {
// // Arrays and structures have to be exact matches.
// Arrays have to be exact matches. if ((left->isArray() || right->isArray() || left->getBasicType() == EbtStruct || right->getBasicType() == EbtStruct)
// && left->getType() != right->getType())
if ((left->isArray() || right->isArray()) && (left->getType() != right->getType()))
return false; return false;
//
// Base assumption: just make the type the same as the left // Base assumption: just make the type the same as the left
// operand. Then only deviations from this need be coded. // operand. Only deviations from this will be coded.
//
setType(left->getType()); setType(left->getType());
type.getQualifier().storage = EvqTemporary; type.getQualifier().clear();
// // Finish all array and structure operations.
// Array operations. if (left->isArray() || left->getBasicType() == EbtStruct) {
//
if (left->isArray()) {
switch (op) { switch (op) {
//
// Promote to conditional
//
case EOpEqual: case EOpEqual:
case EOpNotEqual: case EOpNotEqual:
// Promote to conditional
setType(TType(EbtBool)); setType(TType(EbtBool));
break;
return true;
case EOpAssign: case EOpAssign:
// array information was correctly set above // Keep type from above
break;
return true;
default: default:
return false; return false;
} }
return true;
} }
// //
// All scalars. Code after this test assumes this case is removed! // We now have only scalars, vectors, and matrices to worry about.
// //
if (left->getVectorSize() == 1 && right->getVectorSize() == 1) {
switch (op) { // Do general type checks against individual operands (comparing left and right is coming up, checking mixed shapes after that)
switch (op) {
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
// Relational comparisons need matching numeric types and will promote to scalar Boolean.
if (left->getBasicType() == EbtBool || left->getType().isMatrix())
return false;
// // Fall through
// Promote to conditional
//
case EOpEqual:
case EOpNotEqual:
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
setType(TType(EbtBool));
break;
// case EOpEqual:
// And and Or operate only on conditionals case EOpNotEqual:
// // All the above comparisons result in a bool (but not the vector compares)
case EOpLogicalAnd: setType(TType(EbtBool));
case EOpLogicalOr: break;
if (left->getBasicType() != EbtBool || right->getBasicType() != EbtBool)
return false;
setType(TType(EbtBool));
break;
// case EOpLogicalAnd:
// Check for integer only operands. case EOpLogicalOr:
// case EOpLogicalXor:
case EOpMod: // logical ops operate only on scalar Booleans and will promote to scalar Boolean.
case EOpRightShift: if (left->getBasicType() != EbtBool || left->isVector() || left->isMatrix())
case EOpLeftShift: return false;
case EOpAnd:
case EOpInclusiveOr:
case EOpExclusiveOr:
if ( left->getBasicType() != EbtInt && left->getBasicType() != EbtUint ||
right->getBasicType() != EbtInt && right->getBasicType() != EbtUint)
return false;
break;
case EOpModAssign:
case EOpAndAssign:
case EOpInclusiveOrAssign:
case EOpExclusiveOrAssign:
case EOpLeftShiftAssign:
case EOpRightShiftAssign:
if ( left->getBasicType() != EbtInt && left->getBasicType() != EbtUint ||
right->getBasicType() != EbtInt && right->getBasicType() != EbtUint)
return false;
// fall through
// setType(TType(EbtBool));
// Everything else should have matching types break;
//
default:
if (left->getBasicType() != right->getBasicType() ||
left->isMatrix() != right->isMatrix())
return false;
}
return true; case EOpRightShift:
case EOpLeftShift:
case EOpRightShiftAssign:
case EOpLeftShiftAssign:
case EOpMod:
case EOpModAssign:
case EOpAnd:
case EOpInclusiveOr:
case EOpExclusiveOr:
case EOpAndAssign:
case EOpInclusiveOrAssign:
case EOpExclusiveOrAssign:
// Check for integer-only operands.
if ( left->getBasicType() != EbtInt && left->getBasicType() != EbtUint ||
right->getBasicType() != EbtInt && right->getBasicType() != EbtUint)
return false;
if (left->isMatrix() || right->isMatrix())
return false;
break;
case EOpAdd:
case EOpSub:
case EOpDiv:
case EOpMul:
case EOpAddAssign:
case EOpSubAssign:
case EOpMulAssign:
case EOpDivAssign:
// check for non-Boolean operands
if (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)
return false;
default:
break;
} }
// Compare left and right, and finish with the cases where the operand types must match
switch (op) {
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
case EOpEqual:
case EOpNotEqual:
case EOpLogicalAnd:
case EOpLogicalOr:
case EOpLogicalXor:
return left->getType() == right->getType();
// no shifts: they can mix types (scalar int can shift a vector uint, etc.)
case EOpMod:
case EOpModAssign:
case EOpAnd:
case EOpInclusiveOr:
case EOpExclusiveOr:
case EOpAndAssign:
case EOpInclusiveOrAssign:
case EOpExclusiveOrAssign:
case EOpAdd:
case EOpSub:
case EOpDiv:
case EOpAddAssign:
case EOpSubAssign:
case EOpDivAssign:
// Quick out in case the types do match
if (left->getType() == right->getType())
return true;
// Fall through
case EOpMul:
case EOpMulAssign:
// At least the basic type has to match
if (left->getBasicType() != right->getBasicType())
return false;
default:
break;
}
// Finish up handling the case where both operands are scalars.
if (! left->isVector() && ! left->isMatrix() &&
! right->isVector() && ! right->isMatrix())
return true;
// //
// Can these two operands be combined? // We now have a mix of scalars, vectors, or matrices, for non-relational operations.
// //
// Can these two operands be combined, what is the resulting type?
TBasicType basicType = left->getBasicType(); TBasicType basicType = left->getBasicType();
switch (op) { switch (op) {
case EOpMul: case EOpMul:
@ -1130,7 +1163,7 @@ bool TIntermBinary::promote(TInfoSink& infoSink)
// leave as component product // leave as component product
} else if (left->isVector() || right->isVector()) { } else if (left->isVector() || right->isVector()) {
op = EOpVectorTimesScalar; op = EOpVectorTimesScalar;
if (right->getVectorSize() > 1) if (right->isVector())
setType(TType(basicType, EvqTemporary, right->getVectorSize())); setType(TType(basicType, EvqTemporary, right->getVectorSize()));
} }
} else { } else {
@ -1170,10 +1203,20 @@ bool TIntermBinary::promote(TInfoSink& infoSink)
return false; return false;
} }
break; break;
case EOpRightShift:
case EOpLeftShift:
case EOpRightShiftAssign:
case EOpLeftShiftAssign:
if (right->isVector() && (! left->isVector() || right->getVectorSize() != left->getVectorSize()))
return false;
break;
case EOpAssign: case EOpAssign:
if (left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows()) if (left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows())
return false; return false;
// fall through // fall through
case EOpAdd: case EOpAdd:
case EOpSub: case EOpSub:
case EOpDiv: case EOpDiv:
@ -1194,27 +1237,15 @@ bool TIntermBinary::promote(TInfoSink& infoSink)
setType(TType(basicType, EvqTemporary, right->getVectorSize(), right->getMatrixCols(), right->getMatrixRows())); setType(TType(basicType, EvqTemporary, right->getVectorSize(), right->getMatrixCols(), right->getMatrixRows()));
break; break;
case EOpEqual:
case EOpNotEqual:
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
if (left->isMatrix() && right->isVector() ||
left->isVector() && right->isMatrix() ||
left->getBasicType() != right->getBasicType())
return false;
setType(TType(EbtBool));
break;
default: default:
return false; return false;
} }
// //
// One more check for assignment. The Resulting type has to match the left operand. // One more check for assignment.
// //
switch (op) { switch (op) {
// The resulting type has to match the left operand.
case EOpAssign: case EOpAssign:
case EOpAddAssign: case EOpAddAssign:
case EOpSubAssign: case EOpSubAssign:

View File

@ -416,7 +416,7 @@ bool TParseContext::constErrorCheck(TIntermTyped* node)
// //
bool TParseContext::integerErrorCheck(TIntermTyped* node, const char* token) bool TParseContext::integerErrorCheck(TIntermTyped* node, const char* token)
{ {
if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->getVectorSize() == 1 && ! node->isArray()) if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar() && ! node->isArray())
return false; return false;
error(node->getLine(), "scalar integer expression required", token, ""); error(node->getLine(), "scalar integer expression required", token, "");

View File

@ -601,7 +601,6 @@ function_call
} }
; ;
// TODO: clean up: can we eliminate function_call_or_method and function_call_generic?
function_call_or_method function_call_or_method
: function_call_generic { : function_call_generic {
$$ = $1; $$ = $1;