Be able to determine if two types can be converted without requiring an Expr *.

The Expr::TypeConv() method has been replaced with both a
CanConvertTypes() routine that indicates whether one type
can be converted to another and a TypeConvertExpr()
routine that provides the same functionality as
Expr::TypeConv() used to.
This commit is contained in:
Matt Pharr
2011-10-30 14:12:12 -07:00
parent d5a8538192
commit e009c0a61d
5 changed files with 231 additions and 176 deletions

17
ctx.cpp
View File

@@ -652,18 +652,19 @@ FunctionEmitContext::CurrentLanesReturned(Expr *expr, bool doCoherenceCheck) {
} }
else { else {
if (expr == NULL) { if (expr == NULL) {
Error(funcStartPos, Error(funcStartPos, "Must provide return value for return "
"Must provide return value for return statement for non-void function."); "statement for non-void function.");
return; return;
} }
// Use a masked store to store the value of the expression in the expr = TypeConvertExpr(expr, returnType, "return statement");
// return value memory; this preserves the return values from other if (expr != NULL) {
// lanes that may have executed return statements previously. llvm::Value *retVal = expr->GetValue(this);
Expr *r = expr->TypeConv(returnType, "return statement");
if (r != NULL) {
llvm::Value *retVal = r->GetValue(this);
if (retVal != NULL) if (retVal != NULL)
// Use a masked store to store the value of the expression
// in the return value memory; this preserves the return
// values from other lanes that may have executed return
// statements previously.
StoreInst(retVal, returnValuePtr, GetInternalMask(), returnType); StoreInst(retVal, returnValuePtr, GetInternalMask(), returnType);
} }
} }

339
expr.cpp
View File

