Add support for pointers to the language.

Pointers can be either uniform or varying, and behave correspondingly.
e.g.: "uniform float * varying" is a varying pointer to uniform float
data in memory, and "float * uniform" is a uniform pointer to varying
data in memory.  Like other types, pointers are varying by default.

Pointer-based expressions, & and *, sizeof, ->, pointer arithmetic,
and the array/pointer duality all bahave as in C.  Array arguments
to functions are converted to pointers, also like C.

There is a built-in NULL for a null pointer value; conversion from
compile-time constant 0 values to NULL still needs to be implemented.

Other changes:
- Syntax for references has been updated to be C++ style; a useful
  warning is now issued if the "reference" keyword is used.
- It is now illegal to pass a varying lvalue as a reference parameter
  to a function; references are essentially uniform pointers.
  This case had previously been handled via special case call by value
  return code.  That path has been removed, now that varying pointers
  are available to handle this use case (and much more).
- Some stdlib routines have been updated to take pointers as
  arguments where appropriate (e.g. prefetch and the atomics).
  A number of others still need attention.
- All of the examples have been updated
- Many new tests

TODO: documentation
This commit is contained in:
Matt Pharr
2011-11-21 09:16:29 -08:00
parent 15a7d353ab
commit 975db80ef6
191 changed files with 4746 additions and 3225 deletions

338
type.cpp
View File

