aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'lib/CodeGen')
-rw-r--r--lib/CodeGen/ArrayHandler.cc2
-rw-r--r--lib/CodeGen/CodeGen.cc24
-rw-r--r--lib/CodeGen/Designator.cc27
-rw-r--r--lib/CodeGen/FunctionGen.cc6
-rw-r--r--lib/CodeGen/TypeSystem.cc210
5 files changed, 257 insertions, 12 deletions
diff --git a/lib/CodeGen/ArrayHandler.cc b/lib/CodeGen/ArrayHandler.cc
index 8b37b8f..bd68473 100644
--- a/lib/CodeGen/ArrayHandler.cc
+++ b/lib/CodeGen/ArrayHandler.cc
@@ -321,7 +321,7 @@ bool ArrayHandler::emitArrayArgument(ExprAST *ArgExpr,
Callee.c_str(), ElemTypeName.c_str());
return false;
}
- if (Meta->ElementTypeName != ElemTypeName) {
+ if (!CG.resolveType(Meta->ElementTypeName)->sameType(CG.resolveType(ElemTypeName))) {
CG.reportError("Array argument %s to %s must have element type %s (got %s)",
Var->getName().c_str(), Callee.c_str(),
ElemTypeName.c_str(), Meta->ElementTypeName.c_str());
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index f69fc03..8c01972 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -262,6 +262,7 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const {
const TypeInfo *L = getExprTypeInfo(Bin->getLHS());
const TypeInfo *R = getExprTypeInfo(Bin->getRHS());
+ if ((L && L->isC()) || (R && R->isC())) return nullptr;
// Enum arithmetic: E +/- INTEGER (enum on the left) yields the enum;
// every other user-kind combination has no type (emission rejects it).
if ((L && L->isUserKind()) || (R && R->isUserKind())) {
@@ -387,6 +388,8 @@ Value *CodeGen::coerceValueToType(Value *Val, const TypeInfo *TargetInfo) {
case TypeKind::Enum:
case TypeKind::Record:
case TypeKind::Pointer:
+ case TypeKind::CScalar:
+ case TypeKind::CPointer:
reportError("Internal: a %s value reached representation coercion without a name-level check",
TargetInfo->Name.c_str());
return nullptr;
@@ -507,6 +510,11 @@ void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewli
reportError("Cannot OUTPUT a value of pointer type %s", Info->Name.c_str());
return;
+ case TypeKind::CScalar:
+ case TypeKind::CPointer:
+ reportError("OUTPUT is not supported for type %s", Info->Name.c_str());
+ return;
+
case TypeKind::Void:
reportError("Cannot OUTPUT a VOID value");
return;
@@ -644,6 +652,10 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
// '&' path: their admissible operations are decided by name here.
const TypeInfo *LT = getExprTypeInfo(Bin->getLHS());
const TypeInfo *RT = getExprTypeInfo(Bin->getRHS());
+ if ((LT && LT->isC()) || (RT && RT->isC())) {
+ reportError("C operators require explicit support for their operand types");
+ return nullptr;
+ }
if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) {
return emitUserKindBinaryOp(Bin, LT, RT);
}
@@ -719,7 +731,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
}
if (Name == "ASC") {
const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get());
- if (ArgType && ArgType->isUserKind()) {
+ if (ArgType && (ArgType->isUserKind() || ArgType->isC())) {
reportError("ASC expects a CHAR or STRING (got %s)", ArgType->Name.c_str());
return nullptr;
}
@@ -747,7 +759,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
}
if (Name == "NUM_TO_STR") {
const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get());
- if (ArgType && ArgType->isUserKind()) {
+ if (ArgType && (ArgType->isUserKind() || ArgType->isC())) {
reportError("NUM_TO_STR expects a number (got %s)", ArgType->Name.c_str());
return nullptr;
}
@@ -850,7 +862,7 @@ bool CodeGen::marshalCallArgs(const std::string &Callee,
return false;
}
// BYREF aliases the storage, so the types must match exactly.
- if (Info->TypeName != TypeName) {
+ if (!Types->sameType(Info->TypeName, TypeName)) {
reportError("BYREF argument %s to %s must be of type %s (got %s)",
Var->getName().c_str(), Callee.c_str(),
TypeName.c_str(), Info->TypeName.c_str());
@@ -1093,11 +1105,11 @@ void CodeGen::emitStmt(StmtAST *Stmt) {
reportError("RETURN is only allowed inside a FUNCTION or PROCEDURE");
return;
}
- if (Sig->ReturnTypeName == "VOID" && Ret->getRetVal()) {
+ if (F->getReturnType()->isVoidTy() && Ret->getRetVal()) {
reportError("RETURN with a value is only allowed in a FUNCTION");
return;
}
- if (Sig->ReturnTypeName != "VOID" && !Ret->getRetVal()) {
+ if (!F->getReturnType()->isVoidTy() && !Ret->getRetVal()) {
reportError("RETURN in a FUNCTION requires a value");
return;
}
@@ -1293,7 +1305,7 @@ void CodeGen::emitStmt(StmtAST *Stmt) {
// TYPE declarations are registered by compile()'s pre-pass (the parser
// guarantees they are top-level); nothing is emitted here.
if (dynamic_cast<EnumTypeDeclAST*>(Stmt) || dynamic_cast<PointerTypeDeclAST*>(Stmt) ||
- dynamic_cast<RecordTypeDeclAST*>(Stmt)) {
+ dynamic_cast<RecordTypeDeclAST*>(Stmt) || dynamic_cast<CTypeDeclAST*>(Stmt)) {
return;
}
if (auto *DA = dynamic_cast<DesignatorAssignStmtAST*>(Stmt)) {
diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc
index 9d14d23..c7d0796 100644
--- a/lib/CodeGen/Designator.cc
+++ b/lib/CodeGen/Designator.cc
@@ -42,7 +42,9 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem
for (const auto &S : Statements) {
bool Ok = true;
- if (auto *E = dynamic_cast<EnumTypeDeclAST*>(S.get()))
+ if (auto *C = dynamic_cast<CTypeDeclAST*>(S.get()))
+ Ok = Types->defineCAlias(C->getName(), C->getExpression(), Err);
+ else if (auto *E = dynamic_cast<EnumTypeDeclAST*>(S.get()))
Ok = Types->declareUserType(E->getName(), TypeKind::Enum, Err);
else if (auto *P = dynamic_cast<PointerTypeDeclAST*>(S.get()))
Ok = Types->declareUserType(P->getName(), TypeKind::Pointer, Err);
@@ -163,6 +165,11 @@ bool CodeGen::emitLValue(ExprAST *E, LValueInfo &Out) {
TypeName.c_str());
return false;
}
+ const TypeInfo *Pointee = resolveType(PP->PointeeTypeName);
+ if (!Pointee || Pointee->isVoid()) {
+ reportError("Cannot dereference a void pointer");
+ return false;
+ }
Value *P = Builder->CreateLoad(PointerType::getUnqual(*TheContext), Addr, "deref_ptr");
RuntimeChecker->emitNullDerefCheck(P, D->getLine());
Addr = P;
@@ -213,6 +220,22 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
const TypeInfo *Src = getExprTypeInfo(E);
+ if (Target->isC() || (Src && Src->isC())) {
+ if (auto *Addr = dynamic_cast<AddrOfExprAST*>(E)) {
+ const PointerPayload *PP = Target->asPointer();
+ LValueInfo LV;
+ if (PP && emitLValue(Addr->getTarget(), LV)) {
+ if (Types->sameType(LV.TypeName, PP->PointeeTypeName)) return LV.Addr;
+ }
+ reportError("Address does not match C pointer type %s", Target->Name.c_str());
+ return nullptr;
+ }
+ if (Src && Src->sameType(Target) && !Target->isVoid()) return emitExpr(E);
+ reportError("Cannot implicitly convert %s to %s; use an explicit cast",
+ Src ? Src->Name.c_str() : "expression", Target->Name.c_str());
+ return nullptr;
+ }
+
if (Target->isUserKind()) {
// ^x into a pointer target is structural: the pointee type name must
// match the pointer type's declared pointee exactly (strict — the
@@ -226,7 +249,7 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
}
LValueInfo LV;
if (!emitLValue(Addr->getTarget(), LV)) return nullptr;
- if (LV.TypeName != PP->PointeeTypeName) {
+ if (!Types->sameType(LV.TypeName, PP->PointeeTypeName)) {
reportError("%s is a pointer to %s and cannot point at a value of type %s",
Target->Name.c_str(), PP->PointeeTypeName.c_str(), LV.TypeName.c_str());
return nullptr;
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index afa6074..480d9ba 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -117,6 +117,12 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
std::vector<Type*> ArgTypes;
for (const ParamDecl &P : Proto->getParams()) {
+ const TypeInfo *Info = Types.resolve(P.TypeName);
+ if (Info && Info->isVoid()) {
+ fprintf(stderr, "error: parameter %s cannot have void type\n", P.Name.c_str());
+ HadError = true;
+ return nullptr;
+ }
ParamSig PS;
PS.TypeName = P.TypeName;
PS.IsByRef = P.Mode == PassMode::ByRef;
diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc
index f693ba1..73d4136 100644
--- a/lib/CodeGen/TypeSystem.cc
+++ b/lib/CodeGen/TypeSystem.cc
@@ -2,10 +2,49 @@
#include "cps/TypeAST.h"
#include "llvm/IR/Constants.h"
#include <utility>
+#include <climits>
+#include <limits>
+#include <type_traits>
+#include <sstream>
+#include <cctype>
using namespace llvm;
using namespace cps;
+namespace {
+
+std::string cTypeName(const std::string &Identity) {
+ return "C_TYPE(\"" + Identity + "\")";
+}
+
+// sizeof alone cannot distinguish padded x87 from IEEE quad or double-double.
+template<typename T> Type *nativeFloatType(LLVMContext &C) {
+ constexpr int Digits = std::numeric_limits<T>::digits;
+ constexpr int Exponent = std::numeric_limits<T>::max_exponent;
+ static_assert(std::numeric_limits<T>::radix == 2, "Unsupported native C floating-point radix");
+ static_assert((Digits == 24 && Exponent == 128) ||
+ (Digits == 53 && Exponent == 1024) ||
+ (Digits == 64 && Exponent == 16384) ||
+ (Digits == 113 && Exponent == 16384) ||
+ (Digits == 106 && Exponent == 1024), "Unsupported native C floating-point format");
+ if constexpr (Digits == 24) return Type::getFloatTy(C);
+ if constexpr (Digits == 53) return Type::getDoubleTy(C);
+ if constexpr (Digits == 64) return Type::getX86_FP80Ty(C);
+ if constexpr (Digits == 113) return Type::getFP128Ty(C);
+ return Type::getPPC_FP128Ty(C);
+}
+
+} // namespace
+
+bool TypeInfo::isC() const { return !CIdentity.empty(); }
+
+bool TypeInfo::sameType(const TypeInfo *Other) const {
+ if (!Other) return false;
+ if (isC() || Other->isC())
+ return isC() && Other->isC() && CIdentity == Other->CIdentity;
+ return Name == Other->Name;
+}
+
const RecordFieldInfo *RecordPayload::findField(const std::string &Name) const {
for (const auto &F : Fields) {
if (F.first == Name) return &F.second;
@@ -38,7 +77,7 @@ bool TypeInfo::isChar() const {
}
bool TypeInfo::isVoid() const {
- return Kind == TypeKind::Void;
+ return Kind == TypeKind::Void || (isC() && CIdentity == "void");
}
bool TypeInfo::isEnum() const {
@@ -50,7 +89,7 @@ bool TypeInfo::isRecord() const {
}
bool TypeInfo::isPointer() const {
- return Kind == TypeKind::Pointer;
+ return Kind == TypeKind::Pointer || Kind == TypeKind::CPointer;
}
bool TypeInfo::isUserKind() const {
@@ -69,6 +108,8 @@ bool TypeInfo::isOutputtable() const {
case TypeKind::Void:
case TypeKind::Record:
case TypeKind::Pointer:
+ case TypeKind::CScalar:
+ case TypeKind::CPointer:
return false;
}
return false;
@@ -83,7 +124,7 @@ const EnumPayload *TypeInfo::asEnum() const {
}
const PointerPayload *TypeInfo::asPointer() const {
- return Kind == TypeKind::Pointer ? &Pointer : nullptr;
+ return isPointer() ? &Pointer : nullptr;
}
TypeSystem::TypeSystem(LLVMContext &Ctx) : Context(Ctx) {
@@ -97,6 +138,166 @@ void TypeSystem::registerBuiltins() {
registerType("STRING", PointerType::getUnqual(Context), TypeKind::String);
registerType("CHAR", Type::getInt8Ty(Context), TypeKind::Char);
registerType("VOID", Type::getVoidTy(Context), TypeKind::Void);
+
+ // The compiler's native C/C++ ABI determines representation, not a table
+ // of assumed LP64/LLP64 widths. LLVM's addressable bytes are eight bits.
+ static_assert(CHAR_BIT == 8, "LLVM requires eight-bit addressable bytes");
+ auto Integral = [&](auto Sample, const char *Name, unsigned Rank,
+ const char *InputFormat, const char *OutputFormat) {
+ using T = decltype(Sample);
+ TypeInfo Info;
+ Info.Name = cTypeName(Name);
+ Info.CIdentity = Name;
+ Info.Kind = TypeKind::CScalar;
+ Info.LLVMType = Type::getIntNTy(Context, sizeof(T) * CHAR_BIT);
+ Info.CRank = Rank;
+ Info.CSigned = std::is_signed<T>::value;
+ Info.CInputFormat = InputFormat;
+ Info.COutputFormat = OutputFormat;
+ Types.emplace(Info.Name, std::move(Info));
+ };
+ Integral(char{}, "char", 2, " %c", "%c");
+ Integral(static_cast<signed char>(0), "signed char", 2, "%hhd", "%hhd");
+ Integral(static_cast<unsigned char>(0), "unsigned char", 2, "%hhu", "%hhu");
+ Integral(short{}, "short", 3, "%hd", "%hd");
+ Integral(static_cast<unsigned short>(0), "unsigned short", 3, "%hu", "%hu");
+ Integral(int{}, "int", 4, "%d", "%d");
+ Integral(static_cast<unsigned int>(0), "unsigned int", 4, "%u", "%u");
+ Integral(long{}, "long", 5, "%ld", "%ld");
+ Integral(static_cast<unsigned long>(0), "unsigned long", 5, "%lu", "%lu");
+ Integral(static_cast<long long>(0), "long long", 6, "%lld", "%lld");
+ Integral(static_cast<unsigned long long>(0), "unsigned long long", 6, "%llu", "%llu");
+
+ auto Floating = [&](auto Sample, const char *Name, unsigned Rank, const char *Format) {
+ TypeInfo Info;
+ Info.Name = cTypeName(Name);
+ Info.CIdentity = Name;
+ Info.Kind = TypeKind::CScalar;
+ Info.LLVMType = nativeFloatType<decltype(Sample)>(Context);
+ Info.CRank = Rank;
+ Info.CInputFormat = Format;
+ Info.COutputFormat = Rank == 9 ? "%Lf" : "%f";
+ Types.emplace(Info.Name, std::move(Info));
+ };
+ Floating(float{}, "float", 7, "%f");
+ Floating(double{}, "double", 8, "%lf");
+ Floating(static_cast<long double>(0), "long double", 9, "%Lf");
+
+ TypeInfo Bool;
+ Bool.Name = cTypeName("_Bool");
+ Bool.CIdentity = "_Bool";
+ Bool.Kind = TypeKind::CScalar;
+ Bool.LLVMType = Type::getInt1Ty(Context);
+ Bool.CRank = 1;
+ Types.emplace(Bool.Name, std::move(Bool));
+ TypeInfo Void;
+ Void.Name = cTypeName("void");
+ Void.CIdentity = "void";
+ Void.Kind = TypeKind::CScalar;
+ Void.LLVMType = Type::getVoidTy(Context);
+ Types.emplace(Void.Name, std::move(Void));
+
+ std::string Err;
+ defineCAlias("C_INT", "int", Err);
+ defineCAlias("C_CHAR", "char", Err);
+ defineCAlias("C_STRING", "char*", Err);
+ defineCAlias("C_DOUBLE", "double", Err);
+}
+
+bool TypeSystem::sameType(const std::string &Left, const std::string &Right) const {
+ const TypeInfo *L = resolve(Left);
+ return L && L->sameType(resolve(Right));
+}
+
+const TypeInfo *TypeSystem::importCType(const std::string &Expression, std::string &Err) {
+ // Whitespace is immaterial around stars; C's multiword base type is
+ // validated separately. Stars must all precede or all follow the base.
+ std::istringstream Input(Expression);
+ std::string Word, Text;
+ while (Input >> Word) {
+ if (!Text.empty()) Text += ' ';
+ Text += Word;
+ }
+ unsigned Depth = 0;
+ bool Prefix = !Text.empty() && Text.front() == '*';
+ if (Prefix) {
+ while (!Text.empty() && (Text.front() == '*' || Text.front() == ' ')) {
+ if (Text.front() == '*') ++Depth;
+ Text.erase(Text.begin());
+ }
+ } else {
+ while (!Text.empty() && (Text.back() == '*' || Text.back() == ' ')) {
+ if (Text.back() == '*') ++Depth;
+ Text.pop_back();
+ }
+ }
+ std::istringstream BaseWords(Text);
+ std::map<std::string, unsigned> Counts;
+ bool Valid = !Text.empty();
+ while (BaseWords >> Word) {
+ if (Word != "signed" && Word != "unsigned" && Word != "short" &&
+ Word != "long" && Word != "int" && Word != "char" &&
+ Word != "float" && Word != "double" && Word != "_Bool" && Word != "void")
+ Valid = false;
+ ++Counts[Word];
+ }
+ for (const auto &Entry : Counts)
+ if (Entry.second > (Entry.first == "long" ? 2u : 1u)) Valid = false;
+ auto Has = [&](const char *Name) { return Counts.count(Name) != 0; };
+ if (Has("signed") && Has("unsigned")) Valid = false;
+ std::string Base;
+ if (Has("void") || Has("_Bool") || Has("float")) {
+ if (Counts.size() != 1) Valid = false;
+ Base = Has("void") ? "void" : Has("_Bool") ? "_Bool" : "float";
+ } else if (Has("double")) {
+ if (Counts.size() != (Has("long") ? 2u : 1u) || (Has("long") && Counts["long"] > 1))
+ Valid = false;
+ Base = Has("long") ? "long double" : "double";
+ } else if (Has("char")) {
+ if (Has("int") || Has("short") || Has("long")) Valid = false;
+ Base = Has("unsigned") ? "unsigned char" : Has("signed") ? "signed char" : "char";
+ } else {
+ if (Has("short") && Has("long")) Valid = false;
+ Base = Has("short") ? "short" : Has("long") ?
+ (Counts["long"] == 2 ? "long long" : "long") : "int";
+ if (Has("unsigned")) Base = "unsigned " + Base;
+ }
+ if (!Valid) {
+ Err = "Invalid or unsupported C type expression '" + Expression + "'";
+ return nullptr;
+ }
+ const TypeInfo *Info = resolve(cTypeName(Base));
+ for (unsigned I = 0; I < Depth; ++I) {
+ std::string Identity = Info->CIdentity + '*';
+ const TypeInfo *Existing = resolve(cTypeName(Identity));
+ if (Existing) {
+ Info = Existing;
+ continue;
+ }
+ TypeInfo Pointer;
+ Pointer.Name = cTypeName(Identity);
+ Pointer.CIdentity = Identity;
+ Pointer.Kind = TypeKind::CPointer;
+ Pointer.LLVMType = PointerType::getUnqual(Context);
+ Pointer.Pointer.PointeeTypeName = Info->Name;
+ auto Result = Types.emplace(Pointer.Name, std::move(Pointer));
+ Info = &Result.first->second;
+ }
+ return Info;
+}
+
+bool TypeSystem::defineCAlias(const std::string &Name, const std::string &Expression,
+ std::string &Err) {
+ if (Types.count(Name) || EnumConstants.count(Name)) {
+ Err = "Type name '" + Name + "' is already defined or reserved";
+ return false;
+ }
+ const TypeInfo *C = importCType(Expression, Err);
+ if (!C) return false;
+ TypeInfo Alias = *C;
+ Alias.Name = Name;
+ Types.emplace(Name, std::move(Alias));
+ return true;
}
const TypeInfo *TypeSystem::resolve(const std::string &TypeName) const {
@@ -297,6 +498,9 @@ Constant *TypeSystem::getZeroValue(const std::string &TypeName) const {
return Constant::getNullValue(Info->LLVMType); // zeroinitializer
case TypeKind::Pointer:
return ConstantPointerNull::get(cast<PointerType>(Info->LLVMType));
+ case TypeKind::CScalar:
+ case TypeKind::CPointer:
+ return Constant::getNullValue(Info->LLVMType);
}
return Constant::getNullValue(Info->LLVMType);