diff --git a/ast.cpp b/ast.cpp index bfbc71f6..55f09c34 100644 --- a/ast.cpp +++ b/ast.cpp @@ -323,14 +323,22 @@ static bool lCheckAllOffSafety(ASTNode *node, void *data) { bool *okPtr = (bool *)data; - if (dynamic_cast(node) != NULL) { - // FIXME: If we could somehow determine that the function being - // called was safe (and all of the args Exprs were safe, then it'd - // be nice to be able to return true here. (Consider a call to - // e.g. floatbits() in the stdlib.) Unfortunately for now we just - // have to be conservative. - *okPtr = false; - return false; + FunctionCallExpr *fce; + if ((fce = dynamic_cast(node)) != NULL) { + if (fce->func == NULL) + return false; + + const Type *type = fce->func->GetType(); + const PointerType *pt = dynamic_cast(type); + if (pt != NULL) + type = pt->GetBaseType(); + const FunctionType *ftype = dynamic_cast(type); + Assert(ftype != NULL); + + if (ftype->isSafe == false) { + *okPtr = false; + return false; + } } if (dynamic_cast(node) != NULL) { diff --git a/decl.cpp b/decl.cpp index c95c1a4a..f4382c8b 100644 --- a/decl.cpp +++ b/decl.cpp @@ -538,10 +538,31 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const { return NULL; } - const Type *functionType = + const FunctionType *functionType = new FunctionType(returnType, args, argNames, argDefaults, argPos, isTask, isExported, isExternC); functionType = functionType->ResolveUnboundVariability(Variability::Varying); + + // handle any explicit __declspecs on the function + if (ds != NULL) { + for (int i = 0; i < (int)ds->declSpecList.size(); ++i) { + std::string str = ds->declSpecList[i].first; + SourcePos pos = ds->declSpecList[i].second; + + if (str == "safe") + (const_cast(functionType))->isSafe = true; + else if (!strncmp(str.c_str(), "cost", 4)) { + int cost = atoi(str.c_str() + 4); + if (cost < 0) + Error(pos, "Negative function cost %d is illegal.", + cost); + (const_cast(functionType))->costOverride = cost; + } + else + Error(pos, "__declspec parameter \"%s\" unknown.", str.c_str()); + } + } + return child->GetType(functionType, ds); } default: @@ -555,6 +576,14 @@ const Type * Declarator::GetType(DeclSpecs *ds) const { const Type *baseType = ds->GetBaseType(pos); const Type *type = GetType(baseType, ds); + + if (ds->declSpecList.size() > 0 && + type != NULL & + dynamic_cast(type) == NULL) { + Error(pos, "__declspec specifiers for non-function type \"%s\" are " + "not used.", type->GetString().c_str()); + } + return type; } diff --git a/expr.cpp b/expr.cpp index 3d7ad7fa..0c806054 100644 --- a/expr.cpp +++ b/expr.cpp @@ -3518,18 +3518,23 @@ int FunctionCallExpr::EstimateCost() const { if (isLaunch) return COST_TASK_LAUNCH; - else if (dynamic_cast(func) == NULL) { - // it's going through a function pointer - const Type *fpType = func->GetType(); - if (fpType != NULL) { - Assert(dynamic_cast(fpType) != NULL); - if (fpType->IsUniformType()) - return COST_FUNPTR_UNIFORM; - else - return COST_FUNPTR_VARYING; - } - } - return COST_FUNCALL; + + const Type *type = func->GetType(); + if (type == NULL) + return 0; + + const PointerType *pt = dynamic_cast(type); + if (pt != NULL) + type = type->GetBaseType(); + const FunctionType *ftype = dynamic_cast(type); + + if (ftype->costOverride > -1) + return ftype->costOverride; + + if (pt != NULL) + return pt->IsUniformType() ? COST_FUNPTR_UNIFORM : COST_FUNPTR_VARYING; + else + return COST_FUNCALL; } diff --git a/type.cpp b/type.cpp index f164c9b0..0fb8817e 100644 --- a/type.cpp +++ b/type.cpp @@ -2329,6 +2329,8 @@ FunctionType::FunctionType(const Type *r, const std::vector &a, paramDefaults(std::vector(a.size(), NULL)), paramPositions(std::vector(a.size(), p)) { Assert(returnType != NULL); + isSafe = false; + costOverride = -1; } @@ -2343,6 +2345,8 @@ FunctionType::FunctionType(const Type *r, const std::vector &a, paramNames.size() == paramDefaults.size() && paramDefaults.size() == paramPositions.size()); Assert(returnType != NULL); + isSafe = false; + costOverride = -1; } @@ -2434,8 +2438,13 @@ FunctionType::ResolveUnboundVariability(Variability v) const { pt.push_back(paramTypes[i]->ResolveUnboundVariability(v)); } - return new FunctionType(rt, pt, paramNames, paramDefaults, - paramPositions, isTask, isExported, isExternC); + FunctionType *ret = new FunctionType(rt, pt, paramNames, paramDefaults, + paramPositions, isTask, isExported, + isExternC); + ret->isSafe = isSafe; + ret->costOverride = costOverride; + + return ret; } @@ -2457,6 +2466,12 @@ std::string FunctionType::GetString() const { std::string ret; if (isTask) ret += "task "; + if (isSafe) ret += "/*safe*/ "; + if (costOverride > 0) { + char buf[32]; + sprintf(buf, "/*cost=%d*/ ", costOverride); + ret += buf; + } if (returnType != NULL) ret += returnType->GetString(); else diff --git a/type.h b/type.h index d8306289..94c28f0b 100644 --- a/type.h +++ b/type.h @@ -801,6 +801,14 @@ public: function in the source program. */ const bool isExternC; + /** Indicates whether this function has been declared to be safe to run + with an all-off mask. */ + bool isSafe; + + /** If non-negative, this provides a user-supplied override to the cost + function estimate for the function. */ + int costOverride; + private: const Type * const returnType;