Files
ispc/type.cpp
Aaron Gutierrez a47cab4dfa Replicates all needed state between expanded functions
commit 5e6f06cf59
Author: Aaron Gutierrez <gutierrez.aaron.m@gmail.com>
Date:   Thu May 11 15:42:11 2017 -0400

    Fixed issue with aliasing local variables

    ISPC++ now produces valid code, or an appropriate error message, for all
    of my test cases.

commit bfe723e1b7
Author: Aaron Gutierrez <gutierrez.aaron.m@gmail.com>
Date:   Thu May 11 03:09:38 2017 -0400

    Actually copy the AST.

    Type replacement works except for function parameters.

commit f65b3e6300
Author: Aaron Gutierrez <gutierrez.aaron.m@gmail.com>
Date:   Thu May 11 01:19:50 2017 -0400

    [WIP] Remove cases for ForeachStmt and SymbolExpr

commit 2e28640860
Merge: 6a91c5d d020107
Author: Aaron Gutierrez <gutierrez.aaron.m@gmail.com>
Date:   Wed May 10 23:13:40 2017 -0400

    Merge branch 'master' into copy_ast

commit 6a91c5d5ac
Author: Aaron Gutierrez <gutierrez.aaron.m@gmail.com>
Date:   Wed May 10 11:11:39 2017 -0400

    Attempt to replicate AST when expanding polytypes
2017-05-11 15:43:29 -04:00

4221 lines
120 KiB
C++