@@ -709,6 +709,25 @@ PointerType::PointerType(const Type *t, bool iu, bool ic)
}
PointerType *
PointerType::GetUniform(const Type *t) {
return new PointerType(t, true, false);
}
PointerType *
PointerType::GetVarying(const Type *t) {
return new PointerType(t, false, false);
}
bool
PointerType::IsVoidPointer(const Type *t) {
return Type::EqualIgnoringConst(t->GetAsUniformType(),
PointerType::Void);
}
bool
PointerType::IsUniformType() const {
return isUniform;
@@ -822,9 +841,11 @@ PointerType::GetCDeclaration(const std::string &name) const {
if (baseType == NULL)
return "";
std::string ret = baseType->GetCDeclaration(name);
std::string ret = baseType->GetCDeclaration("");
ret += std::string(" *");
if (isConst) ret += " const";
ret += std::string(" ");
ret += name;
return ret;
}
@@ -834,6 +855,9 @@ PointerType::LLVMType(llvm::LLVMContext *ctx) const {
if (baseType == NULL)
return NULL;
if (isUniform == false)
return LLVMTypes::VoidPointerVectorType;
LLVM_TYPE_CONST llvm::Type *ptype = NULL;
const FunctionType *ftype = dynamic_cast<const FunctionType *>(baseType);
if (ftype != NULL)
@@ -841,15 +865,40 @@ PointerType::LLVMType(llvm::LLVMContext *ctx) const {
// last parameter--i.e. we don't allow taking function pointers of
// exported functions.
ptype = llvm::PointerType::get(ftype->LLVMFunctionType(ctx, true), 0);
else
ptype = llvm::PointerType::get(baseType->LLVMType(ctx), 0);
else {
if (baseType == AtomicType::Void)
ptype = LLVMTypes::VoidPointerType;
else
ptype = llvm::PointerType::get(baseType->LLVMType(ctx), 0);
}
if (isUniform)
return ptype;
else
// Varying pointers are represented as arrays of pointers since
// LLVM doesn't allow vectors of pointers.
return llvm::ArrayType::get(ptype, g->target.vectorWidth);
return ptype;
}
static llvm::DIType
lCreateDIArray(llvm::DIType eltType, int count) {
int lowerBound = 0, upperBound = count-1;
if (count == 0) {
// unsized array -> indicate with low > high
lowerBound = 1;
upperBound = 0;
}
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(lowerBound, upperBound);
std::vector<llvm::Value *> subs;
subs.push_back(sub);
#ifdef LLVM_2_9
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(&subs[0], subs.size());
#else
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(subs);
#endif
uint64_t size = eltType.getSizeInBits() * count;
uint64_t align = eltType.getAlignInBits();
return m->diBuilder->createArrayType(size, align, eltType, subArray);
}
@@ -859,8 +908,15 @@ PointerType::GetDIType(llvm::DIDescriptor scope) const {
return llvm::DIType();
llvm::DIType diTargetType = baseType->GetDIType(scope);
int bitsSize = g->target.is32bit ? 32 : 64;
return m->diBuilder->createPointerType(diTargetType, bitsSize);
int bitsSize = g->target.is32Bit ? 32 : 64;
if (isUniform)
return m->diBuilder->createPointerType(diTargetType, bitsSize);
else {
// emit them as an array of pointers
llvm::DIType eltType = m->diBuilder->createPointerType(diTargetType,
bitsSize);
return lCreateDIArray(eltType, g->target.vectorWidth);
}
}
@@ -1071,28 +1127,7 @@ ArrayType::GetDIType(llvm::DIDescriptor scope) const {
return llvm::DIType();
llvm::DIType eltType = child->GetDIType(scope);
int lowerBound = 0, upperBound = numElements-1;
if (numElements == 0) {
// unsized array -> indicate with low > high
lowerBound = 1;
upperBound = 0;
}
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(lowerBound, upperBound);
std::vector<llvm::Value *> subs;
subs.push_back(sub);
#ifdef LLVM_2_9
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(&subs[0], subs.size());
#else
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(subs);
#endif
// it's intentional that size is zero for unsized arrays
uint64_t size = eltType.getSizeInBits() * numElements;
uint64_t align = eltType.getAlignInBits();
return m->diBuilder->createArrayType(size, align, eltType, subArray);
return lCreateDIArray(eltType, numElements);
}
@@ -1112,30 +1147,49 @@ ArrayType::SizeUnsizedArrays(const Type *type, Expr *initExpr) {
ExprList *exprList = dynamic_cast<ExprList *>(initExpr);
if (exprList == NULL || exprList->exprs.size() == 0)
return type;
// If the current dimension is unsized, then size it according to the
// length of the expression list
if (at->GetElementCount() == 0)
type = at->GetSizedArray(exprList->exprs.size());
// Is there another nested level of expression lists? If not, bail out
// now. Otherwise we'll use the first one to size the next dimension
// (after checking below that it has the same length as all of the
// other ones.
ExprList *nextList = dynamic_cast<ExprList *>(exprList->exprs[0]);
if (nextList == NULL)
return type;
unsigned int nextSize = nextList->exprs.size();
for (unsigned int i = 1; i < exprList->exprs.size(); ++i) {
if (exprList->exprs[i] == NULL) {
assert(m->errorCount > 0);
continue;
}
const Type *nextType = at->GetElementType();
const ArrayType *nextArrayType =
dynamic_cast<const ArrayType *>(nextType);
if (nextArrayType != NULL && nextArrayType->GetElementCount() == 0) {
// If the recursive call to SizeUnsizedArrays at the bottom of the
// function is going to size an unsized dimension, make sure that
// all of the sub-expression lists are the same length--i.e. issue
// an error if we have something like
// int x[][] = { { 1 }, { 1, 2, 3, 4 } };
unsigned int nextSize = nextList->exprs.size();
for (unsigned int i = 1; i < exprList->exprs.size(); ++i) {
if (exprList->exprs[i] == NULL) {
// We should have seen an error earlier in this case.
assert(m->errorCount > 0);
continue;
}
ExprList *el = dynamic_cast<ExprList *>(exprList->exprs[i]);
if (el == NULL || el->exprs.size() != nextSize) {
Error(Union(exprList->exprs[0]->pos, exprList->exprs[i]->pos),
"Inconsistent expression list lengths found in initializer "
"list.");
return NULL;
ExprList *el = dynamic_cast<ExprList *>(exprList->exprs[i]);
if (el == NULL || el->exprs.size() != nextSize) {
Error(Union(exprList->exprs[0]->pos, exprList->exprs[i]->pos),
"Inconsistent initializer expression list lengths "
"make it impossible to size unsized array dimensions.");
return NULL;
}
}
}
// Recursively call SizeUnsizedArrays() to get the child type for the
// array that we were able to size here.
return new ArrayType(SizeUnsizedArrays(at->GetElementType(), nextList),
exprList->exprs.size());
}
@@ -1632,6 +1686,8 @@ StructType::LLVMType(llvm::LLVMContext *ctx) const {
std::vector<LLVM_TYPE_CONST 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));
}
return llvm::StructType::get(*ctx, llvmTypes);
@@ -1732,8 +1788,8 @@ StructType::GetElementNumber(const std::string &n) const {
///////////////////////////////////////////////////////////////////////////
// ReferenceType
ReferenceType::ReferenceType(const Type *t, bool ic)
: isConst(ic), targetType(t->GetAsNonConstType()) {
ReferenceType::ReferenceType(const Type *t)
: targetType(t) {
}
@@ -1769,7 +1825,7 @@ ReferenceType::IsUnsignedType() const {
bool
ReferenceType::IsConstType() const {
return isConst;
return targetType->IsConstType();
}
@@ -1789,7 +1845,7 @@ const ReferenceType *
ReferenceType::GetAsVaryingType() const {
if (IsVaryingType())
return this;
return new ReferenceType(targetType->GetAsVaryingType(), isConst);
return new ReferenceType(targetType->GetAsVaryingType());
}
@@ -1797,13 +1853,13 @@ const ReferenceType *
ReferenceType::GetAsUniformType() const {
if (IsUniformType())
return this;
return new ReferenceType(targetType->GetAsUniformType(), isConst);
return new ReferenceType(targetType->GetAsUniformType());
}
const Type *
ReferenceType::GetSOAType(int width) const {
return new ReferenceType(targetType->GetSOAType(width), isConst);
return new ReferenceType(targetType->GetSOAType(width));
}
@@ -1811,7 +1867,7 @@ const ReferenceType *
ReferenceType::GetAsConstType() const {
if (IsConstType())
return this;
return new ReferenceType(targetType, true);
return new ReferenceType(targetType->GetAsConstType());
}
@@ -1819,17 +1875,18 @@ const ReferenceType *
ReferenceType::GetAsNonConstType() const {
if (!IsConstType())
return this;
return new ReferenceType(targetType, false);
return new ReferenceType(targetType->GetAsNonConstType());
}
std::string
ReferenceType::GetString() const {
std::string ret;
if (isConst || targetType->IsConstType())
ret += "const ";
ret += std::string("reference<") + targetType->GetAsNonConstType()->GetString() +
std::string(">");
if (targetType == NULL)
return "";
std::string ret = targetType->GetString();
ret += std::string(" &");
return ret;
}
@@ -1837,8 +1894,6 @@ ReferenceType::GetString() const {
std::string
ReferenceType::Mangle() const {
std::string ret;
if (isConst)
ret += "C";
ret += std::string("REF") + targetType->Mangle();
return ret;
}
@@ -1851,8 +1906,6 @@ ReferenceType::GetCDeclaration(const std::string &name) const {
if (at->GetElementCount() == 0) {
// emit unsized arrays as pointers to the base type..
std::string ret;
if (isConst || at->GetElementType()->IsConstType())
ret += "const ";
ret += at->GetElementType()->GetAsNonConstType()->GetCDeclaration("") +
std::string(" *");
if (lShouldPrintName(name))
@@ -1866,10 +1919,7 @@ ReferenceType::GetCDeclaration(const std::string &name) const {
}
else {
std::string ret;
if (isConst || targetType->IsConstType())
ret += "const ";
ret += targetType->GetAsNonConstType()->GetCDeclaration("") +
std::string(" *");
ret += targetType->GetCDeclaration("") + std::string(" *");
if (lShouldPrintName(name))
ret += name;
return ret;
@@ -1901,10 +1951,9 @@ ReferenceType::GetDIType(llvm::DIDescriptor scope) const {
FunctionType::FunctionType(const Type *r, const std::vector<const Type *> &a,
SourcePos p)
: isTask(false), isExported(false), isExternC(false), returnType(r),
argTypes(a), argNames(std::vector<std::string>(a.size(), "")),
argDefaults(std::vector<ConstExpr *>(a.size(), NULL)),
argPos(std::vector<SourcePos>(a.size(), p)),
pos(p) {
paramTypes(a), paramNames(std::vector<std::string>(a.size(), "")),
paramDefaults(std::vector<ConstExpr *>(a.size(), NULL)),
paramPositions(std::vector<SourcePos>(a.size(), p)) {
assert(returnType != NULL);
}
@@ -1914,11 +1963,11 @@ FunctionType::FunctionType(const Type *r, const std::vector<const Type *> &a,
const std::vector<ConstExpr *> &ad,
const std::vector<SourcePos> &ap,
bool it, bool is, bool ec)
: isTask(it), isExported(is), isExternC(ec), returnType(r), argTypes(a),
argNames(an), argDefaults(ad), argPos(ap), pos(p) {
assert(argTypes.size() == argNames.size() &&
argNames.size() == argDefaults.size() &&
argDefaults.size() == argPos.size());
: isTask(it), isExported(is), isExternC(ec), returnType(r), paramTypes(a),
paramNames(an), paramDefaults(ad), paramPositions(ap) {
assert(paramTypes.size() == paramNames.size() &&
paramNames.size() == paramDefaults.size() &&
paramDefaults.size() == paramPositions.size());
assert(returnType != NULL);
}
@@ -2007,9 +2056,9 @@ FunctionType::GetString() const {
if (isTask) ret += "task ";
ret += returnType->GetString();
ret += "(";
for (unsigned int i = 0; i < argTypes.size(); ++i) {
ret += argTypes[i]->GetString();
if (i != argTypes.size() - 1)
for (unsigned int i = 0; i < paramTypes.size(); ++i) {
ret += paramTypes[i]->GetString();
if (i != paramTypes.size() - 1)
ret += ", ";
}
ret += ")";
@@ -2020,8 +2069,8 @@ FunctionType::GetString() const {
std::string
FunctionType::Mangle() const {
std::string ret = "___";
for (unsigned int i = 0; i < argTypes.size(); ++i)
ret += argTypes[i]->Mangle();
for (unsigned int i = 0; i < paramTypes.size(); ++i)
ret += paramTypes[i]->Mangle();
return ret;
}
@@ -2033,12 +2082,23 @@ FunctionType::GetCDeclaration(const std::string &fname) const {
ret += " ";
ret += fname;
ret += "(";
for (unsigned int i = 0; i < argTypes.size(); ++i) {
if (argNames[i] != "")
ret += argTypes[i]->GetCDeclaration(argNames[i]);
for (unsigned int i = 0; i < paramTypes.size(); ++i) {
const Type *type = paramTypes[i];
// Convert pointers to arrays to unsized arrays, which are more clear
// to print out for multidimensional arrays (i.e. "float foo[][4] "
// versus "float (foo *)[4]").
const PointerType *pt = dynamic_cast<const PointerType *>(type);
if (pt != NULL &&
dynamic_cast<const ArrayType *>(pt->GetBaseType()) != NULL) {
type = new ArrayType(pt->GetBaseType(), 0);
}
if (paramNames[i] != "")
ret += type->GetCDeclaration(paramNames[i]);
else
ret += argTypes[i]->GetString();
if (i != argTypes.size() - 1)
ret += type->GetString();
if (i != paramTypes.size() - 1)
ret += ", ";
}
ret += ")";
@@ -2063,20 +2123,17 @@ FunctionType::GetDIType(llvm::DIDescriptor scope) const {
LLVM_TYPE_CONST llvm::FunctionType *
FunctionType::LLVMFunctionType(llvm::LLVMContext *ctx, bool includeMask) const {
if (!includeMask && isTask) {
Error(pos, "Function can't have both \"task\" and \"export\" qualifiers");
return NULL;
}
if (isTask == true) assert(includeMask == true);
// Get the LLVM Type *s for the function arguments
std::vector<LLVM_TYPE_CONST llvm::Type *> llvmArgTypes;
for (unsigned int i = 0; i < argTypes.size(); ++i) {
if (!argTypes[i])
for (unsigned int i = 0; i < paramTypes.size(); ++i) {
if (!paramTypes[i])
return NULL;
assert(argTypes[i] != AtomicType::Void);
assert(paramTypes[i] != AtomicType::Void);
LLVM_TYPE_CONST llvm::Type *t = argTypes[i]->LLVMType(ctx);
if (!t)
LLVM_TYPE_CONST llvm::Type *t = paramTypes[i]->LLVMType(ctx);
if (t == NULL)
return NULL;
llvmArgTypes.push_back(t);
}
@@ -2107,10 +2164,31 @@ FunctionType::LLVMFunctionType(llvm::LLVMContext *ctx, bool includeMask) const {
}
std::string
FunctionType::GetArgumentName(int i) const {
assert(i < (int)argNames.size());
return argNames[i];
const Type *
FunctionType::GetParameterType(int i) const {
assert(i < (int)paramTypes.size());
return paramTypes[i];
}
ConstExpr *
FunctionType::GetParameterDefault(int i) const {
assert(i < (int)paramDefaults.size());
return paramDefaults[i];
}
const SourcePos &
FunctionType::GetParameterSourcePos(int i) const {
assert(i < (int)paramPositions.size());
return paramPositions[i];
}
const std::string &
FunctionType::GetParameterName(int i) const {
assert(i < (int)paramNames.size());
return paramNames[i];
}
@@ -2196,17 +2274,15 @@ Type::MoreGeneralType(const Type *t0, const Type *t1, SourcePos pos, const char
// Not the same types, but only a const/non-const difference? Return
// the non-const type as the more general one.
if (Type::Equal(t0->GetAsConstType(), t1->GetAsConstType()))
if (Type::EqualIgnoringConst(t0, t1))
return t0->GetAsNonConstType();
const PointerType *pt0 = dynamic_cast<const PointerType *>(t0);
const PointerType *pt1 = dynamic_cast<const PointerType *>(t1);
if (pt0 != NULL && pt1 != NULL) {
if (Type::Equal(pt0->GetAsUniformType()->GetAsConstType(),
PointerType::Void))
if (PointerType::IsVoidPointer(pt0))
return pt1;
else if (Type::Equal(pt1->GetAsUniformType()->GetAsConstType(),
PointerType::Void))
else if (PointerType::IsVoidPointer(pt1))
return pt0;
else {
Error(pos, "Conversion between incompatible pointer types \"%s\" "
@@ -2318,11 +2394,18 @@ Type::MoreGeneralType(const Type *t0, const Type *t1, SourcePos pos, const char
}
bool
Type::Equal(const Type *a, const Type *b) {
static bool
lCheckTypeEquality(const Type *a, const Type *b, bool ignoreConst) {
if (a == NULL || b == NULL)
return false;
if (ignoreConst == true) {
if (dynamic_cast<const FunctionType *>(a) == NULL)
a = a->GetAsNonConstType();
if (dynamic_cast<const FunctionType *>(b) == NULL)
b = b->GetAsNonConstType();
}
// We can compare AtomicTypes with pointer equality, since the
// AtomicType constructor is private so that there isonly the single
// canonical instance of the AtomicTypes (AtomicType::UniformInt32,
@@ -2346,13 +2429,15 @@ Type::Equal(const Type *a, const Type *b) {
const ArrayType *atb = dynamic_cast<const ArrayType *>(b);
if (ata != NULL && atb != NULL)
return (ata->GetElementCount() == atb->GetElementCount() &&
Equal(ata->GetElementType(), atb->GetElementType()));
lCheckTypeEquality(ata->GetElementType(), atb->GetElementType(),
ignoreConst));
const VectorType *vta = dynamic_cast<const VectorType *>(a);
const VectorType *vtb = dynamic_cast<const VectorType *>(b);
if (vta != NULL && vtb != NULL)
return (vta->GetElementCount() == vtb->GetElementCount() &&
Equal(vta->GetElementType(), vtb->GetElementType()));
lCheckTypeEquality(vta->GetElementType(), vtb->GetElementType(),
ignoreConst));
const StructType *sta = dynamic_cast<const StructType *>(a);
const StructType *stb = dynamic_cast<const StructType *>(b);
@@ -2360,7 +2445,8 @@ Type::Equal(const Type *a, const Type *b) {
if (sta->GetElementCount() != stb->GetElementCount())
return false;
for (int i = 0; i < sta->GetElementCount(); ++i)
if (!Equal(sta->GetElementType(i), stb->GetElementType(i)))
if (!lCheckTypeEquality(sta->GetElementType(i), stb->GetElementType(i),
ignoreConst))
return false;
return true;
}
@@ -2368,16 +2454,16 @@ Type::Equal(const Type *a, const Type *b) {
const ReferenceType *rta = dynamic_cast<const ReferenceType *>(a);
const ReferenceType *rtb = dynamic_cast<const ReferenceType *>(b);
if (rta != NULL && rtb != NULL)
return ((rta->IsConstType() == rtb->IsConstType()) &&
Type::Equal(rta->GetReferenceTarget(),
rtb->GetReferenceTarget()));
return (lCheckTypeEquality(rta->GetReferenceTarget(),
rtb->GetReferenceTarget(), ignoreConst));
const FunctionType *fta = dynamic_cast<const FunctionType *>(a);
const FunctionType *ftb = dynamic_cast<const FunctionType *>(b);
if (fta != NULL && ftb != NULL) {
// Both the return types and all of the argument types must match
// for function types to match
if (!Equal(fta->GetReturnType(), ftb->GetReturnType()))
if (!lCheckTypeEquality(fta->GetReturnType(), ftb->GetReturnType(),
ignoreConst))
return false;
if (fta->isTask != ftb->isTask ||
@@ -2385,13 +2471,14 @@ Type::Equal(const Type *a, const Type *b) {
fta->isExternC != ftb->isExternC)
return false;
const std::vector<const Type *> &aargs = fta->GetArgumentTypes();
const std::vector<const Type *> &bargs = ftb->GetArgumentTypes();
if (aargs.size() != bargs.size())
if (fta->GetNumParameters() != ftb->GetNumParameters())
return false;
for (unsigned int i = 0; i < aargs.size(); ++i)
if (!Equal(aargs[i], bargs[i]))
for (int i = 0; i < fta->GetNumParameters(); ++i)
if (!lCheckTypeEquality(fta->GetParameterType(i),
ftb->GetParameterType(i), ignoreConst))
return false;
return true;
}
@@ -2400,7 +2487,20 @@ Type::Equal(const Type *a, const Type *b) {
if (pta != NULL && ptb != NULL)
return (pta->IsConstType() == ptb->IsConstType() &&
pta->IsUniformType() == ptb->IsUniformType() &&
Type::Equal(pta->GetBaseType(), ptb->GetBaseType()));
lCheckTypeEquality(pta->GetBaseType(), ptb->GetBaseType(),
ignoreConst));
return false;
}
bool
Type::Equal(const Type *a, const Type *b) {
return lCheckTypeEquality(a, b, false);
}
bool
Type::EqualIgnoringConst(const Type *a, const Type *b) {
return lCheckTypeEquality(a, b, true);
}