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:
Matt Pharr
2011-10-18 15:33:18 -07:00
parent 9b8ea3d500
commit f45ab0744e
13 changed files with 562 additions and 498 deletions

View File

@@ -36,9 +36,7 @@
*/ */
#include "ast.h" #include "ast.h"
#include "decl.h"
#include "func.h" #include "func.h"
#include "type.h"
#include "sym.h" #include "sym.h"
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@@ -52,8 +50,8 @@ ASTNode::~ASTNode() {
// AST // AST
void void
AST::AddFunction(DeclSpecs *ds, Declarator *decl, Stmt *code) { AST::AddFunction(Symbol *sym, const std::vector<Symbol *> &args, Stmt *code) {
functions.push_back(new Function(ds, decl, code)); functions.push_back(new Function(sym, args, code));
} }

3
ast.h
View File

@@ -80,7 +80,8 @@ class AST {
public: public:
/** Add the AST for a function described by the given declaration /** Add the AST for a function described by the given declaration
information and source code. */ 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 /** Generate LLVM IR for all of the functions into the current
module. */ module. */

View File

@@ -38,10 +38,13 @@
#include "decl.h" #include "decl.h"
#include "util.h" #include "util.h"
#include "module.h"
#include "sym.h" #include "sym.h"
#include "type.h" #include "type.h"
#include "stmt.h"
#include "expr.h" #include "expr.h"
#include <stdio.h> #include <stdio.h>
#include <llvm/Module.h>
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// DeclSpecs // 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 * static const Type *
lGetType(const Declarator *decl, DeclSpecs *ds, lGetType(const Declarator *decl, DeclSpecs *ds,
std::vector<int>::const_iterator arrayIter) { std::vector<int>::const_iterator arrayIter) {
@@ -292,13 +319,43 @@ Declarator::GetType(DeclSpecs *ds) const {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Declaration // Declaration
void Declaration::Declaration(DeclSpecs *ds, std::vector<Declarator *> *dlist) {
Declaration::AddSymbols(SymbolTable *st) const { declSpecs = ds;
assert(declSpecs->storageClass != SC_TYPEDEF); if (dlist != NULL)
declarators = *dlist;
for (unsigned int i = 0; i < declarators.size(); ++i) for (unsigned int i = 0; i < declarators.size(); ++i)
if (declarators[i]) if (declarators[i] != NULL)
st->AddVariable(declarators[i]->sym); 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
View File

@@ -56,6 +56,11 @@
#include "ispc.h" #include "ispc.h"
struct VariableDeclaration;
class Declaration;
class Declarator;
enum StorageClass { enum StorageClass {
SC_NONE, SC_NONE,
SC_EXTERN, SC_EXTERN,
@@ -137,6 +142,9 @@ public:
DeclSpecs */ DeclSpecs */
const Type *GetType(DeclSpecs *ds) const; const Type *GetType(DeclSpecs *ds) const;
void GetFunctionInfo(DeclSpecs *ds, Symbol **sym,
std::vector<Symbol *> *args);
void Print() const; void Print() const;
const SourcePos pos; const SourcePos pos;
@@ -157,27 +165,13 @@ public:
*/ */
class Declaration { class Declaration {
public: public:
Declaration(DeclSpecs *ds, std::vector<Declarator *> *dlist = NULL) { Declaration(DeclSpecs *ds, std::vector<Declarator *> *dlist = NULL);
declSpecs = ds; Declaration(DeclSpecs *ds, Declarator *d);
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);
}
}
/** Adds the symbols for the variables in the declaration to the symbol
table. */
void AddSymbols(SymbolTable *st) const;
void Print() const; void Print() const;
std::vector<VariableDeclaration> GetVariableDeclarations() const;
DeclSpecs *declSpecs; DeclSpecs *declSpecs;
std::vector<Declarator *> declarators; std::vector<Declarator *> declarators;
}; };

295
func.cpp
View File

@@ -37,7 +37,6 @@
#include "func.h" #include "func.h"
#include "ctx.h" #include "ctx.h"
#include "decl.h"
#include "expr.h" #include "expr.h"
#include "llvmutil.h" #include "llvmutil.h"
#include "module.h" #include "module.h"
@@ -67,7 +66,9 @@
#include <llvm/Support/ToolOutputFile.h> #include <llvm/Support/ToolOutputFile.h>
#include <llvm/Assembly/PrintModulePass.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; code = c;
maskSymbol = m->symbolTable->LookupVariable("__mask"); maskSymbol = m->symbolTable->LookupVariable("__mask");
@@ -80,37 +81,17 @@ Function::Function(DeclSpecs *ds, Declarator *decl, Stmt *c) {
} }
if (g->debugPrint) { if (g->debugPrint) {
printf("Add Function\n"); printf("Add Function %s\n", sym->name.c_str());
ds->Print();
printf("\n");
decl->Print();
printf("\n");
code->Print(0); code->Print(0);
printf("\n\n\n"); printf("\n\n\n");
} }
// Get the symbol for the function from the symbol table. (It should const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
// already have been added to the symbol table by AddGlobal() by the
// time we get here.)
type = dynamic_cast<const FunctionType *>(decl->GetType(ds));
assert(type != NULL); 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); for (unsigned int i = 0; i < args.size(); ++i)
if (dynamic_cast<const ReferenceType *>(args[i]->type) == NULL)
if (decl->functionArgs != NULL) { args[i]->parentFunction = this;
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);
}
}
if (type->isTask) { if (type->isTask) {
threadIndexSym = m->symbolTable->LookupVariable("threadIndex"); 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 * const Type *
Function::GetReturnType() const { Function::GetReturnType() const {
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
assert(type != NULL);
return type->GetReturnType(); return type->GetReturnType();
} }
const FunctionType * const FunctionType *
Function::GetType() const { Function::GetType() const {
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
assert(type != NULL);
return type; return type;
} }
@@ -444,6 +179,8 @@ Function::emitCode(FunctionEmitContext *ctx, llvm::Function *function,
#if 0 #if 0
llvm::BasicBlock *entryBBlock = ctx->GetCurrentBasicBlock(); llvm::BasicBlock *entryBBlock = ctx->GetCurrentBasicBlock();
#endif #endif
const FunctionType *type = dynamic_cast<const FunctionType *>(sym->type);
assert(type != NULL);
if (type->isTask == true) { if (type->isTask == true) {
// For tasks, we there should always be three parmeters: the // For tasks, we there should always be three parmeters: the
// pointer to the structure that holds all of the arguments, 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 // If the function is 'export'-qualified, emit a second version of
// it without a mask parameter and without name mangling so that // it without a mask parameter and without name mangling so that
// the application can call it // 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) { if (!type->isTask) {
LLVM_TYPE_CONST llvm::FunctionType *ftype = LLVM_TYPE_CONST llvm::FunctionType *ftype =
type->LLVMFunctionType(g->ctx); type->LLVMFunctionType(g->ctx);

6
func.h
View File

@@ -43,9 +43,7 @@
class Function { class Function {
public: public:
Function(DeclSpecs *ds, Declarator *decl, Stmt *code); Function(Symbol *sym, const std::vector<Symbol *> &args, Stmt *code);
static Symbol *InitFunctionSymbol(DeclSpecs *ds, Declarator *decl);
const Type *GetReturnType() const; const Type *GetReturnType() const;
const FunctionType *GetType() const; const FunctionType *GetType() const;
@@ -58,10 +56,8 @@ private:
SourcePos firstStmtPos); SourcePos firstStmtPos);
Symbol *sym; Symbol *sym;
const FunctionType *type;
std::vector<Symbol *> args; std::vector<Symbol *> args;
Stmt *code; Stmt *code;
bool isExported;
Symbol *maskSymbol; Symbol *maskSymbol;
Symbol *threadIndexSym, *threadCountSym; Symbol *threadIndexSym, *threadCountSym;
Symbol *taskIndexSym, *taskCountSym; Symbol *taskIndexSym, *taskCountSym;

4
ispc.h
View File

@@ -84,9 +84,6 @@ namespace llvm {
class ArrayType; class ArrayType;
class AtomicType; class AtomicType;
class DeclSpecs;
class Declaration;
class Declarator;
class FunctionEmitContext; class FunctionEmitContext;
class Expr; class Expr;
class ExprList; class ExprList;
@@ -97,6 +94,7 @@ class Stmt;
class Symbol; class Symbol;
class SymbolTable; class SymbolTable;
class Type; class Type;
struct VariableDeclaration;
/** @brief Representation of a range of positions in a source file. /** @brief Representation of a range of positions in a source file.

2
lex.ll
View File

@@ -34,13 +34,13 @@
%{ %{
#include "ispc.h" #include "ispc.h"
#include "decl.h"
#include "sym.h" #include "sym.h"
#include "util.h" #include "util.h"
#include "module.h" #include "module.h"
#include "type.h" #include "type.h"
#include "parse.hh" #include "parse.hh"
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
static uint64_t lParseBinary(const char *ptr, SourcePos pos); static uint64_t lParseBinary(const char *ptr, SourcePos pos);
static void lCComment(SourcePos *); static void lCComment(SourcePos *);

View File

@@ -41,7 +41,6 @@
#include "ctx.h" #include "ctx.h"
#include "func.h" #include "func.h"
#include "builtins.h" #include "builtins.h"
#include "decl.h"
#include "type.h" #include "type.h"
#include "expr.h" #include "expr.h"
#include "sym.h" #include "sym.h"
@@ -95,9 +94,10 @@
// Module // Module
Module::Module(const char *fn) { Module::Module(const char *fn) {
// FIXME: It's a hack to do this here, but it must be done after the // It's a hack to do this here, but it must be done after the target
// target information has been set (so e.g. the vector width is // information has been set (so e.g. the vector width is known...) In
// known...) // 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); InitLLVMUtil(g->ctx, g->target);
filename = fn; filename = fn;
@@ -209,95 +209,74 @@ Module::CompileFile() {
void void
Module::AddGlobal(DeclSpecs *ds, Declarator *decl) { Module::AddTypeDef(Symbol *sym) {
// This function is called for a number of cases: function // Typedefs are easy; just add the mapping between the given name and
// declarations, typedefs, and global variables declarations / // the given type.
// definitions. Figure out what we've got and take care of it. symbolTable->AddType(sym->name.c_str(), sym->type, sym->pos);
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;
} }
// These may be NULL due to errors in parsing; just gracefully
// return here if so. void
if (!decl->sym || !decl->sym->type) { 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 // But if these are NULL and there haven't been any previous
// errors, something surprising is going on // errors, something surprising is going on
assert(errorCount > 0); assert(errorCount > 0);
return; return;
} }
if (ds->storageClass == SC_EXTERN_C) { if (symbolTable->LookupFunction(sym->name.c_str()) != NULL) {
Error(decl->pos, "extern \"C\" qualifier can only be used for functions."); Error(sym->pos, "Global variable \"%s\" shadows previously-declared function.",
sym->name.c_str());
return; 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, // See if we have an initializer expression for the global. If so,
// make sure it's a compile-time constant! // make sure it's a compile-time constant!
llvm::Constant *llvmInitializer = NULL; llvm::Constant *llvmInitializer = NULL;
if (ds->storageClass == SC_EXTERN || ds->storageClass == SC_EXTERN_C) { if (sym->storageClass == SC_EXTERN || sym->storageClass == SC_EXTERN_C) {
if (decl->initExpr != NULL) if (initExpr != NULL)
Error(decl->pos, "Initializer can't be provided with \"extern\" " Error(sym->pos, "Initializer can't be provided with \"extern\" "
"global variable \"%s\".", decl->sym->name.c_str()); "global variable \"%s\".", sym->name.c_str());
} }
else { else if (initExpr != NULL) {
if (decl->initExpr != NULL) { initExpr = initExpr->TypeCheck();
decl->initExpr = decl->initExpr->TypeCheck(); if (initExpr != NULL) {
if (decl->initExpr != NULL) {
// We need to make sure the initializer expression is // We need to make sure the initializer expression is
// the same type as the global. (But not if it's an // the same type as the global. (But not if it's an
// ExprList; they don't have types per se / can't type // ExprList; they don't have types per se / can't type
// convert themselves anyway.) // convert themselves anyway.)
if (dynamic_cast<ExprList *>(decl->initExpr) == NULL) if (dynamic_cast<ExprList *>(initExpr) == NULL)
decl->initExpr = initExpr = initExpr->TypeConv(sym->type, "initializer");
decl->initExpr->TypeConv(decl->sym->type, "initializer");
if (decl->initExpr != NULL) { if (initExpr != NULL) {
decl->initExpr = decl->initExpr->Optimize(); initExpr = initExpr->Optimize();
// Fingers crossed, now let's see if we've got a // Fingers crossed, now let's see if we've got a
// constant value.. // constant value..
llvmInitializer = decl->initExpr->GetConstant(decl->sym->type); llvmInitializer = initExpr->GetConstant(sym->type);
if (llvmInitializer != NULL) { if (llvmInitializer != NULL) {
if (decl->sym->type->IsConstType()) if (sym->type->IsConstType())
// Try to get a ConstExpr associated with // Try to get a ConstExpr associated with
// the symbol. This dynamic_cast can // the symbol. This dynamic_cast can
// validly fail, for example for types like // validly fail, for example for types like
// StructTypes where a ConstExpr can't // StructTypes where a ConstExpr can't
// represent their values. // represent their values.
decl->sym->constValue = sym->constValue =
dynamic_cast<ConstExpr *>(decl->initExpr); dynamic_cast<ConstExpr *>(initExpr);
} }
else else
Error(decl->pos, "Initializer for global variable \"%s\" " Error(sym->pos, "Initializer for global variable \"%s\" "
"must be a constant.", decl->sym->name.c_str()); "must be a constant.", sym->name.c_str());
} }
} }
} }
@@ -306,33 +285,282 @@ Module::AddGlobal(DeclSpecs *ds, Declarator *decl) {
// above, initialize it with zeros.. // above, initialize it with zeros..
if (llvmInitializer == NULL) if (llvmInitializer == NULL)
llvmInitializer = llvm::Constant::getNullValue(llvmType); llvmInitializer = llvm::Constant::getNullValue(llvmType);
}
bool isConst = (ds->typeQualifier & TYPEQUAL_CONST) != 0;
llvm::GlobalValue::LinkageTypes linkage = llvm::GlobalValue::LinkageTypes linkage =
(ds->storageClass == SC_STATIC) ? llvm::GlobalValue::InternalLinkage : (sym->storageClass == SC_STATIC) ? llvm::GlobalValue::InternalLinkage :
llvm::GlobalValue::ExternalLinkage; llvm::GlobalValue::ExternalLinkage;
decl->sym->storagePtr = new llvm::GlobalVariable(*module, llvmType, isConst, sym->storagePtr = new llvm::GlobalVariable(*module, llvmType, isConst,
linkage, llvmInitializer, linkage, llvmInitializer,
decl->sym->name.c_str()); sym->name.c_str());
m->symbolTable->AddVariable(decl->sym); symbolTable->AddVariable(sym);
if (diBuilder && (ds->storageClass != SC_EXTERN)) { if (diBuilder && (sym->storageClass != SC_EXTERN)) {
llvm::DIFile file = decl->pos.GetDIFile(); llvm::DIFile file = sym->pos.GetDIFile();
diBuilder->createGlobalVariable(decl->sym->name, diBuilder->createGlobalVariable(sym->name,
file, file,
decl->pos.first_line, sym->pos.first_line,
decl->sym->type->GetDIType(file), sym->type->GetDIType(file),
(ds->storageClass == SC_STATIC), (sym->storageClass == SC_STATIC),
decl->sym->storagePtr); 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 void
Module::AddFunction(DeclSpecs *ds, Declarator *decl, Stmt *code) { Module::AddFunctionDefinition(Symbol *sym, const std::vector<Symbol *> &args,
ast->AddFunction(ds, decl, code); 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 static void
lPrintExternGlobals(FILE *file, const std::vector<Symbol *> &externGlobals) { lPrintExternGlobals(FILE *file, const std::vector<Symbol *> &externGlobals) {
for (unsigned int i = 0; i < externGlobals.size(); ++i) { for (unsigned int i = 0; i < externGlobals.size(); ++i) {

View File

@@ -58,13 +58,24 @@ public:
SymbolTable. Returns the number of errors during compilation. */ SymbolTable. Returns the number of errors during compilation. */
int CompileFile(); int CompileFile();
/** Adds the global variable described by the declaration information to /** Add a named type definition to the module. */
the module. */ void AddTypeDef(Symbol *sym);
void AddGlobal(DeclSpecs *ds, Declarator *decl);
/** 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 /** Adds the function described by the declaration information and the
provided statements to the module. */ 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 /** After a source file has been compiled, output can be generated in a
number of different formats. */ number of different formats. */

View File

@@ -89,6 +89,7 @@ extern char *yytext;
void yyerror(const char *s) { fprintf(stderr, "Parse error: %s\n", s); } 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 lAddFunctionParams(Declarator *decl);
static void lAddMaskToSymbolTable(SourcePos pos); static void lAddMaskToSymbolTable(SourcePos pos);
static void lAddThreadIndexCountToSymbolTable(SourcePos pos); static void lAddThreadIndexCountToSymbolTable(SourcePos pos);
@@ -460,7 +461,11 @@ constant_expression
; ;
declaration_statement declaration_statement
: declaration { $$ = new DeclStmt(@1, $1, m->symbolTable); } : declaration
{
std::vector<VariableDeclaration> vars = $1->GetVariableDeclarations();
$$ = new DeclStmt(vars, @1);
}
; ;
declaration declaration
@@ -1224,14 +1229,14 @@ external_declaration
{ {
if ($1 != NULL) if ($1 != NULL)
for (unsigned int i = 0; i < $1->declarators.size(); ++i) 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 function_definition
: declaration_specifiers declarator : declaration_specifiers declarator
{ {
m->AddGlobal($1, $2); lAddDeclaration($1, $2);
lAddFunctionParams($2); lAddFunctionParams($2);
lAddMaskToSymbolTable(@2); lAddMaskToSymbolTable(@2);
if ($1->typeQualifier & TYPEQUAL_TASK) if ($1->typeQualifier & TYPEQUAL_TASK)
@@ -1239,14 +1244,17 @@ function_definition
} }
compound_statement 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(); m->symbolTable->PopScope(); // push in lAddFunctionParams();
} }
/* function with no declared return type?? /* function with no declared return type??
func(...) func(...)
| declarator { lAddFunctionParams($1); } compound_statement | declarator { lAddFunctionParams($1); } compound_statement
{ {
m->AddFunction(new DeclSpecs(, $1, $3); m->AddFunction(new DeclSpecs(XXX, $1, $3);
m->symbolTable->PopScope(); // push in lAddFunctionParams(); 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 /** 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. parameters to the symbol table so that they're available.
*/ */

View File

@@ -40,7 +40,6 @@
#include "util.h" #include "util.h"
#include "expr.h" #include "expr.h"
#include "type.h" #include "type.h"
#include "decl.h"
#include "sym.h" #include "sym.h"
#include "module.h" #include "module.h"
#include "llvmutil.h" #include "llvmutil.h"
@@ -116,9 +115,8 @@ ExprStmt::EstimateCost() const {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// DeclStmt // DeclStmt
DeclStmt::DeclStmt(SourcePos p, Declaration *d, SymbolTable *s) DeclStmt::DeclStmt(const std::vector<VariableDeclaration> &v, SourcePos p)
: Stmt(p), declaration(d) { : Stmt(p), vars(v) {
declaration->AddSymbols(s);
} }
@@ -240,16 +238,13 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
if (!ctx->GetCurrentBasicBlock()) if (!ctx->GetCurrentBasicBlock())
return; return;
for (unsigned int i = 0; i < declaration->declarators.size(); ++i) { for (unsigned int i = 0; i < vars.size(); ++i) {
Declarator *decl = declaration->declarators[i]; Symbol *sym = vars[i].sym;
if (!decl || decl->isFunction) assert(sym != NULL);
continue;
Symbol *sym = decl->sym;
assert(decl->sym != NULL);
const Type *type = sym->type; const Type *type = sym->type;
if (!type) if (type == NULL)
continue; continue;
Expr *initExpr = vars[i].init;
// Now that we're in the thick of emitting code, it's easy for us // 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 // 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. // initializer list to finally set the array's size.
const ArrayType *at = dynamic_cast<const ArrayType *>(type); const ArrayType *at = dynamic_cast<const ArrayType *>(type);
if (at && at->GetElementCount() == 0) { if (at && at->GetElementCount() == 0) {
ExprList *exprList = dynamic_cast<ExprList *>(decl->initExpr); ExprList *exprList = dynamic_cast<ExprList *>(initExpr);
if (exprList) { if (exprList) {
ArrayType *t = at->GetSizedArray(exprList->exprs.size()); ArrayType *t = at->GetSizedArray(exprList->exprs.size());
assert(t != NULL); assert(t != NULL);
@@ -280,7 +275,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
} }
// References must have initializer expressions as well. // 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, Error(sym->pos,
"Must provide initializer for reference-type variable \"%s\".", "Must provide initializer for reference-type variable \"%s\".",
sym->name.c_str()); sym->name.c_str());
@@ -290,18 +285,18 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
LLVM_TYPE_CONST llvm::Type *llvmType = type->LLVMType(g->ctx); LLVM_TYPE_CONST llvm::Type *llvmType = type->LLVMType(g->ctx);
assert(llvmType != NULL); 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 static variables, we need a compile-time constant value
// for its initializer; if there's no initializer, we use a // for its initializer; if there's no initializer, we use a
// zero value. // zero value.
llvm::Constant *cinit = NULL; llvm::Constant *cinit = NULL;
if (decl->initExpr) { if (initExpr != NULL) {
cinit = decl->initExpr->GetConstant(type); cinit = initExpr->GetConstant(type);
if (!cinit) if (cinit == NULL)
Error(sym->pos, "Initializer for static variable \"%s\" must be a constant.", Error(sym->pos, "Initializer for static variable \"%s\" must be a constant.",
sym->name.c_str()); sym->name.c_str());
} }
if (!cinit) if (cinit == NULL)
cinit = llvm::Constant::getNullValue(llvmType); cinit = llvm::Constant::getNullValue(llvmType);
// Allocate space for the static variable in global scope, so // Allocate space for the static variable in global scope, so
@@ -323,7 +318,7 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
ctx->EmitVariableDebugInfo(sym); ctx->EmitVariableDebugInfo(sym);
// And then get it initialized... // And then get it initialized...
sym->parentFunction = ctx->GetFunction(); 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); ctx, sym->pos);
} }
} }
@@ -332,10 +327,10 @@ DeclStmt::EmitCode(FunctionEmitContext *ctx) const {
Stmt * Stmt *
DeclStmt::Optimize() { DeclStmt::Optimize() {
for (unsigned int i = 0; i < declaration->declarators.size(); ++i) { for (unsigned int i = 0; i < vars.size(); ++i) {
Declarator *decl = declaration->declarators[i]; if (vars[i].init != NULL) {
if (decl && decl->initExpr) { vars[i].init = vars[i].init->Optimize();
decl->initExpr = decl->initExpr->Optimize(); Expr *init = vars[i].init;
// If the variable is const-qualified, after we've optimized // If the variable is const-qualified, after we've optimized
// the initializer expression, see if we have a ConstExpr. If // 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 // is definitely a compile-time constant for things like
// computing array sizes from non-trivial expressions is // computing array sizes from non-trivial expressions is
// consequently limited. // consequently limited.
Symbol *sym = decl->sym; Symbol *sym = vars[i].sym;
if (sym->type && sym->type->IsConstType() && decl->initExpr && if (sym->type && sym->type->IsConstType() && init != NULL &&
dynamic_cast<ExprList *>(decl->initExpr) == NULL && dynamic_cast<ExprList *>(init) == NULL &&
Type::Equal(decl->initExpr->GetType(), sym->type)) Type::Equal(init->GetType(), sym->type))
sym->constValue = dynamic_cast<ConstExpr *>(decl->initExpr); sym->constValue = dynamic_cast<ConstExpr *>(init);
} }
} }
return this; return this;
@@ -366,30 +361,29 @@ DeclStmt::Optimize() {
Stmt * Stmt *
DeclStmt::TypeCheck() { DeclStmt::TypeCheck() {
bool encounteredError = false; bool encounteredError = false;
for (unsigned int i = 0; i < declaration->declarators.size(); ++i) { for (unsigned int i = 0; i < vars.size(); ++i) {
Declarator *decl = declaration->declarators[i]; if (!vars[i].sym) {
if (!decl) {
encounteredError = true; encounteredError = true;
continue; continue;
} }
if (!decl->initExpr) if (vars[i].init == NULL)
continue; continue;
decl->initExpr = decl->initExpr->TypeCheck(); vars[i].init = vars[i].init->TypeCheck();
if (!decl->initExpr) if (vars[i].init == NULL)
continue; continue;
// get the right type for stuff like const float foo = 2; so that // 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 // the int->float type conversion is in there and we don't return
// an int as the constValue later... // 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 || if (dynamic_cast<const AtomicType *>(type) != NULL ||
dynamic_cast<const EnumType *>(type) != NULL) { dynamic_cast<const EnumType *>(type) != NULL) {
// If it's an expr list with an atomic type, we'll later issue // If it's an expr list with an atomic type, we'll later issue
// an error. Need to leave decl->initExpr as is in that case so it // an error. Need to leave decl->initExpr as is in that case so it
// is in fact caught later, though. // is in fact caught later, though.
if (dynamic_cast<ExprList *>(decl->initExpr) == NULL) if (dynamic_cast<ExprList *>(vars[i].init) == NULL)
decl->initExpr = decl->initExpr->TypeConv(type, "initializer"); vars[i].init = vars[i].init->TypeConv(type, "initializer");
} }
} }
return encounteredError ? NULL : this; return encounteredError ? NULL : this;
@@ -400,8 +394,16 @@ void
DeclStmt::Print(int indent) const { DeclStmt::Print(int indent) const {
printf("%*cDecl Stmt:", indent, ' '); printf("%*cDecl Stmt:", indent, ' ');
pos.Print(); pos.Print();
if (declaration) for (unsigned int i = 0; i < vars.size(); ++i) {
declaration->Print(); 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"); printf("\n");
} }
@@ -409,9 +411,9 @@ DeclStmt::Print(int indent) const {
int int
DeclStmt::EstimateCost() const { DeclStmt::EstimateCost() const {
int cost = 0; int cost = 0;
for (unsigned int i = 0; i < declaration->declarators.size(); ++i) for (unsigned int i = 0; i < vars.size(); ++i)
if (declaration->declarators[i]->initExpr) if (vars[i].init != NULL)
cost += declaration->declarators[i]->initExpr->EstimateCost(); cost += vars[i].init->EstimateCost();
return cost; return cost;
} }
@@ -716,8 +718,8 @@ lSafeToRunWithAllLanesOff(Stmt *stmt) {
DeclStmt *ds; DeclStmt *ds;
if ((ds = dynamic_cast<DeclStmt *>(stmt)) != NULL) { if ((ds = dynamic_cast<DeclStmt *>(stmt)) != NULL) {
for (unsigned int i = 0; i < ds->declaration->declarators.size(); ++i) for (unsigned int i = 0; i < ds->vars.size(); ++i)
if (!lSafeToRunWithAllLanesOff(ds->declaration->declarators[i]->initExpr)) if (!lSafeToRunWithAllLanesOff(ds->vars[i].init))
return false; return false;
return true; return true;
} }

12
stmt.h
View File

@@ -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 /** @brief Statement representing a single declaration (which in turn may declare
a number of variables. */ a number of variables. */
class DeclStmt : public Stmt { class DeclStmt : public Stmt {
public: public:
DeclStmt(SourcePos pos, Declaration *declaration, SymbolTable *symbolTable); DeclStmt(const std::vector<VariableDeclaration> &v, SourcePos pos);
void EmitCode(FunctionEmitContext *ctx) const; void EmitCode(FunctionEmitContext *ctx) const;
void Print(int indent) const; void Print(int indent) const;
@@ -95,7 +103,7 @@ public:
Stmt *TypeCheck(); Stmt *TypeCheck();
int EstimateCost() const; int EstimateCost() const;
Declaration *declaration; std::vector<VariableDeclaration> vars;
}; };