Add support for forward declarations of structures.

Now a declaration like 'struct Foo;' can be used to establish the
name of a struct type, without providing a definition.  One can
pass pointers to such types around the system, but can't do much
else with them (as in C/C++).

Issue #125.
This commit is contained in:
Matt Pharr
2012-04-16 06:27:21 -07:00
parent fefa86e0cf
commit 99a27fe241
13 changed files with 537 additions and 173 deletions

24
ctx.cpp
View File

@@ -2609,14 +2609,22 @@ FunctionEmitContext::maskedStore(llvm::Value *value, llvm::Value *ptr,
const PointerType *pt = dynamic_cast<const PointerType *>(valueType); const PointerType *pt = dynamic_cast<const PointerType *>(valueType);
if (pt != NULL) { if (pt != NULL) {
if (pt->IsSlice()) { if (pt->IsSlice()) {
// For masked stores of (varying) slice pointers to memory, we // Masked store of (varying) slice pointer.
// grab the equivalent StructType and make a recursive call to Assert(pt->IsVaryingType());
// maskedStore, giving it that type for the pointer type; that
// in turn will lead to the base pointer and offset index being // First, extract the pointer from the slice struct and masked
// mask stored to memory.. // store that.
const StructType *sliceStructType = pt->GetSliceStructType(); llvm::Value *v0 = ExtractInst(value, 0);
ptrType = PointerType::GetUniform(sliceStructType); llvm::Value *p0 = AddElementOffset(ptr, 0, ptrType);
maskedStore(value, ptr, ptrType, mask); maskedStore(v0, p0, PointerType::GetUniform(pt->GetAsNonSlice()),
mask);
// And then do same for the integer offset
llvm::Value *v1 = ExtractInst(value, 1);
llvm::Value *p1 = AddElementOffset(ptr, 1, ptrType);
const Type *offsetType = AtomicType::VaryingInt32;
maskedStore(v1, p1, PointerType::GetUniform(offsetType), mask);
return; return;
} }

View File

@@ -4692,12 +4692,13 @@ MemberExpr::create(Expr *e, const char *id, SourcePos p, SourcePos idpos,
exprType = pointerType->GetBaseType(); exprType = pointerType->GetBaseType();
if (derefLValue == true && pointerType == NULL) { if (derefLValue == true && pointerType == NULL) {
if (dynamic_cast<const StructType *>(exprType->GetReferenceTarget()) != NULL) const Type *targetType = exprType->GetReferenceTarget();
Error(p, "Dereference operator \"->\" can't be applied to non-pointer " if (dynamic_cast<const StructType *>(targetType) != NULL)
Error(p, "Member operator \"->\" can't be applied to non-pointer "
"type \"%s\". Did you mean to use \".\"?", "type \"%s\". Did you mean to use \".\"?",
exprType->GetString().c_str()); exprType->GetString().c_str());
else else
Error(p, "Dereference operator \"->\" can't be applied to non-struct " Error(p, "Member operator \"->\" can't be applied to non-struct "
"pointer type \"%s\".", exprType->GetString().c_str()); "pointer type \"%s\".", exprType->GetString().c_str());
return NULL; return NULL;
} }
@@ -4713,6 +4714,12 @@ MemberExpr::create(Expr *e, const char *id, SourcePos p, SourcePos idpos,
return new StructMemberExpr(e, id, p, idpos, derefLValue); return new StructMemberExpr(e, id, p, idpos, derefLValue);
else if (dynamic_cast<const VectorType *>(exprType) != NULL) else if (dynamic_cast<const VectorType *>(exprType) != NULL)
return new VectorMemberExpr(e, id, p, idpos, derefLValue); return new VectorMemberExpr(e, id, p, idpos, derefLValue);
else if (dynamic_cast<const UndefinedStructType *>(exprType)) {
Error(p, "Member operator \"%s\" can't be applied to declared "
"but not defined struct type \"%s\".", derefLValue ? "->" : ".",
exprType->GetString().c_str());
return NULL;
}
else { else {
Error(p, "Member operator \"%s\" can't be used with expression of " Error(p, "Member operator \"%s\" can't be used with expression of "
"\"%s\" type.", derefLValue ? "->" : ".", "\"%s\" type.", derefLValue ? "->" : ".",

View File

@@ -1477,10 +1477,14 @@ lCreateDispatchFunction(llvm::Module *module, llvm::Function *setISAFunc,
continue; continue;
} }
// Grab the type of the function as well. // Grab the type of the function as well. Note that the various
if (ftype != NULL) // functions will have different types if they have arguments that
Assert(ftype == funcs.func[i]->getFunctionType()); // are pointers to structs, due to the fact that we mangle LLVM
else // struct type names with the target vector width. However,
// because we only allow uniform stuff to pass through the
// export'ed function layer, they should all have the same memory
// layout, so this is benign..
if (ftype == NULL)
ftype = funcs.func[i]->getFunctionType(); ftype = funcs.func[i]->getFunctionType();
targetFuncs[i] = targetFuncs[i] =

View File

@@ -874,7 +874,6 @@ struct_or_union_specifier
std::vector<SourcePos> elementPositions; std::vector<SourcePos> elementPositions;
GetStructTypesNamesPositions(*$3, &elementTypes, &elementNames, GetStructTypesNamesPositions(*$3, &elementTypes, &elementNames,
&elementPositions); &elementPositions);
// FIXME: should be unbound
$$ = new StructType("", elementTypes, elementNames, elementPositions, $$ = new StructType("", elementTypes, elementNames, elementPositions,
false, Variability::Unbound, @1); false, Variability::Unbound, @1);
} }
@@ -892,10 +891,9 @@ struct_or_union_specifier
| struct_or_union struct_or_union_name | struct_or_union struct_or_union_name
{ {
const Type *st = m->symbolTable->LookupType($2); const Type *st = m->symbolTable->LookupType($2);
if (!st) { if (st == NULL) {
std::vector<std::string> alternates = m->symbolTable->ClosestTypeMatch($2); st = new UndefinedStructType($2, Variability::Unbound, false, @2);
std::string alts = lGetAlternates(alternates); m->symbolTable->AddType($2, st, @2);
Error(@2, "Struct type \"%s\" unknown.%s", $2, alts.c_str());
} }
else if (dynamic_cast<const StructType *>(st) == NULL) else if (dynamic_cast<const StructType *>(st) == NULL)
Error(@2, "Type \"%s\" is not a struct type! (%s)", $2, Error(@2, "Type \"%s\" is not a struct type! (%s)", $2,

95
sym.cpp
View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2010-2011, Intel Corporation Copyright (c) 2010-2012, Intel Corporation
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -66,7 +66,7 @@ SymbolTable::SymbolTable() {
SymbolTable::~SymbolTable() { SymbolTable::~SymbolTable() {
// Otherwise we have mismatched push/pop scopes // Otherwise we have mismatched push/pop scopes
Assert(variables.size() == 1 && types.size() == 1); Assert(variables.size() == 1);
PopScope(); PopScope();
} }
@@ -74,7 +74,6 @@ SymbolTable::~SymbolTable() {
void void
SymbolTable::PushScope() { SymbolTable::PushScope() {
variables.push_back(new SymbolMapType); variables.push_back(new SymbolMapType);
types.push_back(new TypeMapType);
} }
@@ -83,10 +82,6 @@ SymbolTable::PopScope() {
Assert(variables.size() > 1); Assert(variables.size() > 1);
delete variables.back(); delete variables.back();
variables.pop_back(); variables.pop_back();
Assert(types.size() > 1);
delete types.back();
types.pop_back();
} }
@@ -186,26 +181,17 @@ SymbolTable::LookupFunction(const char *name, const FunctionType *type) {
bool bool
SymbolTable::AddType(const char *name, const Type *type, SourcePos pos) { SymbolTable::AddType(const char *name, const Type *type, SourcePos pos) {
// Like AddVariable(), we go backwards through the type maps, working const Type *t = LookupType(name);
// from innermost scope to outermost. if (t != NULL && dynamic_cast<const UndefinedStructType *>(t) == NULL) {
for (int i = types.size()-1; i >= 0; --i) { // If we have a previous declaration of anything other than an
TypeMapType &sm = *(types[i]); // UndefinedStructType with this struct name, issue an error. If
if (sm.find(name) != sm.end()) { // we have an UndefinedStructType, then we'll fall through to the
if (i == (int)types.size() - 1) { // code below that adds the definition to the type map.
Error(pos, "Ignoring redefinition of type \"%s\".", name); Error(pos, "Ignoring redefinition of type \"%s\".", name);
return false; return false;
}
else {
Warning(pos, "Type \"%s\" shadows type declared in outer scope.", name);
TypeMapType &sm = *(types.back());
sm[name] = type;
return true;
}
}
} }
TypeMapType &sm = *(types.back()); types[name] = type;
sm[name] = type;
return true; return true;
} }
@@ -213,11 +199,9 @@ SymbolTable::AddType(const char *name, const Type *type, SourcePos pos) {
const Type * const Type *
SymbolTable::LookupType(const char *name) const { SymbolTable::LookupType(const char *name) const {
// Again, search through the type maps backward to get scoping right. // Again, search through the type maps backward to get scoping right.
for (int i = types.size()-1; i >= 0; --i) { TypeMapType::const_iterator iter = types.find(name);
TypeMapType &sm = *(types[i]); if (iter != types.end())
if (sm.find(name) != sm.end()) return iter->second;
return sm[name];
}
return NULL; return NULL;
} }
@@ -282,21 +266,19 @@ SymbolTable::closestTypeMatch(const char *str, bool structsVsEnums) const {
const int maxDelta = 2; const int maxDelta = 2;
std::vector<std::string> matches[maxDelta+1]; std::vector<std::string> matches[maxDelta+1];
for (unsigned int i = 0; i < types.size(); ++i) { TypeMapType::const_iterator iter;
TypeMapType::const_iterator iter; for (iter = types.begin(); iter != types.end(); ++iter) {
for (iter = types[i]->begin(); iter != types[i]->end(); ++iter) { // Skip over either StructTypes or EnumTypes, depending on the
// Skip over either StructTypes or EnumTypes, depending on the // value of the structsVsEnums parameter
// value of the structsVsEnums parameter bool isEnum = (dynamic_cast<const EnumType *>(iter->second) != NULL);
bool isEnum = (dynamic_cast<const EnumType *>(iter->second) != NULL); if (isEnum && structsVsEnums)
if (isEnum && structsVsEnums) continue;
continue; else if (!isEnum && !structsVsEnums)
else if (!isEnum && !structsVsEnums) continue;
continue;
int dist = StringEditDistance(str, iter->first, maxDelta+1); int dist = StringEditDistance(str, iter->first, maxDelta+1);
if (dist <= maxDelta) if (dist <= maxDelta)
matches[dist].push_back(iter->first); matches[dist].push_back(iter->first);
}
} }
for (int i = 0; i <= maxDelta; ++i) { for (int i = 0; i <= maxDelta; ++i) {
@@ -336,16 +318,12 @@ SymbolTable::Print() {
depth = 0; depth = 0;
fprintf(stderr, "Named types:\n---------------\n"); fprintf(stderr, "Named types:\n---------------\n");
for (unsigned int i = 0; i < types.size(); ++i) { TypeMapType::iterator siter = types.begin();
TypeMapType &sm = *types[i]; while (siter != types.end()) {
TypeMapType::iterator siter = sm.begin(); fprintf(stderr, "%*c", depth, ' ');
while (siter != sm.end()) { fprintf(stderr, "%s -> %s\n", siter->first.c_str(),
fprintf(stderr, "%*c", depth, ' '); siter->second->GetString().c_str());
fprintf(stderr, "%s -> %s\n", siter->first.c_str(), ++siter;
siter->second->GetString().c_str());
++siter;
}
depth += 4;
} }
} }
@@ -376,14 +354,11 @@ SymbolTable::RandomSymbol() {
const Type * const Type *
SymbolTable::RandomType() { SymbolTable::RandomType() {
int v = ispcRand() % types.size(); int count = types.size();
if (types[v]->size() == 0) TypeMapType::iterator iter = types.begin();
return NULL;
int count = ispcRand() % types[v]->size();
TypeMapType::iterator iter = types[v]->begin();
while (count-- > 0) { while (count-- > 0) {
++iter; ++iter;
Assert(iter != types[v]->end()); Assert(iter != types.end());
} }
return iter->second; return iter->second;
} }

11
sym.h
View File

@@ -1,5 +1,5 @@
/* /*
Copyright (c) 2010-2011, Intel Corporation Copyright (c) 2010-2012, Intel Corporation
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -201,6 +201,9 @@ public:
/** Adds the named type to the symbol table. This is used for both /** Adds the named type to the symbol table. This is used for both
struct definitions (where <tt>struct Foo</tt> causes type \c Foo to struct definitions (where <tt>struct Foo</tt> causes type \c Foo to
be added to the symbol table) as well as for <tt>typedef</tt>s. be added to the symbol table) as well as for <tt>typedef</tt>s.
For structs with forward declarations ("struct Foo;") and are thus
UndefinedStructTypes, this method replaces these with an actual
struct definition if one is provided.
@param name Name of the type to be added @param name Name of the type to be added
@param type Type that \c name represents @param type Type that \c name represents
@@ -265,12 +268,10 @@ private:
typedef std::map<std::string, std::vector<Symbol *> > FunctionMapType; typedef std::map<std::string, std::vector<Symbol *> > FunctionMapType;
FunctionMapType functions; FunctionMapType functions;
/** Type definitions can also be scoped. A new \c TypeMapType /** Type definitions can't currently be scoped.
is added to the back of the \c types \c vector each time a new scope
is entered. (And it's removed when the scope exits).
*/ */
typedef std::map<std::string, const Type *> TypeMapType; typedef std::map<std::string, const Type *> TypeMapType;
std::vector<TypeMapType *> types; TypeMapType types;
}; };

View File

@@ -0,0 +1,36 @@
export uniform int width() { return programCount; }
struct Foo;
void bing(Foo * uniform);
struct Foo {
int i;
varying float f;
Foo * uniform next;
};
void bar(Foo * uniform f) {
bing(f);
}
export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform Foo fa, fb;
fa.next = &fb;
fb.f = aFOO[programIndex];
fb.i = 100;
bar(&fa);
RET[programIndex] = fb.f;
}
void bing(Foo * uniform f) {
f = f->next;
f->f *= 2;
}
export void result(uniform float RET[]) {
RET[programIndex] = 2 + 2*programIndex;
}

View File

@@ -0,0 +1,33 @@
export uniform int width() { return programCount; }
struct Foo;
void bing(varying Foo * uniform);
struct Foo {
float f;
int i;
};
void bar(varying Foo * uniform f) {
bing(f);
}
export void f_f(uniform float RET[], uniform float aFOO[]) {
Foo f;
f.f = aFOO[programIndex];
f.i = programIndex;
bar(&f);
RET[programIndex] = f.f;
}
void bing(varying Foo * uniform f) {
f->f *= 2;
}
export void result(uniform float RET[]) {
RET[programIndex] = 2 + 2*programIndex;
}

View File

@@ -1,4 +1,4 @@
// Dereference operator "->" can't be applied to non-pointer type "varying struct Foo" // Member operator "->" can't be applied to non-pointer type "varying struct Foo"
struct Foo { int x; }; struct Foo { int x; };

View File

@@ -0,0 +1,5 @@
// Member operator "." can't be applied to declared but not defined struct type
struct Foo;
int bar(Foo & foo) { return foo.x; }

View File

@@ -0,0 +1,5 @@
// Member operator "->" can't be applied to declared but not defined struct type
struct Foo;
int bar(Foo * uniform foo) { return foo->x; }

417
type.cpp
View File

@@ -42,6 +42,7 @@
#include "module.h" #include "module.h"
#include <stdio.h> #include <stdio.h>
#include <map>
#include <llvm/Value.h> #include <llvm/Value.h>
#include <llvm/Module.h> #include <llvm/Module.h>
#include <llvm/Analysis/DIBuilder.h> #include <llvm/Analysis/DIBuilder.h>
@@ -59,7 +60,7 @@ static bool
lShouldPrintName(const std::string &name) { lShouldPrintName(const std::string &name) {
if (name.size() == 0) if (name.size() == 0)
return false; return false;
else if (name[0] != '_') else if (name[0] != '_' && name[0] != '$')
return true; return true;
else else
return (name.size() == 1) || (name[1] != '_'); return (name.size() == 1) || (name[1] != '_');
@@ -946,42 +947,6 @@ PointerType::GetAsFrozenSlice() const {
} }
/** Returns a structure corresponding to the pointer representation for
slice pointers; the first member of this structure is a uniform or
varying pointer, and the second element is either a uniform or varying
int32.
*/
const StructType *
PointerType::GetSliceStructType() const {
Assert(isSlice == true);
std::vector<const Type *> eltTypes;
eltTypes.push_back(GetAsNonSlice());
switch (variability.type) {
case Variability::Uniform:
eltTypes.push_back(AtomicType::UniformInt32);
break;
case Variability::Varying:
eltTypes.push_back(AtomicType::VaryingInt32);
break;
default:
FATAL("Unexpected variability in PointerType::GetSliceStructType()");
}
std::vector<std::string> eltNames;
std::vector<SourcePos> eltPos;
eltNames.push_back("ptr");
eltNames.push_back("offset");
eltPos.push_back(SourcePos());
eltPos.push_back(SourcePos());
return new StructType("__ptr_slice_tmp", eltTypes, eltNames, eltPos, isConst,
Variability::Uniform, SourcePos());
}
const PointerType * const PointerType *
PointerType::ResolveUnboundVariability(Variability v) const { PointerType::ResolveUnboundVariability(Variability v) const {
if (baseType == NULL) { if (baseType == NULL) {
@@ -1090,11 +1055,30 @@ PointerType::LLVMType(llvm::LLVMContext *ctx) const {
return NULL; return NULL;
} }
if (isSlice) if (isSlice) {
// Slice pointers are represented as a structure with a pointer and llvm::Type *types[2];
// an integer offset; the corresponding ispc type is returned by types[0] = GetAsNonSlice()->LLVMType(ctx);
// GetSliceStructType().
return GetSliceStructType()->LLVMType(ctx); switch (variability.type) {
case Variability::Uniform:
types[1] = LLVMTypes::Int32Type;
break;
case Variability::Varying:
types[1] = LLVMTypes::Int32VectorType;
break;
case Variability::SOA:
types[1] = llvm::ArrayType::get(LLVMTypes::Int32Type,
variability.soaWidth);
break;
default:
FATAL("unexpected variability for slice pointer in "
"PointerType::LLVMType");
}
llvm::ArrayRef<llvm::Type *> typesArrayRef =
llvm::ArrayRef<llvm::Type *>(types, 2);
return llvm::StructType::get(*g->ctx, typesArrayRef);
}
switch (variability.type) { switch (variability.type) {
case Variability::Uniform: { case Variability::Uniform: {
@@ -1721,12 +1705,103 @@ VectorType::getVectorMemoryCount() const {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// StructType // StructType
// We maintain a map from struct names to LLVM struct types so that we can
// uniquely get the llvm::StructType * for a given ispc struct type. Note
// that we need to mangle the name a bit so that we can e.g. differentiate
// between the uniform and varying variants of a given struct type. This
// is handled by lMangleStructName() below.
static std::map<std::string, llvm::StructType *> lStructTypeMap;
/** Using a struct's name, its variability, and the vector width for the
current compilation target, this function generates a string that
encodes that full structure type, for use in the lStructTypeMap. Note
that the vector width is needed in order to differentiate between
'varying' structs with different compilation targets, which have
different memory layouts...
*/
static std::string
lMangleStructName(const std::string &name, Variability variability) {
char buf[32];
std::string n;
// Encode vector width
sprintf(buf, "v%d", g->target.vectorWidth);
n += buf;
// Variability
switch (variability.type) {
case Variability::Uniform:
n += "_uniform_";
break;
case Variability::Varying:
n += "_varying_";
break;
case Variability::SOA:
sprintf(buf, "_soa%d_", variability.soaWidth);
n += buf;
break;
default:
FATAL("Unexpected varaibility in lMangleStructName()");
}
// And stuff the name at the end....
n += name;
return n;
}
StructType::StructType(const std::string &n, const std::vector<const Type *> &elts, StructType::StructType(const std::string &n, const std::vector<const Type *> &elts,
const std::vector<std::string> &en, const std::vector<std::string> &en,
const std::vector<SourcePos> &ep, const std::vector<SourcePos> &ep,
bool ic, Variability v, SourcePos p) bool ic, Variability v, SourcePos p)
: name(n), elementTypes(elts), elementNames(en), elementPositions(ep), : name(n), elementTypes(elts), elementNames(en), elementPositions(ep),
variability(v), isConst(ic), pos(p) { variability(v), isConst(ic), pos(p) {
if (variability != Variability::Unbound) {
// For structs with non-unbound variability, we'll create the
// correspoing LLVM struct type now, if one hasn't been made
// already.
// Create a unique anonymous struct name if we have an anonymous
// struct (name == ""), or if we are creating a derived type from
// an anonymous struct (e.g. the varying variant--name == '$').
if (name == "" || name[0] == '$') {
char buf[16];
static int count = 0;
sprintf(buf, "$anon%d", count);
name = buf;
++count;
}
// If a non-opaque LLVM struct for this type has already been
// created, we're done. For an opaque struct type, we'll override
// the old definition now that we have a full definition.
std::string mname = lMangleStructName(name, variability);
if (lStructTypeMap.find(mname) != lStructTypeMap.end() &&
lStructTypeMap[mname]->isOpaque() == false)
return;
// Actually make the LLVM struct
std::vector<llvm::Type *> elementTypes;
for (int i = 0; i < GetElementCount(); ++i) {
const Type *type = GetElementType(i);
if (type == NULL) {
Assert(m->errorCount > 0);
return;
}
elementTypes.push_back(type->LLVMType(g->ctx));
}
if (lStructTypeMap.find(mname) == lStructTypeMap.end()) {
// New struct definition
llvm::StructType *st =
llvm::StructType::create(*g->ctx, elementTypes, mname);
lStructTypeMap[mname] = st;
}
else {
// Definition for what was before just a declaration
lStructTypeMap[mname]->setBody(elementTypes);
}
}
} }
@@ -1854,31 +1929,34 @@ StructType::GetAsNonConstType() const {
std::string std::string
StructType::GetString() const { StructType::GetString() const {
std::string ret; std::string ret;
if (isConst) ret += "const "; if (isConst)
ret += "const ";
ret += variability.GetString(); ret += variability.GetString();
ret += " "; ret += " ";
// Don't print the entire struct declaration, just print the struct's name. if (name[0] == '$') {
// @todo Do we need a separate method that prints the declaration? // Print the whole anonymous struct declaration
#if 0 ret += std::string("struct { ") + name;
ret += std::string("struct { ") + name; for (unsigned int i = 0; i < elementTypes.size(); ++i) {
for (unsigned int i = 0; i < elementTypes.size(); ++i) { ret += elementTypes[i]->GetString();
ret += elementTypes[i]->GetString(); ret += " ";
ret += " "; ret += elementNames[i];
ret += elementNames[i]; ret += "; ";
ret += "; "; }
ret += "}";
} }
ret += "}"; else {
#else ret += "struct ";
ret += "struct "; ret += name;
ret += name; }
#endif
return ret; return ret;
} }
std::string /** Mangle a struct name for use in function name mangling. */
StructType::Mangle() const { static std::string
lMangleStruct(Variability variability, bool isConst, const std::string &name) {
Assert(variability != Variability::Unbound); Assert(variability != Variability::Unbound);
std::string ret; std::string ret;
@@ -1890,6 +1968,12 @@ StructType::Mangle() const {
ret += name + std::string("]"); ret += name + std::string("]");
return ret; return ret;
} }
std::string
StructType::Mangle() const {
return lMangleStruct(variability, isConst, name);
}
std::string std::string
@@ -1897,15 +1981,16 @@ StructType::GetCDeclaration(const std::string &n) const {
std::string ret; std::string ret;
if (isConst) ret += "const "; if (isConst) ret += "const ";
ret += std::string("struct ") + name; ret += std::string("struct ") + name;
if (lShouldPrintName(n)) if (lShouldPrintName(n)) {
ret += std::string(" ") + n; ret += std::string(" ") + n;
if (variability.soaWidth > 0) { if (variability.soaWidth > 0) {
char buf[32]; char buf[32];
// This has to match the naming scheme used in lEmitStructDecls() // This has to match the naming scheme used in lEmitStructDecls()
// in module.cpp // in module.cpp
sprintf(buf, "_SOA%d", variability.soaWidth); sprintf(buf, "_SOA%d", variability.soaWidth);
ret += buf; ret += buf;
}
} }
return ret; return ret;
@@ -1914,14 +1999,13 @@ StructType::GetCDeclaration(const std::string &n) const {
llvm::Type * llvm::Type *
StructType::LLVMType(llvm::LLVMContext *ctx) const { StructType::LLVMType(llvm::LLVMContext *ctx) const {
std::vector<llvm::Type *> llvmTypes; Assert(variability != Variability::Unbound);
for (int i = 0; i < GetElementCount(); ++i) { std::string mname = lMangleStructName(name, variability);
const Type *type = GetElementType(i); if (lStructTypeMap.find(mname) == lStructTypeMap.end()) {
if (type == NULL) Assert(m->errorCount > 0);
return NULL; return NULL;
llvmTypes.push_back(type->LLVMType(ctx));
} }
return llvm::StructType::get(*ctx, llvmTypes); return lStructTypeMap[mname];
} }
@@ -2037,6 +2121,170 @@ StructType::checkIfCanBeSOA(const StructType *st) {
} }
///////////////////////////////////////////////////////////////////////////
// UndefinedStructType
UndefinedStructType::UndefinedStructType(const std::string &n,
const Variability var, bool ic,
SourcePos p)
: name(n), variability(var), isConst(ic), pos(p) {
Assert(name != "");
if (variability != Variability::Unbound) {
// Create a new opaque LLVM struct type for this struct name
std::string mname = lMangleStructName(name, variability);
if (lStructTypeMap.find(mname) == lStructTypeMap.end())
lStructTypeMap[mname] = llvm::StructType::create(*g->ctx, mname);
}
}
Variability
UndefinedStructType::GetVariability() const {
return variability;
}
bool
UndefinedStructType::IsBoolType() const {
return false;
}
bool
UndefinedStructType::IsFloatType() const {
return false;
}
bool
UndefinedStructType::IsIntType() const {
return false;
}
bool
UndefinedStructType::IsUnsignedType() const {
return false;
}
bool
UndefinedStructType::IsConstType() const {
return isConst;
}
const Type *
UndefinedStructType::GetBaseType() const {
return this;
}
const UndefinedStructType *
UndefinedStructType::GetAsVaryingType() const {
if (variability == Variability::Varying)
return this;
return new UndefinedStructType(name, Variability::Varying, isConst, pos);
}
const UndefinedStructType *
UndefinedStructType::GetAsUniformType() const {
if (variability == Variability::Uniform)
return this;
return new UndefinedStructType(name, Variability::Uniform, isConst, pos);
}
const UndefinedStructType *
UndefinedStructType::GetAsUnboundVariabilityType() const {
if (variability == Variability::Unbound)
return this;
return new UndefinedStructType(name, Variability::Unbound, isConst, pos);
}
const UndefinedStructType *
UndefinedStructType::GetAsSOAType(int width) const {
FATAL("UndefinedStructType::GetAsSOAType() shouldn't be called.");
return NULL;
}
const UndefinedStructType *
UndefinedStructType::ResolveUnboundVariability(Variability v) const {
if (variability != Variability::Unbound)
return this;
return new UndefinedStructType(name, v, isConst, pos);
}
const UndefinedStructType *
UndefinedStructType::GetAsConstType() const {
if (isConst)
return this;
return new UndefinedStructType(name, variability, true, pos);
}
const UndefinedStructType *
UndefinedStructType::GetAsNonConstType() const {
if (isConst == false)
return this;
return new UndefinedStructType(name, variability, false, pos);
}
std::string
UndefinedStructType::GetString() const {
std::string ret;
if (isConst) ret += "const ";
ret += variability.GetString();
ret += " struct ";
ret += name;
return ret;
}
std::string
UndefinedStructType::Mangle() const {
return lMangleStruct(variability, isConst, name);
}
std::string
UndefinedStructType::GetCDeclaration(const std::string &n) const {
std::string ret;
if (isConst) ret += "const ";
ret += std::string("struct ") + name;
if (lShouldPrintName(n))
ret += std::string(" ") + n;
return ret;
}
llvm::Type *
UndefinedStructType::LLVMType(llvm::LLVMContext *ctx) const {
Assert(variability != Variability::Unbound);
std::string mname = lMangleStructName(name, variability);
if (lStructTypeMap.find(mname) == lStructTypeMap.end()) {
Assert(m->errorCount > 0);
return NULL;
}
return lStructTypeMap[mname];
}
llvm::DIType
UndefinedStructType::GetDIType(llvm::DIDescriptor scope) const {
llvm::DIFile diFile = pos.GetDIFile();
llvm::DIArray elements;
return m->diBuilder->createStructType(scope, name, diFile, pos.first_line,
0 /* size */, 0 /* align */,
0 /* flags */, elements);
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// ReferenceType // ReferenceType
@@ -2889,20 +3137,19 @@ lCheckTypeEquality(const Type *a, const Type *b, bool ignoreConst) {
const StructType *sta = dynamic_cast<const StructType *>(a); const StructType *sta = dynamic_cast<const StructType *>(a);
const StructType *stb = dynamic_cast<const StructType *>(b); const StructType *stb = dynamic_cast<const StructType *>(b);
if (sta != NULL && stb != NULL) { const UndefinedStructType *usta =
if (sta->GetElementCount() != stb->GetElementCount()) dynamic_cast<const UndefinedStructType *>(a);
const UndefinedStructType *ustb =
dynamic_cast<const UndefinedStructType *>(b);
if ((sta != NULL || usta != NULL) && (stb != NULL || ustb != NULL)) {
// Report both defuned and undefined structs as equal if their
// names are the same.
if (a->GetVariability() != b->GetVariability())
return false; return false;
if (sta->GetStructName() != stb->GetStructName())
return false;
if (sta->GetVariability() != stb->GetVariability())
return false;
for (int i = 0; i < sta->GetElementCount(); ++i)
// FIXME: is this redundant now?
if (!lCheckTypeEquality(sta->GetElementType(i), stb->GetElementType(i),
ignoreConst))
return false;
return true; std::string namea = sta ? sta->GetStructName() : usta->GetStructName();
std::string nameb = stb ? stb->GetStructName() : ustb->GetStructName();
return (namea == nameb);
} }
const PointerType *pta = dynamic_cast<const PointerType *>(a); const PointerType *pta = dynamic_cast<const PointerType *>(a);

49
type.h
View File

@@ -409,7 +409,6 @@ public:
const PointerType *GetAsSlice() const; const PointerType *GetAsSlice() const;
const PointerType *GetAsNonSlice() const; const PointerType *GetAsNonSlice() const;
const PointerType *GetAsFrozenSlice() const; const PointerType *GetAsFrozenSlice() const;
const StructType *GetSliceStructType() const;
const Type *GetBaseType() const; const Type *GetBaseType() const;
const PointerType *GetAsVaryingType() const; const PointerType *GetAsVaryingType() const;
@@ -668,7 +667,7 @@ public:
private: private:
static bool checkIfCanBeSOA(const StructType *st); static bool checkIfCanBeSOA(const StructType *st);
const std::string name; /*const*/ std::string name;
/** The types of the struct elements. Note that we store these with /** The types of the struct elements. Note that we store these with
uniform/varying exactly as they were declared in the source file. uniform/varying exactly as they were declared in the source file.
(In other words, even if this struct has a varying qualifier and (In other words, even if this struct has a varying qualifier and
@@ -690,6 +689,52 @@ private:
}; };
/** Type implementation representing a struct name that has been declared
but where the struct members haven't been defined (i.e. "struct Foo;").
This class doesn't do much besides serve as a placeholder that other
code can use to detect the presence of such as truct.
*/
class UndefinedStructType : public Type {
public:
UndefinedStructType(const std::string &name, const Variability variability,
bool isConst, SourcePos pos);
Variability GetVariability() const;
bool IsBoolType() const;
bool IsFloatType() const;
bool IsIntType() const;
bool IsUnsignedType() const;
bool IsConstType() const;
const Type *GetBaseType() const;
const UndefinedStructType *GetAsVaryingType() const;
const UndefinedStructType *GetAsUniformType() const;
const UndefinedStructType *GetAsUnboundVariabilityType() const;
const UndefinedStructType *GetAsSOAType(int width) const;
const UndefinedStructType *ResolveUnboundVariability(Variability v) const;
const UndefinedStructType *GetAsConstType() const;
const UndefinedStructType *GetAsNonConstType() const;
std::string GetString() const;
std::string Mangle() const;
std::string GetCDeclaration(const std::string &name) const;
llvm::Type *LLVMType(llvm::LLVMContext *ctx) const;
llvm::DIType GetDIType(llvm::DIDescriptor scope) const;
/** Returns the name of the structure type. (e.g. struct Foo -> "Foo".) */
const std::string &GetStructName() const { return name; }
private:
const std::string name;
const Variability variability;
const bool isConst;
const SourcePos pos;
};
/** @brief Type representing a reference to another (non-reference) type. /** @brief Type representing a reference to another (non-reference) type.
*/ */
class ReferenceType : public Type { class ReferenceType : public Type {