Significantly reduce the tendrils of DeclSpecs/Declarator/Declaration code
The stuff in decl.h/decl.cpp is messy, largely due to its close mapping to C-style variable declarations. This checkin has updated code throughout all of the declaration statement, variable, and function code that operates on symbols and types directly. Thus, Decl* related stuff is now localized to decl.h/decl.cpp and the parser. Issue #13.
This commit is contained in:
6
ast.cpp
6
ast.cpp
@@ -36,9 +36,7 @@
|
||||
*/
|
||||
|
||||
#include "ast.h"
|
||||
#include "decl.h"
|
||||
#include "func.h"
|
||||
#include "type.h"
|
||||
#include "sym.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -52,8 +50,8 @@ ASTNode::~ASTNode() {
|
||||
// AST
|
||||
|
||||
void
|
||||
AST::AddFunction(DeclSpecs *ds, Declarator *decl, Stmt *code) {
|
||||
functions.push_back(new Function(ds, decl, code));
|
||||
AST::AddFunction(Symbol *sym, const std::vector<Symbol *> &args, Stmt *code) {
|
||||
functions.push_back(new Function(sym, args, code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
3
ast.h
3
ast.h
@@ -80,7 +80,8 @@ class AST {
|
||||
public:
|
||||
/** Add the AST for a function described by the given declaration
|
||||
information and source code. */
|
||||
void AddFunction(DeclSpecs *ds, Declarator *decl, Stmt *code);
|
||||
void AddFunction(Symbol *sym, const std::vector<Symbol *> &args,
|
||||
Stmt *code);
|
||||
|
||||
/** Generate LLVM IR for all of the functions into the current
|
||||
module. */
|
||||
|
||||
69
decl.cpp
69
decl.cpp
@@ -38,10 +38,13 @@
|
||||
|
||||
#include "decl.h"
|
||||
#include "util.h"
|
||||
#include "module.h"
|
||||
#include "sym.h"
|
||||
#include "type.h"
|
||||
#include "stmt.h"
|
||||
#include "expr.h"
|
||||
#include <stdio.h>
|
||||
#include <llvm/Module.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// DeclSpecs
|
||||
@@ -117,6 +120,30 @@ Declarator::Print() const {
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Declarator::GetFunctionInfo(DeclSpecs *ds, Symbol **funSym,
|
||||
std::vector<Symbol *> *funArgs) {
|
||||
// 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.)
|
||||
const FunctionType *type =
|
||||
dynamic_cast<const FunctionType *>(GetType(ds));
|
||||
assert(type != NULL);
|
||||
*funSym = m->symbolTable->LookupFunction(sym->name.c_str(), type);
|
||||
if (*funSym != NULL)
|
||||
// May be NULL due to error earlier in compilation
|
||||
(*funSym)->pos = pos;
|
||||
|
||||
if (functionArgs != NULL) {
|
||||
for (unsigned int i = 0; i < functionArgs->size(); ++i) {
|
||||
Declaration *pdecl = (*functionArgs)[i];
|
||||
assert(pdecl->declarators.size() == 1);
|
||||
funArgs->push_back(pdecl->declarators[0]->sym);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const Type *
|
||||
lGetType(const Declarator *decl, DeclSpecs *ds,
|
||||
std::vector<int>::const_iterator arrayIter) {
|
||||
@@ -292,13 +319,43 @@ Declarator::GetType(DeclSpecs *ds) const {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Declaration
|
||||
|
||||
void
|
||||
Declaration::AddSymbols(SymbolTable *st) const {
|
||||
assert(declSpecs->storageClass != SC_TYPEDEF);
|
||||
|
||||
Declaration::Declaration(DeclSpecs *ds, std::vector<Declarator *> *dlist) {
|
||||
declSpecs = ds;
|
||||
if (dlist != NULL)
|
||||
declarators = *dlist;
|
||||
for (unsigned int i = 0; i < declarators.size(); ++i)
|
||||
if (declarators[i])
|
||||
st->AddVariable(declarators[i]->sym);
|
||||
if (declarators[i] != NULL)
|
||||
declarators[i]->InitFromDeclSpecs(declSpecs);
|
||||
}
|
||||
|
||||
|
||||
Declaration::Declaration(DeclSpecs *ds, Declarator *d) {
|
||||
declSpecs = ds;
|
||||
if (d) {
|
||||
d->InitFromDeclSpecs(ds);
|
||||
declarators.push_back(d);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<VariableDeclaration>
|
||||
Declaration::GetVariableDeclarations() const {
|
||||
assert(declSpecs->storageClass != SC_TYPEDEF);
|
||||
std::vector<VariableDeclaration> vars;
|
||||
|
||||
for (unsigned int i = 0; i < declarators.size(); ++i) {
|
||||
if (declarators[i] == NULL)
|
||||
continue;
|
||||
Declarator *decl = declarators[i];
|
||||
if (!decl || decl->isFunction)
|
||||
continue;
|
||||
|
||||
m->symbolTable->AddVariable(declarators[i]->sym);
|
||||
|
||||
vars.push_back(VariableDeclaration(declarators[i]->sym,
|
||||
declarators[i]->initExpr));
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
|
||||
|
||||
30
decl.h
30
decl.h
@@ -56,6 +56,11 @@
|
||||
|
||||
#include "ispc.h"
|
||||
|
||||
struct VariableDeclaration;
|
||||
|
||||
class Declaration;
|
||||
class Declarator;
|
||||
|
||||
enum StorageClass {
|
||||
SC_NONE,
|
||||
SC_EXTERN,
|
||||
@@ -137,6 +142,9 @@ public:
|
||||
DeclSpecs */
|
||||
const Type *GetType(DeclSpecs *ds) const;
|
||||
|
||||
void GetFunctionInfo(DeclSpecs *ds, Symbol **sym,
|
||||
std::vector<Symbol *> *args);
|
||||
|
||||
void Print() const;
|
||||
|
||||
const SourcePos pos;
|
||||
@@ -157,27 +165,13 @@ public:
|
||||
*/
|
||||
class Declaration {
|
||||
public:
|
||||
Declaration(DeclSpecs *ds, std::vector<Declarator *> *dlist = NULL) {
|
||||
declSpecs = ds;
|
||||
if (dlist != NULL)
|
||||
declarators = *dlist;
|
||||
for (unsigned int i = 0; i < declarators.size(); ++i)
|
||||
if (declarators[i] != NULL)
|
||||
declarators[i]->InitFromDeclSpecs(declSpecs);
|
||||
}
|
||||
Declaration(DeclSpecs *ds, Declarator *d) {
|
||||
declSpecs = ds;
|
||||
if (d) {
|
||||
d->InitFromDeclSpecs(ds);
|
||||
declarators.push_back(d);
|
||||
}
|
||||
}
|
||||
Declaration(DeclSpecs *ds, std::vector<Declarator *> *dlist = NULL);
|
||||
Declaration(DeclSpecs *ds, Declarator *d);
|
||||
|
||||
/** Adds the symbols for the variables in the declaration to the symbol
|
||||
table. */
|
||||
void AddSymbols(SymbolTable *st) const;
|
||||
void Print() const;
|
||||
|
||||
std::vector<VariableDeclaration> GetVariableDeclarations() const;
|
||||
|
||||
DeclSpecs *declSpecs;
|
||||
std::vector<Declarator *> declarators;
|
||||
};
|
||||
|
||||
295
func.cpp
295
func.cpp
@@ -37,7 +37,6 @@
|
||||
|
||||
#include "func.h"
|
||||
#include "ctx.h"
|
||||
#include "decl.h"
|
||||
#include "expr.h"
|
||||
#include "llvmutil.h"
|
||||
#include "module.h"
|
||||
@@ -67,7 +66,9 @@
|
||||
#include <llvm/Support/ToolOutputFile.h>
|
||||
#include <llvm/Assembly/PrintModulePass.h>
|
||||
|
||||
Function::Function(DeclSpecs *ds, Declarator *decl, Stmt *c) {
|
||||
Function::Function(Symbol *s, const std::vector<Symbol *> &a, Stmt *c) {
|
||||
sym = s;
|
||||
args = a;
|
||||
code = c;
|
||||
|
||||
maskSymbol = m->symbolTable->LookupVariable("__mask");
|
||||
@@ -80,37 +81,17 @@ Function::Function(DeclSpecs *ds, Declarator *decl, Stmt *c) {
|
||||
}
|
||||
|
||||
if (g->debugPrint) {
|
||||
printf("Add Function\n");
|
||||
ds->Print();
|
||||
printf("\n");
|
||||
decl->Print();
|
||||
printf("\n");
|
||||
printf("Add Function %s\n", sym->name.c_str());
|
||||
code->Print(0);
|
||||
printf("\n\n\n");
|
||||
}
|
||||
|
||||
// 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.)
|
||||
type = dynamic_cast<const FunctionType *>(decl->GetType(ds));
|
||||
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
|
||||
assert(type != NULL);
|
||||
sym = m->symbolTable->LookupFunction(decl->sym->name.c_str(), type);
|
||||
if (sym != NULL)
|
||||
// May be NULL due to error earlier in compilation
|
||||
sym->pos = decl->pos;
|
||||
|
||||
isExported = (ds->storageClass == SC_EXPORT);
|
||||
|
||||
if (decl->functionArgs != NULL) {
|
||||
for (unsigned int i = 0; i < decl->functionArgs->size(); ++i) {
|
||||
Declaration *pdecl = (*decl->functionArgs)[i];
|
||||
assert(pdecl->declarators.size() == 1);
|
||||
Symbol *sym = pdecl->declarators[0]->sym;
|
||||
if (dynamic_cast<const ReferenceType *>(sym->type) == NULL)
|
||||
sym->parentFunction = this;
|
||||
args.push_back(sym);
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i < args.size(); ++i)
|
||||
if (dynamic_cast<const ReferenceType *>(args[i]->type) == NULL)
|
||||
args[i]->parentFunction = this;
|
||||
|
||||
if (type->isTask) {
|
||||
threadIndexSym = m->symbolTable->LookupVariable("threadIndex");
|
||||
@@ -127,264 +108,18 @@ Function::Function(DeclSpecs *ds, Declarator *decl, Stmt *c) {
|
||||
}
|
||||
|
||||
|
||||
/** Given an arbitrary type, see if it or any of the types contained in it
|
||||
are varying. Returns true if so, false otherwise.
|
||||
*/
|
||||
static bool
|
||||
lRecursiveCheckVarying(const Type *t) {
|
||||
t = t->GetBaseType();
|
||||
if (t->IsVaryingType()) return true;
|
||||
|
||||
const StructType *st = dynamic_cast<const StructType *>(t);
|
||||
if (st) {
|
||||
for (int i = 0; i < st->GetElementCount(); ++i)
|
||||
if (lRecursiveCheckVarying(st->GetElementType(i)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Given a Symbol representing a function parameter, see if it or any
|
||||
contained types are varying. If so, issue an error. (This function
|
||||
should only be called for parameters to 'export'ed functions, where
|
||||
varying parameters is illegal.
|
||||
*/
|
||||
static void
|
||||
lCheckForVaryingParameter(Symbol *sym) {
|
||||
if (lRecursiveCheckVarying(sym->type)) {
|
||||
const Type *t = sym->type->GetBaseType();
|
||||
if (dynamic_cast<const StructType *>(t))
|
||||
Error(sym->pos, "Struct parameter \"%s\" with varying member(s) is illegal "
|
||||
"in an exported function.",
|
||||
sym->name.c_str());
|
||||
else
|
||||
Error(sym->pos, "Varying parameter \"%s\" is illegal in an exported function.",
|
||||
sym->name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Given a function type, loop through the function parameters and see if
|
||||
any are StructTypes. If so, issue an error (this seems to be broken
|
||||
currently).
|
||||
|
||||
@todo Fix passing structs from C/C++ to ispc functions.
|
||||
*/
|
||||
static void
|
||||
lCheckForStructParameters(const FunctionType *ftype, SourcePos pos) {
|
||||
const std::vector<const Type *> &argTypes = ftype->GetArgumentTypes();
|
||||
for (unsigned int i = 0; i < argTypes.size(); ++i) {
|
||||
const Type *type = argTypes[i];
|
||||
if (dynamic_cast<const StructType *>(type) != NULL) {
|
||||
Error(pos, "Passing structs to/from application functions is currently broken. "
|
||||
"Use a reference or const reference instead for now.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** We've got a declaration for a function to process. This function does
|
||||
all the work of creating the corresponding llvm::Function instance,
|
||||
adding the symbol for the function to the symbol table and doing
|
||||
various sanity checks. This function returns true upon success and
|
||||
false if any errors were encountered.
|
||||
*/
|
||||
Symbol *
|
||||
Function::InitFunctionSymbol(DeclSpecs *ds, Declarator *decl) {
|
||||
// Make sure that we've got what we expect here
|
||||
Symbol *funSym = decl->sym;
|
||||
assert(decl->isFunction);
|
||||
assert(decl->arraySize.size() == 0);
|
||||
|
||||
// So far, so good. Go ahead and set the type of the function symbol
|
||||
funSym->type = decl->GetType(ds);
|
||||
|
||||
// If a global variable with the same name has already been declared
|
||||
// issue an error.
|
||||
if (m->symbolTable->LookupVariable(funSym->name.c_str()) != NULL) {
|
||||
Error(decl->pos, "Function \"%s\" shadows previously-declared global variable. "
|
||||
"Ignoring this definition.",
|
||||
funSym->name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ds->storageClass == SC_EXTERN_C) {
|
||||
// Make sure the user hasn't supplied both an 'extern "C"' and a
|
||||
// 'task' qualifier with the function
|
||||
if (ds->typeQualifier & TYPEQUAL_TASK) {
|
||||
Error(funSym->pos, "\"task\" qualifier is illegal with C-linkage extern "
|
||||
"function \"%s\". Ignoring this function.", funSym->name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
std::vector<Symbol *> *funcs;
|
||||
funcs = m->symbolTable->LookupFunction(decl->sym->name.c_str());
|
||||
if (funcs != NULL) {
|
||||
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\"; "
|
||||
"%d functions with the same name have already been declared.",
|
||||
funSym->name.c_str(), (int)funcs->size());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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))
|
||||
return (*funcs)[0];
|
||||
else {
|
||||
Error(funSym->pos, "Can't overload extern \"C\" function \"%s\".",
|
||||
funSym->name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We should have gotten a FunctionType back from the GetType() call above.
|
||||
const FunctionType *functionType =
|
||||
dynamic_cast<const FunctionType *>(funSym->type);
|
||||
assert(functionType != NULL);
|
||||
|
||||
// Get the LLVM FunctionType
|
||||
bool includeMask = (ds->storageClass != SC_EXTERN_C);
|
||||
LLVM_TYPE_CONST llvm::FunctionType *llvmFunctionType =
|
||||
functionType->LLVMFunctionType(g->ctx, includeMask);
|
||||
if (llvmFunctionType == NULL)
|
||||
return NULL;
|
||||
|
||||
// And create the llvm::Function
|
||||
llvm::GlobalValue::LinkageTypes linkage = (ds->storageClass == SC_STATIC ||
|
||||
(ds->typeQualifier & TYPEQUAL_INLINE)) ?
|
||||
llvm::GlobalValue::InternalLinkage : llvm::GlobalValue::ExternalLinkage;
|
||||
std::string functionName = ((ds->storageClass == SC_EXTERN_C) ?
|
||||
funSym->name : funSym->MangledName());
|
||||
if (g->mangleFunctionsWithTarget)
|
||||
functionName += g->target.GetISAString();
|
||||
llvm::Function *function =
|
||||
llvm::Function::Create(llvmFunctionType, linkage, functionName.c_str(), m->module);
|
||||
|
||||
// Set function attributes: we never throw exceptions, and want to
|
||||
// inline everything we can
|
||||
function->setDoesNotThrow(true);
|
||||
if (!(ds->storageClass == SC_EXTERN_C) && !g->generateDebuggingSymbols &&
|
||||
(ds->typeQualifier & TYPEQUAL_INLINE))
|
||||
function->addFnAttr(llvm::Attribute::AlwaysInline);
|
||||
if (functionType->isTask)
|
||||
// This also applies transitively to members I think?
|
||||
function->setDoesNotAlias(1, true);
|
||||
|
||||
// Make sure that the return type isn't 'varying' if the function is
|
||||
// 'export'ed.
|
||||
if (ds->storageClass == SC_EXPORT &&
|
||||
lRecursiveCheckVarying(functionType->GetReturnType()))
|
||||
Error(decl->pos, "Illegal to return a \"varying\" type from exported function \"%s\"",
|
||||
funSym->name.c_str());
|
||||
|
||||
if (functionType->isTask && (functionType->GetReturnType() != AtomicType::Void))
|
||||
Error(funSym->pos, "Task-qualified functions must have void return type.");
|
||||
|
||||
if (functionType->isExported || functionType->isExternC)
|
||||
lCheckForStructParameters(functionType, funSym->pos);
|
||||
|
||||
// Loop over all of the arguments; process default values if present
|
||||
// and do other checks and parameter attribute setting.
|
||||
bool seenDefaultArg = false;
|
||||
std::vector<ConstExpr *> argDefaults;
|
||||
int nArgs = decl->functionArgs ? decl->functionArgs->size() : 0;
|
||||
for (int i = 0; i < nArgs; ++i) {
|
||||
Declaration *pdecl = (*decl->functionArgs)[i];
|
||||
assert(pdecl->declarators.size() == 1);
|
||||
Symbol *sym = pdecl->declarators[0]->sym;
|
||||
|
||||
// If the function is exported, make sure that the parameter
|
||||
// doesn't have any varying stuff going on in it.
|
||||
if (ds->storageClass == SC_EXPORT)
|
||||
lCheckForVaryingParameter(sym);
|
||||
|
||||
// ISPC assumes that all memory passed in is aligned to the native
|
||||
// width and that no pointers alias. (It should be possible to
|
||||
// specify when this is not the case, but this should be the
|
||||
// default.) Set parameter attributes accordingly.
|
||||
if (!functionType->isTask && dynamic_cast<const ReferenceType *>(sym->type) != NULL) {
|
||||
// NOTE: LLVM indexes function parameters starting from 1.
|
||||
// This is unintuitive.
|
||||
function->setDoesNotAlias(i+1, true);
|
||||
int align = 4 * RoundUpPow2(g->target.nativeVectorWidth);
|
||||
function->addAttribute(i+1, llvm::Attribute::constructAlignmentFromInt(align));
|
||||
}
|
||||
|
||||
if (m->symbolTable->LookupFunction(sym->name.c_str()) != NULL)
|
||||
Warning(sym->pos, "Function parameter \"%s\" shadows a function "
|
||||
"declared in global scope.", sym->name.c_str());
|
||||
|
||||
// See if a default argument value was provided with the parameter
|
||||
Expr *defaultValue = pdecl->declarators[0]->initExpr;
|
||||
if (defaultValue != NULL) {
|
||||
// If we have one, make sure it's a compile-time constant
|
||||
seenDefaultArg = true;
|
||||
defaultValue = defaultValue->TypeCheck();
|
||||
defaultValue = defaultValue->Optimize();
|
||||
defaultValue = dynamic_cast<ConstExpr *>(defaultValue);
|
||||
if (!defaultValue) {
|
||||
Error(sym->pos, "Default value for parameter \"%s\" must be "
|
||||
"a compile-time constant.", sym->name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (seenDefaultArg) {
|
||||
// Once one parameter has provided a default value, then all of
|
||||
// the following ones must have them as well.
|
||||
Error(sym->pos, "Parameter \"%s\" is missing default: all parameters after "
|
||||
"the first parameter with a default value must have default values "
|
||||
"as well.", sym->name.c_str());
|
||||
}
|
||||
|
||||
// Add the default value to argDefaults. Note that we make this
|
||||
// call for all parameters, even those where no default value was
|
||||
// provided. In that case, a NULL value is stored here. This
|
||||
// approach means that we can always just look at the i'th entry of
|
||||
// argDefaults to find the default value for the i'th parameter.
|
||||
argDefaults.push_back(dynamic_cast<ConstExpr *>(defaultValue));
|
||||
}
|
||||
|
||||
// And only now can we set the default values in the FunctionType
|
||||
functionType->SetArgumentDefaults(argDefaults);
|
||||
|
||||
// If llvm gave us back a Function * with a different name than the one
|
||||
// we asked for, then there's already a function with that same
|
||||
// (mangled) name in the llvm::Module. In that case, erase the one we
|
||||
// tried to add and just work with the one it already had.
|
||||
if (function->getName() != functionName) {
|
||||
function->eraseFromParent();
|
||||
function = m->module->getFunction(functionName);
|
||||
}
|
||||
funSym->function = function;
|
||||
|
||||
// But if that function has a definition, we don't want to redefine it.
|
||||
if (!function->empty()) {
|
||||
Warning(funSym->pos, "Ignoring redefinition of function \"%s\".",
|
||||
funSym->name.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Finally, we know all is good and we can add the function to the
|
||||
// symbol table
|
||||
bool ok = m->symbolTable->AddFunction(funSym);
|
||||
assert(ok);
|
||||
return funSym;
|
||||
}
|
||||
|
||||
|
||||
const Type *
|
||||
Function::GetReturnType() const {
|
||||
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
|
||||
assert(type != NULL);
|
||||
return type->GetReturnType();
|
||||
}
|
||||
|
||||
|
||||
const FunctionType *
|
||||
Function::GetType() const {
|
||||
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
|
||||
assert(type != NULL);
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -444,6 +179,8 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
|
||||
#if 0
|
||||
llvm::BasicBlock *entryBBlock = ctx->GetCurrentBasicBlock();
|
||||
#endif
|
||||
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
|
||||
assert(type != NULL);
|
||||
if (type->isTask == true) {
|
||||
// For tasks, we there should always be three parmeters: the
|
||||
// pointer to the structure that holds all of the arguments, the
|
||||
@@ -612,7 +349,9 @@ Function::GenerateIR() {
|
||||
// If the function is 'export'-qualified, emit a second version of
|
||||
// it without a mask parameter and without name mangling so that
|
||||
// the application can call it
|
||||
if (isExported) {
|
||||
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
|
||||
assert(type != NULL);
|
||||
if (type->isExported) {
|
||||
if (!type->isTask) {
|
||||
LLVM_TYPE_CONST llvm::FunctionType *ftype =
|
||||
type->LLVMFunctionType(g->ctx);
|
||||
|
||||
6
func.h
6
func.h
@@ -43,9 +43,7 @@
|
||||
|
||||
class Function {
|
||||
public:
|
||||
Function(DeclSpecs *ds, Declarator *decl, Stmt *code);
|
||||
|
||||
static Symbol *InitFunctionSymbol(DeclSpecs *ds, Declarator *decl);
|
||||
Function(Symbol *sym, const std::vector<Symbol *> &args, Stmt *code);
|
||||
|
||||
const Type *GetReturnType() const;
|
||||
const FunctionType *GetType() const;
|
||||
@@ -58,10 +56,8 @@ private:
|
||||
SourcePos firstStmtPos);
|
||||
|
||||
Symbol *sym;
|
||||
const FunctionType *type;
|
||||
std::vector<Symbol *> args;
|
||||
Stmt *code;
|
||||
bool isExported;
|
||||
Symbol *maskSymbol;
|
||||
Symbol *threadIndexSym, *threadCountSym;
|
||||
Symbol *taskIndexSym, *taskCountSym;
|
||||
|
||||
4
ispc.h
4
ispc.h
@@ -84,9 +84,6 @@ namespace llvm {
|
||||
|
||||
class ArrayType;
|
||||
class AtomicType;
|
||||
class DeclSpecs;
|
||||
class Declaration;
|
||||
class Declarator;
|
||||
class FunctionEmitContext;
|
||||
class Expr;
|
||||
class ExprList;
|
||||
@@ -97,6 +94,7 @@ class Stmt;
|
||||
class Symbol;
|
||||
class SymbolTable;
|
||||
class Type;
|
||||
struct VariableDeclaration;
|
||||
|
||||
/** @brief Representation of a range of positions in a source file.
|
||||
|
||||
|
||||
2
lex.ll
2
lex.ll
@@ -34,13 +34,13 @@
|
||||
%{
|
||||
|
||||
#include "ispc.h"
|
||||
#include "decl.h"
|
||||
#include "sym.h"
|
||||
#include "util.h"
|
||||
#include "module.h"
|
||||
#include "type.h"
|
||||
#include "parse.hh"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static uint64_t lParseBinary(const char *ptr, SourcePos pos);
|
||||
static void lCComment(SourcePos *);
|
||||
|
||||
398
module.cpp
398
module.cpp
@@ -41,7 +41,6 @@
|
||||
#include "ctx.h"
|
||||
#include "func.h"
|
||||
#include "builtins.h"
|
||||
#include "decl.h"
|
||||
#include "type.h"
|
||||
#include "expr.h"
|
||||
#include "sym.h"
|
||||
@@ -95,9 +94,10 @@
|
||||
// Module
|
||||
|
||||
Module::Module(const char *fn) {
|
||||
// FIXME: It's a hack to do this here, but it must be done after the
|
||||
// target information has been set (so e.g. the vector width is
|
||||
// known...)
|
||||
// It's a hack to do this here, but it must be done after the target
|
||||
// information has been set (so e.g. the vector width is known...) In
|
||||
// particular, if we're compiling to multiple targets with different
|
||||
// vector widths, this needs to be redone each time through.
|
||||
InitLLVMUtil(g->ctx, g->target);
|
||||
|
||||
filename = fn;
|
||||
@@ -209,95 +209,74 @@ Module::CompileFile() {
|
||||
|
||||
|
||||
void
|
||||
Module::AddGlobal(DeclSpecs *ds, Declarator *decl) {
|
||||
// This function is called for a number of cases: function
|
||||
// declarations, typedefs, and global variables declarations /
|
||||
// definitions. Figure out what we've got and take care of it.
|
||||
|
||||
if (ds == NULL || decl == NULL)
|
||||
// Error happened earlier during parsing
|
||||
return;
|
||||
|
||||
if (decl->isFunction) {
|
||||
// function declaration
|
||||
const Type *t = decl->GetType(ds);
|
||||
const FunctionType *ft = dynamic_cast<const FunctionType *>(t);
|
||||
assert(ft != NULL);
|
||||
if (m->symbolTable->LookupFunction(decl->sym->name.c_str(), ft) != NULL)
|
||||
// Ignore redeclaration of a function with the same name and type
|
||||
return;
|
||||
// Otherwise do all of the llvm Module and SymbolTable work..
|
||||
Function::InitFunctionSymbol(ds, decl);
|
||||
}
|
||||
else if (ds->storageClass == SC_TYPEDEF) {
|
||||
// Typedefs are easy; just add the mapping between the given name
|
||||
// and the given type.
|
||||
m->symbolTable->AddType(decl->sym->name.c_str(), decl->sym->type,
|
||||
decl->sym->pos);
|
||||
}
|
||||
else {
|
||||
// global variable
|
||||
if (m->symbolTable->LookupFunction(decl->sym->name.c_str()) != NULL) {
|
||||
Error(decl->pos, "Global variable \"%s\" shadows previously-declared function.",
|
||||
decl->sym->name.c_str());
|
||||
return;
|
||||
Module::AddTypeDef(Symbol *sym) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// These may be NULL due to errors in parsing; just gracefully
|
||||
// return here if so.
|
||||
if (!decl->sym || !decl->sym->type) {
|
||||
|
||||
void
|
||||
Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) {
|
||||
// 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
|
||||
assert(errorCount > 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ds->storageClass == SC_EXTERN_C) {
|
||||
Error(decl->pos, "extern \"C\" qualifier can only be used for functions.");
|
||||
if (symbolTable->LookupFunction(sym->name.c_str()) != NULL) {
|
||||
Error(sym->pos, "Global variable \"%s\" shadows previously-declared function.",
|
||||
sym->name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
LLVM_TYPE_CONST llvm::Type *llvmType = decl->sym->type->LLVMType(g->ctx);
|
||||
if (sym->storageClass == SC_EXTERN_C) {
|
||||
Error(sym->pos, "extern \"C\" qualifier can only be used for functions.");
|
||||
return;
|
||||
}
|
||||
|
||||
LLVM_TYPE_CONST llvm::Type *llvmType = sym->type->LLVMType(g->ctx);
|
||||
|
||||
// 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 (ds->storageClass == SC_EXTERN || ds->storageClass == SC_EXTERN_C) {
|
||||
if (decl->initExpr != NULL)
|
||||
Error(decl->pos, "Initializer can't be provided with \"extern\" "
|
||||
"global variable \"%s\".", decl->sym->name.c_str());
|
||||
if (sym->storageClass == SC_EXTERN || sym->storageClass == SC_EXTERN_C) {
|
||||
if (initExpr != NULL)
|
||||
Error(sym->pos, "Initializer can't be provided with \"extern\" "
|
||||
"global variable \"%s\".", sym->name.c_str());
|
||||
}
|
||||
else {
|
||||
if (decl->initExpr != NULL) {
|
||||
decl->initExpr = decl->initExpr->TypeCheck();
|
||||
if (decl->initExpr != NULL) {
|
||||
else if (initExpr != NULL) {
|
||||
initExpr = initExpr->TypeCheck();
|
||||
if (initExpr != NULL) {
|
||||
// We need to make sure the initializer expression is
|
||||
// the same type as the global. (But not if it's an
|
||||
// ExprList; they don't have types per se / can't type
|
||||
// convert themselves anyway.)
|
||||
if (dynamic_cast<ExprList *>(decl->initExpr) == NULL)
|
||||
decl->initExpr =
|
||||
decl->initExpr->TypeConv(decl->sym->type, "initializer");
|
||||
if (dynamic_cast<ExprList *>(initExpr) == NULL)
|
||||
initExpr = initExpr->TypeConv(sym->type, "initializer");
|
||||
|
||||
if (decl->initExpr != NULL) {
|
||||
decl->initExpr = decl->initExpr->Optimize();
|
||||
if (initExpr != NULL) {
|
||||
initExpr = initExpr->Optimize();
|
||||
// Fingers crossed, now let's see if we've got a
|
||||
// constant value..
|
||||
llvmInitializer = decl->initExpr->GetConstant(decl->sym->type);
|
||||
llvmInitializer = initExpr->GetConstant(sym->type);
|
||||
|
||||
if (llvmInitializer != NULL) {
|
||||
if (decl->sym->type->IsConstType())
|
||||
if (sym->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.
|
||||
decl->sym->constValue =
|
||||
dynamic_cast<ConstExpr *>(decl->initExpr);
|
||||
sym->constValue =
|
||||
dynamic_cast<ConstExpr *>(initExpr);
|
||||
}
|
||||
else
|
||||
Error(decl->pos, "Initializer for global variable \"%s\" "
|
||||
"must be a constant.", decl->sym->name.c_str());
|
||||
Error(sym->pos, "Initializer for global variable \"%s\" "
|
||||
"must be a constant.", sym->name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,33 +285,282 @@ Module::AddGlobal(DeclSpecs *ds, Declarator *decl) {
|
||||
// above, initialize it with zeros..
|
||||
if (llvmInitializer == NULL)
|
||||
llvmInitializer = llvm::Constant::getNullValue(llvmType);
|
||||
}
|
||||
|
||||
bool isConst = (ds->typeQualifier & TYPEQUAL_CONST) != 0;
|
||||
llvm::GlobalValue::LinkageTypes linkage =
|
||||
(ds->storageClass == SC_STATIC) ? llvm::GlobalValue::InternalLinkage :
|
||||
(sym->storageClass == SC_STATIC) ? llvm::GlobalValue::InternalLinkage :
|
||||
llvm::GlobalValue::ExternalLinkage;
|
||||
decl->sym->storagePtr = new llvm::GlobalVariable(*module, llvmType, isConst,
|
||||
sym->storagePtr = new llvm::GlobalVariable(*module, llvmType, isConst,
|
||||
linkage, llvmInitializer,
|
||||
decl->sym->name.c_str());
|
||||
m->symbolTable->AddVariable(decl->sym);
|
||||
sym->name.c_str());
|
||||
symbolTable->AddVariable(sym);
|
||||
|
||||
if (diBuilder && (ds->storageClass != SC_EXTERN)) {
|
||||
llvm::DIFile file = decl->pos.GetDIFile();
|
||||
diBuilder->createGlobalVariable(decl->sym->name,
|
||||
if (diBuilder && (sym->storageClass != SC_EXTERN)) {
|
||||
llvm::DIFile file = sym->pos.GetDIFile();
|
||||
diBuilder->createGlobalVariable(sym->name,
|
||||
file,
|
||||
decl->pos.first_line,
|
||||
decl->sym->type->GetDIType(file),
|
||||
(ds->storageClass == SC_STATIC),
|
||||
decl->sym->storagePtr);
|
||||
sym->pos.first_line,
|
||||
sym->type->GetDIType(file),
|
||||
(sym->storageClass == SC_STATIC),
|
||||
sym->storagePtr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Given an arbitrary type, see if it or any of the types contained in it
|
||||
are varying. Returns true if so, false otherwise.
|
||||
*/
|
||||
static bool
|
||||
lRecursiveCheckVarying(const Type *t) {
|
||||
t = t->GetBaseType();
|
||||
if (t->IsVaryingType()) return true;
|
||||
|
||||
const StructType *st = dynamic_cast<const StructType *>(t);
|
||||
if (st) {
|
||||
for (int i = 0; i < st->GetElementCount(); ++i)
|
||||
if (lRecursiveCheckVarying(st->GetElementType(i)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Given a Symbol representing a function parameter, see if it or any
|
||||
contained types are varying. If so, issue an error. (This function
|
||||
should only be called for parameters to 'export'ed functions, where
|
||||
varying parameters is illegal.
|
||||
*/
|
||||
static void
|
||||
lCheckForVaryingParameter(Symbol *sym) {
|
||||
if (lRecursiveCheckVarying(sym->type)) {
|
||||
const Type *t = sym->type->GetBaseType();
|
||||
if (dynamic_cast<const StructType *>(t))
|
||||
Error(sym->pos, "Struct parameter \"%s\" with varying member(s) is illegal "
|
||||
"in an exported function.",
|
||||
sym->name.c_str());
|
||||
else
|
||||
Error(sym->pos, "Varying parameter \"%s\" is illegal in an exported function.",
|
||||
sym->name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Given a function type, loop through the function parameters and see if
|
||||
any are StructTypes. If so, issue an error (this seems to be broken
|
||||
currently).
|
||||
|
||||
@todo Fix passing structs from C/C++ to ispc functions.
|
||||
*/
|
||||
static void
|
||||
lCheckForStructParameters(const FunctionType *ftype, SourcePos pos) {
|
||||
const std::vector<const Type *> &argTypes = ftype->GetArgumentTypes();
|
||||
for (unsigned int i = 0; i < argTypes.size(); ++i) {
|
||||
const Type *type = argTypes[i];
|
||||
if (dynamic_cast<const StructType *>(type) != NULL) {
|
||||
Error(pos, "Passing structs to/from application functions is currently broken. "
|
||||
"Use a reference or const reference instead for now.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** We've got a declaration for a function to process. This function does
|
||||
all the work of creating the corresponding llvm::Function instance,
|
||||
adding the symbol for the function to the symbol table and doing
|
||||
various sanity checks. This function returns true upon success and
|
||||
false if any errors were encountered.
|
||||
*/
|
||||
void
|
||||
Module::AddFunctionDeclaration(Symbol *funSym,
|
||||
const std::vector<VariableDeclaration> &args,
|
||||
bool isInline) {
|
||||
// We should have gotten a FunctionType back from the GetType() call above.
|
||||
const FunctionType *functionType =
|
||||
dynamic_cast<const FunctionType *>(funSym->type);
|
||||
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. "
|
||||
"Ignoring this definition.",
|
||||
funSym->name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbolTable->LookupFunction(funSym->name.c_str(),
|
||||
functionType) != NULL)
|
||||
// Ignore redeclaration of a function with the same name and type
|
||||
return;
|
||||
|
||||
if (funSym->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());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Symbol *> *funcs =
|
||||
symbolTable->LookupFunction(funSym->name.c_str());
|
||||
if (funcs != NULL) {
|
||||
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\"; "
|
||||
"%d functions with the same name have already been declared.",
|
||||
funSym->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))
|
||||
return;
|
||||
else {
|
||||
Error(funSym->pos, "Can't overload extern \"C\" function \"%s\".",
|
||||
funSym->name.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the LLVM FunctionType
|
||||
bool includeMask = (funSym->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 ||
|
||||
isInline) ?
|
||||
llvm::GlobalValue::InternalLinkage : llvm::GlobalValue::ExternalLinkage;
|
||||
std::string functionName = ((funSym->storageClass == SC_EXTERN_C) ?
|
||||
funSym->name : funSym->MangledName());
|
||||
if (g->mangleFunctionsWithTarget)
|
||||
functionName += g->target.GetISAString();
|
||||
llvm::Function *function =
|
||||
llvm::Function::Create(llvmFunctionType, linkage, functionName.c_str(),
|
||||
module);
|
||||
|
||||
// Set function attributes: we never throw exceptions, and want to
|
||||
// inline everything we can
|
||||
function->setDoesNotThrow(true);
|
||||
if (!(funSym->storageClass == SC_EXTERN_C) &&
|
||||
!g->generateDebuggingSymbols &&
|
||||
isInline)
|
||||
function->addFnAttr(llvm::Attribute::AlwaysInline);
|
||||
if (functionType->isTask)
|
||||
// This also applies transitively to members I think?
|
||||
function->setDoesNotAlias(1, true);
|
||||
|
||||
// Make sure that the return type isn't 'varying' if the function is
|
||||
// 'export'ed.
|
||||
if (funSym->storageClass == SC_EXPORT &&
|
||||
lRecursiveCheckVarying(functionType->GetReturnType()))
|
||||
Error(funSym->pos, "Illegal to return a \"varying\" type from exported "
|
||||
"function \"%s\"", funSym->name.c_str());
|
||||
|
||||
if (functionType->isTask && (functionType->GetReturnType() != AtomicType::Void))
|
||||
Error(funSym->pos, "Task-qualified functions must have void return type.");
|
||||
|
||||
if (functionType->isExported || functionType->isExternC)
|
||||
lCheckForStructParameters(functionType, funSym->pos);
|
||||
|
||||
// Loop over all of the arguments; process default values if present
|
||||
// and do other checks and parameter attribute setting.
|
||||
bool seenDefaultArg = false;
|
||||
std::vector<ConstExpr *> argDefaults;
|
||||
for (unsigned int i = 0; i < args.size(); ++i) {
|
||||
Symbol *argSym = args[i].sym;
|
||||
|
||||
// If the function is exported, make sure that the parameter
|
||||
// doesn't have any varying stuff going on in it.
|
||||
if (funSym->storageClass == SC_EXPORT)
|
||||
lCheckForVaryingParameter(argSym);
|
||||
|
||||
// ISPC assumes that all memory passed in is aligned to the native
|
||||
// width and that no pointers alias. (It should be possible to
|
||||
// specify when this is not the case, but this should be the
|
||||
// default.) Set parameter attributes accordingly.
|
||||
if (!functionType->isTask &&
|
||||
dynamic_cast<const ReferenceType *>(argSym->type) != NULL) {
|
||||
// NOTE: LLVM indexes function parameters starting from 1.
|
||||
// This is unintuitive.
|
||||
function->setDoesNotAlias(i+1, true);
|
||||
int align = 4 * RoundUpPow2(g->target.nativeVectorWidth);
|
||||
function->addAttribute(i+1, llvm::Attribute::constructAlignmentFromInt(align));
|
||||
}
|
||||
|
||||
if (symbolTable->LookupFunction(argSym->name.c_str()) != NULL)
|
||||
Warning(argSym->pos, "Function parameter \"%s\" shadows a function "
|
||||
"declared in global scope.", argSym->name.c_str());
|
||||
|
||||
// See if a default argument value was provided with the parameter
|
||||
Expr *defaultValue = args[i].init;
|
||||
if (defaultValue != NULL) {
|
||||
// If we have one, make sure it's a compile-time constant
|
||||
seenDefaultArg = true;
|
||||
defaultValue = defaultValue->TypeCheck();
|
||||
defaultValue = defaultValue->Optimize();
|
||||
defaultValue = dynamic_cast<ConstExpr *>(defaultValue);
|
||||
if (!defaultValue) {
|
||||
Error(argSym->pos, "Default value for parameter \"%s\" must be "
|
||||
"a compile-time constant.", argSym->name.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (seenDefaultArg) {
|
||||
// Once one parameter has provided a default value, then all of
|
||||
// the following ones must have them as well.
|
||||
Error(argSym->pos, "Parameter \"%s\" is missing default: all "
|
||||
"parameters after the first parameter with a default value "
|
||||
"must have default values as well.", argSym->name.c_str());
|
||||
}
|
||||
|
||||
// Add the default value to argDefaults. Note that we make this
|
||||
// call for all parameters, even those where no default value was
|
||||
// provided. In that case, a NULL value is stored here. This
|
||||
// approach means that we can always just look at the i'th entry of
|
||||
// argDefaults to find the default value for the i'th parameter.
|
||||
argDefaults.push_back(dynamic_cast<ConstExpr *>(defaultValue));
|
||||
}
|
||||
|
||||
// And only now can we set the default values in the FunctionType
|
||||
functionType->SetArgumentDefaults(argDefaults);
|
||||
|
||||
// If llvm gave us back a Function * with a different name than the one
|
||||
// we asked for, then there's already a function with that same
|
||||
// (mangled) name in the llvm::Module. In that case, erase the one we
|
||||
// tried to add and just work with the one it already had.
|
||||
if (function->getName() != functionName) {
|
||||
function->eraseFromParent();
|
||||
function = module->getFunction(functionName);
|
||||
}
|
||||
funSym->function = function;
|
||||
|
||||
// But if that function has a definition, we don't want to redefine it.
|
||||
if (!function->empty()) {
|
||||
Warning(funSym->pos, "Ignoring redefinition of function \"%s\".",
|
||||
funSym->name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, we know all is good and we can add the function to the
|
||||
// symbol table
|
||||
bool ok = symbolTable->AddFunction(funSym);
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Module::AddFunction(DeclSpecs *ds, Declarator *decl, Stmt *code) {
|
||||
ast->AddFunction(ds, decl, code);
|
||||
Module::AddFunctionDefinition(Symbol *sym, const std::vector<Symbol *> &args,
|
||||
Stmt *code) {
|
||||
ast->AddFunction(sym, args, code);
|
||||
}
|
||||
|
||||
|
||||
@@ -725,24 +953,6 @@ lPrintFunctionDeclarations(FILE *file, const std::vector<Symbol *> &funcs) {
|
||||
}
|
||||
|
||||
|
||||
/** Given an arbitrary type, see if it or any of the types contained in it
|
||||
are varying. Returns true if so, false otherwise.
|
||||
*/
|
||||
static bool
|
||||
lRecursiveCheckVarying(const Type *t) {
|
||||
t = t->GetBaseType();
|
||||
if (t->IsVaryingType()) return true;
|
||||
|
||||
const StructType *st = dynamic_cast<const StructType *>(t);
|
||||
if (st) {
|
||||
for (int i = 0; i < st->GetElementCount(); ++i)
|
||||
if (lRecursiveCheckVarying(st->GetElementType(i)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lPrintExternGlobals(FILE *file, const std::vector<Symbol *> &externGlobals) {
|
||||
for (unsigned int i = 0; i < externGlobals.size(); ++i) {
|
||||
|
||||
19
module.h
19
module.h
@@ -58,13 +58,24 @@ public:
|
||||
SymbolTable. Returns the number of errors during compilation. */
|
||||
int CompileFile();
|
||||
|
||||
/** Adds the global variable described by the declaration information to
|
||||
the module. */
|
||||
void AddGlobal(DeclSpecs *ds, Declarator *decl);
|
||||
/** Add a named type definition to the module. */
|
||||
void AddTypeDef(Symbol *sym);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Add a declaration of the function defined by the given function
|
||||
symbol with given arguments to the module. */
|
||||
void AddFunctionDeclaration(Symbol *funSym,
|
||||
const std::vector<VariableDeclaration> &args,
|
||||
bool isInline);
|
||||
|
||||
/** Adds the function described by the declaration information and the
|
||||
provided statements to the module. */
|
||||
void AddFunction(DeclSpecs *ds, Declarator *decl, Stmt *code);
|
||||
void AddFunctionDefinition(Symbol *sym, const std::vector<Symbol *> &args,
|
||||
Stmt *code);
|
||||
|
||||
/** After a source file has been compiled, output can be generated in a
|
||||
number of different formats. */
|
||||
|
||||
60
parse.yy
60
parse.yy
@@ -89,6 +89,7 @@ extern char *yytext;
|
||||
|
||||
void yyerror(const char *s) { fprintf(stderr, "Parse error: %s\n", s); }
|
||||
|
||||
static void lAddDeclaration(DeclSpecs *ds, Declarator *decl);
|
||||
static void lAddFunctionParams(Declarator *decl);
|
||||
static void lAddMaskToSymbolTable(SourcePos pos);
|
||||
static void lAddThreadIndexCountToSymbolTable(SourcePos pos);
|
||||
@@ -460,7 +461,11 @@ constant_expression
|
||||
;
|
||||
|
||||
declaration_statement
|
||||
: declaration { $$ = new DeclStmt(@1, $1, m->symbolTable); }
|
||||
: declaration
|
||||
{
|
||||
std::vector<VariableDeclaration> vars = $1->GetVariableDeclarations();
|
||||
$$ = new DeclStmt(vars, @1);
|
||||
}
|
||||
;
|
||||
|
||||
declaration
|
||||
@@ -1224,14 +1229,14 @@ external_declaration
|
||||
{
|
||||
if ($1 != NULL)
|
||||
for (unsigned int i = 0; i < $1->declarators.size(); ++i)
|
||||
m->AddGlobal($1->declSpecs, $1->declarators[i]);
|
||||
lAddDeclaration($1->declSpecs, $1->declarators[i]);
|
||||
}
|
||||
;
|
||||
|
||||
function_definition
|
||||
: declaration_specifiers declarator
|
||||
{
|
||||
m->AddGlobal($1, $2);
|
||||
lAddDeclaration($1, $2);
|
||||
lAddFunctionParams($2);
|
||||
lAddMaskToSymbolTable(@2);
|
||||
if ($1->typeQualifier & TYPEQUAL_TASK)
|
||||
@@ -1239,14 +1244,17 @@ function_definition
|
||||
}
|
||||
compound_statement
|
||||
{
|
||||
m->AddFunction($1, $2, $4);
|
||||
Symbol *sym;
|
||||
std::vector<Symbol *> args;
|
||||
$2->GetFunctionInfo($1, &sym, &args);
|
||||
m->AddFunctionDefinition(sym, args, $4);
|
||||
m->symbolTable->PopScope(); // push in lAddFunctionParams();
|
||||
}
|
||||
/* function with no declared return type??
|
||||
func(...)
|
||||
| declarator { lAddFunctionParams($1); } compound_statement
|
||||
{
|
||||
m->AddFunction(new DeclSpecs(, $1, $3);
|
||||
m->AddFunction(new DeclSpecs(XXX, $1, $3);
|
||||
m->symbolTable->PopScope(); // push in lAddFunctionParams();
|
||||
}
|
||||
*/
|
||||
@@ -1255,6 +1263,48 @@ func(...)
|
||||
%%
|
||||
|
||||
|
||||
static void
|
||||
lAddDeclaration(DeclSpecs *ds, Declarator *decl) {
|
||||
if (ds == NULL || decl == NULL)
|
||||
// Error happened earlier during parsing
|
||||
return;
|
||||
|
||||
if (decl->isFunction) {
|
||||
// function declaration
|
||||
const Type *t = decl->GetType(ds);
|
||||
const FunctionType *ft = dynamic_cast<const FunctionType *>(t);
|
||||
assert(ft != NULL);
|
||||
|
||||
// Make sure that we've got what we expect here
|
||||
Symbol *funSym = decl->sym;
|
||||
assert(decl->isFunction);
|
||||
assert(decl->arraySize.size() == 0);
|
||||
|
||||
// So far, so good. Go ahead and set the type of the function symbol
|
||||
funSym->type = decl->GetType(ds);
|
||||
funSym->storageClass = ds->storageClass;
|
||||
|
||||
std::vector<VariableDeclaration> args;
|
||||
int nArgs = decl->functionArgs ? decl->functionArgs->size() : 0;
|
||||
for (int i = 0; i < nArgs; ++i) {
|
||||
Declaration *pdecl = (*decl->functionArgs)[i];
|
||||
assert(pdecl->declarators.size() == 1);
|
||||
Symbol *sym = pdecl->declarators[0]->sym;
|
||||
Expr *defaultExpr = pdecl->declarators[0]->initExpr;
|
||||
args.push_back(VariableDeclaration(sym, defaultExpr));
|
||||
}
|
||||
|
||||
bool isInline = (ds->typeQualifier & TYPEQUAL_INLINE);
|
||||
m->AddFunctionDeclaration(funSym, args, isInline);
|
||||
}
|
||||
else if (ds->storageClass == SC_TYPEDEF)
|
||||
m->AddTypeDef(decl->sym);
|
||||
else
|
||||
m->AddGlobalVariable(decl->sym, decl->initExpr,
|
||||
(ds->typeQualifier & TYPEQUAL_CONST) != 0);
|
||||
}
|
||||
|
||||
|
||||
/** We're about to start parsing the body of a function; add all of the
|
||||
parameters to the symbol table so that they're available.
|
||||
*/
|
||||
|
||||
92
stmt.cpp
92
stmt.cpp
@@ -40,7 +40,6 @@
|
||||
#include "util.h"
|
||||
#include "expr.h"
|
||||
#include "type.h"
|
||||
#include "decl.h"
|
||||
#include "sym.h"
|
||||
#include "module.h"
|
||||
#include "llvmutil.h"
|
||||
@@ -116,9 +115,8 @@ ExprStmt::EstimateCost() const {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// DeclStmt
|
||||
|
||||
DeclStmt::DeclStmt(SourcePos p, Declaration *d, SymbolTable *s)
|
||||
: Stmt(p), declaration(d) {
|
||||
declaration->AddSymbols(s);
|
||||
DeclStmt::DeclStmt(const std::vector<VariableDeclaration> &v, SourcePos p)
|
||||
: Stmt(p), vars(v) {
|
||||
}
|
||||
|
||||
|
||||
@@ -240,16 +238,13 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
if (!ctx->GetCurrentBasicBlock())
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < declaration->declarators.size(); ++i) {
|
||||
Declarator *decl = declaration->declarators[i];
|
||||
if (!decl || decl->isFunction)
|
||||
continue;
|
||||
|
||||
Symbol *sym = decl->sym;
|
||||
assert(decl->sym != NULL);
|
||||
for (unsigned int i = 0; i < vars.size(); ++i) {
|
||||
Symbol *sym = vars[i].sym;
|
||||
assert(sym != NULL);
|
||||
const Type *type = sym->type;
|
||||
if (!type)
|
||||
if (type == NULL)
|
||||
continue;
|
||||
Expr *initExpr = vars[i].init;
|
||||
|
||||
// Now that we're in the thick of emitting code, it's easy for us
|
||||
// to find out the level of nesting of varying control flow we're
|
||||
@@ -265,7 +260,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
// initializer list to finally set the array's size.
|
||||
const ArrayType *at = dynamic_cast<const ArrayType *>(type);
|
||||
if (at && at->GetElementCount() == 0) {
|
||||
ExprList *exprList = dynamic_cast<ExprList *>(decl->initExpr);
|
||||
ExprList *exprList = dynamic_cast<ExprList *>(initExpr);
|
||||
if (exprList) {
|
||||
ArrayType *t = at->GetSizedArray(exprList->exprs.size());
|
||||
assert(t != NULL);
|
||||
@@ -280,7 +275,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
}
|
||||
|
||||
// References must have initializer expressions as well.
|
||||
if (dynamic_cast<const ReferenceType *>(type) && decl->initExpr == NULL) {
|
||||
if (dynamic_cast<const ReferenceType *>(type) && initExpr == NULL) {
|
||||
Error(sym->pos,
|
||||
"Must provide initializer for reference-type variable \"%s\".",
|
||||
sym->name.c_str());
|
||||
@@ -290,18 +285,18 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
LLVM_TYPE_CONST llvm::Type *llvmType = type->LLVMType(g->ctx);
|
||||
assert(llvmType != NULL);
|
||||
|
||||
if (declaration->declSpecs->storageClass == SC_STATIC) {
|
||||
if (sym->storageClass == SC_STATIC) {
|
||||
// For static variables, we need a compile-time constant value
|
||||
// for its initializer; if there's no initializer, we use a
|
||||
// zero value.
|
||||
llvm::Constant *cinit = NULL;
|
||||
if (decl->initExpr) {
|
||||
cinit = decl->initExpr->GetConstant(type);
|
||||
if (!cinit)
|
||||
if (initExpr != NULL) {
|
||||
cinit = initExpr->GetConstant(type);
|
||||
if (cinit == NULL)
|
||||
Error(sym->pos, "Initializer for static variable \"%s\" must be a constant.",
|
||||
sym->name.c_str());
|
||||
}
|
||||
if (!cinit)
|
||||
if (cinit == NULL)
|
||||
cinit = llvm::Constant::getNullValue(llvmType);
|
||||
|
||||
// Allocate space for the static variable in global scope, so
|
||||
@@ -323,7 +318,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
ctx->EmitVariableDebugInfo(sym);
|
||||
// And then get it initialized...
|
||||
sym->parentFunction = ctx->GetFunction();
|
||||
lInitSymbol(sym->storagePtr, sym->name.c_str(), type, decl->initExpr,
|
||||
lInitSymbol(sym->storagePtr, sym->name.c_str(), type, initExpr,
|
||||
ctx, sym->pos);
|
||||
}
|
||||
}
|
||||
@@ -332,10 +327,10 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
|
||||
|
||||
Stmt *
|
||||
DeclStmt::Optimize() {
|
||||
for (unsigned int i = 0; i < declaration->declarators.size(); ++i) {
|
||||
Declarator *decl = declaration->declarators[i];
|
||||
if (decl && decl->initExpr) {
|
||||
decl->initExpr = decl->initExpr->Optimize();
|
||||
for (unsigned int i = 0; i < vars.size(); ++i) {
|
||||
if (vars[i].init != NULL) {
|
||||
vars[i].init = vars[i].init->Optimize();
|
||||
Expr *init = vars[i].init;
|
||||
|
||||
// If the variable is const-qualified, after we've optimized
|
||||
// the initializer expression, see if we have a ConstExpr. If
|
||||
@@ -352,11 +347,11 @@ DeclStmt::Optimize() {
|
||||
// is definitely a compile-time constant for things like
|
||||
// computing array sizes from non-trivial expressions is
|
||||
// consequently limited.
|
||||
Symbol *sym = decl->sym;
|
||||
if (sym->type && sym->type->IsConstType() && decl->initExpr &&
|
||||
dynamic_cast<ExprList *>(decl->initExpr) == NULL &&
|
||||
Type::Equal(decl->initExpr->GetType(), sym->type))
|
||||
sym->constValue = dynamic_cast<ConstExpr *>(decl->initExpr);
|
||||
Symbol *sym = vars[i].sym;
|
||||
if (sym->type && sym->type->IsConstType() && init != NULL &&
|
||||
dynamic_cast<ExprList *>(init) == NULL &&
|
||||
Type::Equal(init->GetType(), sym->type))
|
||||
sym->constValue = dynamic_cast<ConstExpr *>(init);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
@@ -366,30 +361,29 @@ DeclStmt::Optimize() {
|
||||
Stmt *
|
||||
DeclStmt::TypeCheck() {
|
||||
bool encounteredError = false;
|
||||
for (unsigned int i = 0; i < declaration->declarators.size(); ++i) {
|
||||
Declarator *decl = declaration->declarators[i];
|
||||
if (!decl) {
|
||||
for (unsigned int i = 0; i < vars.size(); ++i) {
|
||||
if (!vars[i].sym) {
|
||||
encounteredError = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!decl->initExpr)
|
||||
if (vars[i].init == NULL)
|
||||
continue;
|
||||
decl->initExpr = decl->initExpr->TypeCheck();
|
||||
if (!decl->initExpr)
|
||||
vars[i].init = vars[i].init->TypeCheck();
|
||||
if (vars[i].init == NULL)
|
||||
continue;
|
||||
|
||||
// get the right type for stuff like const float foo = 2; so that
|
||||
// the int->float type conversion is in there and we don't return
|
||||
// an int as the constValue later...
|
||||
const Type *type = decl->sym->type;
|
||||
const Type *type = vars[i].sym->type;
|
||||
if (dynamic_cast<const AtomicType *>(type) != NULL ||
|
||||
dynamic_cast<const EnumType *>(type) != NULL) {
|
||||
// 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
|
||||
// is in fact caught later, though.
|
||||
if (dynamic_cast<ExprList *>(decl->initExpr) == NULL)
|
||||
decl->initExpr = decl->initExpr->TypeConv(type, "initializer");
|
||||
if (dynamic_cast<ExprList *>(vars[i].init) == NULL)
|
||||
vars[i].init = vars[i].init->TypeConv(type, "initializer");
|
||||
}
|
||||
}
|
||||
return encounteredError ? NULL : this;
|
||||
@@ -400,8 +394,16 @@ void
|
||||
DeclStmt::Print(int indent) const {
|
||||
printf("%*cDecl Stmt:", indent, ' ');
|
||||
pos.Print();
|
||||
if (declaration)
|
||||
declaration->Print();
|
||||
for (unsigned int i = 0; i < vars.size(); ++i) {
|
||||
printf("%*cVariable %s (%s)", indent+4, ' ',
|
||||
vars[i].sym->name.c_str(),
|
||||
vars[i].sym->type->GetString().c_str());
|
||||
if (vars[i].init != NULL) {
|
||||
printf(" = ");
|
||||
vars[i].init->Print();
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@@ -409,9 +411,9 @@ DeclStmt::Print(int indent) const {
|
||||
int
|
||||
DeclStmt::EstimateCost() const {
|
||||
int cost = 0;
|
||||
for (unsigned int i = 0; i < declaration->declarators.size(); ++i)
|
||||
if (declaration->declarators[i]->initExpr)
|
||||
cost += declaration->declarators[i]->initExpr->EstimateCost();
|
||||
for (unsigned int i = 0; i < vars.size(); ++i)
|
||||
if (vars[i].init != NULL)
|
||||
cost += vars[i].init->EstimateCost();
|
||||
return cost;
|
||||
}
|
||||
|
||||
@@ -716,8 +718,8 @@ lSafeToRunWithAllLanesOff(Stmt *stmt) {
|
||||
|
||||
DeclStmt *ds;
|
||||
if ((ds = dynamic_cast<DeclStmt *>(stmt)) != NULL) {
|
||||
for (unsigned int i = 0; i < ds->declaration->declarators.size(); ++i)
|
||||
if (!lSafeToRunWithAllLanesOff(ds->declaration->declarators[i]->initExpr))
|
||||
for (unsigned int i = 0; i < ds->vars.size(); ++i)
|
||||
if (!lSafeToRunWithAllLanesOff(ds->vars[i].init))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
12
stmt.h
12
stmt.h
@@ -82,11 +82,19 @@ public:
|
||||
};
|
||||
|
||||
|
||||
struct VariableDeclaration {
|
||||
VariableDeclaration(Symbol *s = NULL, Expr *i = NULL) {
|
||||
sym = s; init = i;
|
||||
}
|
||||
Symbol *sym;
|
||||
Expr *init;
|
||||
};
|
||||
|
||||
/** @brief Statement representing a single declaration (which in turn may declare
|
||||
a number of variables. */
|
||||
class DeclStmt : public Stmt {
|
||||
public:
|
||||
DeclStmt(SourcePos pos, Declaration *declaration, SymbolTable *symbolTable);
|
||||
DeclStmt(const std::vector<VariableDeclaration> &v, SourcePos pos);
|
||||
|
||||
void EmitCode(FunctionEmitContext *ctx) const;
|
||||
void Print(int indent) const;
|
||||
@@ -95,7 +103,7 @@ public:
|
||||
Stmt *TypeCheck();
|
||||
int EstimateCost() const;
|
||||
|
||||
Declaration *declaration;
|
||||
std::vector<VariableDeclaration> vars;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user