@@ -125,33 +125,34 @@ lMaybeIssuePrecisionWarning(const AtomicType *toAtomicType,
} }
#endif #endif
Expr * ///////////////////////////////////////////////////////////////////////////
Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
bool issuePrecisionWarnings) { static bool
lDoTypeConv(const Type *fromType, const Type *toType, Expr **expr,
bool failureOk, const char *errorMsgBase, SourcePos pos) {
/* This function is way too long and complex. Is type conversion stuff /* This function is way too long and complex. Is type conversion stuff
always this messy, or can this be cleaned up somehow? */ always this messy, or can this be cleaned up somehow? */
assert(failureOk || errorMsgBase != NULL); assert(failureOk || errorMsgBase != NULL);
const Type *fromType = GetType();
if (toType == NULL || fromType == NULL) if (toType == NULL || fromType == NULL)
return this; return false;
// The types are equal; there's nothing to do // The types are equal; there's nothing to do
if (Type::Equal(toType, fromType)) if (Type::Equal(toType, fromType))
return this; return true;
if (fromType == AtomicType::Void) { if (fromType == AtomicType::Void) {
if (!failureOk) if (!failureOk)
Error(pos, "Can't convert from \"void\" to \"%s\" for %s.", Error(pos, "Can't convert from \"void\" to \"%s\" for %s.",
toType->GetString().c_str(), errorMsgBase); toType->GetString().c_str(), errorMsgBase);
return NULL; return false;
} }
if (toType == AtomicType::Void) { if (toType == AtomicType::Void) {
if (!failureOk) if (!failureOk)
Error(pos, "Can't convert type \"%s\" to \"void\" for %s.", Error(pos, "Can't convert type \"%s\" to \"void\" for %s.",
fromType->GetString().c_str(), errorMsgBase); fromType->GetString().c_str(), errorMsgBase);
return NULL; return false;
} }
if (toType->IsUniformType() && fromType->IsVaryingType()) { if (toType->IsUniformType() && fromType->IsVaryingType()) {
@@ -159,13 +160,24 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
Error(pos, "Can't convert from varying type \"%s\" to uniform " Error(pos, "Can't convert from varying type \"%s\" to uniform "
"type \"%s\" for %s.", fromType->GetString().c_str(), "type \"%s\" for %s.", fromType->GetString().c_str(),
toType->GetString().c_str(), errorMsgBase); toType->GetString().c_str(), errorMsgBase);
return NULL; return false;
} }
const ArrayType *toArrayType = dynamic_cast<const ArrayType *>(toType);
const ArrayType *fromArrayType = dynamic_cast<const ArrayType *>(fromType);
const VectorType *toVectorType = dynamic_cast<const VectorType *>(toType);
const VectorType *fromVectorType = dynamic_cast<const VectorType *>(fromType);
const StructType *toStructType = dynamic_cast<const StructType *>(toType);
const StructType *fromStructType = dynamic_cast<const StructType *>(fromType);
const EnumType *toEnumType = dynamic_cast<const EnumType *>(toType);
const EnumType *fromEnumType = dynamic_cast<const EnumType *>(fromType);
const AtomicType *toAtomicType = dynamic_cast<const AtomicType *>(toType);
const AtomicType *fromAtomicType = dynamic_cast<const AtomicType *>(fromType);
// Convert from type T -> const T; just return a TypeCast expr, which // Convert from type T -> const T; just return a TypeCast expr, which
// can handle this // can handle this
if (Type::Equal(toType, fromType->GetAsConstType())) if (Type::Equal(toType, fromType->GetAsConstType()))
return new TypeCastExpr(toType, this, false, pos); goto typecast_ok;
if (dynamic_cast<const ReferenceType *>(fromType)) { if (dynamic_cast<const ReferenceType *>(fromType)) {
if (dynamic_cast<const ReferenceType *>(toType)) { if (dynamic_cast<const ReferenceType *>(toType)) {
@@ -173,75 +185,91 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
// this is handled by TypeCastExpr // this is handled by TypeCastExpr
if (Type::Equal(toType->GetReferenceTarget(), if (Type::Equal(toType->GetReferenceTarget(),
fromType->GetReferenceTarget()->GetAsConstType())) fromType->GetReferenceTarget()->GetAsConstType()))
return new TypeCastExpr(toType, this, false, pos); goto typecast_ok;
const ArrayType *atFrom =
dynamic_cast<const ArrayType *>(fromType->GetReferenceTarget());
const ArrayType *atTo =
dynamic_cast<const ArrayType *>(toType->GetReferenceTarget());
const ArrayType *atFrom = dynamic_cast<const ArrayType *>(fromType->GetReferenceTarget());
const ArrayType *atTo = dynamic_cast<const ArrayType *>(toType->GetReferenceTarget());
if (atFrom != NULL && atTo != NULL && if (atFrom != NULL && atTo != NULL &&
Type::Equal(atFrom->GetElementType(), atTo->GetElementType())) Type::Equal(atFrom->GetElementType(), atTo->GetElementType())) {
return new TypeCastExpr(toType, this, false, pos); goto typecast_ok;
}
else { else {
if (!failureOk) if (!failureOk)
Error(pos, "Can't convert between incompatible reference types \"%s\" " Error(pos, "Can't convert between incompatible reference types \"%s\" "
"and \"%s\" for %s.", fromType->GetString().c_str(), "and \"%s\" for %s.", fromType->GetString().c_str(),
toType->GetString().c_str(), errorMsgBase); toType->GetString().c_str(), errorMsgBase);
return NULL; return false;
} }
} }
else { else {
// convert from a reference T -> T // convert from a reference T -> T
Expr *fromExpr = new DereferenceExpr(this, pos); if (expr != NULL) {
if (fromExpr->GetType() == NULL) Expr *drExpr = new DereferenceExpr(*expr, pos);
return NULL; if (lDoTypeConv(drExpr->GetType(), toType, &drExpr, failureOk,
return fromExpr->TypeConv(toType, errorMsgBase, failureOk); errorMsgBase, pos) == true) {
*expr = drExpr;
return true;
}
return false;
}
else
return lDoTypeConv(fromType->GetReferenceTarget(), toType, NULL,
failureOk, errorMsgBase, pos);
} }
} }
else if (dynamic_cast<const ReferenceType *>(toType)) { else if (dynamic_cast<const ReferenceType *>(toType)) {
// T -> reference T // T -> reference T
Expr *fromExpr = new ReferenceExpr(this, pos); if (expr != NULL) {
if (fromExpr->GetType() == NULL) Expr *rExpr = new ReferenceExpr(*expr, pos);
return NULL; if (lDoTypeConv(rExpr->GetType(), toType, &rExpr, failureOk,
return fromExpr->TypeConv(toType, errorMsgBase, failureOk); errorMsgBase, pos) == true) {
*expr = rExpr;
return true;
}
return false;
}
else
return lDoTypeConv(new ReferenceType(fromType, toType->IsConstType()),
toType, NULL, failureOk, errorMsgBase, pos);
} }
else if (Type::Equal(toType, fromType->GetAsNonConstType())) else if (Type::Equal(toType, fromType->GetAsNonConstType()))
// convert: const T -> T (as long as T isn't a reference) // convert: const T -> T (as long as T isn't a reference)
return new TypeCastExpr(toType, this, false, pos); goto typecast_ok;
fromType = fromType->GetReferenceTarget(); fromType = fromType->GetReferenceTarget();
toType = toType->GetReferenceTarget(); toType = toType->GetReferenceTarget();
// I don't think this is necessary
//CO if (Type::Equal(toType, fromType))
//CO return fromExpr;
const ArrayType *toArrayType = dynamic_cast<const ArrayType *>(toType);
const ArrayType *fromArrayType = dynamic_cast<const ArrayType *>(fromType);
if (toArrayType && fromArrayType) { if (toArrayType && fromArrayType) {
if (Type::Equal(toArrayType->GetElementType(), if (Type::Equal(toArrayType->GetElementType(),
fromArrayType->GetElementType())) { fromArrayType->GetElementType())) {
// the case of different element counts should have returned // the case of different element counts should have returned
// out earlier, yes?? // out earlier, yes??
assert(toArrayType->GetElementCount() != fromArrayType->GetElementCount()); assert(toArrayType->GetElementCount() != fromArrayType->GetElementCount());
return new TypeCastExpr(new ReferenceType(toType, false), this, if (expr != NULL)
false, pos); *expr = new TypeCastExpr(new ReferenceType(toType, false),
*expr, false, pos);
return true;
} }
else if (Type::Equal(toArrayType->GetElementType(), else if (Type::Equal(toArrayType->GetElementType(),
fromArrayType->GetElementType()->GetAsConstType())) { fromArrayType->GetElementType()->GetAsConstType())) {
// T[x] -> const T[x] // T[x] -> const T[x]
return new TypeCastExpr(new ReferenceType(toType, false), this, if (expr != NULL)
false, pos); *expr = new TypeCastExpr(new ReferenceType(toType, false),
*expr, false, pos);
return true;
} }
else { else {
if (!failureOk) if (!failureOk)
Error(pos, "Array type \"%s\" can't be converted to type \"%s\" for %s.", Error(pos, "Array type \"%s\" can't be converted to type \"%s\" for %s.",
fromType->GetString().c_str(), toType->GetString().c_str(), fromType->GetString().c_str(), toType->GetString().c_str(),
errorMsgBase); errorMsgBase);
return NULL; return false;
} }
} }
const VectorType *toVectorType = dynamic_cast<const VectorType *>(toType);
const VectorType *fromVectorType = dynamic_cast<const VectorType *>(fromType);
if (toVectorType && fromVectorType) { if (toVectorType && fromVectorType) {
// converting e.g. int<n> -> float<n> // converting e.g. int<n> -> float<n>
if (fromVectorType->GetElementCount() != toVectorType->GetElementCount()) { if (fromVectorType->GetElementCount() != toVectorType->GetElementCount()) {
@@ -249,13 +277,11 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
Error(pos, "Can't convert between differently sized vector types " Error(pos, "Can't convert between differently sized vector types "
"\"%s\" -> \"%s\" for %s.", fromType->GetString().c_str(), "\"%s\" -> \"%s\" for %s.", fromType->GetString().c_str(),
toType->GetString().c_str(), errorMsgBase); toType->GetString().c_str(), errorMsgBase);
return NULL; return false;
} }
return new TypeCastExpr(toType, this, false, pos); goto typecast_ok;
} }
const StructType *toStructType = dynamic_cast<const StructType *>(toType);
const StructType *fromStructType = dynamic_cast<const StructType *>(fromType);
if (toStructType && fromStructType) { if (toStructType && fromStructType) {
if (!Type::Equal(toStructType->GetAsUniformType()->GetAsConstType(), if (!Type::Equal(toStructType->GetAsUniformType()->GetAsConstType(),
fromStructType->GetAsUniformType()->GetAsConstType())) { fromStructType->GetAsUniformType()->GetAsConstType())) {
@@ -263,14 +289,11 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
Error(pos, "Can't convert between different struct types " Error(pos, "Can't convert between different struct types "
"\"%s\" -> \"%s\".", fromStructType->GetString().c_str(), "\"%s\" -> \"%s\".", fromStructType->GetString().c_str(),
toStructType->GetString().c_str()); toStructType->GetString().c_str());
return NULL; return false;
} }
goto typecast_ok;
return new TypeCastExpr(toType, this, false, pos);
} }
const EnumType *toEnumType = dynamic_cast<const EnumType *>(toType);
const EnumType *fromEnumType = dynamic_cast<const EnumType *>(fromType);
if (toEnumType != NULL && fromEnumType != NULL) { if (toEnumType != NULL && fromEnumType != NULL) {
// No implicit conversions between different enum types // No implicit conversions between different enum types
if (!Type::Equal(toEnumType->GetAsUniformType()->GetAsConstType(), if (!Type::Equal(toEnumType->GetAsUniformType()->GetAsConstType(),
@@ -279,19 +302,15 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
Error(pos, "Can't convert between different enum types " Error(pos, "Can't convert between different enum types "
"\"%s\" -> \"%s\".", fromEnumType->GetString().c_str(), "\"%s\" -> \"%s\".", fromEnumType->GetString().c_str(),
toEnumType->GetString().c_str()); toEnumType->GetString().c_str());
return NULL; return false;
} }
goto typecast_ok;
return new TypeCastExpr(toType, this, false, pos);
} }
const AtomicType *toAtomicType = dynamic_cast<const AtomicType *>(toType);
const AtomicType *fromAtomicType = dynamic_cast<const AtomicType *>(fromType);
// enum -> atomic (integer, generally...) is always ok // enum -> atomic (integer, generally...) is always ok
if (fromEnumType != NULL) { if (fromEnumType != NULL) {
assert(toAtomicType != NULL || toVectorType != NULL); assert(toAtomicType != NULL || toVectorType != NULL);
return new TypeCastExpr(toType, this, false, pos); goto typecast_ok;
} }
// from here on out, the from type can only be atomic something or // from here on out, the from type can only be atomic something or
@@ -301,12 +320,12 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
Error(pos, "Type conversion only possible from atomic types, not " Error(pos, "Type conversion only possible from atomic types, not "
"from \"%s\" to \"%s\", for %s.", fromType->GetString().c_str(), "from \"%s\" to \"%s\", for %s.", fromType->GetString().c_str(),
toType->GetString().c_str(), errorMsgBase); toType->GetString().c_str(), errorMsgBase);
return NULL; return false;
} }
// scalar -> short-vector conversions // scalar -> short-vector conversions
if (toVectorType != NULL) if (toVectorType != NULL)
return new TypeCastExpr(toType, this, false, pos); goto typecast_ok;
// ok, it better be a scalar->scalar conversion of some sort by now // ok, it better be a scalar->scalar conversion of some sort by now
if (toAtomicType == NULL) { if (toAtomicType == NULL) {
@@ -315,17 +334,34 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk,
"from \"%s\" to \"%s\", for %s.", "from \"%s\" to \"%s\", for %s.",
fromType->GetString().c_str(), toType->GetString().c_str(), fromType->GetString().c_str(), toType->GetString().c_str(),
errorMsgBase); errorMsgBase);
return NULL; return false;
} }
#if 0 typecast_ok:
// Disable: it's not clear this is actually all that useful if (expr != NULL)
if (!failureOk && issuePrecisionWarnings) *expr = new TypeCastExpr(toType, *expr, false, pos);
lMaybeIssuePrecisionWarning(toAtomicType, fromAtomicType, pos, return true;
errorMsgBase); }
#endif
return new TypeCastExpr(toType, this, false, pos);
bool
CanConvertTypes(const Type *fromType, const Type *toType) {
return lDoTypeConv(fromType, toType, NULL, true, NULL, SourcePos());
}
Expr *
TypeConvertExpr(Expr *expr, const Type *toType, const char *errorMsgBase) {
if (expr == NULL)
return NULL;
const Type *fromType = expr->GetType();
Expr *e = expr;
if (lDoTypeConv(fromType, toType, &e, false, errorMsgBase,
expr->pos))
return e;
else
return NULL;
} }
@@ -770,8 +806,8 @@ UnaryExpr::TypeCheck() {
} }
else if (op == LogicalNot) { else if (op == LogicalNot) {
const Type *boolType = lMatchingBoolType(type); const Type *boolType = lMatchingBoolType(type);
expr = expr->TypeConv(boolType, "logical not"); expr = TypeConvertExpr(expr, boolType, "logical not");
if (!expr) if (expr == NULL)
return NULL; return NULL;
} }
else if (op == BitNot) { else if (op == BitNot) {
@@ -1383,10 +1419,13 @@ BinaryExpr::TypeCheck() {
bool isVarying = (type0->IsVaryingType() || bool isVarying = (type0->IsVaryingType() ||
type1->IsVaryingType()); type1->IsVaryingType());
if (isVarying) { if (isVarying) {
arg0 = arg0->TypeConv(type0->GetAsVaryingType(), "shift operator"); arg0 = TypeConvertExpr(arg0, type0->GetAsVaryingType(),
"shift operator");
if (arg0 == NULL)
return NULL;
type0 = arg0->GetType(); type0 = arg0->GetType();
} }
arg1 = arg1->TypeConv(type0, "shift operator", false, false); arg1 = TypeConvertExpr(arg1, type0, "shift operator");
if (arg1 == NULL) if (arg1 == NULL)
return NULL; return NULL;
} }
@@ -1396,8 +1435,8 @@ BinaryExpr::TypeCheck() {
if (promotedType == NULL) if (promotedType == NULL)
return NULL; return NULL;
arg0 = arg0->TypeConv(promotedType, "binary bit op"); arg0 = TypeConvertExpr(arg0, promotedType, "binary bit op");
arg1 = arg1->TypeConv(promotedType, "binary bit op"); arg1 = TypeConvertExpr(arg1, promotedType, "binary bit op");
if (arg0 == NULL || arg1 == NULL) if (arg0 == NULL || arg1 == NULL)
return NULL; return NULL;
} }
@@ -1431,9 +1470,9 @@ BinaryExpr::TypeCheck() {
if (promotedType == NULL) if (promotedType == NULL)
return NULL; return NULL;
arg0 = arg0->TypeConv(promotedType, lOpString(op)); arg0 = TypeConvertExpr(arg0, promotedType, lOpString(op));
arg1 = arg1->TypeConv(promotedType, lOpString(op)); arg1 = TypeConvertExpr(arg1, promotedType, lOpString(op));
if (!arg0 || !arg1) if (arg0 == NULL || arg1 == NULL)
return NULL; return NULL;
return this; return this;
} }
@@ -1459,9 +1498,9 @@ BinaryExpr::TypeCheck() {
if (promotedType == NULL) if (promotedType == NULL)
return NULL; return NULL;
arg0 = arg0->TypeConv(promotedType, lOpString(op)); arg0 = TypeConvertExpr(arg0, promotedType, lOpString(op));
arg1 = arg1->TypeConv(promotedType, lOpString(op)); arg1 = TypeConvertExpr(arg1, promotedType, lOpString(op));
if (!arg0 || !arg1) if (arg0 == NULL || arg1 == NULL)
return NULL; return NULL;
return this; return this;
} }
@@ -1490,10 +1529,10 @@ BinaryExpr::TypeCheck() {
destType = new VectorType(boolType, vtype1->GetElementCount()); destType = new VectorType(boolType, vtype1->GetElementCount());
else else
destType = boolType; destType = boolType;
arg0 = arg0->TypeConv(destType, lOpString(op)); arg0 = TypeConvertExpr(arg0, destType, lOpString(op));
arg1 = arg1->TypeConv(destType, lOpString(op)); arg1 = TypeConvertExpr(arg1, destType, lOpString(op));
if (!arg0 || !arg1) if (arg0 == NULL || arg1 == NULL)
return NULL; return NULL;
return this; return this;
} }
@@ -1726,9 +1765,11 @@ AssignExpr::TypeCheck() {
lvalue = lvalue->TypeCheck(); lvalue = lvalue->TypeCheck();
if (rvalue != NULL) if (rvalue != NULL)
rvalue = rvalue->TypeCheck(); rvalue = rvalue->TypeCheck();
if (rvalue != NULL && lvalue != NULL) if (lvalue == NULL || rvalue == NULL)
rvalue = rvalue->TypeConv(lvalue->GetType(), "assignment"); return NULL;
if (rvalue == NULL || lvalue == NULL)
rvalue = TypeConvertExpr(rvalue, lvalue->GetType(), "assignment");
if (rvalue == NULL)
return NULL; return NULL;
if (lvalue->GetType()->IsConstType()) { if (lvalue->GetType()->IsConstType()) {
@@ -1982,8 +2023,8 @@ SelectExpr::TypeCheck() {
const Type *testType = test->GetType(); const Type *testType = test->GetType();
if (testType == NULL) if (testType == NULL)
return NULL; return NULL;
test = test->TypeConv(lMatchingBoolType(testType), "select"); test = TypeConvertExpr(test, lMatchingBoolType(testType), "select");
if (testType == NULL) if (test == NULL)
return NULL; return NULL;
testType = test->GetType(); testType = test->GetType();
@@ -1994,9 +2035,9 @@ SelectExpr::TypeCheck() {
if (promotedType == NULL) if (promotedType == NULL)
return NULL; return NULL;
expr1 = expr1->TypeConv(promotedType, "select"); expr1 = TypeConvertExpr(expr1, promotedType, "select");
expr2 = expr2->TypeConv(promotedType, "select"); expr2 = TypeConvertExpr(expr2, promotedType, "select");
if (!expr1 || !expr2) if (expr1 == NULL || expr2 == NULL)
return NULL; return NULL;
return this; return this;
@@ -2099,7 +2140,8 @@ FunctionCallExpr::GetValue(FunctionEmitContext *ctx) const {
} }
// Do whatever type conversion is needed // Do whatever type conversion is needed
argExpr = argExpr->TypeConv(argTypes[i], "function call argument"); argExpr = TypeConvertExpr(argExpr, argTypes[i],
"function call argument");
// The function overload resolution code should have ensured that // The function overload resolution code should have ensured that
// we can successfully do any type conversions needed here. // we can successfully do any type conversions needed here.
assert(argExpr != NULL); assert(argExpr != NULL);
@@ -2112,13 +2154,11 @@ FunctionCallExpr::GetValue(FunctionEmitContext *ctx) const {
// FIXME: should we do this during type checking? // FIXME: should we do this during type checking?
const std::vector<ConstExpr *> &argumentDefaults = ft->GetArgumentDefaults(); const std::vector<ConstExpr *> &argumentDefaults = ft->GetArgumentDefaults();
for (unsigned int i = callargs.size(); i < argumentDefaults.size(); ++i) { for (unsigned int i = callargs.size(); i < argumentDefaults.size(); ++i) {
assert(argumentDefaults[i] != NULL); Expr * d = TypeConvertExpr(argumentDefaults[i], argTypes[i],
Expr *defaultExpr = argumentDefaults[i]->TypeConv(argTypes[i], "function call default argument");
"function call default argument"); if (d == NULL)
if (defaultExpr == NULL)
return NULL; return NULL;
callargs.push_back(d);
callargs.push_back(defaultExpr);
} }
// Now evaluate the values of all of the parameters being passed. We // Now evaluate the values of all of the parameters being passed. We
@@ -2256,7 +2296,17 @@ FunctionCallExpr::TypeCheck() {
return NULL; return NULL;
} }
if (fse->ResolveOverloads(args->exprs) == true) { std::vector<const Type *> argTypes;
for (unsigned int i = 0; i < args->exprs.size(); ++i) {
if (args->exprs[i] == NULL)
return NULL;
const Type *t = args->exprs[i]->GetType();
if (t == NULL)
return NULL;
argTypes.push_back(t);
}
if (fse->ResolveOverloads(argTypes) == true) {
func = fse->TypeCheck(); func = fse->TypeCheck();
if (func != NULL) { if (func != NULL) {
@@ -2271,9 +2321,9 @@ FunctionCallExpr::TypeCheck() {
return NULL; return NULL;
launchCountExpr = launchCountExpr =
launchCountExpr->TypeConv(AtomicType::UniformInt32, TypeConvertExpr(launchCountExpr, AtomicType::UniformInt32,
"task launch count"); "task launch count");
if (!launchCountExpr) if (launchCountExpr == NULL)
return NULL; return NULL;
} }
else { else {
@@ -2621,8 +2671,8 @@ IndexExpr::TypeCheck() {
!g->opt.disableUniformMemoryOptimizations); !g->opt.disableUniformMemoryOptimizations);
const Type *indexType = isUniform ? AtomicType::UniformInt32 : const Type *indexType = isUniform ? AtomicType::UniformInt32 :
AtomicType::VaryingInt32; AtomicType::VaryingInt32;
index = index->TypeConv(indexType, "array index"); index = TypeConvertExpr(index, indexType, "array index");
if (!index) if (index == NULL)
return NULL; return NULL;
return this; return this;
@@ -5191,16 +5241,16 @@ lPrintFunctionOverloads(const std::string &name,
static void static void
lPrintPassedTypes(const char *funName, const std::vector<Expr *> &argExprs) { lPrintPassedTypes(const char *funName,
const std::vector<const Type *> &argTypes) {
fprintf(stderr, "Passed types: %*c(", (int)strlen(funName), ' '); fprintf(stderr, "Passed types: %*c(", (int)strlen(funName), ' ');
for (unsigned int i = 0; i < argExprs.size(); ++i) { for (unsigned int i = 0; i < argTypes.size(); ++i) {
const Type *t; if (argTypes[i] != NULL)
if (argExprs[i] != NULL && (t = argExprs[i]->GetType()) != NULL) fprintf(stderr, "%s%s", argTypes[i]->GetString().c_str(),
fprintf(stderr, "%s%s", t->GetString().c_str(), (i < argTypes.size()-1) ? ", " : ")\n\n");
(i < argExprs.size()-1) ? ", " : ")\n\n");
else else
fprintf(stderr, "(unknown type)%s", fprintf(stderr, "(unknown type)%s",
(i < argExprs.size()-1) ? ", " : ")\n\n"); (i < argTypes.size()-1) ? ", " : ")\n\n");
} }
} }
@@ -5211,9 +5261,7 @@ lPrintPassedTypes(const char *funName, const std::vector<Expr *> &argExprs) {
failure. failure.
*/ */
static int static int
lExactMatch(Expr *callArg, const Type *funcArgType) { lExactMatch(const Type *callType, const Type *funcArgType) {
const Type *callType = callArg->GetType();
if (dynamic_cast<const ReferenceType *>(callType) == NULL) if (dynamic_cast<const ReferenceType *>(callType) == NULL)
callType = callType->GetAsNonConstType(); callType = callType->GetAsNonConstType();
if (dynamic_cast<const ReferenceType *>(funcArgType) != NULL && if (dynamic_cast<const ReferenceType *>(funcArgType) != NULL &&
@@ -5229,12 +5277,12 @@ lExactMatch(Expr *callArg, const Type *funcArgType) {
modulo conversion to a reference type if needed. modulo conversion to a reference type if needed.
*/ */
static int static int
lMatchIgnoringReferences(Expr *callArg, const Type *funcArgType) { lMatchIgnoringReferences(const Type *callType, const Type *funcArgType) {
int prev = lExactMatch(callArg, funcArgType); int prev = lExactMatch(callType, funcArgType);
if (prev != -1) if (prev != -1)
return prev; return prev;
const Type *callType = callArg->GetType()->GetReferenceTarget(); callType = callType->GetReferenceTarget();
if (funcArgType->IsConstType()) if (funcArgType->IsConstType())
callType = callType->GetAsConstType(); callType = callType->GetAsConstType();
@@ -5247,12 +5295,11 @@ lMatchIgnoringReferences(Expr *callArg, const Type *funcArgType) {
conversion that won't lose information. Otherwise reports failure. conversion that won't lose information. Otherwise reports failure.
*/ */
static int static int
lMatchWithTypeWidening(Expr *callArg, const Type *funcArgType) { lMatchWithTypeWidening(const Type *callType, const Type *funcArgType) {
int prev = lMatchIgnoringReferences(callArg, funcArgType); int prev = lMatchIgnoringReferences(callType, funcArgType);
if (prev != -1) if (prev != -1)
return prev; return prev;
const Type *callType = callArg->GetType();
const AtomicType *callAt = dynamic_cast<const AtomicType *>(callType); const AtomicType *callAt = dynamic_cast<const AtomicType *>(callType);
const AtomicType *funcAt = dynamic_cast<const AtomicType *>(funcArgType); const AtomicType *funcAt = dynamic_cast<const AtomicType *>(funcArgType);
if (callAt == NULL || funcAt == NULL) if (callAt == NULL || funcAt == NULL)
@@ -5299,12 +5346,11 @@ lMatchWithTypeWidening(Expr *callArg, const Type *funcArgType) {
exactly the same type. exactly the same type.
*/ */
static int static int
lMatchIgnoringUniform(Expr *callArg, const Type *funcArgType) { lMatchIgnoringUniform(const Type *callType, const Type *funcArgType) {
int prev = lMatchWithTypeWidening(callArg, funcArgType); int prev = lMatchWithTypeWidening(callType, funcArgType);
if (prev != -1) if (prev != -1)
return prev; return prev;
const Type *callType = callArg->GetType();
if (dynamic_cast<const ReferenceType *>(callType) == NULL) if (dynamic_cast<const ReferenceType *>(callType) == NULL)
callType = callType->GetAsNonConstType(); callType = callType->GetAsNonConstType();
@@ -5319,15 +5365,14 @@ lMatchIgnoringUniform(Expr *callArg, const Type *funcArgType) {
argument type, but without doing a uniform -> varying conversion. argument type, but without doing a uniform -> varying conversion.
*/ */
static int static int
lMatchWithTypeConvSameVariability(Expr *callArg, const Type *funcArgType) { lMatchWithTypeConvSameVariability(const Type *callType,
int prev = lMatchIgnoringUniform(callArg, funcArgType); const Type *funcArgType) {
int prev = lMatchIgnoringUniform(callType, funcArgType);
if (prev != -1) if (prev != -1)
return prev; return prev;
Expr *te = callArg->TypeConv(funcArgType, if (CanConvertTypes(callType, funcArgType) &&
"function call argument", true); (callType->IsUniformType() == funcArgType->IsUniformType()))
if (te != NULL &&
te->GetType()->IsUniformType() == callArg->GetType()->IsUniformType())
return 1; return 1;
else else
return -1; return -1;
@@ -5339,14 +5384,12 @@ lMatchWithTypeConvSameVariability(Expr *callArg, const Type *funcArgType) {
argument type to the function argument type. argument type to the function argument type.
*/ */
static int static int
lMatchWithTypeConv(Expr *callArg, const Type *funcArgType) { lMatchWithTypeConv(const Type *callType, const Type *funcArgType) {
int prev = lMatchWithTypeConvSameVariability(callArg, funcArgType); int prev = lMatchWithTypeConvSameVariability(callType, funcArgType);
if (prev != -1) if (prev != -1)
return prev; return prev;
Expr *te = callArg->TypeConv(funcArgType, return CanConvertTypes(callType, funcArgType) ? 0 : -1;
"function call argument", true);
return (te != NULL) ? 0 : -1;
} }
@@ -5383,8 +5426,8 @@ lGetBestMatch(std::vector<std::pair<int, Symbol *> > &matches) {
finding multiple ambiguous matches. finding multiple ambiguous matches.
*/ */
bool bool
FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *), FunctionSymbolExpr::tryResolve(int (*matchFunc)(const Type *, const Type *),
const std::vector<Expr *> &callArgs) { const std::vector<const Type *> &callTypes) {
const char *funName = candidateFunctions->front()->name.c_str(); const char *funName = candidateFunctions->front()->name.c_str();
std::vector<std::pair<int, Symbol *> > matches; std::vector<std::pair<int, Symbol *> > matches;
@@ -5401,7 +5444,7 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *),
// There's no way to match if the caller is passing more arguments // There's no way to match if the caller is passing more arguments
// than this function instance takes. // than this function instance takes.
if (callArgs.size() > funcArgTypes.size()) if (callTypes.size() > funcArgTypes.size())
continue; continue;
unsigned int i; unsigned int i;
@@ -5410,23 +5453,23 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *),
// function than are passed, if the function has default argument // function than are passed, if the function has default argument
// values. This case is handled below. // values. This case is handled below.
int cost = 0; int cost = 0;
for (i = 0; i < callArgs.size(); ++i) { for (i = 0; i < callTypes.size(); ++i) {
// This may happen if there's an error earlier in compilation. // This may happen if there's an error earlier in compilation.
// It's kind of a silly to redundantly discover this for each // It's kind of a silly to redundantly discover this for each
// potential match versus detecting this earlier in the // potential match versus detecting this earlier in the
// matching process and just giving up. // matching process and just giving up.
if (!callArgs[i] || !callArgs[i]->GetType() || !funcArgTypes[i] || if (callTypes[i] == NULL || funcArgTypes[i] == NULL ||
dynamic_cast<const FunctionType *>(callArgs[i]->GetType()) != NULL) dynamic_cast<const FunctionType *>(callTypes[i]) != NULL)
return false; return false;
int argCost = matchFunc(callArgs[i], funcArgTypes[i]); int argCost = matchFunc(callTypes[i], funcArgTypes[i]);
if (argCost == -1) if (argCost == -1)
// If the predicate function returns -1, we have failed no // If the predicate function returns -1, we have failed no
// matter what else happens, so we stop trying // matter what else happens, so we stop trying
break; break;
cost += argCost; cost += argCost;
} }
if (i == callArgs.size()) { if (i == callTypes.size()) {
// All of the arguments matched! // All of the arguments matched!
if (i == funcArgTypes.size()) if (i == funcArgTypes.size())
// And we have exactly as many arguments as the function // And we have exactly as many arguments as the function
@@ -5454,7 +5497,7 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *),
Error(pos, "Multiple overloaded instances of function \"%s\" matched.", Error(pos, "Multiple overloaded instances of function \"%s\" matched.",
funName); funName);
lPrintFunctionOverloads(funName, matches); lPrintFunctionOverloads(funName, matches);
lPrintPassedTypes(funName, callArgs); lPrintPassedTypes(funName, callTypes);
// Stop trying to find more matches after an ambigious set of // Stop trying to find more matches after an ambigious set of
// matches. // matches.
return true; return true;
@@ -5463,7 +5506,7 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *),
bool bool
FunctionSymbolExpr::ResolveOverloads(const std::vector<Expr *> &callArgs) { FunctionSymbolExpr::ResolveOverloads(const std::vector<const Type *> &argTypes) {
// Functions with names that start with "__" should only be various // Functions with names that start with "__" should only be various
// builtins. For those, we'll demand an exact match, since we'll // builtins. For those, we'll demand an exact match, since we'll
// expect whichever function in stdlib.ispc is calling out to one of // expect whichever function in stdlib.ispc is calling out to one of
@@ -5474,32 +5517,32 @@ FunctionSymbolExpr::ResolveOverloads(const std::vector<Expr *> &callArgs) {
// Is there an exact match that doesn't require any argument type // Is there an exact match that doesn't require any argument type
// conversion (other than converting type -> reference type)? // conversion (other than converting type -> reference type)?
if (tryResolve(lExactMatch, callArgs)) if (tryResolve(lExactMatch, argTypes))
return true; return true;
if (exactMatchOnly == false) { if (exactMatchOnly == false) {
// Try to find a single match ignoring references // Try to find a single match ignoring references
if (tryResolve(lMatchIgnoringReferences, callArgs)) if (tryResolve(lMatchIgnoringReferences, argTypes))
return true; return true;
// Try to find an exact match via type widening--i.e. int8 -> // Try to find an exact match via type widening--i.e. int8 ->
// int16, etc.--things that don't lose data. // int16, etc.--things that don't lose data.
if (tryResolve(lMatchWithTypeWidening, callArgs)) if (tryResolve(lMatchWithTypeWidening, argTypes))
return true; return true;
// Next try to see if there's a match via just uniform -> varying // Next try to see if there's a match via just uniform -> varying
// promotions. // promotions.
if (tryResolve(lMatchIgnoringUniform, callArgs)) if (tryResolve(lMatchIgnoringUniform, argTypes))
return true; return true;
// Try to find a match via type conversion, but don't change // Try to find a match via type conversion, but don't change
// unif->varying // unif->varying
if (tryResolve(lMatchWithTypeConvSameVariability, if (tryResolve(lMatchWithTypeConvSameVariability,
callArgs)) argTypes))
return true; return true;
// Last chance: try to find a match via arbitrary type conversion. // Last chance: try to find a match via arbitrary type conversion.
if (tryResolve(lMatchWithTypeConv, callArgs)) if (tryResolve(lMatchWithTypeConv, argTypes))
return true; return true;
} }
@@ -5508,7 +5551,7 @@ FunctionSymbolExpr::ResolveOverloads(const std::vector<Expr *> &callArgs) {
Error(pos, "Unable to find matching overload for call to function \"%s\"%s.", Error(pos, "Unable to find matching overload for call to function \"%s\"%s.",
funName, exactMatchOnly ? " only considering exact matches" : ""); funName, exactMatchOnly ? " only considering exact matches" : "");
lPrintFunctionOverloads(funName, *candidateFunctions); lPrintFunctionOverloads(funName, *candidateFunctions);
lPrintPassedTypes(funName, callArgs); lPrintPassedTypes(funName, argTypes);
return false; return false;
} }

29
expr.h
View File

@@ -89,14 +89,6 @@ public:
/** Prints the expression to standard output (used for debugging). */ /** Prints the expression to standard output (used for debugging). */
virtual void Print() const = 0; virtual void Print() const = 0;
/** This method tries to convert the expression to the given type. In
the event of failure, if the failureOk parameter is true, then no
error is issued. If failureOk is false, then an error is printed
that incorporates the given error message string. In either
failure case, NULL is returned. */
Expr *TypeConv(const Type *type, const char *errorMsgBase = NULL,
bool failureOk = false, bool issuePrecisionWarnings = true);
}; };
@@ -577,12 +569,12 @@ public:
void Print() const; void Print() const;
int EstimateCost() const; int EstimateCost() const;
bool ResolveOverloads(const std::vector<Expr *> &args); bool ResolveOverloads(const std::vector<const Type *> &argTypes);
Symbol *GetMatchingFunction(); Symbol *GetMatchingFunction();
private: private:
bool tryResolve(int (*matchFunc)(Expr *, const Type *), bool tryResolve(int (*matchFunc)(const Type *, const Type *),
const std::vector<Expr *> &args); const std::vector<const Type *> &argTypes);
/** Name of the function that is being called. */ /** Name of the function that is being called. */
std::string name; std::string name;
@@ -611,4 +603,19 @@ public:
int EstimateCost() const; int EstimateCost() const;
}; };
/** This function indicates whether it's legal to convert from fromType to
toType.
*/
bool CanConvertTypes(const Type *fromType, const Type *toType);
/** This function attempts to convert the given expression to the given
type, returning a pointer to a new expression that is the result. If
the required type conversion is illegal, it returns NULL and prints an
error message using the provided string to indicate the context for
which type conversion was being applied (e.g. "function call
parameter").
*/
Expr *TypeConvertExpr(Expr *expr, const Type *toType, const char *errorMsgBase);
#endif // ISPC_EXPR_H #endif // ISPC_EXPR_H

View File

@@ -256,8 +256,8 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
// ExprList; they don't have types per se / can't type // ExprList; they don't have types per se / can't type
// convert themselves anyway.) // convert themselves anyway.)
if (dynamic_cast<ExprList *>(initExpr) == NULL) if (dynamic_cast<ExprList *>(initExpr) == NULL)
initExpr = initExpr->TypeConv(sym->type, "initializer"); initExpr = TypeConvertExpr(initExpr, sym->type, "initializer");
if (initExpr != NULL) { if (initExpr != NULL) {
initExpr = initExpr->Optimize(); initExpr = initExpr->Optimize();
// Fingers crossed, now let's see if we've got a // Fingers crossed, now let's see if we've got a

View File

@@ -146,9 +146,9 @@ lInitSymbol(llvm::Value *lvalue, const char *symName, const Type *type,
// ExprList, then we'll see if we can type convert it to the type of // ExprList, then we'll see if we can type convert it to the type of
// the variable. // the variable.
if (dynamic_cast<ExprList *>(initExpr) == NULL) { if (dynamic_cast<ExprList *>(initExpr) == NULL) {
Expr *tcInit = initExpr->TypeConv(type, "inititalizer", true); initExpr = TypeConvertExpr(initExpr, type, "initializer");
if (tcInit != NULL) { if (initExpr != NULL) {
llvm::Value *initializerValue = tcInit->GetValue(ctx); llvm::Value *initializerValue = initExpr->GetValue(ctx);
if (initializerValue != NULL) if (initializerValue != NULL)
// Bingo; store the value in the variable's storage // Bingo; store the value in the variable's storage
ctx->StoreInst(initializerValue, lvalue); ctx->StoreInst(initializerValue, lvalue);
@@ -380,10 +380,14 @@ DeclStmt::TypeCheck() {
if (dynamic_cast<const AtomicType *>(type) != NULL || if (dynamic_cast<const AtomicType *>(type) != NULL ||
dynamic_cast<const EnumType *>(type) != NULL) { dynamic_cast<const EnumType *>(type) != NULL) {
// If it's an expr list with an atomic type, we'll later issue // If it's an expr list with an atomic type, we'll later issue
// an error. Need to leave decl->initExpr as is in that case so it // an error. Need to leave vars[i].init as is in that case so
// is in fact caught later, though. // it is in fact caught later, though.
if (dynamic_cast<ExprList *>(vars[i].init) == NULL) if (dynamic_cast<ExprList *>(vars[i].init) == NULL) {
vars[i].init = vars[i].init->TypeConv(type, "initializer"); vars[i].init = TypeConvertExpr(vars[i].init, type,
"initializer");
if (vars[i].init == NULL)
encounteredError = true;
}
} }
} }
return encounteredError ? NULL : this; return encounteredError ? NULL : this;