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

417
type.cpp
View File

@@ -42,6 +42,7 @@
#include "module.h"
#include <stdio.h>
#include <map>
#include <llvm/Value.h>
#include <llvm/Module.h>
#include <llvm/Analysis/DIBuilder.h>
@@ -59,7 +60,7 @@ static bool
lShouldPrintName(const std::string &name) {
if (name.size() == 0)
return false;
else if (name[0] != '_')
else if (name[0] != '_' && name[0] != '$')
return true;
else
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 *
PointerType::ResolveUnboundVariability(Variability v) const {
if (baseType == NULL) {
@@ -1090,11 +1055,30 @@ PointerType::LLVMType(llvm::LLVMContext *ctx) const {
return NULL;
}
if (isSlice)
// Slice pointers are represented as a structure with a pointer and
// an integer offset; the corresponding ispc type is returned by
// GetSliceStructType().
return GetSliceStructType()->LLVMType(ctx);
if (isSlice) {
llvm::Type *types[2];
types[0] = GetAsNonSlice()->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) {
case Variability::Uniform: {
@@ -1721,12 +1705,103 @@ VectorType::getVectorMemoryCount() const {
///////////////////////////////////////////////////////////////////////////
// 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,
const std::vector<std::string> &en,
const std::vector<SourcePos> &ep,
bool ic, Variability v, SourcePos p)
: name(n), elementTypes(elts), elementNames(en), elementPositions(ep),
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
StructType::GetString() const {
std::string ret;
if (isConst) ret += "const ";
if (isConst)
ret += "const ";
ret += variability.GetString();
ret += " ";
// Don't print the entire struct declaration, just print the struct's name.
// @todo Do we need a separate method that prints the declaration?
#if 0
ret += std::string("struct { ") + name;
for (unsigned int i = 0; i < elementTypes.size(); ++i) {
ret += elementTypes[i]->GetString();
ret += " ";
ret += elementNames[i];
ret += "; ";
if (name[0] == '$') {
// Print the whole anonymous struct declaration
ret += std::string("struct { ") + name;
for (unsigned int i = 0; i < elementTypes.size(); ++i) {
ret += elementTypes[i]->GetString();
ret += " ";
ret += elementNames[i];
ret += "; ";
}
ret += "}";
}
ret += "}";
#else
ret += "struct ";
ret += name;
#endif
else {
ret += "struct ";
ret += name;
}
return ret;
}
std::string
StructType::Mangle() const {
/** Mangle a struct name for use in function name mangling. */
static std::string
lMangleStruct(Variability variability, bool isConst, const std::string &name) {
Assert(variability != Variability::Unbound);
std::string ret;
@@ -1890,6 +1968,12 @@ StructType::Mangle() const {
ret += name + std::string("]");
return ret;
}
std::string
StructType::Mangle() const {
return lMangleStruct(variability, isConst, name);
}
std::string
@@ -1897,15 +1981,16 @@ StructType::GetCDeclaration(const std::string &n) const {
std::string ret;
if (isConst) ret += "const ";
ret += std::string("struct ") + name;
if (lShouldPrintName(n))
if (lShouldPrintName(n)) {
ret += std::string(" ") + n;
if (variability.soaWidth > 0) {
char buf[32];
// This has to match the naming scheme used in lEmitStructDecls()
// in module.cpp
sprintf(buf, "_SOA%d", variability.soaWidth);
ret += buf;
if (variability.soaWidth > 0) {
char buf[32];
// This has to match the naming scheme used in lEmitStructDecls()
// in module.cpp
sprintf(buf, "_SOA%d", variability.soaWidth);
ret += buf;
}
}
return ret;
@@ -1914,14 +1999,13 @@ StructType::GetCDeclaration(const std::string &n) const {
llvm::Type *
StructType::LLVMType(llvm::LLVMContext *ctx) const {
std::vector<llvm::Type *> llvmTypes;
for (int i = 0; i < GetElementCount(); ++i) {
const Type *type = GetElementType(i);
if (type == NULL)
return NULL;
llvmTypes.push_back(type->LLVMType(ctx));
Assert(variability != Variability::Unbound);
std::string mname = lMangleStructName(name, variability);
if (lStructTypeMap.find(mname) == lStructTypeMap.end()) {
Assert(m->errorCount > 0);
return NULL;
}
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
@@ -2889,20 +3137,19 @@ lCheckTypeEquality(const Type *a, const Type *b, bool ignoreConst) {
const StructType *sta = dynamic_cast<const StructType *>(a);
const StructType *stb = dynamic_cast<const StructType *>(b);
if (sta != NULL && stb != NULL) {
if (sta->GetElementCount() != stb->GetElementCount())
const UndefinedStructType *usta =
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;
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);