Revamp handling of function types, conversion to function ptr types.

Implicit conversion to function types is now a more standard part of
the type conversion infrastructure, rather than special cases of things
like FunctionSymbolExpr immediately returning a pointer type, etc.

Improved AddressOfExpr::TypeCheck() to actually issue errors in cases
where it's illegal to take the address of an expression.

Added AddressOfExpr::GetConstant() implementation that handles taking
the address of functions.

Issue #223.
This commit is contained in:
Matt Pharr
2012-04-03 10:09:07 -07:00
parent b813452d33
commit 4cd0cf1650
8 changed files with 149 additions and 41 deletions

165
expr.cpp
View File

@@ -212,11 +212,27 @@ lDoTypeConv(const Type *fromType, const Type *toType, Expr **expr,
}
if (dynamic_cast<const FunctionType *>(fromType)) {
if (!failureOk)
Error(pos, "Can't convert function type \"%s\" to \"%s\" for %s.",
fromType->GetString().c_str(),
toType->GetString().c_str(), errorMsgBase);
return false;
if (dynamic_cast<const PointerType *>(toType) != NULL) {
// Convert function type to pointer to function type
if (expr != NULL) {
Expr *aoe = new AddressOfExpr(*expr, (*expr)->pos);
if (lDoTypeConv(aoe->GetType(), toType, &aoe, failureOk,
errorMsgBase, pos)) {
*expr = aoe;
return true;
}
}
else
return lDoTypeConv(PointerType::GetUniform(fromType), toType, NULL,
failureOk, errorMsgBase, pos);
}
else {
if (!failureOk)
Error(pos, "Can't convert function type \"%s\" to \"%s\" for %s.",
fromType->GetString().c_str(),
toType->GetString().c_str(), errorMsgBase);
return false;
}
}
if (dynamic_cast<const FunctionType *>(toType)) {
if (!failureOk)
@@ -3434,10 +3450,15 @@ FunctionCallExpr::TypeCheck() {
if (func == NULL)
return NULL;
const PointerType *pt =
dynamic_cast<const PointerType *>(func->GetType());
const FunctionType *ft = (pt == NULL) ? NULL :
dynamic_cast<const FunctionType *>(pt->GetBaseType());
const FunctionType *ft =
dynamic_cast<const FunctionType *>(func->GetType());
if (ft == NULL) {
const PointerType *pt =
dynamic_cast<const PointerType *>(func->GetType());
ft = (pt == NULL) ? NULL :
dynamic_cast<const FunctionType *>(pt->GetBaseType());
}
if (ft == NULL) {
Error(pos, "Valid function name must be used for function call.");
return NULL;
@@ -6774,6 +6795,34 @@ TypeCastExpr::GetBaseSymbol() const {
}
static
llvm::Constant *
lConvertPointerConstant(llvm::Constant *c, const Type *constType) {
if (c == NULL || constType->IsUniformType())
return c;
// Handle conversion to int and then to vector of int or array of int
// (for varying and soa types, respectively)
llvm::Constant *intPtr =
llvm::ConstantExpr::getPtrToInt(c, LLVMTypes::PointerIntType);
Assert(constType->IsVaryingType() || constType->IsSOAType());
int count = constType->IsVaryingType() ? g->target.vectorWidth :
constType->GetSOAWidth();
std::vector<llvm::Constant *> smear;
for (int i = 0; i < count; ++i)
smear.push_back(intPtr);
if (constType->IsVaryingType())
return llvm::ConstantVector::get(smear);
else {
LLVM_TYPE_CONST llvm::ArrayType *at =
llvm::ArrayType::get(LLVMTypes::PointerIntType, count);
return llvm::ConstantArray::get(at, smear);
}
}
llvm::Constant *
TypeCastExpr::GetConstant(const Type *constType) const {
// We don't need to worry about most the basic cases where the type
@@ -6781,11 +6830,18 @@ TypeCastExpr::GetConstant(const Type *constType) const {
// TypeCastExpr::Optimize() method generally ends up doing the type
// conversion and returning a ConstExpr, which in turn will have its
// GetConstant() method called. However, because ConstExpr currently
// can't represent pointer values, we have to handle two cases here:
// 1. Null pointers (NULL, 0) valued initializers, and
// 2. Converting a uniform function pointer to a varying function
// pointer of the same type.
return expr->GetConstant(constType);
// can't represent pointer values, we have to handle a few cases
// related to pointers here:
//
// 1. Null pointer (NULL, 0) valued initializers
// 2. Converting function types to pointer-to-function types
// 3. And converting these from uniform to the varying/soa equivalents.
//
if (dynamic_cast<const PointerType *>(constType) == NULL)
return NULL;
llvm::Constant *c = expr->GetConstant(constType->GetAsUniformType());
return lConvertPointerConstant(c, constType);
}
@@ -7078,7 +7134,8 @@ AddressOfExpr::GetValue(FunctionEmitContext *ctx) const {
return NULL;
const Type *exprType = expr->GetType();
if (dynamic_cast<const ReferenceType *>(exprType) != NULL)
if (dynamic_cast<const ReferenceType *>(exprType) != NULL ||
dynamic_cast<const FunctionType *>(exprType) != NULL)
return expr->GetValue(ctx);
else
return expr->GetLValue(ctx);
@@ -7093,8 +7150,18 @@ AddressOfExpr::GetType() const {
const Type *exprType = expr->GetType();
if (dynamic_cast<const ReferenceType *>(exprType) != NULL)
return PointerType::GetUniform(exprType->GetReferenceTarget());
else
return expr->GetLValueType();
const Type *t = expr->GetLValueType();
if (t != NULL)
return t;
else {
t = expr->GetType();
if (t == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return PointerType::GetUniform(t);
}
}
@@ -7118,7 +7185,22 @@ AddressOfExpr::Print() const {
Expr *
AddressOfExpr::TypeCheck() {
return this;
const Type *exprType;
if (expr == NULL || (exprType = expr->GetType()) == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (dynamic_cast<const ReferenceType *>(exprType) != NULL||
dynamic_cast<const FunctionType *>(exprType) != NULL) {
return this;
}
if (expr->GetLValueType() != NULL)
return this;
Error(expr->pos, "Illegal to take address of non-lvalue or function.");
return NULL;
}
@@ -7134,6 +7216,29 @@ AddressOfExpr::EstimateCost() const {
}
llvm::Constant *
AddressOfExpr::GetConstant(const Type *type) const {
const Type *exprType;
if (expr == NULL || (exprType = expr->GetType()) == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
const PointerType *pt = dynamic_cast<const PointerType *>(type);
if (pt == NULL)
return NULL;
const FunctionType *ft =
dynamic_cast<const FunctionType *>(pt->GetBaseType());
if (ft != NULL) {
llvm::Constant *c = expr->GetConstant(ft);
return lConvertPointerConstant(c, type);
}
else
return NULL;
}
///////////////////////////////////////////////////////////////////////////
// SizeOfExpr
@@ -7313,8 +7418,7 @@ FunctionSymbolExpr::GetType() const {
return NULL;
}
return matchingFunc ?
new PointerType(matchingFunc->type, Variability::Uniform, true) : NULL;
return matchingFunc ? matchingFunc->type : NULL;
}
@@ -7364,27 +7468,14 @@ FunctionSymbolExpr::GetConstant(const Type *type) const {
if (matchingFunc == NULL || matchingFunc->function == NULL)
return NULL;
const FunctionType *ft;
if (dynamic_cast<const PointerType *>(type) == NULL ||
(ft = dynamic_cast<const FunctionType *>(type->GetBaseType())) == NULL)
const FunctionType *ft = dynamic_cast<const FunctionType *>(type);
if (ft == NULL)
return NULL;
LLVM_TYPE_CONST llvm::Type *llvmUnifType =
type->GetAsUniformType()->LLVMType(g->ctx);
if (llvmUnifType != matchingFunc->function->getType())
if (Type::Equal(type, matchingFunc->type) == false)
return NULL;
if (type->IsUniformType())
return matchingFunc->function;
else {
llvm::Constant *intPtr =
llvm::ConstantExpr::getPtrToInt(matchingFunc->function,
LLVMTypes::PointerIntType);
std::vector<llvm::Constant *> smear;
for (int i = 0; i < g->target.vectorWidth; ++i)
smear.push_back(intPtr);
return llvm::ConstantVector::get(smear);
}
return matchingFunc->function;
}

1
expr.h
View File

@@ -584,6 +584,7 @@ public:
Expr *TypeCheck();
Expr *Optimize();
int EstimateCost() const;
llvm::Constant *GetConstant(const Type *type) const;
Expr *expr;
};

View File

@@ -15,7 +15,7 @@ export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float b = aFOO[0]-1;
uniform FuncType func = foo;
RET[programIndex] = (func ? func : bar)(a, b);
RET[programIndex] = (func ? func : &bar)(a, b);
}
export void result(uniform float RET[]) {

View File

@@ -14,7 +14,7 @@ static float bar(float a, float b) {
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float b = aFOO[0]-1;
FuncType func = foo;
FuncType func = &foo;
if (a == 2)
func = NULL;
if (func != NULL)

View File

@@ -16,7 +16,7 @@ export void f_f(uniform float RET[], uniform float aFOO[]) {
float b = aFOO[0]-1;
FuncType func = NULL;
if (a == 2)
func = foo;
func = &foo;
if (!func)
RET[programIndex] = -1;
else

View File

@@ -14,7 +14,7 @@ static float bar(float a, float b) {
export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = aFOO[programIndex];
float b = aFOO[0]-1;
uniform FuncType func = bar;
uniform FuncType func = &bar;
if (aFOO[0] == 1)
func = foo;
RET[programIndex] = func(a, b);

View File

@@ -0,0 +1,5 @@
// Illegal to take address of non-lvalue or function
void foo() {
int *ptr = &(1+1);
}

View File

@@ -2695,6 +2695,17 @@ Type::MoreGeneralType(const Type *t0, const Type *t1, SourcePos pos, const char
bool forceVarying, int vecSize) {
Assert(reason != NULL);
// First, if one or both types are function types, convert them to
// pointer to function types and then try again.
if (dynamic_cast<const FunctionType *>(t0) ||
dynamic_cast<const FunctionType *>(t1)) {
if (dynamic_cast<const FunctionType *>(t0))
t0 = PointerType::GetUniform(t0);
if (dynamic_cast<const FunctionType *>(t1))
t1 = PointerType::GetUniform(t1);
return MoreGeneralType(t0, t1, pos, reason, forceVarying, vecSize);
}
// First, if we need to go varying, promote both of the types to be
// varying.
if (t0->IsVaryingType() || t1->IsVaryingType() || forceVarying) {