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:
417
type.cpp
417
type.cpp
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user