Substantial rewrite (again) of decl handling.

The decl.* code now no longer interacts with Symbols, but just returns
names, types, initializer expressions, etc., as needed.  This makes the
code a bit more understandable.

Fixes issues #171 and #130.
This commit is contained in:
Matt Pharr
2012-04-12 17:28:30 -07:00
parent d88dbf3612
commit 5ece6fec04
15 changed files with 396 additions and 423 deletions

View File

@@ -55,10 +55,10 @@ ASTNode::~ASTNode() {
// AST
void
AST::AddFunction(Symbol *sym, const std::vector<Symbol *> &args, Stmt *code) {
AST::AddFunction(Symbol *sym, Stmt *code) {
if (sym == NULL)
return;
functions.push_back(new Function(sym, args, code));
functions.push_back(new Function(sym, code));
}

5
ast.h
View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2011, Intel Corporation
Copyright (c) 2011-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -84,8 +84,7 @@ class AST {
public:
/** Add the AST for a function described by the given declaration
information and source code. */
void AddFunction(Symbol *sym, const std::vector<Symbol *> &args,
Stmt *code);
void AddFunction(Symbol *sym, Stmt *code);
/** Generate LLVM IR for all of the functions into the current
module. */

409
decl.cpp
View File

@@ -33,7 +33,7 @@
/** @file decl.cpp
@brief Implementations of classes related to turning declarations into
symbols and types.
symbol names and types.
*/
#include "decl.h"
@@ -218,50 +218,44 @@ Declarator::Declarator(DeclaratorKind dk, SourcePos p)
: pos(p), kind(dk) {
child = NULL;
typeQualifiers = 0;
storageClass = SC_NONE;
arraySize = -1;
sym = NULL;
type = NULL;
initExpr = NULL;
}
void
Declarator::InitFromDeclSpecs(DeclSpecs *ds) {
const Type *t = GetType(ds);
if (t == NULL) {
const Type *baseType = ds->GetBaseType(pos);
InitFromType(baseType, ds);
if (type == NULL) {
Assert(m->errorCount > 0);
return;
}
Symbol *sym = GetSymbol();
if (sym != NULL) {
sym->type = t;
sym->storageClass = ds->storageClass;
storageClass = ds->storageClass;
if (ds->declSpecList.size() > 0 &&
dynamic_cast<const FunctionType *>(type) == NULL) {
Error(pos, "__declspec specifiers for non-function type \"%s\" are "
"not used.", type->GetString().c_str());
}
}
Symbol *
Declarator::GetSymbol() const {
// The symbol lives at the last child in the chain, so walk down there
// and return the one there.
const Declarator *d = this;
while (d->child != NULL)
d = d->child;
return d->sym;
}
void
Declarator::Print(int indent) const {
printf("%*cdeclarator: [", indent, ' ');
pos.Print();
lPrintTypeQualifiers(typeQualifiers);
Symbol *sym = GetSymbol();
if (sym != NULL)
printf("%s", sym->name.c_str());
printf("%s ", lGetStorageClassName(storageClass));
if (name.size() > 0)
printf("%s", name.c_str());
else
printf("(null symbol)");
printf("(unnamed)");
printf(", array size = %d", arraySize);
@@ -295,55 +289,8 @@ Declarator::Print(int indent) const {
}
Symbol *
Declarator::GetFunctionInfo(DeclSpecs *ds, std::vector<Symbol *> *funArgs) {
const FunctionType *type =
dynamic_cast<const FunctionType *>(GetType(ds));
if (type == NULL)
return NULL;
Symbol *declSym = GetSymbol();
Assert(declSym != NULL);
// Get the symbol for the function from the symbol table. (It should
// already have been added to the symbol table by AddGlobal() by the
// time we get here.)
Symbol *funSym = m->symbolTable->LookupFunction(declSym->name.c_str(), type);
if (funSym == NULL)
// May be NULL due to error earlier in compilation
Assert(m->errorCount > 0);
else
funSym->pos = pos;
// Walk down to the declarator for the function. (We have to get past
// the stuff that specifies the function's return type before we get to
// the function's declarator.)
Declarator *d = this;
while (d != NULL && d->kind != DK_FUNCTION)
d = d->child;
Assert(d != NULL);
for (unsigned int i = 0; i < d->functionParams.size(); ++i) {
Symbol *sym = d->GetSymbolForFunctionParameter(i);
if (sym->type == NULL) {
Assert(m->errorCount > 0);
continue;
}
else
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
funArgs->push_back(sym);
}
if (funSym != NULL)
funSym->type = funSym->type->ResolveUnboundVariability(Variability::Varying);
return funSym;
}
const Type *
Declarator::GetType(const Type *base, DeclSpecs *ds) const {
void
Declarator::InitFromType(const Type *baseType, DeclSpecs *ds) {
bool hasUniformQual = ((typeQualifiers & TYPEQUAL_UNIFORM) != 0);
bool hasVaryingQual = ((typeQualifiers & TYPEQUAL_VARYING) != 0);
bool isTask = ((typeQualifiers & TYPEQUAL_TASK) != 0);
@@ -352,12 +299,16 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
if (hasUniformQual && hasVaryingQual) {
Error(pos, "Can't provide both \"uniform\" and \"varying\" qualifiers.");
return NULL;
return;
}
if (kind != DK_FUNCTION && isTask)
if (kind != DK_FUNCTION && isTask) {
Error(pos, "\"task\" qualifier illegal in variable declaration.");
if (kind != DK_FUNCTION && isExported)
return;
}
if (kind != DK_FUNCTION && isExported) {
Error(pos, "\"export\" qualifier illegal in variable declaration.");
return;
}
Variability variability(Variability::Unbound);
if (hasUniformQual)
@@ -365,66 +316,76 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
else if (hasVaryingQual)
variability = Variability::Varying;
const Type *type = base;
switch (kind) {
case DK_BASE:
if (kind == DK_BASE) {
// All of the type qualifiers should be in the DeclSpecs for the
// base declarator
Assert(typeQualifiers == 0);
Assert(child == NULL);
return type;
case DK_POINTER:
type = baseType;
}
else if (kind == DK_POINTER) {
/* For now, any pointer to an SOA type gets the slice property; if
we add the capability to declare pointers as slices or not,
we'll want to set this based on a type qualifier here. */
type = new PointerType(type, variability, isConst, type->IsSOAType());
if (child != NULL)
return child->GetType(type, ds);
const Type *ptrType = new PointerType(baseType, variability, isConst,
baseType->IsSOAType());
if (child != NULL) {
child->InitFromType(ptrType, ds);
type = child->type;
name = child->name;
}
else
return type;
break;
case DK_REFERENCE:
if (hasUniformQual)
type = ptrType;
}
else if (kind == DK_REFERENCE) {
if (hasUniformQual) {
Error(pos, "\"uniform\" qualifier is illegal to apply to references.");
if (hasVaryingQual)
return;
}
if (hasVaryingQual) {
Error(pos, "\"varying\" qualifier is illegal to apply to references.");
if (isConst)
return;
}
if (isConst) {
Error(pos, "\"const\" qualifier is to illegal apply to references.");
return;
}
// The parser should disallow this already, but double check.
if (dynamic_cast<const ReferenceType *>(type) != NULL) {
if (dynamic_cast<const ReferenceType *>(baseType) != NULL) {
Error(pos, "References to references are illegal.");
return NULL;
return;
}
type = new ReferenceType(type);
if (child != NULL)
return child->GetType(type, ds);
const Type *refType = new ReferenceType(baseType);
if (child != NULL) {
child->InitFromType(refType, ds);
type = child->type;
name = child->name;
}
else
return type;
break;
case DK_ARRAY:
if (Type::Equal(type, AtomicType::Void)) {
type = refType;
}
else if (kind == DK_ARRAY) {
if (Type::Equal(baseType, AtomicType::Void)) {
Error(pos, "Arrays of \"void\" type are illegal.");
return NULL;
return;
}
if (dynamic_cast<const ReferenceType *>(type)) {
if (dynamic_cast<const ReferenceType *>(baseType)) {
Error(pos, "Arrays of references (type \"%s\") are illegal.",
type->GetString().c_str());
return NULL;
baseType->GetString().c_str());
return;
}
type = new ArrayType(type, arraySize);
if (child)
return child->GetType(type, ds);
const Type *arrayType = new ArrayType(baseType, arraySize);
if (child != NULL) {
child->InitFromType(arrayType, ds);
type = child->type;
name = child->name;
}
else
return type;
break;
case DK_FUNCTION: {
type = arrayType;
}
else if (kind == DK_FUNCTION) {
std::vector<const Type *> args;
std::vector<std::string> argNames;
std::vector<Expr *> argDefaults;
@@ -436,20 +397,40 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
for (unsigned int i = 0; i < functionParams.size(); ++i) {
Declaration *d = functionParams[i];
Symbol *sym = GetSymbolForFunctionParameter(i);
if (d->declSpecs->storageClass != SC_NONE)
Error(sym->pos, "Storage class \"%s\" is illegal in "
"function parameter declaration for parameter \"%s\".",
lGetStorageClassName(d->declSpecs->storageClass),
sym->name.c_str());
if (Type::Equal(sym->type, AtomicType::Void)) {
Error(sym->pos, "Parameter with type \"void\" illegal in function "
"parameter list.");
sym->type = NULL;
if (d == NULL) {
Assert(m->errorCount > 0);
continue;
}
if (d->declarators.size() == 0) {
// function declaration like foo(float), w/o a name for the
// parameter; wire up a placeholder Declarator for it
d->declarators.push_back(new Declarator(DK_BASE, pos));
d->declarators[0]->InitFromDeclSpecs(d->declSpecs);
}
const ArrayType *at = dynamic_cast<const ArrayType *>(sym->type);
Assert(d->declarators.size() == 1);
Declarator *decl = d->declarators[0];
if (decl->name == "") {
// Give a name to any anonymous parameter declarations
char buf[32];
sprintf(buf, "__anon_parameter_%d", i);
decl->name = buf;
}
decl->type = decl->type->ResolveUnboundVariability(Variability::Varying);
if (d->declSpecs->storageClass != SC_NONE)
Error(decl->pos, "Storage class \"%s\" is illegal in "
"function parameter declaration for parameter \"%s\".",
lGetStorageClassName(d->declSpecs->storageClass),
decl->name.c_str());
if (Type::Equal(decl->type, AtomicType::Void)) {
Error(decl->pos, "Parameter with type \"void\" illegal in function "
"parameter list.");
decl->type = NULL;
}
const ArrayType *at = dynamic_cast<const ArrayType *>(decl->type);
if (at != NULL) {
// As in C, arrays are passed to functions as pointers to
// their element type. We'll just immediately make this
@@ -459,69 +440,66 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
// report this differently than it was originally declared
// in the function, but it's not clear that this is a
// significant problem.)
if (at->GetElementType() == NULL) {
const Type *targetType = at->GetElementType();
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
return;
}
const Type *targetType = at->GetElementType();
targetType =
targetType->ResolveUnboundVariability(Variability::Varying);
sym->type = PointerType::GetUniform(targetType);
decl->type = PointerType::GetUniform(targetType);
// Make sure there are no unsized arrays (other than the
// first dimension) in function parameter lists.
at = dynamic_cast<const ArrayType *>(at->GetElementType());
at = dynamic_cast<const ArrayType *>(targetType);
while (at != NULL) {
if (at->GetElementCount() == 0)
Error(sym->pos, "Arrays with unsized dimensions in "
Error(decl->pos, "Arrays with unsized dimensions in "
"dimensions after the first one are illegal in "
"function parameter lists.");
at = dynamic_cast<const ArrayType *>(at->GetElementType());
}
}
args.push_back(sym->type);
argNames.push_back(sym->name);
argPos.push_back(sym->pos);
args.push_back(decl->type);
argNames.push_back(decl->name);
argPos.push_back(decl->pos);
Expr *init = NULL;
if (d->declarators.size()) {
// Try to find an initializer expression.
Declarator *decl = d->declarators[0];
while (decl != NULL) {
// Try to find an initializer expression.
while (decl != NULL) {
if (decl->initExpr != NULL) {
decl->initExpr = TypeCheck(decl->initExpr);
decl->initExpr = Optimize(decl->initExpr);
if (decl->initExpr != NULL) {
decl->initExpr = TypeCheck(decl->initExpr);
decl->initExpr = Optimize(decl->initExpr);
if (decl->initExpr != NULL) {
init = dynamic_cast<ConstExpr *>(decl->initExpr);
if (init == NULL)
init = dynamic_cast<NullPointerExpr *>(decl->initExpr);
if (init == NULL)
Error(decl->initExpr->pos, "Default value for parameter "
"\"%s\" must be a compile-time constant.",
sym->name.c_str());
}
break;
init = dynamic_cast<ConstExpr *>(decl->initExpr);
if (init == NULL)
init = dynamic_cast<NullPointerExpr *>(decl->initExpr);
if (init == NULL)
Error(decl->initExpr->pos, "Default value for parameter "
"\"%s\" must be a compile-time constant.",
decl->name.c_str());
}
else
decl = decl->child;
break;
}
else
decl = decl->child;
}
argDefaults.push_back(init);
}
const Type *returnType = type;
const Type *returnType = baseType;
if (returnType == NULL) {
Error(pos, "No return type provided in function declaration.");
return NULL;
return;
}
if (dynamic_cast<const FunctionType *>(returnType) != NULL) {
Error(pos, "Illegal to return function type from function.");
return NULL;
return;
}
returnType = returnType->ResolveUnboundVariability(Variability::Varying);
bool isExternC = ds && (ds->storageClass == SC_EXTERN_C);
bool isExported = ds && ((ds->typeQualifiers & TYPEQUAL_EXPORT) != 0);
bool isTask = ds && ((ds->typeQualifiers & TYPEQUAL_TASK) != 0);
@@ -529,28 +507,27 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
if (isExported && isTask) {
Error(pos, "Function can't have both \"task\" and \"export\" "
"qualifiers");
return NULL;
return;
}
if (isExternC && isTask) {
Error(pos, "Function can't have both \"extern \"C\"\" and \"task\" "
"qualifiers");
return NULL;
return;
}
if (isExternC && isExported) {
Error(pos, "Function can't have both \"extern \"C\"\" and \"export\" "
"qualifiers");
return NULL;
return;
}
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
return;
}
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) {
@@ -572,60 +549,12 @@ Declarator::GetType(const Type *base, DeclSpecs *ds) const {
}
}
return child->GetType(functionType, ds);
}
default:
FATAL("Unexpected decl kind");
return NULL;
child->InitFromType(functionType, ds);
type = child->type;
name = child->name;
}
}
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<const FunctionType *>(type) == NULL) {
Error(pos, "__declspec specifiers for non-function type \"%s\" are "
"not used.", type->GetString().c_str());
}
return type;
}
Symbol *
Declarator::GetSymbolForFunctionParameter(int paramNum) const {
Assert(paramNum < (int)functionParams.size());
Declaration *d = functionParams[paramNum];
char buf[32];
Symbol *sym;
if (d->declarators.size() == 0) {
// function declaration like foo(float), w/o a name for
// the parameter
sprintf(buf, "__anon_parameter_%d", paramNum);
sym = new Symbol(buf, pos);
sym->type = d->declSpecs->GetBaseType(pos);
}
else {
Assert(d->declarators.size() == 1);
sym = d->declarators[0]->GetSymbol();
if (sym == NULL) {
// Handle more complex anonymous declarations like
// float (float **).
sprintf(buf, "__anon_parameter_%d", paramNum);
sym = new Symbol(buf, d->declarators[0]->pos);
sym->type = d->declarators[0]->GetType(d->declSpecs);
}
}
return sym;
}
///////////////////////////////////////////////////////////////////////////
// Declaration
@@ -655,27 +584,23 @@ Declaration::GetVariableDeclarations() const {
for (unsigned int i = 0; i < declarators.size(); ++i) {
Declarator *decl = declarators[i];
if (decl == NULL) {
if (decl == NULL || decl->type == NULL) {
// Ignore earlier errors
Assert(m->errorCount > 0);
continue;
}
Symbol *sym = decl->GetSymbol();
if (sym == NULL || sym->type == NULL) {
// Ignore errors
Assert(m->errorCount > 0);
continue;
}
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
if (Type::Equal(sym->type, AtomicType::Void))
Error(sym->pos, "\"void\" type variable illegal in declaration.");
else if (dynamic_cast<const FunctionType *>(sym->type) == NULL) {
if (Type::Equal(decl->type, AtomicType::Void))
Error(decl->pos, "\"void\" type variable illegal in declaration.");
else if (dynamic_cast<const FunctionType *>(decl->type) == NULL) {
decl->type = decl->type->ResolveUnboundVariability(Variability::Varying);
Symbol *sym = new Symbol(decl->name, decl->pos, decl->type,
decl->storageClass);
m->symbolTable->AddVariable(sym);
vars.push_back(VariableDeclaration(sym, decl->initExpr));
}
}
return vars;
}
@@ -686,25 +611,20 @@ Declaration::DeclareFunctions() {
for (unsigned int i = 0; i < declarators.size(); ++i) {
Declarator *decl = declarators[i];
if (decl == NULL) {
if (decl == NULL || decl->type == NULL) {
// Ignore earlier errors
Assert(m->errorCount > 0);
continue;
}
Symbol *sym = decl->GetSymbol();
if (sym == NULL || sym->type == NULL) {
// Ignore errors
Assert(m->errorCount > 0);
continue;
}
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
if (dynamic_cast<const FunctionType *>(sym->type) == NULL)
const FunctionType *ftype =
dynamic_cast<const FunctionType *>(decl->type);
if (ftype == NULL)
continue;
bool isInline = (declSpecs->typeQualifiers & TYPEQUAL_INLINE);
m->AddFunctionDeclaration(sym, isInline);
m->AddFunctionDeclaration(decl->name, ftype, decl->storageClass,
isInline, decl->pos);
}
}
@@ -718,6 +638,7 @@ Declaration::Print(int indent) const {
declarators[i]->Print(indent+4);
}
///////////////////////////////////////////////////////////////////////////
void
@@ -748,21 +669,19 @@ GetStructTypesNamesPositions(const std::vector<StructDeclaration *> &sd,
Declarator *d = (*sd[i]->declarators)[j];
d->InitFromDeclSpecs(&ds);
Symbol *sym = d->GetSymbol();
if (Type::Equal(sym->type, AtomicType::Void))
if (Type::Equal(d->type, AtomicType::Void))
Error(d->pos, "\"void\" type illegal for struct member.");
elementTypes->push_back(sym->type);
elementTypes->push_back(d->type);
if (seenNames.find(sym->name) != seenNames.end())
if (seenNames.find(d->name) != seenNames.end())
Error(d->pos, "Struct member \"%s\" has same name as a "
"previously-declared member.", sym->name.c_str());
"previously-declared member.", d->name.c_str());
else
seenNames.insert(sym->name);
seenNames.insert(d->name);
elementNames->push_back(sym->name);
elementPositions->push_back(sym->pos);
elementNames->push_back(d->name);
elementPositions->push_back(d->pos);
}
}

45
decl.h
View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2011, Intel Corporation
Copyright (c) 2010-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -47,8 +47,8 @@
variables--here, that the declaration has the 'static' and 'uniform'
qualifiers, and that it's basic type is 'int'. Then for each variable
declaration, the Declaraiton class holds an instance of a Declarator,
which in turn records the per-variable information like the symbol
name, array size (if any), initializer expression, etc.
which in turn records the per-variable information like the name, array
size (if any), initializer expression, etc.
*/
#ifndef ISPC_DECL_H
@@ -61,15 +61,6 @@ struct VariableDeclaration;
class Declaration;
class Declarator;
enum StorageClass {
SC_NONE,
SC_EXTERN,
SC_STATIC,
SC_TYPEDEF,
SC_EXTERN_C
};
/* Multiple qualifiers can be provided with types in declarations;
therefore, they are set up so that they can be ANDed together into an
int. */
@@ -141,25 +132,11 @@ public:
Declarator(DeclaratorKind dk, SourcePos p);
/** Once a DeclSpecs instance is available, this method completes the
initialization of the Symbol, setting its Type accordingly.
initialization of the type member.
*/
void InitFromDeclSpecs(DeclSpecs *ds);
/** Get the actual type of the combination of Declarator and the given
DeclSpecs. If an explicit base type is provided, the declarator is
applied to that type; otherwise the base type from the DeclSpecs is
used. */
const Type *GetType(DeclSpecs *ds) const;
const Type *GetType(const Type *base, DeclSpecs *ds) const;
/** Returns the symbol corresponding to the function declared by this
declarator and symbols for its arguments in *args. */
Symbol *GetFunctionInfo(DeclSpecs *ds, std::vector<Symbol *> *args);
Symbol *GetSymbolForFunctionParameter(int paramNum) const;
/** Returns the symbol associated with the declarator. */
Symbol *GetSymbol() const;
void InitFromType(const Type *base, DeclSpecs *ds);
void Print(int indent) const;
@@ -180,18 +157,24 @@ public:
/** Type qualifiers provided with the declarator. */
int typeQualifiers;
StorageClass storageClass;
/** For array declarators, this gives the declared size of the array.
Unsized arrays have arraySize == 0. */
int arraySize;
/** Symbol associated with the declarator. */
Symbol *sym;
/** Name associated with the declarator. */
std::string name;
/** Initialization expression for the variable. May be NULL. */
Expr *initExpr;
/** Type of the declarator. This is NULL until InitFromDeclSpecs() or
InitFromType() is called. */
const Type *type;
/** For function declarations, this holds the Declaration *s for the
funciton's parameters. */
function's parameters. */
std::vector<Declaration *> functionParams;
};

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2011, Intel Corporation
Copyright (c) 2011-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -66,9 +66,8 @@
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Assembly/PrintModulePass.h>
Function::Function(Symbol *s, const std::vector<Symbol *> &a, Stmt *c) {
Function::Function(Symbol *s, Stmt *c) {
sym = s;
args = a;
code = c;
maskSymbol = m->symbolTable->LookupVariable("__mask");
@@ -104,9 +103,17 @@ Function::Function(Symbol *s, const std::vector<Symbol *> &a, Stmt *c) {
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
Assert(type != NULL);
for (unsigned int i = 0; i < args.size(); ++i)
if (dynamic_cast<const ReferenceType *>(args[i]->type) == NULL)
args[i]->parentFunction = this;
for (int i = 0; i < type->GetNumParameters(); ++i) {
const char *paramName = type->GetParameterName(i).c_str();
Symbol *sym = m->symbolTable->LookupVariable(paramName);
if (sym == NULL)
Assert(strncmp(paramName, "__anon_parameter_", 17) == 0);
args.push_back(sym);
const Type *t = type->GetParameterType(i);
if (sym != NULL && dynamic_cast<const ReferenceType *>(t) == NULL)
sym->parentFunction = this;
}
if (type->isTask) {
threadIndexSym = m->symbolTable->LookupVariable("threadIndex");
@@ -145,7 +152,8 @@ Function::GetType() const {
'mem2reg' pass will in turn promote to SSA registers..
*/
static void
lCopyInTaskParameter(int i, llvm::Value *structArgPtr, const std::vector<Symbol *> &args,
lCopyInTaskParameter(int i, llvm::Value *structArgPtr, const
std::vector<Symbol *> &args,
FunctionEmitContext *ctx) {
// We expect the argument structure to come in as a poitner to a
// structure. Confirm and figure out its type here.
@@ -160,6 +168,10 @@ lCopyInTaskParameter(int i, llvm::Value *structArgPtr, const std::vector<Symbol
LLVM_TYPE_CONST llvm::Type *argType = argStructType->getElementType(i);
Symbol *sym = args[i];
if (sym == NULL)
// anonymous parameter, so don't worry about it
return;
// allocate space to copy the parameter in to
sym->storagePtr = ctx->AllocaInst(argType, sym->name.c_str());
@@ -240,6 +252,10 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
llvm::Function::arg_iterator argIter = function->arg_begin();
for (unsigned int i = 0; i < args.size(); ++i, ++argIter) {
Symbol *sym = args[i];
if (sym == NULL)
// anonymous function parameter
continue;
argIter->setName(sym->name.c_str());
// Allocate stack storage for the parameter and emit code

4
func.h
View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2011, Intel Corporation
Copyright (c) 2011-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,7 @@
class Function {
public:
Function(Symbol *sym, const std::vector<Symbol *> &args, Stmt *code);
Function(Symbol *sym, Stmt *code);
const Type *GetReturnType() const;
const FunctionType *GetType() const;

9
ispc.h
View File

@@ -116,6 +116,15 @@ class SymbolTable;
class Type;
struct VariableDeclaration;
enum StorageClass {
SC_NONE,
SC_EXTERN,
SC_STATIC,
SC_TYPEDEF,
SC_EXTERN_C
};
/** @brief Representation of a range of positions in a source file.
This class represents a range of characters in a source file

View File

@@ -231,64 +231,65 @@ Module::CompileFile() {
void
Module::AddTypeDef(Symbol *sym) {
Module::AddTypeDef(const std::string &name, const Type *type,
SourcePos pos) {
// Typedefs are easy; just add the mapping between the given name and
// the given type.
symbolTable->AddType(sym->name.c_str(), sym->type, sym->pos);
symbolTable->AddType(name.c_str(), type, pos);
}
void
Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
Module::AddGlobalVariable(const std::string &name, const Type *type, Expr *initExpr,
bool isConst, StorageClass storageClass, SourcePos pos) {
// These may be NULL due to errors in parsing; just gracefully return
// here if so.
if (sym == NULL || sym->type == NULL) {
// But if these are NULL and there haven't been any previous
// errors, something surprising is going on
if (name == "" || type == NULL) {
Assert(errorCount > 0);
return;
}
if (symbolTable->LookupFunction(sym->name.c_str())) {
Error(sym->pos, "Global variable \"%s\" shadows previously-declared "
"function.", sym->name.c_str());
if (symbolTable->LookupFunction(name.c_str())) {
Error(pos, "Global variable \"%s\" shadows previously-declared "
"function.", name.c_str());
return;
}
if (sym->storageClass == SC_EXTERN_C) {
Error(sym->pos, "extern \"C\" qualifier can only be used for "
if (storageClass == SC_EXTERN_C) {
Error(pos, "extern \"C\" qualifier can only be used for "
"functions.");
return;
}
if (Type::Equal(sym->type, AtomicType::Void)) {
Error(sym->pos, "\"void\" type global variable is illegal.");
if (Type::Equal(type, AtomicType::Void)) {
Error(pos, "\"void\" type global variable is illegal.");
return;
}
sym->type = ArrayType::SizeUnsizedArrays(sym->type, initExpr);
if (sym->type == NULL)
type = ArrayType::SizeUnsizedArrays(type, initExpr);
if (type == NULL)
return;
const ArrayType *at = dynamic_cast<const ArrayType *>(sym->type);
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
if (at != NULL && at->TotalElementCount() == 0) {
Error(sym->pos, "Illegal to declare a global variable with unsized "
Error(pos, "Illegal to declare a global variable with unsized "
"array dimensions that aren't set with an initializer "
"expression.");
return;
}
LLVM_TYPE_CONST llvm::Type *llvmType = sym->type->LLVMType(g->ctx);
LLVM_TYPE_CONST llvm::Type *llvmType = type->LLVMType(g->ctx);
if (llvmType == NULL)
return;
// See if we have an initializer expression for the global. If so,
// make sure it's a compile-time constant!
llvm::Constant *llvmInitializer = NULL;
if (sym->storageClass == SC_EXTERN || sym->storageClass == SC_EXTERN_C) {
ConstExpr *constValue = NULL;
if (storageClass == SC_EXTERN || storageClass == SC_EXTERN_C) {
if (initExpr != NULL)
Error(sym->pos, "Initializer can't be provided with \"extern\" "
"global variable \"%s\".", sym->name.c_str());
Error(pos, "Initializer can't be provided with \"extern\" "
"global variable \"%s\".", name.c_str());
}
else {
if (initExpr != NULL) {
@@ -299,27 +300,26 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
// ExprList; they don't have types per se / can't type
// convert themselves anyway.)
if (dynamic_cast<ExprList *>(initExpr) == NULL)
initExpr = TypeConvertExpr(initExpr, sym->type, "initializer");
initExpr = TypeConvertExpr(initExpr, type, "initializer");
if (initExpr != NULL) {
initExpr = Optimize(initExpr);
// Fingers crossed, now let's see if we've got a
// constant value..
llvmInitializer = initExpr->GetConstant(sym->type);
llvmInitializer = initExpr->GetConstant(type);
if (llvmInitializer != NULL) {
if (sym->type->IsConstType())
if (type->IsConstType())
// Try to get a ConstExpr associated with
// the symbol. This dynamic_cast can
// validly fail, for example for types like
// StructTypes where a ConstExpr can't
// represent their values.
sym->constValue =
dynamic_cast<ConstExpr *>(initExpr);
constValue = dynamic_cast<ConstExpr *>(initExpr);
}
else
Error(initExpr->pos, "Initializer for global variable \"%s\" "
"must be a constant.", sym->name.c_str());
"must be a constant.", name.c_str());
}
}
}
@@ -330,30 +330,33 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
llvmInitializer = llvm::Constant::getNullValue(llvmType);
}
Symbol *stSym = symbolTable->LookupVariable(sym->name.c_str());
Symbol *sym = symbolTable->LookupVariable(name.c_str());
llvm::GlobalVariable *oldGV = NULL;
if (stSym != NULL) {
if (sym != NULL) {
// We've already seen either a declaration or a definition of this
// global.
// If the type doesn't match with the previous one, issue an error.
if (!Type::Equal(sym->type, stSym->type)) {
Error(sym->pos, "Definition of variable \"%s\" conflicts with "
"definition at %s:%d.", sym->name.c_str(),
stSym->pos.name, stSym->pos.first_line);
if (!Type::Equal(sym->type, type) ||
(sym->storageClass != SC_EXTERN &&
sym->storageClass != SC_EXTERN_C &&
sym->storageClass != storageClass)) {
Error(pos, "Definition of variable \"%s\" conflicts with "
"definition at %s:%d.", name.c_str(),
sym->pos.name, sym->pos.first_line);
return;
}
llvm::GlobalVariable *gv =
llvm::dyn_cast<llvm::GlobalVariable>(stSym->storagePtr);
llvm::dyn_cast<llvm::GlobalVariable>(sym->storagePtr);
Assert(gv != NULL);
// And issue an error if this is a redefinition of a variable
if (gv->hasInitializer() &&
sym->storageClass != SC_EXTERN && sym->storageClass != SC_EXTERN_C) {
Error(sym->pos, "Redefinition of variable \"%s\" is illegal. "
Error(pos, "Redefinition of variable \"%s\" is illegal. "
"(Previous definition at %s:%d.)", sym->name.c_str(),
stSym->pos.name, stSym->pos.first_line);
sym->pos.name, sym->pos.first_line);
return;
}
@@ -361,17 +364,12 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
// of a previously-declared global. First, save the pointer to the
// previous llvm::GlobalVariable
oldGV = gv;
// Now copy over all of the members of the current Symbol to the
// symbol in the symbol table.
*stSym = *sym;
// And copy the pointer of the one in the symbol table to sym, so
// that the operations below update storagePtr for the Symbol
// already in the symbol table.
sym = stSym;
}
else
else {
sym = new Symbol(name, pos, type, storageClass);
symbolTable->AddVariable(sym);
}
sym->constValue = constValue;
llvm::GlobalValue::LinkageTypes linkage =
(sym->storageClass == SC_STATIC) ? llvm::GlobalValue::InternalLinkage :
@@ -393,10 +391,10 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
}
if (diBuilder) {
llvm::DIFile file = sym->pos.GetDIFile();
diBuilder->createGlobalVariable(sym->name,
llvm::DIFile file = pos.GetDIFile();
diBuilder->createGlobalVariable(name,
file,
sym->pos.first_line,
pos.first_line,
sym->type->GetDIType(file),
(sym->storageClass == SC_STATIC),
sym->storagePtr);
@@ -487,22 +485,23 @@ lCheckForStructParameters(const FunctionType *ftype, SourcePos pos) {
false if any errors were encountered.
*/
void
Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
const FunctionType *functionType =
dynamic_cast<const FunctionType *>(funSym->type);
Module::AddFunctionDeclaration(const std::string &name,
const FunctionType *functionType,
StorageClass storageClass, bool isInline,
SourcePos pos) {
Assert(functionType != NULL);
// If a global variable with the same name has already been declared
// issue an error.
if (symbolTable->LookupVariable(funSym->name.c_str()) != NULL) {
Error(funSym->pos, "Function \"%s\" shadows previously-declared global variable. "
if (symbolTable->LookupVariable(name.c_str()) != NULL) {
Error(pos, "Function \"%s\" shadows previously-declared global variable. "
"Ignoring this definition.",
funSym->name.c_str());
name.c_str());
return;
}
std::vector<Symbol *> overloadFuncs;
symbolTable->LookupFunction(funSym->name.c_str(), &overloadFuncs);
symbolTable->LookupFunction(name.c_str(), &overloadFuncs);
if (overloadFuncs.size() > 0) {
for (unsigned int i = 0; i < overloadFuncs.size(); ++i) {
Symbol *overloadFunc = overloadFuncs[i];
@@ -528,7 +527,7 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
if (i == functionType->GetNumParameters()) {
std::string thisRetType = functionType->GetReturnTypeString();
std::string otherRetType = ofType->GetReturnTypeString();
Error(funSym->pos, "Illegal to overload function by return "
Error(pos, "Illegal to overload function by return "
"type only. This function returns \"%s\" while "
"previous declaration at %s:%d returns \"%s\".",
thisRetType.c_str(), overloadFunc->pos.name,
@@ -539,55 +538,54 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
}
}
if (funSym->storageClass == SC_EXTERN_C) {
if (storageClass == SC_EXTERN_C) {
// Make sure the user hasn't supplied both an 'extern "C"' and a
// 'task' qualifier with the function
if (functionType->isTask) {
Error(funSym->pos, "\"task\" qualifier is illegal with C-linkage extern "
"function \"%s\". Ignoring this function.", funSym->name.c_str());
Error(pos, "\"task\" qualifier is illegal with C-linkage extern "
"function \"%s\". Ignoring this function.", name.c_str());
return;
}
std::vector<Symbol *> funcs;
symbolTable->LookupFunction(funSym->name.c_str(), &funcs);
symbolTable->LookupFunction(name.c_str(), &funcs);
if (funcs.size() > 0) {
if (funcs.size() > 1) {
// Multiple functions with this name have already been declared;
// can't overload here
Error(funSym->pos, "Can't overload extern \"C\" function \"%s\"; "
Error(pos, "Can't overload extern \"C\" function \"%s\"; "
"%d functions with the same name have already been declared.",
funSym->name.c_str(), (int)funcs.size());
name.c_str(), (int)funcs.size());
return;
}
// One function with the same name has been declared; see if it
// has the same type as this one, in which case it's ok.
if (Type::Equal(funcs[0]->type, funSym->type))
if (Type::Equal(funcs[0]->type, functionType))
return;
else {
Error(funSym->pos, "Can't overload extern \"C\" function \"%s\".",
funSym->name.c_str());
Error(pos, "Can't overload extern \"C\" function \"%s\".",
name.c_str());
return;
}
}
}
// Get the LLVM FunctionType
bool includeMask = (funSym->storageClass != SC_EXTERN_C);
bool includeMask = (storageClass != SC_EXTERN_C);
LLVM_TYPE_CONST llvm::FunctionType *llvmFunctionType =
functionType->LLVMFunctionType(g->ctx, includeMask);
if (llvmFunctionType == NULL)
return;
// And create the llvm::Function
llvm::GlobalValue::LinkageTypes linkage = (funSym->storageClass == SC_STATIC ||
llvm::GlobalValue::LinkageTypes linkage = (storageClass == SC_STATIC ||
isInline) ?
llvm::GlobalValue::InternalLinkage : llvm::GlobalValue::ExternalLinkage;
std::string functionName;
if (funSym->storageClass == SC_EXTERN_C)
functionName = funSym->name;
else {
functionName = funSym->MangledName();
std::string functionName = name;
if (storageClass != SC_EXTERN_C) {
functionName += functionType->Mangle();
if (g->mangleFunctionsWithTarget)
functionName += g->target.GetISAString();
}
@@ -597,7 +595,7 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
// Set function attributes: we never throw exceptions
function->setDoesNotThrow(true);
if (!(funSym->storageClass == SC_EXTERN_C) &&
if (storageClass != SC_EXTERN_C &&
!g->generateDebuggingSymbols &&
isInline)
function->addFnAttr(llvm::Attribute::AlwaysInline);
@@ -609,15 +607,15 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
// 'export'ed.
if (functionType->isExported &&
lRecursiveCheckValidParamType(functionType->GetReturnType()))
Error(funSym->pos, "Illegal to return a \"varying\" type from exported "
"function \"%s\"", funSym->name.c_str());
Error(pos, "Illegal to return a \"varying\" type from exported "
"function \"%s\"", name.c_str());
if (functionType->isTask &&
Type::Equal(functionType->GetReturnType(), AtomicType::Void) == false)
Error(funSym->pos, "Task-qualified functions must have void return type.");
Error(pos, "Task-qualified functions must have void return type.");
if (functionType->isExported || functionType->isExternC)
lCheckForStructParameters(functionType, funSym->pos);
lCheckForStructParameters(functionType, pos);
// Loop over all of the arguments; process default values if present
// and do other checks and parameter attribute setting.
@@ -675,19 +673,33 @@ Module::AddFunctionDeclaration(Symbol *funSym, bool isInline) {
function->eraseFromParent();
function = module->getFunction(functionName);
}
funSym->function = function;
// Finally, we know all is good and we can add the function to the
// symbol table
Symbol *funSym = new Symbol(name, pos, functionType, storageClass);
funSym->function = function;
bool ok = symbolTable->AddFunction(funSym);
Assert(ok);
}
void
Module::AddFunctionDefinition(Symbol *sym, const std::vector<Symbol *> &args,
Module::AddFunctionDefinition(const std::string &name, const FunctionType *type,
Stmt *code) {
ast->AddFunction(sym, args, code);
Symbol *sym = symbolTable->LookupFunction(name.c_str(), type);
if (sym == NULL) {
Assert(m->errorCount > 0);
return;
}
// FIXME: because we encode the parameter names in the function type,
// we need to override the function type here in case the function had
// earlier been declared with anonymous parameter names but is now
// defined with actual names. This is yet another reason we shouldn't
// include the names in FunctionType...
sym->type = type;
ast->AddFunction(sym, code);
}

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2011, Intel Corporation
Copyright (c) 2010-2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -59,21 +59,26 @@ public:
int CompileFile();
/** Add a named type definition to the module. */
void AddTypeDef(Symbol *sym);
void AddTypeDef(const std::string &name, const Type *type,
SourcePos pos);
/** Add a new global variable corresponding to the given Symbol to the
module. If non-NULL, initExpr gives the initiailizer expression
for the global's inital value. */
void AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst);
void AddGlobalVariable(const std::string &name, const Type *type,
Expr *initExpr, bool isConst,
StorageClass storageClass, SourcePos pos);
/** Add a declaration of the function defined by the given function
symbol to the module. */
void AddFunctionDeclaration(Symbol *funSym, bool isInline);
void AddFunctionDeclaration(const std::string &name,
const FunctionType *ftype,
StorageClass sc, bool isInline, SourcePos pos);
/** Adds the function described by the declaration information and the
provided statements to the module. */
void AddFunctionDefinition(Symbol *sym, const std::vector<Symbol *> &args,
Stmt *code);
void AddFunctionDefinition(const std::string &name,
const FunctionType *ftype, Stmt *code);
/** After a source file has been compiled, output can be generated in a
number of different formats. */

View File

@@ -631,7 +631,9 @@ declaration_statement
if ($1->declarators[i] == NULL)
Assert(m->errorCount > 0);
else
m->AddTypeDef($1->declarators[i]->GetSymbol());
m->AddTypeDef($1->declarators[i]->name,
$1->declarators[i]->type,
$1->declarators[i]->pos);
}
$$ = NULL;
}
@@ -1174,7 +1176,7 @@ direct_declarator
: TOKEN_IDENTIFIER
{
Declarator *d = new Declarator(DK_BASE, @1);
d->sym = new Symbol(yytext, @1);
d->name = yytext;
$$ = d;
}
| '(' declarator ')'
@@ -1349,8 +1351,10 @@ type_name
{
if ($1 == NULL || $2 == NULL)
$$ = NULL;
else
$$ = $2->GetType($1, NULL);
else {
$2->InitFromType($1, NULL);
$$ = $2->type;
}
}
;
@@ -1854,11 +1858,14 @@ function_definition
}
compound_statement
{
std::vector<Symbol *> args;
if ($2 != NULL) {
Symbol *sym = $2->GetFunctionInfo($1, &args);
if (sym != NULL)
m->AddFunctionDefinition(sym, args, $4);
$2->InitFromDeclSpecs($1);
const FunctionType *funcType =
dynamic_cast<const FunctionType *>($2->type);
if (funcType == NULL)
Assert(m->errorCount > 0);
else
m->AddFunctionDefinition($2->name, funcType, $4);
}
m->symbolTable->PopScope(); // push in lAddFunctionParams();
}
@@ -1968,35 +1975,27 @@ lAddDeclaration(DeclSpecs *ds, Declarator *decl) {
// Error happened earlier during parsing
return;
decl->InitFromDeclSpecs(ds);
if (ds->storageClass == SC_TYPEDEF)
m->AddTypeDef(decl->GetSymbol());
m->AddTypeDef(decl->name, decl->type, decl->pos);
else {
const Type *t = decl->GetType(ds);
if (t == NULL) {
if (decl->type == NULL) {
Assert(m->errorCount > 0);
return;
}
Symbol *sym = decl->GetSymbol();
if (sym == NULL) {
Assert(m->errorCount > 0);
return;
}
const FunctionType *ft = dynamic_cast<const FunctionType *>(t);
decl->type = decl->type->ResolveUnboundVariability(Variability::Varying);
const FunctionType *ft = dynamic_cast<const FunctionType *>(decl->type);
if (ft != NULL) {
sym->type = ft;
sym->storageClass = ds->storageClass;
bool isInline = (ds->typeQualifiers & TYPEQUAL_INLINE);
m->AddFunctionDeclaration(sym, isInline);
m->AddFunctionDeclaration(decl->name, ft, ds->storageClass,
isInline, decl->pos);
}
else {
if (sym->type == NULL)
Assert(m->errorCount > 0);
else
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
bool isConst = (ds->typeQualifiers & TYPEQUAL_CONST) != 0;
m->AddGlobalVariable(sym, decl->initExpr, isConst);
m->AddGlobalVariable(decl->name, decl->type, decl->initExpr,
isConst, decl->storageClass, decl->pos);
}
}
}
@@ -2025,16 +2024,13 @@ lAddFunctionParams(Declarator *decl) {
// now loop over its parameters and add them to the symbol table
for (unsigned int i = 0; i < decl->functionParams.size(); ++i) {
Declaration *pdecl = decl->functionParams[i];
if (pdecl == NULL || pdecl->declarators.size() == 0)
// zero size declarators array corresponds to an anonymous
// parameter
continue;
Assert(pdecl->declarators.size() == 1);
Symbol *sym = pdecl->declarators[0]->GetSymbol();
if (sym == NULL || sym->type == NULL)
Assert(pdecl != NULL && pdecl->declarators.size() == 1);
Declarator *declarator = pdecl->declarators[0];
if (declarator == NULL)
Assert(m->errorCount > 0);
else {
sym->type = sym->type->ResolveUnboundVariability(Variability::Varying);
Symbol *sym = new Symbol(declarator->name, declarator->pos,
declarator->type, declarator->storageClass);
#ifndef NDEBUG
bool ok = m->symbolTable->AddVariable(sym);
if (ok == false)

View File

@@ -56,12 +56,6 @@ Symbol::Symbol(const std::string &n, SourcePos p, const Type *t,
}
std::string
Symbol::MangledName() const {
return name + type->Mangle();
}
///////////////////////////////////////////////////////////////////////////
// SymbolTable

7
sym.h
View File

@@ -67,13 +67,6 @@ public:
Symbol(const std::string &name, SourcePos pos, const Type *t = NULL,
StorageClass sc = SC_NONE);
/** This method should only be called for function symbols; for them,
it returns a mangled version of the function name with the argument
types encoded into the returned name. This is used to generate
unique symbols in object files for overloaded functions.
*/
std::string MangledName() const;
SourcePos pos; /*!< Source file position where the symbol was defined */
std::string name; /*!< Symbol's name */
llvm::Value *storagePtr; /*!< For symbols with storage associated with

View File

@@ -0,0 +1,15 @@
export uniform int width() { return programCount; }
float foo(float &) { return 1; }
float bar(uniform float []) { return 2; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
float x = 0;
RET[programIndex] = foo(x) + bar(aFOO);
}
export void result(uniform float RET[]) {
RET[programIndex] = 3;
}

View File

@@ -0,0 +1,14 @@
export uniform int width() { return programCount; }
extern int foo;
int foo = 1;
export void f_f(uniform float RET[], uniform float aFOO[]) {
RET[programIndex] = foo;
}
export void result(uniform float RET[]) {
RET[programIndex] = 1;
}

View File

@@ -0,0 +1,18 @@
export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform int x[2][10];
for (uniform int i = 0; i < 2; ++i) {
for (uniform int j = 0; j < 10; ++j) {
x[i][j] = 10*i+j;
}
}
uniform int (* varying y)[10] = x;
RET[programIndex] = y[1][programIndex % 5];
}
export void result(uniform float RET[]) {
RET[programIndex] = 10+ (programIndex % 5);
}