#include "TypeManager.hh" #include "Context.hh" #include "ExprNode.hh" #include "Log.hh" namespace pcc { TypeManager::TypeManager() : builtins_{std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared(), std::make_shared()} { PointerDifferenceType_ = GetBuiltinType(BuiltinType::INTEGER); PointerIndexType_ = GetBuiltinType(BuiltinType::INTEGER); builtins_.push_back( GetPointerType(GetBuiltinType(BuiltinType::CHAR), "string")); // llvm doesn't have void* type, we use char* instead PointerTypes_.emplace( GetBuiltinType(BuiltinType::VOID), std::make_shared(GetBuiltinType(BuiltinType::CHAR), PointerDifferenceType_, PointerIndexType_, "^void")); } std::shared_ptr TypeManager::GetBuiltinType(BuiltinType type) { return builtins_[ToUnderlying(type)]; } std::pair, Value> TypeManager::CreateOperation(BinaryOperator op, std::shared_ptr LeftType, std::shared_ptr RightType, Value left, Value right, Context* context) { llvm::IRBuilder<>* builder = context->GetBuilder(); std::shared_ptr LeftIntegerBaseType = std::dynamic_pointer_cast(LeftType); std::shared_ptr RightIntegerBaseType = std::dynamic_pointer_cast(RightType); std::shared_ptr LeftPointerType = std::dynamic_pointer_cast(LeftType); std::shared_ptr RightPointerType = std::dynamic_pointer_cast(RightType); std::shared_ptr LeftArrayType = std::dynamic_pointer_cast(LeftType); // add/sub a pointer with a integer if (LeftPointerType != nullptr && RightPointerType == nullptr) { if (op != BinaryOperator::ADD && op != BinaryOperator::SUB) { Log(LogLevel::PCC_ERROR, "unsupported operation {0} on %s and {1}", GetOperatorName(op), LeftType->GetCommonName(), RightType->GetCommonName()); return {nullptr, nullptr}; } if (RightIntegerBaseType == nullptr) { Log(LogLevel::PCC_ERROR, "can not add pointer with non-integer type {0}", RightType->GetCommonName()); return {nullptr, nullptr}; } Value CastedInedx = builder->CreateSExtOrTrunc(right, PointerIndexType_->GetLLVMType()); return {LeftType, builder->CreateInBoundsGEP(left, CastedInedx)}; } // pointer sub case if (LeftPointerType != nullptr && RightPointerType != nullptr && op == BinaryOperator::SUB) { if (LeftType != RightType) { Log(LogLevel::PCC_ERROR, "cannot sub to pointers of different type"); return {nullptr, nullptr}; } Value result = LeftType->CreateBinaryOperation(op, left, right, context); return {PointerDifferenceType_, result}; } // other operations will require the types of left and right are the same. std::shared_ptr LeftRealType = std::dynamic_pointer_cast(LeftType); std::shared_ptr RightRealType = std::dynamic_pointer_cast(RightType); Value ConvertedLeft, ConvertedRight; std::shared_ptr CommonType; if (op == BinaryOperator::REAL_DIV) { // we can only set the CommonType to real for REAL_DIV on builtin types, // for other types, REAL_DIV and DIV are unavailable. CommonType = builtins_[ToUnderlying(BuiltinType::REAL)]; op = BinaryOperator::DIV; } else if (LeftType != RightType) { // we can cast the boolean, char, integer and real. if (op == BinaryOperator::REAL_DIV) { CommonType = builtins_[ToUnderlying(BuiltinType::REAL)]; } else if (LeftRealType != nullptr && RightIntegerBaseType != nullptr) { CommonType = LeftType; } else if (RightRealType != nullptr && LeftIntegerBaseType != nullptr) { CommonType = RightType; } else if (LeftIntegerBaseType != nullptr && RightIntegerBaseType != nullptr) { CommonType = (LeftIntegerBaseType->GetWidth() > RightIntegerBaseType->GetWidth() ? LeftType : RightType); } else { Log(LogLevel::PCC_ERROR, "can not apply binary operator on two different type {0} and {1}", LeftType->GetCommonName(), RightType->GetCommonName()); return {nullptr, nullptr}; } } else { CommonType = LeftType; } ConvertedLeft = CreateCast(CommonType, LeftType, left, context); ConvertedRight = CreateCast(CommonType, RightType, right, context); Value result = CommonType->CreateBinaryOperation(op, ConvertedLeft, ConvertedRight, context); if (result == nullptr) { Log(LogLevel::PCC_ERROR, "binary operator {0} is not supported on type {1}", GetOperatorName(op), CommonType->GetCommonName()); } // logical operators return boolean type if (ToUnderlying(op) >= ToUnderlying(BinaryOperator::LT) && ToUnderlying(op) <= ToUnderlying(BinaryOperator::NE)) { CommonType = builtins_[ToUnderlying(BuiltinType::BOOLEAN)]; } return {CommonType, result}; } Value TypeManager::CreateOperation(UnaryOperator op, std::shared_ptr type, Value v, Context* context) { Value result = type->CreateUnaryOperation(op, v, context); if (result == nullptr) { Log(LogLevel::PCC_ERROR, "unary operator {0} is not supported on type {1}", GetOperatorName(op), type->GetCommonName()); } return result; } Value TypeManager::CreateCast(std::shared_ptr DstType, std::shared_ptr SrcType, Value v, Context* context) { if (DstType == SrcType) { return v; } llvm::IRBuilder<>* builder = context->GetBuilder(); // we can only cast between integer and real std::shared_ptr DstIntegerBaseType = std::dynamic_pointer_cast(DstType); std::shared_ptr SrcIntegerBaseType = std::dynamic_pointer_cast(SrcType); std::shared_ptr DstRealType = std::dynamic_pointer_cast(DstType); std::shared_ptr SrcRealType = std::dynamic_pointer_cast(SrcType); if (SrcRealType != nullptr && DstIntegerBaseType != nullptr) { return builder->CreateFPToSI(v, DstType->GetLLVMType()); } else if (SrcIntegerBaseType != nullptr) { if (DstRealType != nullptr) { return builder->CreateSIToFP(v, DstType->GetLLVMType()); } else if (DstIntegerBaseType != nullptr) { return builder->CreateSExtOrTrunc(v, DstType->GetLLVMType()); } } // array to pointer decay std::shared_ptr SrcPointerType = std::dynamic_pointer_cast(SrcType); std::shared_ptr DstPointerType = std::dynamic_pointer_cast(DstType); if (SrcPointerType != nullptr && DstPointerType != nullptr) { std::shared_ptr SrcInnerType = std::dynamic_pointer_cast( SrcPointerType->GetElementType()); if (SrcInnerType->GetElementType() == DstPointerType->GetElementType()) { Value zero = llvm::ConstantInt::get(PointerIndexType_->GetLLVMType(), 0); Value indices[2] = {zero, zero}; return builder->CreateInBoundsGEP(v, indices); } } Log(LogLevel::PCC_ERROR, "unsupported type casting from {0} to {1}", SrcType->GetCommonName(), DstType->GetCommonName()); return nullptr; } std::shared_ptr TypeManager::GetPointerType(std::shared_ptr ElementType, const char* CustomName) { decltype(PointerTypes_)::iterator result = PointerTypes_.find(ElementType); if (result != PointerTypes_.end()) { return std::get<1>(*result); } std::shared_ptr NewType = std::make_shared( ElementType, PointerDifferenceType_, PointerIndexType_, CustomName == nullptr ? "^" + std::string(ElementType->GetCommonName()) : CustomName); PointerTypes_.insert({ElementType, NewType}); return NewType; } std::shared_ptr TypeManager::CreateArrayType(std::shared_ptr ElementType, std::shared_ptr start, std::shared_ptr end) { llvm::Constant* StartValue = llvm::cast(start->CodeGen()); llvm::Constant* EndValue = llvm::cast(end->CodeGen()); std::shared_ptr IndexType = start->GetType(); if (IndexType != end->GetType()) { Log(LogLevel::PCC_ERROR, "the start and end indices of an array type must be same " "(start={0}, end={1})", IndexType->GetCommonName(), end->GetType()->GetCommonName()); return nullptr; } if (std::dynamic_pointer_cast(IndexType) == nullptr) { Log(LogLevel::PCC_ERROR, "array index must have a integer-like type (i.e. integer, char)"); return nullptr; } uint64_t StartIndex = StartValue->getUniqueInteger().getZExtValue(); uint64_t ElementNumber = EndValue->getUniqueInteger().getZExtValue() - StartIndex + 1; std::shared_ptr NewType = std::make_shared( ElementType, ElementNumber, IndexType, StartValue, EndValue, std::string(ElementType->GetCommonName()) + "[" + std::to_string(ElementNumber) + "]", StartIndex == 0); ArrayTypes_.push_back(NewType); return NewType; } std::shared_ptr TypeManager::CreateRecordType(const std::vector& members) { std::string name = "{"; std::vector::const_iterator MemberIt = members.cbegin(); name.append(std::get<1>(*MemberIt)->GetCommonName()); for (++MemberIt; MemberIt != members.cend(); ++MemberIt) { name.append(", "); name.append(std::get<1>(*MemberIt)->GetCommonName()); } std::shared_ptr NewType = std::make_shared(members, name + "}"); RecordTypes_.push_back(NewType); return NewType; } std::shared_ptr TypeManager::CreateFunctionType(std::shared_ptr ReturnType, std::vector> ArgTypes, bool IsVariadic) { decltype(ArgTypes)::const_iterator ArgIt = ArgTypes.cbegin(); std::string name = " (*)("; if (!ArgTypes.empty()) { name.append((*ArgIt)->GetCommonName()); for (++ArgIt; ArgIt != ArgTypes.cend(); ++ArgIt) { name.append(", "); name.append((*ArgIt)->GetCommonName()); } } std::shared_ptr NewType = std::make_shared( ReturnType, ArgTypes, ReturnType->GetCommonName() + name + ")", IsVariadic); FunctionTypes_.push_back(NewType); return NewType; } std::shared_ptr TypeManager::GetPointerIndexType() { return PointerIndexType_; } std::shared_ptr TypeManager::GetPointerDifferenceType() { return PointerDifferenceType_; } } // namespace pcc