Files
triton/lib/lang/type.cc

505 lines
13 KiB
C++
Raw Normal View History

#include "triton/lang/type.h"
#include "triton/lang/ast.h"
#include "triton/lang/scope.h"
#include "triton/lang/token.h"
#include <cassert>
#include <algorithm>
#include <iostream>
static MemPoolImp<VoidType> voidTypePool;
static MemPoolImp<ArrayType> arrayTypePool;
static MemPoolImp<TileType> tileTypePool;
static MemPoolImp<FuncType> funcTypePool;
static MemPoolImp<PointerType> pointerTypePool;
static MemPoolImp<StructType> structUnionTypePool;
static MemPoolImp<ArithmType> arithmTypePool;
QualType Type::MayCast(QualType type, bool inProtoScope) {
auto funcType = type->ToFunc();
auto arrayType = type->ToArray();
if (funcType) {
return PointerType::New(funcType);
} else if (arrayType) {
auto ret = PointerType::New(arrayType->Derived());
// C11 6.7.6.3 [7]: qualifiers are specified in '[]'
// As we do not support qualifiers in '[]', the qualifier whould be none
return QualType(ret, inProtoScope? 0: Qualifier::CONST);
}
return type;
}
const Type* Type::ScalarType() const {
if(IsScalar())
return this;
if(const TileType* p = ToTile())
return p->Derived().GetPtr();
return nullptr;
}
Type* Type::ScalarType() {
auto cthis = const_cast<const Type*>(this);
return const_cast<Type*>(cthis->ScalarType());
}
VoidType* VoidType::New() {
static auto ret = new (voidTypePool.Alloc()) VoidType(&voidTypePool);
return ret;
}
ArithmType* ArithmType::New(int typeSpec) {
#define NEW_TYPE(tag) \
new (arithmTypePool.Alloc()) ArithmType(&arithmTypePool, tag);
static auto boolType = NEW_TYPE(T_BOOL);
static auto charType = NEW_TYPE(T_CHAR);
static auto ucharType = NEW_TYPE(T_UNSIGNED | T_CHAR);
static auto shortType = NEW_TYPE(T_SHORT);
static auto ushortType = NEW_TYPE(T_UNSIGNED | T_SHORT);
static auto intType = NEW_TYPE(T_INT);
static auto uintType = NEW_TYPE(T_UNSIGNED | T_INT);
static auto longType = NEW_TYPE(T_LONG);
static auto ulongType = NEW_TYPE(T_UNSIGNED | T_LONG);
static auto llongType = NEW_TYPE(T_LLONG)
static auto ullongType = NEW_TYPE(T_UNSIGNED | T_LLONG);
static auto halfType = NEW_TYPE(T_HALF);
static auto floatType = NEW_TYPE(T_FLOAT);
static auto doubleType = NEW_TYPE(T_DOUBLE);
static auto ldoubleType = NEW_TYPE(T_LONG | T_DOUBLE);
auto tag = ArithmType::Spec2Tag(typeSpec);
switch (tag) {
case T_BOOL: return boolType;
case T_CHAR: return charType;
case T_UNSIGNED | T_CHAR: return ucharType;
case T_SHORT: return shortType;
case T_UNSIGNED | T_SHORT:return ushortType;
case T_INT: return intType;
case T_UNSIGNED:
case T_UNSIGNED | T_INT: return uintType;
case T_LONG: return longType;
case T_UNSIGNED | T_LONG: return ulongType;
case T_LLONG: return llongType;
case T_UNSIGNED | T_LLONG:return ullongType;
case T_HALF: return halfType;
case T_FLOAT: return floatType;
case T_DOUBLE: return doubleType;
case T_LONG | T_DOUBLE: return ldoubleType;
default:
assert(tag & T_COMPLEX);
Error("complex not supported yet");
}
return nullptr; // Make compiler happy
#undef NEW_TYPE
}
ArrayType* ArrayType::New(int len, QualType eleType) {
return new (arrayTypePool.Alloc())
ArrayType(&arrayTypePool, len, eleType);
}
ArrayType* ArrayType::New(Expr* expr, QualType eleType) {
return new (arrayTypePool.Alloc())
ArrayType(&arrayTypePool, expr, eleType);
}
TileType* TileType::New(const ShapeExpr &expr, QualType eleType) {
return new (tileTypePool.Alloc())
TileType(&tileTypePool, expr, eleType);
}
TileType* TileType::New(const ShapeInt &shape, QualType eleType) {
return new (tileTypePool.Alloc())
TileType(&tileTypePool, shape, eleType);
}
FuncType* FuncType::New(QualType derived,
int funcSpec,
bool variadic,
const ParamList& params) {
return new (funcTypePool.Alloc())
FuncType(&funcTypePool, derived, funcSpec, variadic, params);
}
PointerType* PointerType::New(QualType derived) {
return new (pointerTypePool.Alloc())
PointerType(&pointerTypePool, derived);
}
StructType* StructType::New(bool isStruct,
bool hasTag,
Scope* parent) {
return new (structUnionTypePool.Alloc())
StructType(&structUnionTypePool, isStruct, hasTag, parent);
}
int ArithmType::Width() const {
switch (tag_) {
case T_BOOL: case T_CHAR: case T_UNSIGNED | T_CHAR:
return 1;
case T_SHORT: case T_UNSIGNED | T_SHORT:
return intWidth_ >> 1;
case T_INT: case T_UNSIGNED: case T_UNSIGNED | T_INT:
return intWidth_;
case T_LONG: case T_UNSIGNED | T_LONG:
return intWidth_ << 1;
case T_LLONG: case T_UNSIGNED | T_LLONG:
return intWidth_ << 1;
case T_HALF:
return intWidth_ >> 1;
case T_FLOAT:
return intWidth_;
case T_DOUBLE:
return intWidth_ << 1;
case T_LONG | T_DOUBLE:
return intWidth_ << 1;
case T_HALF | T_COMPLEX:
return intWidth_;
case T_FLOAT | T_COMPLEX:
return intWidth_ << 1;
case T_DOUBLE | T_COMPLEX:
return intWidth_ << 2;
case T_LONG | T_DOUBLE | T_COMPLEX:
return intWidth_ << 2;
default:
assert(false);
}
return intWidth_; // Make compiler happy
}
int ArithmType::Rank() const {
switch (tag_) {
case T_BOOL: return 0;
case T_CHAR: case T_UNSIGNED | T_CHAR: return 1;
case T_SHORT: case T_UNSIGNED | T_SHORT: return 2;
case T_INT: case T_UNSIGNED: case T_UNSIGNED | T_INT: return 3;
case T_LONG: case T_UNSIGNED | T_LONG: return 4;
case T_LLONG: case T_UNSIGNED | T_LLONG: return 5;
case T_HALF: return 6;
case T_FLOAT: return 7;
case T_DOUBLE: return 8;
case T_LONG | T_DOUBLE: return 9;
default:
assert(tag_ & T_COMPLEX);
Error("complex not supported yet");
}
return 0;
}
ArithmType* ArithmType::MaxType(ArithmType* lhs,
ArithmType* rhs) {
if (lhs->IsInteger())
lhs = ArithmType::IntegerPromote(lhs);
if (rhs->IsInteger())
rhs = ArithmType::IntegerPromote(rhs);
auto ret = lhs->Rank() > rhs->Rank() ? lhs: rhs;
if (lhs->Width() == rhs->Width() && (lhs->IsUnsigned() || rhs->IsUnsigned()))
return ArithmType::New(T_UNSIGNED | ret->Tag());
return ret;
}
/*
* Converting from type specifier to type tag
*/
int ArithmType::Spec2Tag(int spec) {
if (spec == T_SIGNED) {
return T_INT;
}
spec &= ~T_SIGNED;
if ((spec & T_SHORT) || (spec & T_LONG)
|| (spec & T_LLONG)) {
spec &= ~T_INT;
}
return spec;
}
std::string ArithmType::Str() const {
std::string width = ":" + std::to_string(Width());
switch (tag_) {
case T_BOOL:
return "bool" + width;
case T_CHAR:
return "char" + width;
case T_UNSIGNED | T_CHAR:
return "unsigned char" + width;
case T_SHORT:
return "short" + width;
case T_UNSIGNED | T_SHORT:
return "unsigned short" + width;
case T_INT:
return "int" + width;
case T_UNSIGNED:
return "unsigned int" + width;
case T_LONG:
return "long" + width;
case T_UNSIGNED | T_LONG:
return "unsigned long" + width;
case T_LLONG:
return "long long" + width;
case T_UNSIGNED | T_LLONG:
return "unsigned long long" + width;
case T_FLOAT:
return "float" + width;
case T_DOUBLE:
return "double" + width;
case T_LONG | T_DOUBLE:
return "long double" + width;
case T_FLOAT | T_COMPLEX:
return "float complex" + width;
case T_DOUBLE | T_COMPLEX:
return "double complex" + width;
case T_LONG | T_DOUBLE | T_COMPLEX:
return "long double complex" + width;
default:
assert(false);
}
return "error"; // Make compiler happy
}
bool PointerType::Compatible(const Type& other) const {
// C11 6.7.6.1 [2]: pointer compatibility
auto otherPointer = other.ToPointer();
return otherPointer &&
derived_->Compatible(*otherPointer->derived_);
// FIXME(wgtdkp): cannot loose compatible constraints
//return other.IsInteger() ||
// (otherPointer && derived_->Compatible(*otherPointer->derived_));
}
bool ArrayType::Compatible(const Type& other) const {
// C11 6.7.6.2 [6]: For two array type to be compatible,
// the element types must be compatible, and have same length
// if both specified.
auto otherArray = other.ToArray();
if (!otherArray) return false;
if (!derived_->Compatible(*otherArray->derived_)) return false;
// The lengths should equal if both specified
if (complete_ && otherArray->complete_)
return len_ == otherArray->len_;
return true;
}
bool TileType::Compatible(const Type& other) const {
// For two tile type to be compatible,
// the element types must be compatible
// and they must have the same shapea
auto otherTile = other.ToTile();
if(!otherTile)
return false;
if (!derived_->Compatible(*otherTile->derived_))
return false;
// The shapes should be equal if both specified
if(complete_ && otherTile->complete_)
return shape_ == otherTile->shape_;
return true;
}
bool FuncType::Compatible(const Type& other) const {
auto otherFunc = other.ToFunc();
// The other type is not an function type
if (!otherFunc) return false;
// TODO(wgtdkp): do we need to check the type of return value when deciding
// compatibility of two function types ??
if (!derived_->Compatible(*otherFunc->derived_))
return false;
if (params_.size() != otherFunc->params_.size())
return false;
auto thisIter = params_.begin();
auto otherIter = otherFunc->params_.begin();
while (thisIter != params_.end()) {
if (!(*thisIter)->Type()->Compatible(*(*otherIter)->Type()))
return false;
++thisIter;
++otherIter;
}
return true;
}
std::string FuncType::Str() const {
auto str = derived_->Str() + "(";
auto iter = params_.begin();
for (; iter != params_.end(); ++iter) {
str += (*iter)->Type()->Str() + ", ";
}
if (variadic_)
str += "...";
else if (params_.size())
str.resize(str.size() - 2);
return str + ")";
}
StructType::StructType(MemPool* pool,
bool isStruct,
bool hasTag,
Scope* parent)
: Type(pool, false),
isStruct_(isStruct),
hasTag_(hasTag),
memberMap_(new Scope(parent, S_BLOCK)),
offset_(0),
width_(0),
// If a struct type has no member, it gets alignment of 1
align_(1),
bitFieldAlign_(1) {}
Object* StructType::GetMember(const std::string& member) {
auto ident = memberMap_->FindInCurScope(member);
if (ident == nullptr)
return nullptr;
return ident->ToObject();
}
void StructType::CalcWidth() {
width_ = 0;
auto iter = memberMap_->identMap_.begin();
for (; iter != memberMap_->identMap_.end(); ++iter) {
width_ += iter->second->Type()->Width();
}
}
bool StructType::Compatible(const Type& other) const {
return this == &other; // Pointer comparison
}
// TODO(wgtdkp): more detailed representation
std::string StructType::Str() const {
std::string str = isStruct_ ? "struct": "union";
return str + ":" + std::to_string(width_);
}
// Remove useless unnamed bitfield members as they are just for parsing
void StructType::Finalize() {
for (auto iter = members_.begin(); iter != members_.end();) {
if ((*iter)->BitFieldWidth() && (*iter)->Anonymous()) {
members_.erase(iter++);
} else {
++iter;
}
}
}
void StructType::AddMember(Object* member) {
auto offset = MakeAlign(offset_, member->Align());
member->SetOffset(offset);
members_.push_back(member);
memberMap_->Insert(member->Name(), member);
align_ = std::max(align_, member->Align());
bitFieldAlign_ = std::max(bitFieldAlign_, align_);
if (isStruct_) {
offset_ = offset + member->Type()->Width();
width_ = MakeAlign(offset_, align_);
} else {
assert(offset_ == 0);
width_ = std::max(width_, member->Type()->Width());
width_ = MakeAlign(width_, align_);
}
}
void StructType::AddBitField(Object* bitField, int offset) {
bitField->SetOffset(offset);
members_.push_back(bitField);
if (!bitField->Anonymous())
memberMap_->Insert(bitField->Name(), bitField);
auto bytes = MakeAlign(bitField->BitFieldEnd(), 8) / 8;
bitFieldAlign_ = std::max(bitFieldAlign_, bitField->Align());
// Does not aligned, default is 1
if (isStruct_) {
offset_ = offset + bytes;
width_ = MakeAlign(offset_, std::max(bitFieldAlign_, bitField->Align()));
} else {
assert(offset_ == 0);
width_ = std::max(width_, bitField->Type()->Width());
}
}
// Move members of Anonymous struct/union to external struct/union
void StructType::MergeAnony(Object* anony) {
auto anonyType = anony->Type()->ToStruct();
auto offset = MakeAlign(offset_, anony->Align());
// Members in map are never anonymous
for (auto& kv: *anonyType->memberMap_) {
auto& name = kv.first;
auto member = kv.second->ToObject();
if (member == nullptr) {
continue;
}
// Every member of anonymous struct/union
// are offseted by external struct/union
member->SetOffset(offset + member->Offset());
if (GetMember(name)) {
Error(member, "duplicated member '%s'", name.c_str());
}
// Simplify anony struct's member searching
memberMap_->Insert(name, member);
}
anony->SetOffset(offset);
members_.push_back(anony);
align_ = std::max(align_, anony->Align());
if (isStruct_) {
offset_ = offset + anonyType->Width();
width_ = MakeAlign(offset_, align_);
} else {
assert(offset_ == 0);
width_ = std::max(width_, anonyType->Width());
}
}