/*
Copyright (c) 2010-2015, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file type.cpp
@brief Definitions for classes related to type representation
*/
#include "type.h"
#include "expr.h"
#include "sym.h"
#include "llvmutil.h"
#include "module.h"
#include <stdio.h>
#include <map>
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
#include <llvm/Value.h>
#include <llvm/Module.h>
#else
#include <llvm/IR/Value.h>
#include <llvm/IR/Module.h>
#endif
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_5 // LLVM 3.5+
#include <llvm/IR/DebugInfo.h>
#include <llvm/IR/DIBuilder.h>
#else
#include <llvm/DebugInfo.h>
#include <llvm/DIBuilder.h>
#endif
#include <llvm/Support/Dwarf.h>
/** Utility routine used in code that prints out declarations; returns true
if the given name should be printed, false otherwise. This allows us
to omit the names for various internal things (whose names start with
double underscores) and emit anonymous declarations for them instead.
*/
static bool
lShouldPrintName(const std::string &name) {
if (name.size() == 0)
return false;
else if (name[0] != '_' && name[0] != '$')
return true;
else
return (name.size() == 1) || (name[1] != '_');
}
/** Utility routine to create a llvm array type of the given number of
the given element type. */
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
static llvm::DIType lCreateDIArray(llvm::DIType eltType, int count) {
#else // LLVM 3.7++
static llvm::DIType *lCreateDIArray(llvm::DIType *eltType, int count) {
#endif
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
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);
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, count);
#endif
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
std::vector<llvm::Value *> subs;
#else // LLVM 3.6++
llvm::Metadata *sub = m->diBuilder->getOrCreateSubrange(0, count);
std::vector<llvm::Metadata *> subs;
#endif
subs.push_back(sub);
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(subs);
uint64_t size = eltType.getSizeInBits() * count;
uint64_t align = eltType.getAlignInBits();
#else // LLVM 3.7++
llvm::DINodeArray subArray = m->diBuilder->getOrCreateArray(subs);
uint64_t size = eltType->getSizeInBits() * count;
uint64_t align = eltType->getAlignInBits();
#endif
return m->diBuilder->createArrayType(size, align, eltType, subArray);
}
///////////////////////////////////////////////////////////////////////////
// Variability
std::string
Variability::GetString() const {
switch (type) {
case Uniform: return "uniform";
case Varying: return "varying";
case SOA: {
char buf[32];
sprintf(buf, "soa<%d>", soaWidth);
return buf;
}
case Unbound: return "/*unbound*/";
default:
FATAL("Unhandled variability");
return "";
}
}
std::string
Variability::MangleString() const {
switch (type) {
case Uniform:
return "un";
case Varying:
return "vy";
case SOA: {
char buf[32];
sprintf(buf, "soa<%d>", soaWidth);
return buf;
}
case Unbound:
FATAL("Unbound unexpected in Variability::MangleString()");
default:
FATAL("Unhandled variability");
return "";
}
}
///////////////////////////////////////////////////////////////////////////
// AtomicType
const AtomicType *AtomicType::UniformBool =
new AtomicType(AtomicType::TYPE_BOOL, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingBool =
new AtomicType(AtomicType::TYPE_BOOL, Variability::Varying, false);
const AtomicType *AtomicType::UniformInt8 =
new AtomicType(AtomicType::TYPE_INT8, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingInt8 =
new AtomicType(AtomicType::TYPE_INT8, Variability::Varying, false);
const AtomicType *AtomicType::UniformUInt8 =
new AtomicType(AtomicType::TYPE_UINT8, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingUInt8 =
new AtomicType(AtomicType::TYPE_UINT8, Variability::Varying, false);
const AtomicType *AtomicType::UniformInt16 =
new AtomicType(AtomicType::TYPE_INT16, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingInt16 =
new AtomicType(AtomicType::TYPE_INT16, Variability::Varying, false);
const AtomicType *AtomicType::UniformUInt16 =
new AtomicType(AtomicType::TYPE_UINT16, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingUInt16 =
new AtomicType(AtomicType::TYPE_UINT16, Variability::Varying, false);
const AtomicType *AtomicType::UniformInt32 =
new AtomicType(AtomicType::TYPE_INT32, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingInt32 =
new AtomicType(AtomicType::TYPE_INT32, Variability::Varying, false);
const AtomicType *AtomicType::UniformUInt32 =
new AtomicType(AtomicType::TYPE_UINT32, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingUInt32 =
new AtomicType(AtomicType::TYPE_UINT32, Variability::Varying, false);
const AtomicType *AtomicType::UniformFloat =
new AtomicType(AtomicType::TYPE_FLOAT, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingFloat =
new AtomicType(AtomicType::TYPE_FLOAT, Variability::Varying, false);
const AtomicType *AtomicType::UniformInt64 =
new AtomicType(AtomicType::TYPE_INT64, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingInt64 =
new AtomicType(AtomicType::TYPE_INT64, Variability::Varying, false);
const AtomicType *AtomicType::UniformUInt64 =
new AtomicType(AtomicType::TYPE_UINT64, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingUInt64 =
new AtomicType(AtomicType::TYPE_UINT64, Variability::Varying, false);
const AtomicType *AtomicType::UniformDouble =
new AtomicType(AtomicType::TYPE_DOUBLE, Variability::Uniform, false);
const AtomicType *AtomicType::VaryingDouble =
new AtomicType(AtomicType::TYPE_DOUBLE, Variability::Varying, false);
const AtomicType *AtomicType::Void =
new AtomicType(TYPE_VOID, Variability::Uniform, false);
AtomicType::AtomicType(BasicType bt, Variability v, bool ic)
: Type(ATOMIC_TYPE), basicType(bt), variability(v), isConst(ic) {
asOtherConstType = NULL;
asUniformType = asVaryingType = NULL;
}
Variability
AtomicType::GetVariability() const {
return variability;
}
bool
Type::IsPointerType() const {
return (CastType<PointerType>(this) != NULL);
}
bool
Type::IsArrayType() const {
return (CastType<ArrayType>(this) != NULL);
}
bool
Type::IsReferenceType() const {
return (CastType<ReferenceType>(this) != NULL);
}
bool
Type::IsVoidType() const {
return EqualIgnoringConst(this, AtomicType::Void);
}
bool
Type::IsPolymorphicType() const {
const FunctionType *ft = CastType<FunctionType>(this);
if (ft) {
for (int i=0; i<ft->GetNumParameters(); i++) {
if (ft->GetParameterType(i)->IsPolymorphicType())
return true;
}
return false;
}
return (CastType<PolyType>(GetBaseType()) != NULL);
}
bool
AtomicType::IsFloatType() const {
return (basicType == TYPE_FLOAT || basicType == TYPE_DOUBLE);
}
bool
AtomicType::IsIntType() const {
return (basicType == TYPE_INT8 || basicType == TYPE_UINT8 ||
basicType == TYPE_INT16 || basicType == TYPE_UINT16 ||
basicType == TYPE_INT32 || basicType == TYPE_UINT32 ||
basicType == TYPE_INT64 || basicType == TYPE_UINT64);
}
bool
AtomicType::IsUnsignedType() const {
return (basicType == TYPE_UINT8 || basicType == TYPE_UINT16 ||
basicType == TYPE_UINT32 || basicType == TYPE_UINT64);
}
bool
AtomicType::IsBoolType() const {
return basicType == TYPE_BOOL;
}
bool
AtomicType::IsConstType() const {
return isConst;
}
const AtomicType *
AtomicType::GetAsUnsignedType() const {
if (IsUnsignedType() == true)
return this;
if (IsIntType() == false)
return NULL;
switch (basicType) {
case TYPE_INT8:
return new AtomicType(TYPE_UINT8, variability, isConst);
case TYPE_INT16:
return new AtomicType(TYPE_UINT16, variability, isConst);
case TYPE_INT32:
return new AtomicType(TYPE_UINT32, variability, isConst);
case TYPE_INT64:
return new AtomicType(TYPE_UINT64, variability, isConst);
default:
FATAL("Unexpected basicType in GetAsUnsignedType()");
return NULL;
}
}
const AtomicType *
AtomicType::GetAsConstType() const {
if (isConst == true)
return this;
if (asOtherConstType == NULL) {
asOtherConstType = new AtomicType(basicType, variability, true);
asOtherConstType->asOtherConstType = this;
}
return asOtherConstType;
}
const AtomicType *
AtomicType::GetAsNonConstType() const {
if (isConst == false)
return this;
if (asOtherConstType == NULL) {
asOtherConstType = new AtomicType(basicType, variability, false);
asOtherConstType->asOtherConstType = this;
}
return asOtherConstType;
}
const AtomicType *
AtomicType::GetBaseType() const {
return this;
}
const AtomicType *
AtomicType::GetAsVaryingType() const {
Assert(basicType != TYPE_VOID);
if (variability == Variability::Varying)
return this;
if (asVaryingType == NULL) {
asVaryingType = new AtomicType(basicType, Variability::Varying, isConst);
if (variability == Variability::Uniform)
asVaryingType->asUniformType = this;
}
return asVaryingType;
}
const AtomicType *
AtomicType::GetAsUniformType() const {
Assert(basicType != TYPE_VOID);
if (variability == Variability::Uniform)
return this;
if (asUniformType == NULL) {
asUniformType = new AtomicType(basicType, Variability::Uniform, isConst);
if (variability == Variability::Varying)
asUniformType->asVaryingType = this;
}
return asUniformType;
}
const AtomicType *
AtomicType::GetAsUnboundVariabilityType() const {
Assert(basicType != TYPE_VOID);
if (variability == Variability::Unbound)
return this;
return new AtomicType(basicType, Variability::Unbound, isConst);
}
const AtomicType *
AtomicType::GetAsSOAType(int width) const {
Assert(basicType != TYPE_VOID);
if (variability == Variability(Variability::SOA, width))
return this;
return new AtomicType(basicType, Variability(Variability::SOA, width), isConst);
}
const AtomicType *
AtomicType::ResolveUnboundVariability(Variability v) const {
Assert(v != Variability::Unbound);
if (variability != Variability::Unbound)
return this;
return new AtomicType(basicType, v, isConst);
}
std::string
AtomicType::GetString() const {
std::string ret;
if (isConst) ret += "const ";
if (basicType != TYPE_VOID) {
ret += variability.GetString();
ret += " ";
}
switch (basicType) {
case TYPE_VOID: ret += "void"; break;
case TYPE_BOOL: ret += "bool"; break;
case TYPE_INT8: ret += "int8"; break;
case TYPE_UINT8: ret += "unsigned int8"; break;
case TYPE_INT16: ret += "int16"; break;
case TYPE_UINT16: ret += "unsigned int16"; break;
case TYPE_INT32: ret += "int32"; break;
case TYPE_UINT32: ret += "unsigned int32"; break;
case TYPE_FLOAT: ret += "float"; break;
case TYPE_INT64: ret += "int64"; break;
case TYPE_UINT64: ret += "unsigned int64"; break;
case TYPE_DOUBLE: ret += "double"; break;
default: FATAL("Logic error in AtomicType::GetString()");
}
return ret;
}
std::string
AtomicType::Mangle() const {
std::string ret;
if (isConst) ret += "C";
ret += variability.MangleString();
switch (basicType) {
case TYPE_VOID: ret += "v"; break;
case TYPE_BOOL: ret += "b"; break;
case TYPE_INT8: ret += "t"; break;
case TYPE_UINT8: ret += "T"; break;
case TYPE_INT16: ret += "s"; break;
case TYPE_UINT16: ret += "S"; break;
case TYPE_INT32: ret += "i"; break;
case TYPE_UINT32: ret += "u"; break;
case TYPE_FLOAT: ret += "f"; break;
case TYPE_INT64: ret += "I"; break;
case TYPE_UINT64: ret += "U"; break;
case TYPE_DOUBLE: ret += "d"; break;
default: FATAL("Logic error in AtomicType::Mangle()");
}
return ret;
}
std::string
AtomicType::GetCDeclaration(const std::string &name) const {
std::string ret;
if (variability == Variability::Unbound) {
Assert(m->errorCount > 0);
return ret;
}
if (isConst) ret += "const ";
switch (basicType) {
case TYPE_VOID: ret += "void"; break;
case TYPE_BOOL: ret += "bool"; break;
case TYPE_INT8: ret += "int8_t"; break;
case TYPE_UINT8: ret += "uint8_t"; break;
case TYPE_INT16: ret += "int16_t"; break;
case TYPE_UINT16: ret += "uint16_t"; break;
case TYPE_INT32: ret += "int32_t"; break;
case TYPE_UINT32: ret += "uint32_t"; break;
case TYPE_FLOAT: ret += "float"; break;
case TYPE_INT64: ret += "int64_t"; break;
case TYPE_UINT64: ret += "uint64_t"; break;
case TYPE_DOUBLE: ret += "double"; break;
default: FATAL("Logic error in AtomicType::GetCDeclaration()");
}
if (lShouldPrintName(name)) {
ret += " ";
ret += name;
}
if (variability == Variability::SOA) {
char buf[32];
sprintf(buf, "[%d]", variability.soaWidth);
ret += buf;
}
return ret;
}
llvm::Type *
AtomicType::LLVMType(llvm::LLVMContext *ctx) const {
Assert(variability.type != Variability::Unbound);
bool isUniform = (variability == Variability::Uniform);
bool isVarying = (variability == Variability::Varying);
if (isUniform || isVarying) {
switch (basicType) {
case TYPE_VOID:
return llvm::Type::getVoidTy(*ctx);
case TYPE_BOOL:
return isUniform ? LLVMTypes::BoolType : LLVMTypes::BoolVectorType;
case TYPE_INT8:
case TYPE_UINT8:
return isUniform ? LLVMTypes::Int8Type : LLVMTypes::Int8VectorType;
case TYPE_INT16:
case TYPE_UINT16:
return isUniform ? LLVMTypes::Int16Type : LLVMTypes::Int16VectorType;
case TYPE_INT32:
case TYPE_UINT32:
return isUniform ? LLVMTypes::Int32Type : LLVMTypes::Int32VectorType;
case TYPE_FLOAT:
return isUniform ? LLVMTypes::FloatType : LLVMTypes::FloatVectorType;
case TYPE_INT64:
case TYPE_UINT64:
return isUniform ? LLVMTypes::Int64Type : LLVMTypes::Int64VectorType;
case TYPE_DOUBLE:
return isUniform ? LLVMTypes::DoubleType : LLVMTypes::DoubleVectorType;
default:
FATAL("logic error in AtomicType::LLVMType");
return NULL;
}
}
else {
ArrayType at(GetAsUniformType(), variability.soaWidth);
return at.LLVMType(ctx);
}
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType AtomicType::GetDIType(llvm::DIDescriptor scope) const {
#else //LLVM 3.7++
llvm::DIType *AtomicType::GetDIType(llvm::DIScope *scope) const {
#endif
Assert(variability.type != Variability::Unbound);
if (variability.type == Variability::Uniform) {
switch (basicType) {
case TYPE_VOID:
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
return llvm::DIType();
#else //LLVM 3.7++
return NULL;
#endif
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
case TYPE_BOOL:
return m->diBuilder->createBasicType("bool", 32 /* size */, 32 /* align */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_INT8:
return m->diBuilder->createBasicType("int8", 8 /* size */, 8 /* align */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT8:
return m->diBuilder->createBasicType("uint8", 8 /* size */, 8 /* align */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_INT16:
return m->diBuilder->createBasicType("int16", 16 /* size */, 16 /* align */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT16:
return m->diBuilder->createBasicType("uint16", 16 /* size */, 16 /* align */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_INT32:
return m->diBuilder->createBasicType("int32", 32 /* size */, 32 /* align */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT32:
return m->diBuilder->createBasicType("uint32", 32 /* size */, 32 /* align */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_FLOAT:
return m->diBuilder->createBasicType("float", 32 /* size */, 32 /* align */,
llvm::dwarf::DW_ATE_float);
break;
case TYPE_DOUBLE:
return m->diBuilder->createBasicType("double", 64 /* size */, 64 /* align */,
llvm::dwarf::DW_ATE_float);
break;
case TYPE_INT64:
return m->diBuilder->createBasicType("int64", 64 /* size */, 64 /* align */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT64:
return m->diBuilder->createBasicType("uint64", 64 /* size */, 64 /* align */,
llvm::dwarf::DW_ATE_unsigned);
break;
#else // LLVM 4.0+
case TYPE_BOOL:
return m->diBuilder->createBasicType("bool", 32 /* size */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_INT8:
return m->diBuilder->createBasicType("int8", 8 /* size */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT8:
return m->diBuilder->createBasicType("uint8", 8 /* size */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_INT16:
return m->diBuilder->createBasicType("int16", 16 /* size */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT16:
return m->diBuilder->createBasicType("uint16", 16 /* size */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_INT32:
return m->diBuilder->createBasicType("int32", 32 /* size */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT32:
return m->diBuilder->createBasicType("uint32", 32 /* size */,
llvm::dwarf::DW_ATE_unsigned);
break;
case TYPE_FLOAT:
return m->diBuilder->createBasicType("float", 32 /* size */,
llvm::dwarf::DW_ATE_float);
break;
case TYPE_DOUBLE:
return m->diBuilder->createBasicType("double", 64 /* size */,
llvm::dwarf::DW_ATE_float);
break;
case TYPE_INT64:
return m->diBuilder->createBasicType("int64", 64 /* size */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_UINT64:
return m->diBuilder->createBasicType("uint64", 64 /* size */,
llvm::dwarf::DW_ATE_unsigned);
break;
#endif
default:
FATAL("unhandled basic type in AtomicType::GetDIType()");
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
return llvm::DIType();
#else //LLVM 3.7+
return NULL;
#endif
}
}
else if (variability == Variability::Varying) {
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth()-1);
#elif ISPC_LLVM_VERSION > ISPC_VERSION_3_2 && ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth());
#else // LLVM 3.6+
llvm::Metadata *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth());
#endif
#if ISPC_LLVM_VERSION > ISPC_VERSION_3_2 && ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(sub);
llvm::DIType unifType = GetAsUniformType()->GetDIType(scope);
uint64_t size = unifType.getSizeInBits() * g->target->getVectorWidth();
uint64_t align = unifType.getAlignInBits() * g->target->getVectorWidth();
#else // LLVM 3.7+
llvm::DINodeArray subArray = m->diBuilder->getOrCreateArray(sub);
llvm::DIType *unifType = GetAsUniformType()->GetDIType(scope);
//llvm::DebugNodeArray subArray = m->diBuilder->getOrCreateArray(sub);
//llvm::MDType *unifType = GetAsUniformType()->GetDIType(scope);
uint64_t size = unifType->getSizeInBits() * g->target->getVectorWidth();
uint64_t align = unifType->getAlignInBits()* g->target->getVectorWidth();
#endif
return m->diBuilder->createVectorType(size, align, unifType, subArray);
}
else {
Assert(variability == Variability::SOA);
ArrayType at(GetAsUniformType(), variability.soaWidth);
return at.GetDIType(scope);
}
}
///////////////////////////////////////////////////////////////////////////
// PolyType
const PolyType *PolyType::UniformInteger =
new PolyType(PolyType::TYPE_INTEGER, Variability::Uniform, false);
const PolyType *PolyType::VaryingInteger =
new PolyType(PolyType::TYPE_INTEGER, Variability::Varying, false);
const PolyType *PolyType::UniformFloating =
new PolyType(PolyType::TYPE_FLOATING, Variability::Uniform, false);
const PolyType *PolyType::VaryingFloating =
new PolyType(PolyType::TYPE_FLOATING, Variability::Varying, false);
const PolyType *PolyType::UniformNumber =
new PolyType(PolyType::TYPE_NUMBER, Variability::Uniform, false);
const PolyType *PolyType::VaryingNumber =
new PolyType(PolyType::TYPE_NUMBER, Variability::Varying, false);
const Type *
PolyType::ReplaceType(const Type *from, const Type *to) {
const Type *t = to;
if (from->IsPointerType()) {
t = new PointerType(to,
from->GetVariability(),
from->IsConstType());
} else if (from->IsArrayType()) {
t = new ArrayType(to,
CastType<ArrayType>(from)->GetElementCount());
} else if (from->IsReferenceType()) {
t = new ReferenceType(to);
}
if (from->IsVaryingType())
t = t->GetAsVaryingType();
if (g->debugPrint) {
fprintf(stderr, "Replacing type \"%s\" with \"%s\"\n",
from->GetString().c_str(),
t->GetString().c_str());
}
return t;
}
bool
PolyType::Less(const Type *a, const Type *b) {
const PolyType *pa = CastType<PolyType>(a->GetBaseType());
const PolyType *pb = CastType<PolyType>(b->GetBaseType());
if (!pa || !pb) {
char buf[1024];
snprintf(buf, 1024, "Calling lPolyTypeLess on non-polymorphic types"
"\"%s\" and \"%s\"\n",
a->GetString().c_str(), b->GetString().c_str());
FATAL(buf);
}
if (pa->restriction < pb->restriction)
return true;
if (pa->restriction > pb->restriction)
return false;
if (pa->GetQuant() < pb->GetQuant())
return true;
return false;
}
PolyType::PolyType(PolyRestriction r, Variability v, bool ic)
: Type(POLY_TYPE), restriction(r), variability(v), isConst(ic), quant(-1) {
asOtherConstType = NULL;
asUniformType = asVaryingType = NULL;
expandedTypes = NULL;
}
PolyType::PolyType(PolyRestriction r, Variability v, bool ic, int q)
: Type(POLY_TYPE), restriction(r), variability(v), isConst(ic), quant(q) {
asOtherConstType = NULL;
asUniformType = asVaryingType = NULL;
expandedTypes = NULL;
}
Variability
PolyType::GetVariability() const {
return variability;
}
int
PolyType::GetQuant() const {
return quant;
}
bool
PolyType::IsFloatType() const {
return (restriction == TYPE_FLOATING);
}
bool
PolyType::IsIntType() const {
return (restriction == TYPE_INTEGER);
}
bool
PolyType::IsUnsignedType() const {
return false;
}
bool
PolyType::IsBoolType() const {
return false;
}
bool
PolyType::IsConstType() const {
return isConst;
}
const PolyType *
PolyType::GetAsUnsignedType() const {
return NULL;
}
const PolyType *
PolyType::GetAsConstType() const {
if (isConst == true)
return this;
if (asOtherConstType == NULL) {
asOtherConstType = new PolyType(restriction, variability, true, quant);
asOtherConstType->asOtherConstType = this;
}
return asOtherConstType;
}
const PolyType *
PolyType::GetAsNonConstType() const {
if (isConst == false)
return this;
if (asOtherConstType == NULL) {
asOtherConstType = new PolyType(restriction, variability, false, quant);
asOtherConstType->asOtherConstType = this;
}
return asOtherConstType;
}
const PolyType *
PolyType::GetBaseType() const {
return this;
}
const PolyType *
PolyType::GetAsVaryingType() const {
if (variability == Variability::Varying)
return this;
if (asVaryingType == NULL) {
asVaryingType = new PolyType(restriction, Variability::Varying,
isConst, quant);
if (variability == Variability::Uniform)
asVaryingType->asUniformType = this;
}
return asVaryingType;
}
const PolyType *
PolyType::GetAsUniformType() const {
if (variability == Variability::Uniform)
return this;
if (asUniformType == NULL) {
asUniformType = new PolyType(restriction, Variability::Uniform,
isConst, quant);
if (variability == Variability::Varying)
asUniformType->asVaryingType = this;
}
return asUniformType;
}
const std::vector<AtomicType *>::iterator
PolyType::ExpandBegin() const {
if (expandedTypes)
return expandedTypes->begin();
expandedTypes = new std::vector<AtomicType *>();
if (restriction == TYPE_INTEGER || restriction == TYPE_NUMBER) {
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_INT8, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_UINT8, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_INT16, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_UINT16, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_INT32, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_UINT32, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_INT64, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_UINT64, variability, isConst));
}
if (restriction == TYPE_FLOATING || restriction == TYPE_NUMBER) {
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_FLOAT, variability, isConst));
expandedTypes->push_back(new AtomicType(AtomicType::TYPE_DOUBLE, variability, isConst));
}
return expandedTypes->begin();
}
const std::vector<AtomicType *>::iterator
PolyType::ExpandEnd() const {
Assert(expandedTypes != NULL);
return expandedTypes->end();
}
const PolyType *
PolyType::GetAsUnboundVariabilityType() const {
if (variability == Variability::Unbound)
return this;
return new PolyType(restriction, Variability::Unbound, isConst, quant);
}
const PolyType *
PolyType::GetAsSOAType(int width) const {
if (variability == Variability(Variability::SOA, width))
return this;
return new PolyType(restriction, Variability(Variability::SOA, width),
isConst, quant);
}
const PolyType *
PolyType::ResolveUnboundVariability(Variability v) const {
Assert(v != Variability::Unbound);
if (variability != Variability::Unbound)
return this;
return new PolyType(restriction, v, isConst, quant);
}
const PolyType *
PolyType::Quantify(int q) const {
return new PolyType(restriction, variability, isConst, q);
}
bool
PolyType::CanBeType(const Type *t) const {
const PolyType *pt = CastType<PolyType>(t);
if (pt) {
return (restriction == pt->restriction ||
restriction == TYPE_NUMBER);
}
const AtomicType *at = CastType<AtomicType>(t);
if (at) {
switch (restriction) {
case TYPE_INTEGER:
return at->IsIntType();
case TYPE_FLOATING:
return at->IsFloatType();
case TYPE_NUMBER:
return at->IsIntType() || at->IsFloatType();
default:
FATAL("Unmatched case for polymorphic restriction");
}
}
// not an atomic type or polymorphic type
return false;
}
std::string
PolyType::GetString() const {
std::string ret;
if (isConst) ret += "const ";
ret += variability.GetString();
ret += " ";
switch (restriction) {
case TYPE_INTEGER: ret += "integer"; break;
case TYPE_FLOATING: ret += "floating"; break;
case TYPE_NUMBER: ret += "number"; break;
default: FATAL("Logic error in PolyType::GetString()");
}
if (quant >= 0) {
ret += "$";
ret += std::to_string(quant);
}
return ret;
}
std::string
PolyType::Mangle() const {
std::string ret;
if (isConst) ret += "C";
ret += variability.MangleString();
switch (restriction) {
case TYPE_INTEGER: ret += "Z"; break;
case TYPE_FLOATING: ret += "Q"; break;
case TYPE_NUMBER: ret += "R"; break;
default: FATAL("Logic error in PolyType::Mangle()");
}
return ret;
}
std::string
PolyType::GetCDeclaration(const std::string &name) const {
std::string ret;
if (variability == Variability::Unbound) {
Assert(m->errorCount > 0);
return ret;
}
if (isConst) ret += "const ";
switch (restriction) {
case TYPE_INTEGER: ret += "int32_t"; break;
case TYPE_FLOATING: ret += "double"; break;
case TYPE_NUMBER: ret += "double"; break;
default: FATAL("Logic error in PolyType::GetCDeclaration()");
}
if (lShouldPrintName(name)) {
ret += " ";
ret += name;
}
if (variability == Variability::SOA) {
char buf[32];
sprintf(buf, "[%d]", variability.soaWidth);
ret += buf;
}
return ret;
}
llvm::Type *
PolyType::LLVMType(llvm::LLVMContext *ctx) const {
Assert(variability.type != Variability::Unbound);
bool isUniform = (variability == Variability::Uniform);
bool isVarying = (variability == Variability::Varying);
if (isUniform || isVarying) {
switch (restriction) {
case TYPE_INTEGER:
return isUniform ? LLVMTypes::Int32Type : LLVMTypes::Int32VectorType;
case TYPE_FLOATING:
case TYPE_NUMBER:
return isUniform ? LLVMTypes::DoubleType : LLVMTypes::DoubleVectorType;
default:
FATAL("logic error in PolyType::LLVMType");
return NULL;
}
}
else {
ArrayType at(GetAsUniformType(), variability.soaWidth);
return at.LLVMType(ctx);
}
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType PolyType::GetDIType(llvm::DIDescriptor scope) const {
#else //LLVM 3.7++
llvm::DIType *PolyType::GetDIType(llvm::DIScope *scope) const {
#endif
Assert(variability.type != Variability::Unbound);
if (variability.type == Variability::Uniform) {
switch (restriction) {
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
case TYPE_INTEGER:
return m->diBuilder->createBasicType("int32", 32 /* size */, 32 /* align */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_FLOATING:
case TYPE_NUMBER:
return m->diBuilder->createBasicType("double", 64 /* size */, 64 /* align */,
llvm::dwarf::DW_ATE_float);
break;
#else // LLVM 4.0+
case TYPE_INTEGER:
return m->diBuilder->createBasicType("int32", 32 /* size */,
llvm::dwarf::DW_ATE_signed);
break;
case TYPE_FLOATING:
case TYPE_NUMBER:
return m->diBuilder->createBasicType("double", 64 /* size */,
llvm::dwarf::DW_ATE_float);
break;
#endif
default:
FATAL("unhandled basic type in PolyType::GetDIType()");
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
return llvm::DIType();
#else //LLVM 3.7+
return NULL;
#endif
}
}
else if (variability == Variability::Varying) {
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth()-1);
#elif ISPC_LLVM_VERSION > ISPC_VERSION_3_2 && ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth());
#else // LLVM 3.6+
llvm::Metadata *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth());
#endif
#if ISPC_LLVM_VERSION > ISPC_VERSION_3_2 && ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(sub);
llvm::DIType unifType = GetAsUniformType()->GetDIType(scope);
uint64_t size = unifType.getSizeInBits() * g->target->getVectorWidth();
uint64_t align = unifType.getAlignInBits() * g->target->getVectorWidth();
#else // LLVM 3.7+
llvm::DINodeArray subArray = m->diBuilder->getOrCreateArray(sub);
llvm::DIType *unifType = GetAsUniformType()->GetDIType(scope);
//llvm::DebugNodeArray subArray = m->diBuilder->getOrCreateArray(sub);
//llvm::MDType *unifType = GetAsUniformType()->GetDIType(scope);
uint64_t size = unifType->getSizeInBits() * g->target->getVectorWidth();
uint64_t align = unifType->getAlignInBits()* g->target->getVectorWidth();
#endif
return m->diBuilder->createVectorType(size, align, unifType, subArray);
}
else {
Assert(variability == Variability::SOA);
ArrayType at(GetAsUniformType(), variability.soaWidth);
return at.GetDIType(scope);
}
}
///////////////////////////////////////////////////////////////////////////
// EnumType
EnumType::EnumType(SourcePos p)
: Type(ENUM_TYPE), pos(p) {
// name = "/* (anonymous) */";
isConst = false;
variability = Variability(Variability::Unbound);
}
EnumType::EnumType(const char *n, SourcePos p)
: Type(ENUM_TYPE), pos(p), name(n) {
isConst = false;
variability = Variability(Variability::Unbound);
}
Variability
EnumType::GetVariability() const {
return variability;
}
bool
EnumType::IsBoolType() const {
return false;
}
bool
EnumType::IsFloatType() const {
return false;
}
bool
EnumType::IsIntType() const {
return true;
}
bool
EnumType::IsUnsignedType() const {
return true;
}
bool
EnumType::IsConstType() const {
return isConst;
}
const EnumType *
EnumType::GetBaseType() const {
return this;
}
const EnumType *
EnumType::GetAsUniformType() const {
if (IsUniformType())
return this;
else {
EnumType *enumType = new EnumType(*this);
enumType->variability = Variability::Uniform;
return enumType;
}
}
const EnumType *
EnumType::ResolveUnboundVariability(Variability v) const {
if (variability != Variability::Unbound)
return this;
else {
EnumType *enumType = new EnumType(*this);
enumType->variability = v;
return enumType;
}
}
const EnumType *
EnumType::GetAsVaryingType() const {
if (IsVaryingType())
return this;
else {
EnumType *enumType = new EnumType(*this);
enumType->variability = Variability(Variability::Varying);
return enumType;
}
}
const EnumType *
EnumType::GetAsUnboundVariabilityType() const {
if (HasUnboundVariability())
return this;
else {
EnumType *enumType = new EnumType(*this);
enumType->variability = Variability(Variability::Unbound);
return enumType;
}
}
const EnumType *
EnumType::GetAsSOAType(int width) const {
if (GetSOAWidth() == width)
return this;
else {
EnumType *enumType = new EnumType(*this);
enumType->variability = Variability(Variability::SOA, width);
return enumType;
}
}
const EnumType *
EnumType::GetAsConstType() const {
if (isConst)
return this;
else {
EnumType *enumType = new EnumType(*this);
enumType->isConst = true;
return enumType;
}
}
const EnumType *
EnumType::GetAsNonConstType() const {
if (!isConst)
return this;
else {
EnumType *enumType = new EnumType(*this);
enumType->isConst = false;
return enumType;
}
}
std::string
EnumType::GetString() const {
std::string ret;
if (isConst) ret += "const ";
ret += variability.GetString();
ret += " enum ";
if (name.size())
ret += name;
return ret;
}
std::string
EnumType::Mangle() const {
Assert(variability != Variability::Unbound);
std::string ret;
if (isConst) ret += "C";
ret += variability.MangleString();
// ret += std::string("enum[") + name + std::string("]");
ret += std::string("enum_5B_") + name + std::string("_5D_");
return ret;
}
std::string
EnumType::GetCDeclaration(const std::string &varName) const {
if (variability == Variability::Unbound) {
Assert(m->errorCount > 0);
return "";
}
std::string ret;
if (isConst) ret += "const ";
ret += "enum";
if (name.size())
ret += std::string(" ") + name;
if (lShouldPrintName(varName)) {
ret += " ";
ret += varName;
}
if (variability == Variability::SOA ||
variability == Variability::Varying) {
int vWidth = (variability == Variability::Varying) ?
g->target->getVectorWidth() :
variability.soaWidth;
char buf[32];
sprintf(buf, "[%d]", vWidth);
ret += buf;
}
return ret;
}
llvm::Type *
EnumType::LLVMType(llvm::LLVMContext *ctx) const {
Assert(variability != Variability::Unbound);
switch (variability.type) {
case Variability::Uniform:
return LLVMTypes::Int32Type;
case Variability::Varying:
return LLVMTypes::Int32VectorType;
case Variability::SOA: {
ArrayType at(AtomicType::UniformInt32, variability.soaWidth);
return at.LLVMType(ctx);
}
default:
FATAL("Unexpected variability in EnumType::LLVMType()");
return NULL;
}
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType EnumType::GetDIType(llvm::DIDescriptor scope) const {
#else // LLVM 3.7+
llvm::DIType *EnumType::GetDIType(llvm::DIScope *scope) const {
#endif
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
std::vector<llvm::Value *> enumeratorDescriptors;
#else // LLVM 3.6+
std::vector<llvm::Metadata *> enumeratorDescriptors;
#endif
for (unsigned int i = 0; i < enumerators.size(); ++i) {
unsigned int enumeratorValue;
Assert(enumerators[i]->constValue != NULL);
int count = enumerators[i]->constValue->GetValues(&enumeratorValue);
Assert(count == 1);
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
llvm::Value *descriptor =
#else // LLVM 3.6+
llvm::Metadata *descriptor =
#endif
m->diBuilder->createEnumerator(enumerators[i]->name, enumeratorValue);
enumeratorDescriptors.push_back(descriptor);
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIArray elementArray =
m->diBuilder->getOrCreateArray(enumeratorDescriptors);
llvm::DIFile diFile = pos.GetDIFile();
llvm::DIType diType =
m->diBuilder->createEnumerationType(diFile, name, diFile, pos.first_line,
32 /* size in bits */,
32 /* align in bits */,
elementArray, llvm::DIType());
#else // LLVM 3.7+
llvm::DINodeArray elementArray =
m->diBuilder->getOrCreateArray(enumeratorDescriptors);
llvm::DIFile *diFile = pos.GetDIFile();
llvm::DIType *diType =
m->diBuilder->createEnumerationType(diFile, name, diFile, pos.first_line,
32 /* size in bits */,
32 /* align in bits */,
elementArray, NULL);
#endif
switch (variability.type) {
case Variability::Uniform:
return diType;
case Variability::Varying: {
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth()-1);
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth());
#else // LLVM 3.6++
llvm::Metadata *sub = m->diBuilder->getOrCreateSubrange(0, g->target->getVectorWidth());
#endif
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(sub);
uint64_t size = diType.getSizeInBits() * g->target->getVectorWidth();
uint64_t align = diType.getAlignInBits() * g->target->getVectorWidth();
#elif ISPC_LLVM_VERSION >= ISPC_LLVM_3_7 // LLVM 3.7+
llvm::DINodeArray subArray = m->diBuilder->getOrCreateArray(sub);
//llvm::DebugNodeArray subArray = m->diBuilder->getOrCreateArray(sub);
uint64_t size = diType->getSizeInBits() * g->target->getVectorWidth();
uint64_t align = diType->getAlignInBits()* g->target->getVectorWidth();
#endif
return m->diBuilder->createVectorType(size, align, diType, subArray);
}
case Variability::SOA: {
return lCreateDIArray(diType, variability.soaWidth);
}
default:
FATAL("Unexpected variability in EnumType::GetDIType()");
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
return llvm::DIType();
#else //LLVM 3.7++
return NULL;
#endif
}
}
void
EnumType::SetEnumerators(const std::vector<Symbol *> &e) {
enumerators = e;
}
int
EnumType::GetEnumeratorCount() const {
return (int)enumerators.size();
}
const Symbol *
EnumType::GetEnumerator(int i) const {
return enumerators[i];
}
///////////////////////////////////////////////////////////////////////////
// PointerType
PointerType *PointerType::Void =
new PointerType(AtomicType::Void, Variability(Variability::Uniform), false);
PointerType::PointerType(const Type *t, Variability v, bool ic, bool is,
bool fr)
: Type(POINTER_TYPE), variability(v), isConst(ic), isSlice(is), isFrozen(fr) {
baseType = t;
}
PointerType *
PointerType::GetUniform(const Type *t, bool is) {
return new PointerType(t, Variability(Variability::Uniform), false, is);
}
PointerType *
PointerType::GetVarying(const Type *t) {
return new PointerType(t, Variability(Variability::Varying), false);
}
bool
PointerType::IsVoidPointer(const Type *t) {
return Type::EqualIgnoringConst(t->GetAsUniformType(),
PointerType::Void);
}
Variability
PointerType::GetVariability() const {
return variability;
}
bool
PointerType::IsBoolType() const {
return false;
}
bool
PointerType::IsFloatType() const {
return false;
}
bool
PointerType::IsIntType() const {
return false;
}
bool
PointerType::IsUnsignedType() const {
return false;
}
bool
PointerType::IsConstType() const {
return isConst;
}
const Type *
PointerType::GetBaseType() const {
return baseType;
}
const PointerType *
PointerType::GetAsVaryingType() const {
if (variability == Variability::Varying)
return this;
else
return new PointerType(baseType, Variability(Variability::Varying),
isConst, isSlice, isFrozen);
}
const PointerType *
PointerType::GetAsUniformType() const {
if (variability == Variability::Uniform)
return this;
else
return new PointerType(baseType, Variability(Variability::Uniform),
isConst, isSlice, isFrozen);
}
const PointerType *
PointerType::GetAsUnboundVariabilityType() const {
if (variability == Variability::Unbound)
return this;
else
return new PointerType(baseType, Variability(Variability::Unbound),
isConst, isSlice, isFrozen);
}
const PointerType *
PointerType::GetAsSOAType(int width) const {
if (GetSOAWidth() == width)
return this;
else
return new PointerType(baseType, Variability(Variability::SOA, width),
isConst, isSlice, isFrozen);
}
const PointerType *
PointerType::GetAsSlice() const {
if (isSlice)
return this;
return new PointerType(baseType, variability, isConst, true);
}
const PointerType *
PointerType::GetAsNonSlice() const {
if (isSlice == false)
return this;
return new PointerType(baseType, variability, isConst, false);
}
const PointerType *
PointerType::GetAsFrozenSlice() const {
if (isFrozen)
return this;
return new PointerType(baseType, variability, isConst, true, true);
}
const PointerType *
PointerType::ResolveUnboundVariability(Variability v) const {
if (baseType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
Assert(v != Variability::Unbound);
Variability ptrVariability = (variability == Variability::Unbound) ? v :
variability;
const Type *resolvedBaseType =
baseType->ResolveUnboundVariability(Variability::Uniform);
return new PointerType(resolvedBaseType, ptrVariability, isConst, isSlice,
isFrozen);
}
const PointerType *
PointerType::GetAsConstType() const {
if (isConst == true)
return this;
else
return new PointerType(baseType, variability, true, isSlice);
}
const PointerType *
PointerType::GetAsNonConstType() const {
if (isConst == false)
return this;
else
return new PointerType(baseType, variability, false, isSlice);
}
std::string
PointerType::GetString() const {
if (baseType == NULL) {
Assert(m->errorCount > 0);
return "";
}
std::string ret = baseType->GetString();
ret += std::string(" * ");
if (isConst) ret += "const ";
if (isSlice) ret += "slice ";
if (isFrozen) ret += "/*frozen*/ ";
ret += variability.GetString();
return ret;
}
std::string
PointerType::Mangle() const {
Assert(variability != Variability::Unbound);
if (baseType == NULL) {
Assert(m->errorCount > 0);
return "";
}
std::string ret = variability.MangleString() + std::string("_3C_"); // <
if (isSlice || isFrozen) ret += "-";
if (isSlice) ret += "s";
if (isFrozen) ret += "f";
if (isSlice || isFrozen) ret += "-";
return ret + baseType->Mangle() + std::string("_3E_"); // >
}
std::string
PointerType::GetCDeclaration(const std::string &name) const {
if (isSlice ||
(variability == Variability::Unbound)) {
Assert(m->errorCount > 0);
return "";
}
if (baseType == NULL) {
Assert(m->errorCount > 0);
return "";
}
std::string ret = baseType->GetCDeclaration("");
bool baseIsBasicVarying = (IsBasicType(baseType)) && (baseType->IsVaryingType());
if (baseIsBasicVarying) ret += std::string("(");
ret += std::string(" *");
if (isConst) ret += " const";
ret += std::string(" ");
ret += name;
if (baseIsBasicVarying) ret += std::string(")");
if (variability == Variability::SOA) {
char buf[32];
sprintf(buf, "[%d]", variability.soaWidth);
ret += buf;
}
if (baseIsBasicVarying) {
int vWidth = g->target->getVectorWidth();
char buf[32];
sprintf(buf, "[%d]", vWidth);
ret += buf;
}
return ret;
}
llvm::Type *
PointerType::LLVMType(llvm::LLVMContext *ctx) const {
if (baseType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
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: {
llvm::Type *ptype = NULL;
const FunctionType *ftype = CastType<FunctionType>(baseType);
if (ftype != NULL)
ptype = llvm::PointerType::get(ftype->LLVMFunctionType(ctx), 0);
else {
if (baseType->IsVoidType())
ptype = LLVMTypes::VoidPointerType;
else
ptype = llvm::PointerType::get(baseType->LLVMType(ctx), 0);
}
return ptype;
}
case Variability::Varying:
// always the same, since we currently use int vectors for varying
// pointers
return LLVMTypes::VoidPointerVectorType;
case Variability::SOA: {
ArrayType at(GetAsUniformType(), variability.soaWidth);
return at.LLVMType(ctx);
}
default:
FATAL("Unexpected variability in PointerType::LLVMType()");
return NULL;
}
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType PointerType::GetDIType(llvm::DIDescriptor scope) const {
if (baseType == NULL) {
Assert(m->errorCount > 0);
return llvm::DIType();
}
llvm::DIType diTargetType = baseType->GetDIType(scope);
#else //LLVM 3.7++
llvm::DIType *PointerType::GetDIType(llvm::DIScope *scope) const {
if (baseType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvm::DIType *diTargetType = baseType->GetDIType(scope);
#endif
int bitsSize = g->target->is32Bit() ? 32 : 64;
int ptrAlignBits = bitsSize;
switch (variability.type) {
case Variability::Uniform:
return m->diBuilder->createPointerType(diTargetType, bitsSize,
ptrAlignBits);
case Variability::Varying: {
// emit them as an array of pointers
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType eltType =
#else //LLVM 3.7++
llvm::DIDerivedType *eltType =
#endif
m->diBuilder->createPointerType(diTargetType, bitsSize, ptrAlignBits);
return lCreateDIArray(eltType, g->target->getVectorWidth());
}
case Variability::SOA: {
ArrayType at(GetAsUniformType(), variability.soaWidth);
return at.GetDIType(scope);
}
default:
FATAL("Unexpected variability in PointerType::GetDIType()");
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
return llvm::DIType();
#else //LLVM 3.7++
return NULL;
#endif
}
}
///////////////////////////////////////////////////////////////////////////
// SequentialType
const Type *SequentialType::GetElementType(int index) const {
return GetElementType();
}
///////////////////////////////////////////////////////////////////////////
// ArrayType
ArrayType::ArrayType(const Type *c, int a)
: SequentialType(ARRAY_TYPE), child(c), numElements(a) {
// 0 -> unsized array.
Assert(numElements >= 0);
Assert(c->IsVoidType() == false);
}
llvm::ArrayType *
ArrayType::LLVMType(llvm::LLVMContext *ctx) const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvm::Type *ct = child->LLVMType(ctx);
if (ct == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return llvm::ArrayType::get(ct, numElements);
}
Variability
ArrayType::GetVariability() const {
return child ? child->GetVariability() : Variability(Variability::Uniform);
}
bool
ArrayType::IsFloatType() const {
return false;
}
bool
ArrayType::IsIntType() const {
return false;
}
bool
ArrayType::IsUnsignedType() const {
return false;
}
bool
ArrayType::IsBoolType() const {
return false;
}
bool
ArrayType::IsConstType() const {
return child ? child->IsConstType() : false;
}
const Type *
ArrayType::GetBaseType() const {
const Type *type = child;
const ArrayType *at = CastType<ArrayType>(type);
// Keep walking until we reach a child that isn't itself an array
while (at) {
type = at->child;
at = CastType<ArrayType>(type);
}
return type;
}
const ArrayType *
ArrayType::GetAsVaryingType() const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->GetAsVaryingType(), numElements);
}
const ArrayType *
ArrayType::GetAsUniformType() const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->GetAsUniformType(), numElements);
}
const ArrayType *
ArrayType::GetAsUnboundVariabilityType() const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->GetAsUnboundVariabilityType(), numElements);
}
const ArrayType *
ArrayType::GetAsSOAType(int width) const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->GetAsSOAType(width), numElements);
}
const ArrayType *
ArrayType::ResolveUnboundVariability(Variability v) const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->ResolveUnboundVariability(v), numElements);
}
const ArrayType *
ArrayType::GetAsUnsignedType() const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->GetAsUnsignedType(), numElements);
}
const ArrayType *
ArrayType::GetAsConstType() const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->GetAsConstType(), numElements);
}
const ArrayType *
ArrayType::GetAsNonConstType() const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ArrayType(child->GetAsNonConstType(), numElements);
}
int
ArrayType::GetElementCount() const {
return numElements;
}
const Type *
ArrayType::GetElementType() const {
return child;
}
std::string
ArrayType::GetString() const {
const Type *base = GetBaseType();
if (base == NULL) {
Assert(m->errorCount > 0);
return "";
}
std::string s = base->GetString();
const ArrayType *at = this;
// Walk through this and any children arrays and print all of their
// dimensions
while (at) {
char buf[16];
if (at->numElements > 0)
sprintf(buf, "%d", at->numElements);
else
buf[0] = '\0';
s += std::string("[") + std::string(buf) + std::string("]");
at = CastType<ArrayType>(at->child);
}
return s;
}
std::string
ArrayType::Mangle() const {
if (child == NULL) {
Assert(m->errorCount > 0);
return "(error)";
}
std::string s = child->Mangle();
char buf[16];
if (numElements > 0)
sprintf(buf, "%d", numElements);
else
buf[0] = '\0';
// return s + "[" + buf + "]";
return s + "_5B_" + buf + "_5D_";
}
std::string
ArrayType::GetCDeclaration(const std::string &name) const {
const Type *base = GetBaseType();
if (base == NULL) {
Assert(m->errorCount > 0);
return "";
}
int soaWidth = base->GetSOAWidth();
int vWidth = (base->IsVaryingType()) ? g->target->getVectorWidth() : 0;
base = base->GetAsUniformType();
std::string s = base->GetCDeclaration(name);
const ArrayType *at = this;
while (at) {
char buf[16];
if (at->numElements > 0)
sprintf(buf, "%d", at->numElements);
else
buf[0] = '\0';
s += std::string("[") + std::string(buf) + std::string("]");
at = CastType<ArrayType>(at->child);
}
if (soaWidth > 0) {
char buf[16];
sprintf(buf, "[%d]", soaWidth);
s += buf;
}
if (vWidth > 0) {
char buf[16];
sprintf(buf, "[%d]", vWidth);
s += buf;
}
return s;
}
int
ArrayType::TotalElementCount() const {
const ArrayType *ct = CastType<ArrayType>(child);
if (ct != NULL)
return numElements * ct->TotalElementCount();
else
return numElements;
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType ArrayType::GetDIType(llvm::DIDescriptor scope) const {
if (child == NULL) {
Assert(m->errorCount > 0);
return llvm::DIType();
}
llvm::DIType eltType = child->GetDIType(scope);
#else //LLVM 3.7++
llvm::DIType *ArrayType::GetDIType(llvm::DIScope *scope) const {
if (child == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvm::DIType *eltType = child->GetDIType(scope);
#endif
return lCreateDIArray(eltType, numElements);
}
ArrayType *
ArrayType::GetSizedArray(int sz) const {
Assert(numElements == 0);
return new ArrayType(child, sz);
}
const Type *
ArrayType::SizeUnsizedArrays(const Type *type, Expr *initExpr) {
const ArrayType *at = CastType<ArrayType>(type);
if (at == NULL)
return type;
ExprList *exprList = llvm::dyn_cast_or_null<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());
at = CastType<ArrayType>(type);
}
// 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 = llvm::dyn_cast_or_null<ExprList>(exprList->exprs[0]);
if (nextList == NULL)
return type;
const Type *nextType = at->GetElementType();
const ArrayType *nextArrayType = CastType<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 = llvm::dyn_cast_or_null<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),
at->GetElementCount());
}
///////////////////////////////////////////////////////////////////////////
// VectorType
VectorType::VectorType(const AtomicType *b, int a)
: SequentialType(VECTOR_TYPE), base(b), numElements(a) {
Assert(numElements > 0);
Assert(base != NULL);
}
Variability
VectorType::GetVariability() const {
return base->GetVariability();
}
bool
VectorType::IsFloatType() const {
return base->IsFloatType();
}
bool
VectorType::IsIntType() const {
return base->IsIntType();
}
bool
VectorType::IsUnsignedType() const {
return base->IsUnsignedType();
}
bool
VectorType::IsBoolType() const {
return base->IsBoolType();
}
bool
VectorType::IsConstType() const {
return base->IsConstType();
}
const Type *
VectorType::GetBaseType() const {
return base;
}
const VectorType *
VectorType::GetAsVaryingType() const {
return new VectorType(base->GetAsVaryingType(), numElements);
}
const VectorType *
VectorType::GetAsUniformType() const {
return new VectorType(base->GetAsUniformType(), numElements);
}
const VectorType *
VectorType::GetAsUnboundVariabilityType() const {
return new VectorType(base->GetAsUnboundVariabilityType(), numElements);
}
const VectorType *
VectorType::GetAsSOAType(int width) const {
return new VectorType(base->GetAsSOAType(width), numElements);
}
const VectorType *
VectorType::ResolveUnboundVariability(Variability v) const {
return new VectorType(base->ResolveUnboundVariability(v), numElements);
}
const VectorType *
VectorType::GetAsConstType() const {
return new VectorType(base->GetAsConstType(), numElements);
}
const VectorType *
VectorType::GetAsNonConstType() const {
return new VectorType(base->GetAsNonConstType(), numElements);
}
std::string
VectorType::GetString() const {
std::string s = base->GetString();
char buf[16];
sprintf(buf, "<%d>", numElements);
return s + std::string(buf);
}
std::string
VectorType::Mangle() const {
std::string s = base->Mangle();
char buf[16];
sprintf(buf, "_3C_%d_3E_", numElements); // "<%d>"
return s + std::string(buf);
}
std::string
VectorType::GetCDeclaration(const std::string &name) const {
std::string s = base->GetCDeclaration("");
char buf[16];
sprintf(buf, "%d", numElements);
return s + std::string(buf) + " " + name;
}
int
VectorType::GetElementCount() const {
return numElements;
}
const AtomicType *
VectorType::GetElementType() const {
return base;
}
llvm::Type *
VectorType::LLVMType(llvm::LLVMContext *ctx) const {
if (base == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvm::Type *bt = base->LLVMType(ctx);
if (!bt)
return NULL;
if (base->IsUniformType())
// vectors of uniform types are laid out across LLVM vectors, with
// the llvm vector size set to be a multiple of the machine's
// natural vector size (e.g. 4 on SSE). This is a roundabout way
// of ensuring that LLVM lays them out into machine vector
// registers so that e.g. if we want to add two uniform 4 float
// vectors, that is turned into a single addps on SSE.
return llvm::VectorType::get(bt, getVectorMemoryCount());
else if (base->IsVaryingType())
// varying types are already laid out to fill HW vector registers,
// so a vector type here is just expanded out as an llvm array.
return llvm::ArrayType::get(bt, getVectorMemoryCount());
else if (base->IsSOAType())
return llvm::ArrayType::get(bt, numElements);
else {
FATAL("Unexpected variability in VectorType::LLVMType()");
return NULL;
}
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType VectorType::GetDIType(llvm::DIDescriptor scope) const {
llvm::DIType eltType = base->GetDIType(scope);
#else //LLVM 3.7++
llvm::DIType *VectorType::GetDIType(llvm::DIScope *scope) const {
llvm::DIType *eltType = base->GetDIType(scope);
#endif
#if ISPC_LLVM_VERSION == ISPC_LLVM_3_2
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, numElements-1);
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
llvm::Value *sub = m->diBuilder->getOrCreateSubrange(0, numElements);
#else // LLVM 3.6++
llvm::Metadata *sub = m->diBuilder->getOrCreateSubrange(0, numElements);
#endif
// vectors of varying types are already naturally aligned to the
// machine's vector width, but arrays of uniform types need to be
// explicitly aligned to the machines natural vector alignment.
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIArray subArray = m->diBuilder->getOrCreateArray(sub);
uint64_t sizeBits = eltType.getSizeInBits() * numElements;
uint64_t align = eltType.getAlignInBits();
#else // LLVM 3.7++
llvm::DINodeArray subArray = m->diBuilder->getOrCreateArray(sub);
uint64_t sizeBits = eltType->getSizeInBits() * numElements;
uint64_t align = eltType->getAlignInBits();
#endif
if (IsUniformType())
align = 4 * g->target->getNativeVectorWidth();
if (IsUniformType() || IsVaryingType())
return m->diBuilder->createVectorType(sizeBits, align, eltType, subArray);
else if (IsSOAType()) {
ArrayType at(base, numElements);
return at.GetDIType(scope);
}
else {
FATAL("Unexpected variability in VectorType::GetDIType()");
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
return llvm::DIType();
#else // LLVM 3.7++
return NULL;
#endif
}
}
int
VectorType::getVectorMemoryCount() const {
if (base->IsVaryingType())
return numElements;
else if (base->IsUniformType()) {
int nativeWidth = g->target->getNativeVectorWidth();
if (Type::Equal(base->GetAsUniformType(), AtomicType::UniformInt64) ||
Type::Equal(base->GetAsUniformType(), AtomicType::UniformUInt64) ||
Type::Equal(base->GetAsUniformType(), AtomicType::UniformDouble))
// target.getNativeVectorWidth() should be in terms of 32-bit
// values, so for the 64-bit guys, it takes half as many of
// them to fill the native width
nativeWidth /= 2;
// and now round up the element count to be a multiple of
// nativeWidth
return (numElements + (nativeWidth - 1)) & ~(nativeWidth-1);
}
else if (base->IsSOAType()) {
FATAL("VectorType SOA getVectorMemoryCount");
return -1;
}
else {
FATAL("Unexpected variability in VectorType::getVectorMemoryCount()");
return -1;
}
}
///////////////////////////////////////////////////////////////////////////
// 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->getVectorWidth());
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 variability in lMangleStructName()");
}
// And stuff the name at the end....
n += name;
return n;
}
StructType::StructType(const std::string &n, const llvm::SmallVector<const Type *, 8> &elts,
const llvm::SmallVector<std::string, 8> &en,
const llvm::SmallVector<SourcePos, 8> &ep,
bool ic, Variability v, SourcePos p)
: CollectionType(STRUCT_TYPE), name(n), elementTypes(elts), elementNames(en),
elementPositions(ep), variability(v), isConst(ic), pos(p) {
oppositeConstStructType = NULL;
finalElementTypes.resize(elts.size(), NULL);
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;
int nElements = GetElementCount();
if (nElements == 0) {
elementTypes.push_back(LLVMTypes::Int8Type);
}
else {
for (int i = 0; i < nElements; ++i) {
const Type *type = GetElementType(i);
if (type == NULL) {
Assert(m->errorCount > 0);
return;
}
else if (CastType<FunctionType>(type) != NULL) {
Error(elementPositions[i], "Method declarations are not "
"supported.");
return;
}
else
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);
}
}
}
const std::string
StructType::GetCStructName() const {
// only return mangled name for varying structs for backwards
// compatibility...
if (variability == Variability::Varying) {
return lMangleStructName(name, variability);
}
else {
return GetStructName();
}
}
Variability
StructType::GetVariability() const {
return variability;
}
bool
StructType::IsBoolType() const {
return false;
}
bool
StructType::IsFloatType() const {
return false;
}
bool
StructType::IsIntType() const {
return false;
}
bool
StructType::IsUnsignedType() const {
return false;
}
bool
StructType::IsConstType() const {
return isConst;
}
bool
StructType::IsDefined() const {
for (int i = 0; i < GetElementCount(); i++) {
const Type *t = GetElementType(i);
const UndefinedStructType *ust = CastType<UndefinedStructType>(t);
if (ust != NULL) {
return false;
}
const StructType *st = CastType<StructType>(t);
if (st != NULL) {
if (!st->IsDefined()) {
return false;
}
}
}
return true;
}
const Type *
StructType::GetBaseType() const {
return this;
}
const StructType *
StructType::GetAsVaryingType() const {
if (IsVaryingType())
return this;
else
return new StructType(name, elementTypes, elementNames, elementPositions,
isConst, Variability(Variability::Varying), pos);
}
const StructType *
StructType::GetAsUniformType() const {
if (IsUniformType())
return this;
else
return new StructType(name, elementTypes, elementNames, elementPositions,
isConst, Variability(Variability::Uniform), pos);
}
const StructType *
StructType::GetAsUnboundVariabilityType() const {
if (HasUnboundVariability())
return this;
else
return new StructType(name, elementTypes, elementNames, elementPositions,
isConst, Variability(Variability::Unbound), pos);
}
const StructType *
StructType::GetAsSOAType(int width) const {
if (GetSOAWidth() == width)
return this;
if (checkIfCanBeSOA(this) == false)
return NULL;
return new StructType(name, elementTypes, elementNames, elementPositions,
isConst, Variability(Variability::SOA, width), pos);
}
const StructType *
StructType::ResolveUnboundVariability(Variability v) const {
Assert(v != Variability::Unbound);
if (variability != Variability::Unbound)
return this;
// We don't resolve the members here but leave them unbound, so that if
// resolve to varying but later want to get the uniform version of this
// type, for example, then we still have the information around about
// which element types were originally unbound...
return new StructType(name, elementTypes, elementNames, elementPositions,
isConst, v, pos);
}
const StructType *
StructType::GetAsConstType() const {
if (isConst == true)
return this;
else if (oppositeConstStructType != NULL)
return oppositeConstStructType;
else {
oppositeConstStructType =
new StructType(name, elementTypes, elementNames, elementPositions,
true, variability, pos);
oppositeConstStructType->oppositeConstStructType = this;
return oppositeConstStructType;
}
}
const StructType *
StructType::GetAsNonConstType() const {
if (isConst == false)
return this;
else if (oppositeConstStructType != NULL)
return oppositeConstStructType;
else {
oppositeConstStructType =
new StructType(name, elementTypes, elementNames, elementPositions,
false, variability, pos);
oppositeConstStructType->oppositeConstStructType = this;
return oppositeConstStructType;
}
}
std::string
StructType::GetString() const {
std::string ret;
if (isConst)
ret += "const ";
ret += variability.GetString();
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 += "}";
}
else {
ret += "struct ";
ret += name;
}
return ret;
}
/** 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;
// ret += "s[";
ret += "s_5B_";
if (isConst)
ret += "_c_";
ret += variability.MangleString();
// ret += name + std::string("]");
ret += name + std::string("_5D_");
return ret;
}
std::string
StructType::Mangle() const {
return lMangleStruct(variability, isConst, name);
}
std::string
StructType::GetCDeclaration(const std::string &n) const {
std::string ret;
if (isConst) ret += "const ";
ret += std::string("struct ") + GetCStructName();
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;
}
}
return ret;
}
llvm::Type *
StructType::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];
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType StructType::GetDIType(llvm::DIDescriptor scope) const {
#else //LLVM 3.7++
llvm::DIType *StructType::GetDIType(llvm::DIScope *scope) const {
#endif
uint64_t currentSize = 0, align = 0;
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
std::vector<llvm::Value *> elementLLVMTypes;
#else // LLVM 3.6++
std::vector<llvm::Metadata *> elementLLVMTypes;
#endif
// Walk through the elements of the struct; for each one figure out its
// alignment and size, using that to figure out its offset w.r.t. the
// start of the structure.
for (unsigned int i = 0; i < elementTypes.size(); ++i) {
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType eltType = GetElementType(i)->GetDIType(scope);
uint64_t eltAlign = eltType.getAlignInBits();
uint64_t eltSize = eltType.getSizeInBits();
#else // LLVM 3.7++
llvm::DIType *eltType = GetElementType(i)->GetDIType(scope);
uint64_t eltAlign = eltType->getAlignInBits();
uint64_t eltSize = eltType->getSizeInBits();
#endif
Assert(eltAlign != 0);
// The alignment for the entire structure is the maximum of the
// required alignments of its elements
align = std::max(align, eltAlign);
// Move the current size forward if needed so that the current
// element starts at an offset that's the correct alignment.
if (currentSize > 0 && (currentSize % eltAlign))
currentSize += eltAlign - (currentSize % eltAlign);
Assert((currentSize == 0) || (currentSize % eltAlign) == 0);
int line = elementPositions[i].first_line;
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIFile diFile = elementPositions[i].GetDIFile();
llvm::DIType fieldType =
#else // LLVM 3.7++
llvm::DIFile *diFile = elementPositions[i].GetDIFile();
llvm::DIDerivedType *fieldType =
#endif
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
m->diBuilder->createMemberType(scope, elementNames[i], diFile,
line, eltSize, eltAlign,
currentSize, 0, eltType);
#else // LLVM 4.0+
m->diBuilder->createMemberType(scope, elementNames[i], diFile,
line, eltSize, eltAlign,
currentSize, llvm::DINode::FlagZero, eltType);
#endif
elementLLVMTypes.push_back(fieldType);
currentSize += eltSize;
}
// Round up the struct's entire size so that it's a multiple of the
// required alignment that we figured out along the way...
if (currentSize > 0 && (currentSize % align))
currentSize += align - (currentSize % align);
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIArray elements = m->diBuilder->getOrCreateArray(elementLLVMTypes);
llvm::DIFile diFile = pos.GetDIFile();
#else // LLVM 3.7++
llvm::DINodeArray elements = m->diBuilder->getOrCreateArray(elementLLVMTypes);
llvm::DIFile *diFile = pos.GetDIFile();
#endif
return m->diBuilder->createStructType(
diFile,
name,
diFile,
pos.first_line, // Line number
currentSize, // Size in bits
align, // Alignment in bits
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
0, // Flags
#else // LLVM 4.0+
llvm::DINode::FlagZero, // Flags
#endif
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_3 && ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType(), // DerivedFrom
#elif ISPC_LLVM_VERSION >= ISPC_LLVM_3_7 // LLVM 3.7++
NULL,
#endif
elements);
}
const Type *
StructType::GetElementType(int i) const {
Assert(variability != Variability::Unbound);
Assert(i < (int)elementTypes.size());
if (finalElementTypes[i] == NULL) {
const Type *type = elementTypes[i];
if (type == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
// If the element has unbound variability, resolve its variability to
// the struct type's variability
type = type ->ResolveUnboundVariability(variability);
if (isConst)
type = type->GetAsConstType();
finalElementTypes[i] = type;
}
return finalElementTypes[i];
}
const Type *
StructType::GetElementType(const std::string &n) const {
for (unsigned int i = 0; i < elementNames.size(); ++i)
if (elementNames[i] == n)
return GetElementType(i);
return NULL;
}
int
StructType::GetElementNumber(const std::string &n) const {
for (unsigned int i = 0; i < elementNames.size(); ++i)
if (elementNames[i] == n)
return i;
return -1;
}
bool
StructType::checkIfCanBeSOA(const StructType *st) {
bool ok = true;
for (int i = 0; i < (int)st->elementTypes.size(); ++i) {
const Type *eltType = st->elementTypes[i];
const StructType *childStructType = CastType<StructType>(eltType);
if (childStructType != NULL)
ok &= checkIfCanBeSOA(childStructType);
else if (eltType->HasUnboundVariability() == false) {
Error(st->elementPositions[i], "Unable to apply SOA conversion to "
"struct due to \"%s\" member \"%s\" with bound \"%s\" "
"variability.", eltType->GetString().c_str(),
st->elementNames[i].c_str(),
eltType->IsUniformType() ? "uniform" : "varying");
ok = false;
}
else if (CastType<ReferenceType>(eltType)) {
Error(st->elementPositions[i], "Unable to apply SOA conversion to "
"struct due to member \"%s\" with reference type \"%s\".",
st->elementNames[i].c_str(), eltType->GetString().c_str());
ok = false;
}
}
return ok;
}
///////////////////////////////////////////////////////////////////////////
// UndefinedStructType
UndefinedStructType::UndefinedStructType(const std::string &n,
const Variability var, bool ic,
SourcePos p)
: Type(UNDEFINED_STRUCT_TYPE), 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];
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType UndefinedStructType::GetDIType(llvm::DIDescriptor scope) const {
llvm::DIFile diFile = pos.GetDIFile();
llvm::DIArray elements;
#else //LLVM 3.7++
llvm::DIType *UndefinedStructType::GetDIType(llvm::DIScope *scope) const {
llvm::DIFile *diFile = pos.GetDIFile();
llvm::DINodeArray elements;
#endif
return m->diBuilder->createStructType(
diFile,
name,
diFile,
pos.first_line, // Line number
0, // Size
0, // Align
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_9
0, // Flags
#else // LLVM 4.0+
llvm::DINode::FlagZero, // Flags
#endif
#if ISPC_LLVM_VERSION >= ISPC_LLVM_3_3 && ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType(), // DerivedFrom
#elif ISPC_LLVM_VERSION >= ISPC_LLVM_3_7 // LLVM 3.7+
NULL,
#endif
elements);
}
///////////////////////////////////////////////////////////////////////////
// ReferenceType
ReferenceType::ReferenceType(const Type *t)
: Type(REFERENCE_TYPE), targetType(t) {
asOtherConstType = NULL;
}
Variability
ReferenceType::GetVariability() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return Variability(Variability::Unbound);
}
return targetType->GetVariability();
}
bool
ReferenceType::IsBoolType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return false;
}
return targetType->IsBoolType();
}
bool
ReferenceType::IsFloatType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return false;
}
return targetType->IsFloatType();
}
bool
ReferenceType::IsIntType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return false;
}
return targetType->IsIntType();
}
bool
ReferenceType::IsUnsignedType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return false;
}
return targetType->IsUnsignedType();
}
bool
ReferenceType::IsConstType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return false;
}
return targetType->IsConstType();
}
const Type *
ReferenceType::GetReferenceTarget() const {
return targetType;
}
const Type *
ReferenceType::GetBaseType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return targetType->GetBaseType();
}
const ReferenceType *
ReferenceType::GetAsVaryingType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (IsVaryingType())
return this;
return new ReferenceType(targetType->GetAsVaryingType());
}
const ReferenceType *
ReferenceType::GetAsUniformType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (IsUniformType())
return this;
return new ReferenceType(targetType->GetAsUniformType());
}
const ReferenceType *
ReferenceType::GetAsUnboundVariabilityType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (HasUnboundVariability())
return this;
return new ReferenceType(targetType->GetAsUnboundVariabilityType());
}
const Type *
ReferenceType::GetAsSOAType(int width) const {
// FIXME: is this right?
return new ArrayType(this, width);
}
const ReferenceType *
ReferenceType::ResolveUnboundVariability(Variability v) const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return new ReferenceType(targetType->ResolveUnboundVariability(v));
}
const ReferenceType *
ReferenceType::GetAsConstType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (IsConstType())
return this;
if (asOtherConstType == NULL) {
asOtherConstType = new ReferenceType(targetType->GetAsConstType());
asOtherConstType->asOtherConstType = this;
}
return asOtherConstType;
}
const ReferenceType *
ReferenceType::GetAsNonConstType() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
if (!IsConstType())
return this;
if (asOtherConstType == NULL) {
asOtherConstType = new ReferenceType(targetType->GetAsNonConstType());
asOtherConstType->asOtherConstType = this;
}
return asOtherConstType;
}
std::string
ReferenceType::GetString() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return "";
}
std::string ret = targetType->GetString();
ret += std::string(" &");
return ret;
}
std::string
ReferenceType::Mangle() const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return "";
}
std::string ret;
ret += std::string("REF") + targetType->Mangle();
return ret;
}
std::string
ReferenceType::GetCDeclaration(const std::string &name) const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return "";
}
const ArrayType *at = CastType<ArrayType>(targetType);
if (at != NULL) {
if (at->GetElementCount() == 0) {
// emit unsized arrays as pointers to the base type..
std::string ret;
ret += at->GetElementType()->GetAsNonConstType()->GetCDeclaration("") +
std::string(" *");
if (lShouldPrintName(name))
ret += name;
return ret;
}
else
// otherwise forget about the reference part if it's an
// array since C already passes arrays by reference...
return targetType->GetCDeclaration(name);
}
else {
std::string ret;
ret += targetType->GetCDeclaration("") + std::string(" &");
if (lShouldPrintName(name))
ret += name;
return ret;
}
}
llvm::Type *
ReferenceType::LLVMType(llvm::LLVMContext *ctx) const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvm::Type *t = targetType->LLVMType(ctx);
if (t == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
return llvm::PointerType::get(t, 0);
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType ReferenceType::GetDIType(llvm::DIDescriptor scope) const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return llvm::DIType();
}
llvm::DIType diTargetType = targetType->GetDIType(scope);
#else //LLVM 3.7++
llvm::DIType *ReferenceType::GetDIType(llvm::DIScope *scope) const {
if (targetType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvm::DIType *diTargetType = targetType->GetDIType(scope);
#endif
return m->diBuilder->createReferenceType(llvm::dwarf::DW_TAG_reference_type,
diTargetType);
}
///////////////////////////////////////////////////////////////////////////
// FunctionType
FunctionType::FunctionType(const Type *r,
const llvm::SmallVector<const Type *, 8> &a,
SourcePos p)
: Type(FUNCTION_TYPE), isTask(false), isExported(false), isExternC(false),
isUnmasked(false), returnType(r), paramTypes(a),
paramNames(llvm::SmallVector<std::string, 8>(a.size(), "")),
paramDefaults(llvm::SmallVector<Expr *, 8>(a.size(), NULL)),
paramPositions(llvm::SmallVector<SourcePos, 8>(a.size(), p)) {
Assert(returnType != NULL);
isSafe = false;
costOverride = -1;
}
FunctionType::FunctionType(const Type *r,
const llvm::SmallVector<const Type *, 8> &a,
const llvm::SmallVector<std::string, 8> &an,
const llvm::SmallVector<Expr *, 8> &ad,
const llvm::SmallVector<SourcePos, 8> &ap,
bool it, bool is, bool ec, bool ium)
: Type(FUNCTION_TYPE), isTask(it), isExported(is), isExternC(ec),
isUnmasked(ium), 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);
isSafe = false;
costOverride = -1;
}
Variability
FunctionType::GetVariability() const {
return Variability(Variability::Uniform);
}
bool
FunctionType::IsFloatType() const {
return false;
}
bool
FunctionType::IsIntType() const {
return false;
}
bool
FunctionType::IsBoolType() const {
return false;
}
bool
FunctionType::IsUnsignedType() const {
return false;
}
bool
FunctionType::IsConstType() const {
return false;
}
const Type *
FunctionType::GetBaseType() const {
FATAL("FunctionType::GetBaseType() shouldn't be called");
return NULL;
}
const Type *
FunctionType::GetAsVaryingType() const {
FATAL("FunctionType::GetAsVaryingType shouldn't be called");
return NULL;
}
const Type *
FunctionType::GetAsUniformType() const {
FATAL("FunctionType::GetAsUniformType shouldn't be called");
return NULL;
}
const Type *
FunctionType::GetAsUnboundVariabilityType() const {
FATAL("FunctionType::GetAsUnboundVariabilityType shouldn't be called");
return NULL;
}
const Type *
FunctionType::GetAsSOAType(int width) const {
FATAL("FunctionType::GetAsSOAType() shouldn't be called");
return NULL;
}
const FunctionType *
FunctionType::ResolveUnboundVariability(Variability v) const {
if (returnType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
const Type *rt = returnType->ResolveUnboundVariability(v);
llvm::SmallVector<const Type *, 8> pt;
for (unsigned int i = 0; i < paramTypes.size(); ++i) {
if (paramTypes[i] == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
pt.push_back(paramTypes[i]->ResolveUnboundVariability(v));
}
FunctionType *ret = new FunctionType(rt, pt, paramNames, paramDefaults,
paramPositions, isTask, isExported,
isExternC, isUnmasked);
ret->isSafe = isSafe;
ret->costOverride = costOverride;
return ret;
}
const Type *
FunctionType::GetAsConstType() const {
return this;
}
const Type *
FunctionType::GetAsNonConstType() const {
return this;
}
std::string
FunctionType::GetString() const {
std::string ret = GetReturnTypeString();
ret += "(";
for (unsigned int i = 0; i < paramTypes.size(); ++i) {
if (paramTypes[i] == NULL)
ret += "/* ERROR */";
else
ret += paramTypes[i]->GetString();
if (i != paramTypes.size() - 1)
ret += ", ";
}
ret += ")";
return ret;
}
std::string
FunctionType::Mangle() const {
std::string ret = "___";
if (isUnmasked)
ret += "UM_";
for (unsigned int i = 0; i < paramTypes.size(); ++i)
if (paramTypes[i] == NULL)
Assert(m->errorCount > 0);
else
ret += paramTypes[i]->Mangle();
return ret;
}
std::string
FunctionType::GetCDeclaration(const std::string &fname) const {
std::string ret;
ret += returnType->GetCDeclaration("");
ret += " ";
ret += fname;
ret += "(";
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 = CastType<PointerType>(type);
if (pt != NULL &&
CastType<ArrayType>(pt->GetBaseType()) != NULL) {
type = new ArrayType(pt->GetBaseType(), 0);
}
if (paramNames[i] != "")
ret += type->GetCDeclaration(paramNames[i]);
else
ret += type->GetString();
if (i != paramTypes.size() - 1)
ret += ", ";
}
ret += ")";
return ret;
}
std::string
FunctionType::GetCCall(const std::string &fname) const {
std::string ret;
ret += fname;
ret += "(";
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 = CastType<PointerType>(type);
if (pt != NULL &&
CastType<ArrayType>(pt->GetBaseType()) != NULL) {
type = new ArrayType(pt->GetBaseType(), 0);
}
if (paramNames[i] != "")
ret += paramNames[i];
else
FATAL("Exporting a polymorphic function with incomplete arguments");
if (i != paramTypes.size() - 1)
ret += ", ";
}
ret += ")";
return ret;
}
std::string
FunctionType::GetCDeclarationForDispatch(const std::string &fname) const {
std::string ret;
ret += returnType->GetCDeclaration("");
ret += " ";
ret += fname;
ret += "(";
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 = CastType<PointerType>(type);
if (pt != NULL &&
CastType<ArrayType>(pt->GetBaseType()) != NULL) {
type = new ArrayType(pt->GetBaseType(), 0);
}
// Change pointers to varying thingies to void *
if (pt != NULL && pt->GetBaseType()->IsVaryingType()) {
PointerType *t = PointerType::Void;
if (paramNames[i] != "")
ret += t->GetCDeclaration(paramNames[i]);
else
ret += t->GetString();
}
else {
if (paramNames[i] != "")
ret += type->GetCDeclaration(paramNames[i]);
else
ret += type->GetString();
}
if (i != paramTypes.size() - 1)
ret += ", ";
}
ret += ")";
return ret;
}
llvm::Type *
FunctionType::LLVMType(llvm::LLVMContext *ctx) const {
FATAL("FunctionType::LLVMType() shouldn't be called");
return NULL;
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
llvm::DIType FunctionType::GetDIType(llvm::DIDescriptor scope) const {
#else //LLVM 3.7++
llvm::DIType *FunctionType::GetDIType(llvm::DIScope *scope) const {
#endif
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
std::vector<llvm::Value *> retArgTypes;
#else // LLVM 3.6++
std::vector<llvm::Metadata *> retArgTypes;
#endif
retArgTypes.push_back(returnType->GetDIType(scope));
for (int i = 0; i < GetNumParameters(); ++i) {
const Type *t = GetParameterType(i);
if (t == NULL)
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_3
return llvm::DIType();
#elif ISPC_LLVM_VERSION <= ISPC_LLVM_3_6
return llvm::DICompositeType();
#else // LLVM 3.7++
return NULL;
#endif
retArgTypes.push_back(t->GetDIType(scope));
}
#if ISPC_LLVM_VERSION <= ISPC_LLVM_3_5
llvm::DIArray retArgTypesArray =
m->diBuilder->getOrCreateArray(llvm::ArrayRef<llvm::Value *>(retArgTypes));
llvm::DIType diType =
// FIXME: DIFile
m->diBuilder->createSubroutineType(llvm::DIFile(), retArgTypesArray);
#elif ISPC_LLVM_VERSION == ISPC_LLVM_3_6
llvm::DITypeArray retArgTypesArray =
m->diBuilder->getOrCreateTypeArray(retArgTypes);
llvm::DIType diType =
// FIXME: DIFile
m->diBuilder->createSubroutineType(llvm::DIFile(), retArgTypesArray);
#elif ISPC_LLVM_VERSION == ISPC_LLVM_3_7 // LLVM 3.7
llvm::DITypeRefArray retArgTypesArray =
m->diBuilder->getOrCreateTypeArray(retArgTypes);
llvm::DIType *diType =
m->diBuilder->createSubroutineType(NULL, retArgTypesArray);
#else // LLVM 3.8+
llvm::DITypeRefArray retArgTypesArray =
m->diBuilder->getOrCreateTypeArray(retArgTypes);
llvm::DIType *diType =
m->diBuilder->createSubroutineType(retArgTypesArray);
#endif
return diType;
}
const std::string
FunctionType::GetReturnTypeString() const {
if (returnType == NULL)
return "/* ERROR */";
std::string ret;
if (isTask)
ret += "task ";
if (isExported)
ret += "export ";
if (isExternC)
ret += "extern \"C\" ";
if (isUnmasked)
ret += "unmasked ";
if (isSafe)
ret += "/*safe*/ ";
if (costOverride > 0) {
char buf[32];
sprintf(buf, "/*cost=%d*/ ", costOverride);
ret += buf;
}
return ret + returnType->GetString();
}
llvm::FunctionType *
FunctionType::LLVMFunctionType(llvm::LLVMContext *ctx, bool removeMask) const {
if (isTask == true)
Assert(removeMask == false);
// Get the LLVM Type *s for the function arguments
std::vector<llvm::Type *> llvmArgTypes;
for (unsigned int i = 0; i < paramTypes.size(); ++i) {
if (paramTypes[i] == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
Assert(paramTypes[i]->IsVoidType() == false);
llvm::Type *t = paramTypes[i]->LLVMType(ctx);
if (t == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvmArgTypes.push_back(t);
}
// And add the function mask, if asked for
if (!(removeMask || isUnmasked))
llvmArgTypes.push_back(LLVMTypes::MaskType);
std::vector<llvm::Type *> callTypes;
if (isTask
#ifdef ISPC_NVPTX_ENABLED
&& (g->target->getISA() != Target::NVPTX)
#endif
){
// Tasks take three arguments: a pointer to a struct that holds the
// actual task arguments, the thread index, and the total number of
// threads the tasks system has running. (Task arguments are
// marshalled in a struct so that it's easy to allocate space to
// hold them until the task actually runs.)
llvm::Type *st = llvm::StructType::get(*ctx, llvmArgTypes);
callTypes.push_back(llvm::PointerType::getUnqual(st));
callTypes.push_back(LLVMTypes::Int32Type); // threadIndex
callTypes.push_back(LLVMTypes::Int32Type); // threadCount
callTypes.push_back(LLVMTypes::Int32Type); // taskIndex
callTypes.push_back(LLVMTypes::Int32Type); // taskCount
callTypes.push_back(LLVMTypes::Int32Type); // taskIndex0
callTypes.push_back(LLVMTypes::Int32Type); // taskIndex1
callTypes.push_back(LLVMTypes::Int32Type); // taskIndex2
callTypes.push_back(LLVMTypes::Int32Type); // taskCount0
callTypes.push_back(LLVMTypes::Int32Type); // taskCount1
callTypes.push_back(LLVMTypes::Int32Type); // taskCount2
}
else
// Otherwise we already have the types of the arguments
callTypes = llvmArgTypes;
if (returnType == NULL) {
Assert(m->errorCount > 0);
return NULL;
}
llvm::Type *llvmReturnType = returnType->LLVMType(g->ctx);
if (llvmReturnType == NULL)
return NULL;
return llvm::FunctionType::get(llvmReturnType, callTypes, false);
}
const Type *
FunctionType::GetParameterType(int i) const {
Assert(i < (int)paramTypes.size());
return paramTypes[i];
}
Expr *
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];
}
///////////////////////////////////////////////////////////////////////////
// Type
const Type *
Type::GetReferenceTarget() const {
// only ReferenceType needs to override this method
return this;
}
const Type *
Type::GetAsUnsignedType() const {
// For many types, this doesn't make any sesne
return NULL;
}
/** Given an atomic or vector type, return a vector type of the given
vecSize. Issue an error if given a vector type that isn't already that
size.
*/
static const Type *
lVectorConvert(const Type *type, SourcePos pos, const char *reason, int vecSize) {
const VectorType *vt = CastType<VectorType>(type);
if (vt) {
if (vt->GetElementCount() != vecSize) {
Error(pos, "Implicit conversion between from vector type "
"\"%s\" to vector type of length %d for %s is not possible.",
type->GetString().c_str(), vecSize, reason);
return NULL;
}
return vt;
}
else {
const AtomicType *at = CastType<AtomicType>(type);
if (!at) {
Error(pos, "Non-atomic type \"%s\" can't be converted to vector type "
"for %s.", type->GetString().c_str(), reason);
return NULL;
}
return new VectorType(at, vecSize);
}
}
const Type *
Type::MoreGeneralType(const Type *t0, const Type *t1, SourcePos pos, const char *reason,
bool forceVarying, int vecSize) {
Assert(reason != NULL);
// First, if one or both types are function types, convert them to
// pointer to function types and then try again.
if (CastType<FunctionType>(t0) || CastType<FunctionType>(t1)) {
if (CastType<FunctionType>(t0))
t0 = PointerType::GetUniform(t0);
if (CastType<FunctionType>(t1))
t1 = PointerType::GetUniform(t1);
return MoreGeneralType(t0, t1, pos, reason, forceVarying, vecSize);
}
// First, if we need to go varying, promote both of the types to be
// varying.
if (t0->IsVaryingType() || t1->IsVaryingType() || forceVarying) {
t0 = t0->GetAsVaryingType();
t1 = t1->GetAsVaryingType();
}
// And similarly, promote them both to vectors if the caller requested
// a particular vector size
if (vecSize > 0) {
t0 = lVectorConvert(t0, pos, reason, vecSize);
t1 = lVectorConvert(t1, pos, reason, vecSize);
if (!t0 || !t1)
return NULL;
}
// Are they both the same type? If so, we're done, QED.
if (Type::Equal(t0, t1))
return t0;
// If they're function types, it's hopeless if they didn't match in the
// Type::Equal() call above. Fail here so that we don't get into
// trouble calling GetAsConstType()...
if (CastType<FunctionType>(t0) || CastType<FunctionType>(t1)) {
Error(pos, "Incompatible function types \"%s\" and \"%s\" in %s.",
t0->GetString().c_str(), t1->GetString().c_str(), reason);
return NULL;
}
// Not the same types, but only a const/non-const difference? Return
// the non-const type as the more general one.
if (Type::EqualIgnoringConst(t0, t1))
return t0->GetAsNonConstType();
const PointerType *pt0 = CastType<PointerType>(t0);
const PointerType *pt1 = CastType<PointerType>(t1);
if (pt0 != NULL && pt1 != NULL) {
if (PointerType::IsVoidPointer(pt0))
return pt1;
else if (PointerType::IsVoidPointer(pt1))
return pt0;
else {
Error(pos, "Conversion between incompatible pointer types \"%s\" "
"and \"%s\" isn't possible.", t0->GetString().c_str(),
t1->GetString().c_str());
return NULL;
}
}
const VectorType *vt0 = CastType<VectorType>(t0);
const VectorType *vt1 = CastType<VectorType>(t1);
if (vt0 && vt1) {
// both are vectors; convert their base types and make a new vector
// type, as long as their lengths match
if (vt0->GetElementCount() != vt1->GetElementCount()) {
Error(pos, "Implicit conversion between differently sized vector types "
"(%s, %s) for %s is not possible.", t0->GetString().c_str(),
t1->GetString().c_str(), reason);
return NULL;
}
const Type *t = MoreGeneralType(vt0->GetElementType(), vt1->GetElementType(),
pos, reason, forceVarying);
if (!t)
return NULL;
// The 'more general' version of the two vector element types must
// be an AtomicType (that's all that vectors can hold...)
const AtomicType *at = CastType<AtomicType>(t);
Assert(at != NULL);
return new VectorType(at, vt0->GetElementCount());
}
else if (vt0) {
// If one type is a vector type but the other isn't, see if we can
// promote the other one to a vector type. This will fail and
// return NULL if t1 is e.g. an array type and it's illegal to have
// a vector of it..
const Type *t = MoreGeneralType(vt0->GetElementType(), t1, pos,
reason, forceVarying);
if (!t)
return NULL;
const AtomicType *at = CastType<AtomicType>(t);
Assert(at != NULL);
return new VectorType(at, vt0->GetElementCount());
}
else if (vt1) {
// As in the above case, see if we can promote t0 to make a vector
// that matches vt1.
const Type *t = MoreGeneralType(t0, vt1->GetElementType(), pos,
reason, forceVarying);
if (!t)
return NULL;
const AtomicType *at = CastType<AtomicType>(t);
Assert(at != NULL);
return new VectorType(at, vt1->GetElementCount());
}
// TODO: what do we need to do about references here, if anything??
const AtomicType *at0 = CastType<AtomicType>(t0->GetReferenceTarget());
const AtomicType *at1 = CastType<AtomicType>(t1->GetReferenceTarget());
const EnumType *et0 = CastType<EnumType>(t0->GetReferenceTarget());
const EnumType *et1 = CastType<EnumType>(t1->GetReferenceTarget());
if (et0 != NULL && et1 != NULL) {
// Two different enum types -> make them uint32s...
Assert(et0->IsVaryingType() == et1->IsVaryingType());
return et0->IsVaryingType() ? AtomicType::VaryingUInt32 :
AtomicType::UniformUInt32;
}
else if (et0 != NULL) {
if (at1 != NULL)
// Enum type and atomic type -> convert the enum to the atomic type
// TODO: should we return uint32 here, unless the atomic type is
// a 64-bit atomic type, in which case we return that?
return at1;
else {
Error(pos, "Implicit conversion from enum type \"%s\" to "
"non-atomic type \"%s\" for %s not possible.",
t0->GetString().c_str(), t1->GetString().c_str(), reason);
return NULL;
}
}
else if (et1 != NULL) {
if (at0 != NULL)
// Enum type and atomic type; see TODO above here as well...
return at0;
else {
Error(pos, "Implicit conversion from enum type \"%s\" to "
"non-atomic type \"%s\" for %s not possible.",
t1->GetString().c_str(), t0->GetString().c_str(), reason);
return NULL;
}
}
const PolyType *pyt0 = CastType<PolyType>(t0);
const PolyType *pyt1 = CastType<PolyType>(t1);
if (pyt0 || pyt1) {
// one of the types is polymorphic
if (pyt0 && pyt0->CanBeType(t1)) {
return pyt0;
} else if (pyt1 && pyt1->CanBeType(t0)) {
return pyt1;
} else {
// polymorphic type cannot represent the other type
// this is most likely bad
Error(pos, "Polymorphic type incompatible for \"%s\" and \"%s\""
" for %s.", t0->GetString().c_str(),
t1->GetString().c_str(), reason);
}
}
// Now all we can do is promote atomic types...
if (at0 == NULL || at1 == NULL) {
Assert(reason != NULL);
Error(pos, "Implicit conversion from type \"%s\" to \"%s\" for %s not possible.",
t0->GetString().c_str(), t1->GetString().c_str(), reason);
return NULL;
}
// Finally, to determine which of the two atomic types is more general,
// use the ordering of entries in the AtomicType::BasicType enumerator.
return (int(at0->basicType) >= int(at1->basicType)) ? at0 : at1;
}
bool
Type::IsBasicType(const Type *type) {
return (CastType<AtomicType>(type) != NULL ||
CastType<EnumType>(type) != NULL ||
CastType<PointerType>(type) != NULL ||
CastType<PolyType>(type) != NULL);
}
static bool
lCheckTypeEquality(const Type *a, const Type *b, bool ignoreConst) {
if (a == NULL || b == NULL)
return false;
if (ignoreConst == false &&
a->IsConstType() != b->IsConstType())
return false;
const AtomicType *ata = CastType<AtomicType>(a);
const AtomicType *atb = CastType<AtomicType>(b);
if (ata != NULL && atb != NULL) {
return ((ata->basicType == atb->basicType) &&
(ata->GetVariability() == atb->GetVariability()));
}
const PolyType *pyta = CastType<PolyType>(a);
const PolyType *pytb = CastType<PolyType>(b);
if (pyta != NULL && pytb != NULL) {
return ((pyta->restriction == pytb->restriction) &&
(pyta->GetVariability() == pytb->GetVariability()) &&
(pyta->GetQuant() == pytb->GetQuant()));
}
// For all of the other types, we need to see if we have the same two
// general types. If so, then we dig into the details of the type and
// see if all of the relevant bits are equal...
const EnumType *eta = CastType<EnumType>(a);
const EnumType *etb = CastType<EnumType>(b);
if (eta != NULL && etb != NULL)
// Kind of goofy, but this sufficies to check
return (eta->pos == etb->pos &&
eta->GetVariability() == etb->GetVariability());
const ArrayType *arta = CastType<ArrayType>(a);
const ArrayType *artb = CastType<ArrayType>(b);
if (arta != NULL && artb != NULL)
return (arta->GetElementCount() == artb->GetElementCount() &&
lCheckTypeEquality(arta->GetElementType(), artb->GetElementType(),
ignoreConst));
const VectorType *vta = CastType<VectorType>(a);
const VectorType *vtb = CastType<VectorType>(b);
if (vta != NULL && vtb != NULL)
return (vta->GetElementCount() == vtb->GetElementCount() &&
lCheckTypeEquality(vta->GetElementType(), vtb->GetElementType(),
ignoreConst));
const StructType *sta = CastType<StructType>(a);
const StructType *stb = CastType<StructType>(b);
const UndefinedStructType *usta = CastType<UndefinedStructType>(a);
const UndefinedStructType *ustb = CastType<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;
const std::string &namea = sta ? sta->GetStructName() :
usta->GetStructName();
const std::string &nameb = stb ? stb->GetStructName() :
ustb->GetStructName();
return (namea == nameb);
}
const PointerType *pta = CastType<PointerType>(a);
const PointerType *ptb = CastType<PointerType>(b);
if (pta != NULL && ptb != NULL)
return (pta->IsUniformType() == ptb->IsUniformType() &&
pta->IsSlice() == ptb->IsSlice() &&
pta->IsFrozenSlice() == ptb->IsFrozenSlice() &&
lCheckTypeEquality(pta->GetBaseType(), ptb->GetBaseType(),
ignoreConst));
const ReferenceType *rta = CastType<ReferenceType>(a);
const ReferenceType *rtb = CastType<ReferenceType>(b);
if (rta != NULL && rtb != NULL)
return (lCheckTypeEquality(rta->GetReferenceTarget(),
rtb->GetReferenceTarget(), ignoreConst));
const FunctionType *fta = CastType<FunctionType>(a);
const FunctionType *ftb = CastType<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 (!lCheckTypeEquality(fta->GetReturnType(), ftb->GetReturnType(),
ignoreConst))
return false;
if (fta->isTask != ftb->isTask ||
fta->isExported != ftb->isExported ||
fta->isExternC != ftb->isExternC ||
fta->isUnmasked != ftb->isUnmasked)
return false;
if (fta->GetNumParameters() != ftb->GetNumParameters())
return false;
for (int i = 0; i < fta->GetNumParameters(); ++i)
if (!lCheckTypeEquality(fta->GetParameterType(i),
ftb->GetParameterType(i), ignoreConst))
return false;
return true;
}
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);
}
bool
Type::EqualForReplacement(const Type *a, const Type *b) {
const PolyType *pa = CastType<PolyType>(a);
const PolyType *pb = CastType<PolyType>(b);
if (!pa || !pb)
return false;
return pa->restriction == pb->restriction &&
pa->GetQuant() == pb->GetQuant();
}