aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'lib/CodeGen')
-rw-r--r--lib/CodeGen/ArrayHandler.cc3
-rw-r--r--lib/CodeGen/ArrayParameters.cc22
-rw-r--r--lib/CodeGen/Builtins.h11
-rw-r--r--lib/CodeGen/CTypeGen.cc14
-rw-r--r--lib/CodeGen/CodeGen.cc24
-rw-r--r--lib/CodeGen/ConstantGen.cc27
-rw-r--r--lib/CodeGen/ConstructorGen.cc37
-rw-r--r--lib/CodeGen/ControlFlowGen.cc11
-rw-r--r--lib/CodeGen/Designator.cc3
-rw-r--r--lib/CodeGen/ExpressionGen.cc108
-rw-r--r--lib/CodeGen/FileHandler.cc18
-rw-r--r--lib/CodeGen/FileRuntimeIO.cc1
-rw-r--r--lib/CodeGen/FunctionGen.cc2
-rw-r--r--lib/CodeGen/GlobalGen.cc14
-rw-r--r--lib/CodeGen/NativeTypeSystem.cc1
-rw-r--r--lib/CodeGen/OperatorGen.cc6
-rw-r--r--lib/CodeGen/OutputGen.cc17
-rw-r--r--lib/CodeGen/StatementGen.cc38
-rw-r--r--lib/CodeGen/StringConversionHandler.cc74
-rw-r--r--lib/CodeGen/StringHandler.cc206
-rw-r--r--lib/CodeGen/StringValueGen.cc75
-rw-r--r--lib/CodeGen/TypeCheckGen.cc2
-rw-r--r--lib/CodeGen/TypeSystem.cc8
-rw-r--r--lib/CodeGen/ValueTypeGen.cc34
24 files changed, 264 insertions, 492 deletions
diff --git a/lib/CodeGen/ArrayHandler.cc b/lib/CodeGen/ArrayHandler.cc
index 7eefc20..1cace28 100644
--- a/lib/CodeGen/ArrayHandler.cc
+++ b/lib/CodeGen/ArrayHandler.cc
@@ -222,9 +222,6 @@ Value *ArrayHandler::loadElement(const std::string &Name, Value *Offset, CodeGen
if (!ElemPtr) return nullptr;
Value *Val = Builder->CreateLoad(Meta->ElementType, ElemPtr, Name + "_elem");
- if (Meta->ElementTypeName == "STRING") {
- Val = CG.emitStringNullGuard(Val);
- }
return Val;
}
diff --git a/lib/CodeGen/ArrayParameters.cc b/lib/CodeGen/ArrayParameters.cc
index 3359c66..a5f0beb 100644
--- a/lib/CodeGen/ArrayParameters.cc
+++ b/lib/CodeGen/ArrayParameters.cc
@@ -51,6 +51,28 @@ void ArrayHandler::bindArrayParameter(const std::string &Name,
false);
FunctionCallee MemcpyFunc = TheModule->getOrInsertFunction("memcpy", MemcpyType);
Builder->CreateCall(MemcpyFunc, {Copy, DataPtr, TotalBytes});
+ if (CG.containsString(ElemInfo)) {
+ auto *F = Builder->GetInsertBlock()->getParent();
+ auto *Index = CG.CreateEntryBlockAlloca(F, Type::getInt64Ty(*TheContext), "copy_index");
+ auto *Check = BasicBlock::Create(*TheContext, "copy_check", F);
+ auto *Body = BasicBlock::Create(*TheContext, "copy_element", F);
+ auto *Done = BasicBlock::Create(*TheContext, "copy_done", F);
+ Builder->CreateStore(Builder->getInt64(0), Index);
+ Builder->CreateBr(Check);
+ Builder->SetInsertPoint(Check);
+ Value *I = Builder->CreateLoad(Builder->getInt64Ty(), Index);
+ Builder->CreateCondBr(Builder->CreateICmpULT(I, TotalElements), Body, Done);
+ Builder->SetInsertPoint(Body);
+ std::vector<size_t> Transfers;
+ Value *Source = Builder->CreateGEP(ElemInfo->LLVMType, DataPtr, I);
+ Value *Value = CG.copyClassValue(ElemInfo, Source, Transfers);
+ if (!Value) return;
+ Builder->CreateStore(Value, Builder->CreateGEP(ElemInfo->LLVMType, Copy, I));
+ CG.transferObjects(Transfers);
+ Builder->CreateStore(Builder->CreateAdd(I, Builder->getInt64(1)), Index);
+ Builder->CreateBr(Check);
+ Builder->SetInsertPoint(Done);
+ }
Ptr = Copy;
}
diff --git a/lib/CodeGen/Builtins.h b/lib/CodeGen/Builtins.h
index b74afc4..2163b3e 100644
--- a/lib/CodeGen/Builtins.h
+++ b/lib/CodeGen/Builtins.h
@@ -14,17 +14,6 @@ struct BuiltinInfo {
};
inline constexpr BuiltinInfo Builtins[] = {
- {"LENGTH", 1, "INTEGER"},
- {"MID", 3, "STRING"},
- {"RIGHT", 2, "STRING"},
- {"LEFT", 2, "STRING"},
- {"LCASE", 1, "STRING"},
- {"UCASE", 1, "STRING"},
- {"ASC", 1, "INTEGER"},
- {"CHR", 1, "STRING"},
- {"IS_NUM", 1, "BOOLEAN"},
- {"NUM_TO_STR", 1, "STRING"},
- {"STR_TO_NUM", 1, "REAL"},
{"EOF", 1, "BOOLEAN"},
{"MALLOC", 1, "C_TYPE(\"void*\")"},
{"REALLOC", 2, "C_TYPE(\"void*\")"},
diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc
index b30814e..18326cb 100644
--- a/lib/CodeGen/CTypeGen.cc
+++ b/lib/CodeGen/CTypeGen.cc
@@ -62,6 +62,16 @@ Value *CodeGen::emitCast(CastExprAST *Cast) {
return nullptr;
}
const TypeInfo *Source = getExprTypeInfo(Operand);
+ if (Source && Source->isString() && Target->CIdentity == "char*")
+ return emitCStringExpr(Operand);
+ if (Source && Source->CIdentity == "char*" && Target->isString())
+ return emitStringFromCString(emitStringNullGuard(emitExpr(Operand)));
+ if (Source && Source->isString() && Target->isString()) {
+ std::vector<size_t> Transfers;
+ Value *Result = emitOwnedValue(Operand, Source, Transfers);
+ transferObjects(Transfers);
+ return Result;
+ }
if (Source && ((Source->Kind == TypeKind::Pointer && Target->CIdentity == "void*") ||
(Source->CIdentity == "void*" && Target->Kind == TypeKind::Pointer)))
return emitExpr(Operand);
@@ -73,10 +83,6 @@ Value *CodeGen::emitCast(CastExprAST *Cast) {
return convertNumericValue(emitExpr(Operand), Source, Target);
if (Source->Kind == TypeKind::CPointer && Target->Kind == TypeKind::CPointer)
return emitExpr(Operand);
- if (Source->isString() && Target->CIdentity == "char*") return emitExpr(Operand);
- if (Source->CIdentity == "char*" && Target->isString())
- return emitStringNullGuard(emitExpr(Operand));
- if (Source->isString() && Target->isString()) return emitExpr(Operand);
reportError("Cannot cast %s to %s", Source->Name.c_str(), Target->Name.c_str());
return nullptr;
}
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 49fc7b1..30b6131 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -59,8 +59,6 @@ CodeGen::CodeGen() {
ArithHandler = std::make_unique<ArithmeticHandler>(*TheContext, *Builder, HadError,
*RuntimeChecker);
- StrHandler = std::make_unique<StringHandler>(*TheContext, *Builder, *TheModule);
- StrConvHandler = std::make_unique<StringConversionHandler>(*TheContext, *Builder, *TheModule);
Files = std::make_unique<FileHandler>(*TheContext, *Builder, *TheModule, *RuntimeChecker,
*Exceptions);
@@ -92,11 +90,6 @@ void CodeGen::SetupExternalFunctions() {
FunctionType *ScanfType = FunctionType::get(Type::getInt32Ty(*TheContext), PrintfArgs, true);
ScanfFunc = TheModule->getOrInsertFunction("scanf", ScanfType);
- FunctionType *MallocType = FunctionType::get(PointerType::getUnqual(*TheContext),
- {Type::getInt64Ty(*TheContext)},
- false);
- MallocFunc = TheModule->getOrInsertFunction("malloc", MallocType);
-
PrintfFormatStr = Builder->CreateGlobalString("%lld\n", "fmt_nl", 0, TheModule.get());
PrintfFloatFormatStr = Builder->CreateGlobalString("%f\n", "fmt_flt", 0, TheModule.get());
PrintfStringFormatStr = Builder->CreateGlobalString("%s\n", "fmt_str", 0, TheModule.get());
@@ -104,7 +97,6 @@ void CodeGen::SetupExternalFunctions() {
ScanfFormatStr = Builder->CreateGlobalString("%lld", "fmt_in", 0, TheModule.get());
ScanfFloatFormatStr = Builder->CreateGlobalString("%lf", "fmt_in_flt", 0, TheModule.get());
- ScanfStringFormatStr = Builder->CreateGlobalString("%s", "fmt_in_str", 0, TheModule.get());
TrueStr = Builder->CreateGlobalString("TRUE", "str_true", 0, TheModule.get());
FalseStr = Builder->CreateGlobalString("FALSE", "str_false", 0, TheModule.get());
@@ -192,19 +184,25 @@ void CodeGen::compile(const std::vector<SourceUnit> &Sources) {
});
FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); });
- struct Definition { size_t Unit; bool IsFunction; bool IsOverload; bool IsExtern; };
+ struct Definition { size_t Unit; bool IsFunction; bool IsOverload; bool IsExtern; int Line; };
IdentifierMap<Definition> Definitions;
std::optional<size_t> CodeUnit;
FunctionDefAST *Entry = nullptr;
size_t EntryUnit = 0;
auto Reserve = [&](const std::string &Name, bool IsFunction = false, bool IsOverload = false,
bool IsExtern = false) {
- auto [It, Added] = Definitions.emplace(Name, Definition{CurrentUnit, IsFunction, IsOverload, IsExtern});
+ auto [It, Added] = Definitions.emplace(Name, Definition{CurrentUnit, IsFunction, IsOverload, IsExtern, DiagnosticLine});
bool Compatible = (IsFunction && IsOverload && It->second.IsFunction && It->second.IsOverload) ||
(IsExtern && It->second.IsExtern);
- if (!Added && It->second.Unit != CurrentUnit && !Compatible)
+ if (!Added && It->second.Unit != CurrentUnit && !Compatible) {
+ // Default library units follow user inputs; point at the user's conflicting definition.
+ bool UserConflict = Sources[CurrentUnit].IsLibrary && !Sources[It->second.Unit].IsLibrary;
+ SaveAndRestore<int> Location(DiagnosticLine, UserConflict ? It->second.Line : DiagnosticLine);
+ SaveAndRestore<std::string> Source(DiagnosticSource,
+ UserConflict ? Sources[It->second.Unit].Path : DiagnosticSource);
reportError("Duplicate definition %s in %s and %s", Name.c_str(),
Sources[It->second.Unit].Path.c_str(), Sources[CurrentUnit].Path.c_str());
+ }
};
for (size_t I = 0; I < Sources.size(); ++I) {
selectUnit(I);
@@ -453,7 +451,9 @@ bool CodeGen::marshalCallArgs(const FuncSig &Signature,
reportError("Unknown variable %s in BYREF call", Var->getName().c_str());
return false;
}
- if (!requireWritable(*Info, Var->getName())) return false;
+ const TypeInfo *ParameterType = resolveType(TypeName);
+ if (!(InternalBinding && ParameterType && ParameterType->isString()) &&
+ !requireWritable(*Info, Var->getName())) return false;
// BYREF aliases the storage, so the types must match exactly.
if (!Types->sameType(Info->TypeName, TypeName)) {
reportError("BYREF argument %s to %s must be of type %s (got %s)",
diff --git a/lib/CodeGen/ConstantGen.cc b/lib/CodeGen/ConstantGen.cc
index fc5d178..374b423 100644
--- a/lib/CodeGen/ConstantGen.cc
+++ b/lib/CodeGen/ConstantGen.cc
@@ -39,7 +39,7 @@ bool CodeGen::prepareSymbolDeclaration(const std::string &Name, bool IsConstant)
reportError("Declaration of %s conflicts with an existing declaration in this scope", Name.c_str());
return false;
}
- if (IsConstant || It->second.ConstantValue || It->second.IsGlobal) Scope.Saved.emplace(Name, It->second);
+ if (IsConstant || It->second.isConstant() || It->second.IsGlobal) Scope.Saved.emplace(Name, It->second);
} else if (IsConstant) {
Scope.Saved.emplace(Name, std::nullopt);
}
@@ -48,7 +48,7 @@ bool CodeGen::prepareSymbolDeclaration(const std::string &Name, bool IsConstant)
}
bool CodeGen::requireWritable(const SymbolInfo &Info, const std::string &Name) {
- if (!Info.ConstantValue) return true;
+ if (!Info.isConstant()) return true;
reportError("Constant %s is not a writable location", Name.c_str());
return false;
}
@@ -65,8 +65,31 @@ void CodeGen::emitConstantStmt(ConstantStmtAST *Stmt) {
return;
}
if (!prepareSymbolDeclaration(Name, true)) return;
+ auto Existing = StringConstants.find(Stmt);
+ if (Existing != StringConstants.end()) {
+ Symbols[Name] = Existing->second;
+ return;
+ }
const TypeInfo *Type = getExprTypeInfo(Stmt->getValue());
Value *Val = emitExpr(Stmt->getValue());
+ if (Type && Type->isString() && Val) {
+ Value *Storage;
+ bool TopLevel = ConstantScopes.back().ID == 0;
+ if (TopLevel) {
+ Storage = new GlobalVariable(*TheModule, Type->LLVMType, false,
+ GlobalValue::InternalLinkage, Types->getZeroValue(Type->Name), "__cps_constant." + Name);
+ } else {
+ Storage = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), Type->LLVMType, Name);
+ }
+ size_t Lifetime = registerStackObject(Storage, Type->Name, DiagnosticLine);
+ Builder->CreateStore(Val, Storage);
+ activateStackObject(Lifetime);
+ SymbolInfo Info{Storage, Type->Name, false, nullptr, ConstantScopes.back().ID};
+ Info.ReadOnly = true;
+ Symbols[Name] = Info;
+ if (TopLevel) StringConstants.emplace(Stmt, Info);
+ return;
+ }
auto *Literal = dyn_cast_or_null<Constant>(Val);
if (!Type || !Literal) {
reportError("CONSTANT initializer must be a literal");
diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc
index 9fc762c..ec97fca 100644
--- a/lib/CodeGen/ConstructorGen.cc
+++ b/lib/CodeGen/ConstructorGen.cc
@@ -20,13 +20,17 @@ void CodeGen::transferObjects(const std::vector<size_t> &Objects) {
Value *CodeGen::emitOwnedValue(ExprAST *Expr, const TypeInfo *Type,
std::vector<size_t> &Transfers) {
- if (!Type || !Type->IsClass) return emitCoercedExpr(Expr, Type);
+ if (!Type || (!Type->IsClass && !containsString(Type))) return emitCoercedExpr(Expr, Type);
const TypeInfo *Source = getExprTypeInfo(Expr);
if (!Source || !Source->sameType(Type)) return emitCoercedExpr(Expr, Type);
Value *Value = nullptr;
if (isObjectLValue(Expr)) {
LValueInfo LV;
- if (!emitLValue(Expr, LV)) return nullptr;
+ const auto *Var = dynamic_cast<VariableExprAST *>(Expr);
+ const auto *Symbol = Var ? getSymbolInfo(Var->getName()) : nullptr;
+ if (Symbol && Symbol->isConstant() && Symbol->Storage) {
+ LV = {symbolAddress(*Symbol), Symbol->TypeName};
+ } else if (!emitLValue(Expr, LV)) return nullptr;
return copyClassValue(Type, LV.Addr, Transfers);
}
Value = emitCoercedExpr(Expr, Type);
@@ -44,7 +48,25 @@ Value *CodeGen::copyClassValue(const TypeInfo *Type, Value *Address,
if (Sig->IsOverload && Sig->Params.size() == 1 && Sig->Params[0].IsByRef &&
!Sig->Params[0].IsArray && Type->sameType(resolveType(Sig->Params[0].TypeName)))
Copy = Sig;
- if (!Copy) return Builder->CreateLoad(Type->LLVMType, Address);
+ if (!Copy) {
+ if (!containsString(Type) || Type->isString())
+ return Builder->CreateLoad(Type->LLVMType, Address);
+ Value *Result = UndefValue::get(Type->LLVMType);
+ std::vector<size_t> Fields;
+ for (const auto &[Name, Field] : Type->Record.Fields) {
+ const auto *FieldType = resolveType(Field.TypeName);
+ Value *Source = Builder->CreateStructGEP(Type->LLVMType, Address, Field.Index);
+ Value *Value = containsString(FieldType)
+ ? copyClassValue(FieldType, Source, Fields)
+ : Builder->CreateLoad(FieldType->LLVMType, Source);
+ if (!Value) return nullptr;
+ Result = Builder->CreateInsertValue(Result, Value, {Field.Index});
+ }
+ auto *Storage = materializeOperatorReceiver(Result, Type);
+ transferObjects(Fields);
+ if (typeNeedsDestruction(Type->Name)) Transfers.push_back(StackObjects.size() - 1);
+ return Builder->CreateLoad(Type->LLVMType, Storage);
+ }
if (!checkOperatorAccess(*Copy)) return nullptr;
auto *Storage = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(),
Type->LLVMType, "copy_object");
@@ -156,6 +178,15 @@ void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const Symb
return;
}
}
+ if (Info && containsString(Info)) {
+ std::vector<size_t> Transfers;
+ Value *Val = emitOwnedValue(Expr, Info, Transfers);
+ if (!Val) return;
+ if (!emitDestroy(Info->Name, Target.Addr, DiagnosticLine)) return;
+ Builder->CreateStore(Val, Target.Addr);
+ transferObjects(Transfers);
+ return;
+ }
Value *Val = emitCoercedExpr(Expr, Info);
if (Val) Builder->CreateStore(Val, Target.Addr);
}
diff --git a/lib/CodeGen/ControlFlowGen.cc b/lib/CodeGen/ControlFlowGen.cc
index 414fe19..771669d 100644
--- a/lib/CodeGen/ControlFlowGen.cc
+++ b/lib/CodeGen/ControlFlowGen.cc
@@ -189,7 +189,9 @@ void CodeGen::emitCaseStmt(CaseStmtAST *Stmt) {
return;
}
const TypeInfo *SelectorType = getExprTypeInfo(&Selector);
- Value *Selected = emitExpr(&Selector);
+ std::vector<size_t> Captured;
+ Value *Selected = containsString(SelectorType)
+ ? emitOwnedValue(&Selector, SelectorType, Captured) : emitExpr(&Selector);
if (!Selected || !SelectorType) return;
auto *F = Builder->GetInsertBlock()->getParent();
@@ -210,7 +212,10 @@ void CodeGen::emitCaseStmt(CaseStmtAST *Stmt) {
reportError("Cannot determine CASE label type");
return nullptr;
}
+ size_t Temporaries = StackObjects.size();
Value *LabelValue = emitExpr(Label);
+ if (containsString(LabelType) && !isObjectLValue(Label))
+ materializeOperatorReceiver(LabelValue, LabelType);
auto Overload = resolveOperator(Op, SelectorType, LabelType);
if (Overload.Signature && !Overload.Ambiguous) {
const TypeInfo *Result = resolveType(Overload.Signature->ReturnTypeName);
@@ -219,7 +224,9 @@ void CodeGen::emitCaseStmt(CaseStmtAST *Stmt) {
return nullptr;
}
}
- return emitBinaryOp(Op, Selected, SelectorType, LabelValue, LabelType, Branch.Line);
+ Value *Result = emitBinaryOp(Op, Selected, SelectorType, LabelValue, LabelType, Branch.Line);
+ cleanupTemporaries(Temporaries);
+ return Result;
};
if (!Branch.Lower) {
Builder->CreateBr(Body);
diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc
index b9c9a1a..12d3b60 100644
--- a/lib/CodeGen/Designator.cc
+++ b/lib/CodeGen/Designator.cc
@@ -262,8 +262,5 @@ Value *CodeGen::loadFromLValue(const LValueInfo &LV) {
return nullptr;
}
Value *Val = Builder->CreateLoad(Info->LLVMType, LV.Addr);
- if (Info->isString()) {
- Val = emitStringNullGuard(Val);
- }
return Val;
}
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index a01cab2..15d0fe3 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -36,7 +36,8 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
}
if (auto *Str = dynamic_cast<StringExprAST*>(Expr)) {
- return StrHandler->createLiteral(Str->getVal());
+ Value *Text = Builder->CreateGlobalString(Str->getVal());
+ return Str->isCString() ? Text : emitStringFromCString(Text);
}
if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) {
@@ -50,7 +51,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
reportError("Unknown variable name %s", Var->getName().c_str());
return nullptr;
}
- if (Info->ConstantValue) return Info->ConstantValue;
+ if (Info->ConstantValue && !Info->Storage) return Info->ConstantValue;
if (Info->IsArray) {
return symbolAddress(*Info);
}
@@ -62,9 +63,6 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
}
Value *Loaded = Builder->CreateLoad(TypeInfo->LLVMType, symbolAddress(*Info), Var->getName().c_str());
- if (TypeInfo->isString()) {
- Loaded = emitStringNullGuard(Loaded);
- }
return Loaded;
}
@@ -190,23 +188,6 @@ Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT,
return emitCBinaryOp(Op, L, LT, R, RT, Line);
if ((LT && LT->isUserKind()) || (RT && RT->isUserKind()))
return emitUserKindBinaryOp(Op, L, LT, R, RT, Line);
- if (Op == '&') {
- L = coerceValueToType(L, resolveType("STRING"));
- R = coerceValueToType(R, resolveType("STRING"));
- return L && R ? StrHandler->emitConcat(L, R) : nullptr;
- }
- switch (Op) {
- case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge:
- if ((LT && LT->isString()) || (RT && RT->isString())) {
- if (!(LT && LT->isString() && RT && RT->isString())) {
- reportError("STRING comparison requires two STRING operands");
- return nullptr;
- }
- return StrHandler->emitCompare(Op, L, R);
- }
- break;
- default: break;
- }
return ArithHandler->emitBinaryOp(Op, L, R, Line);
}
@@ -238,93 +219,12 @@ Value *CodeGen::emitCall(const std::string &Callee,
if (!Address) return nullptr;
return Memory->emitRelease(Address, Line);
}
- if (Name == "LENGTH") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- if (!Str) return nullptr;
- return StrHandler->emitLength(Str);
- }
- if (Name == "MID") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- Value *Start = emitCoercedExpr(ArgExprs[1].get(), resolveType("INTEGER"));
- Value *Len = emitCoercedExpr(ArgExprs[2].get(), resolveType("INTEGER"));
- if (!Str || !Start || !Len) return nullptr;
- return StrHandler->emitMid(Str, Start, Len);
- }
- if (Name == "RIGHT") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- Value *Len = emitCoercedExpr(ArgExprs[1].get(), resolveType("INTEGER"));
- if (!Str || !Len) return nullptr;
- return StrHandler->emitRight(Str, Len);
- }
- if (Name == "LEFT") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- Value *Len = emitCoercedExpr(ArgExprs[1].get(), resolveType("INTEGER"));
- if (!Str || !Len) return nullptr;
- return StrHandler->emitLeft(Str, Len);
- }
- if (Name == "LCASE") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- if (!Str) return nullptr;
- return StrHandler->emitLCase(Str);
- }
- if (Name == "UCASE") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- if (!Str) return nullptr;
- return StrHandler->emitUCase(Str);
- }
- if (Name == "ASC") {
- const TypeInfo *ArgType = getExprTypeInfo(ArgExprs[0].get());
- if (ArgType && (ArgType->isUserKind() || ArgType->isC())) {
- reportError("ASC expects a CHAR or STRING (got %s)", ArgType->Name.c_str());
- return nullptr;
- }
- Value *ArgVal = emitExpr(ArgExprs[0].get());
- if (!ArgVal) return nullptr;
- Value *CharVal = nullptr;
- if (ArgType && ArgType->isChar()) {
- CharVal = coerceValueToType(ArgVal, resolveType("CHAR"));
- } else {
- CharVal = Builder->CreateLoad(Type::getInt8Ty(*TheContext), ArgVal, "char_load");
- }
- Value *AscVal = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "asc_val");
- return coerceValueToType(AscVal, resolveType("INTEGER"));
- }
- if (Name == "CHR") {
- Value *IntVal = emitCoercedExpr(ArgExprs[0].get(), resolveType("INTEGER"));
- if (!IntVal) return nullptr;
- Value *CharVal = Builder->CreateTrunc(IntVal, Type::getInt8Ty(*TheContext), "chr_val");
- return coerceValueToType(CharVal, resolveType("STRING"));
- }
- if (Name == "IS_NUM") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- if (!Str) return nullptr;
- return StrConvHandler->emitIsNum(Str);
- }
- if (Name == "NUM_TO_STR") {
- const TypeInfo *ArgType = getExprTypeInfo(ArgExprs[0].get());
- if (ArgType && (ArgType->isUserKind() || ArgType->isC())) {
- reportError("NUM_TO_STR expects a number (got %s)", ArgType->Name.c_str());
- return nullptr;
- }
- Value *NumV = emitExpr(ArgExprs[0].get());
- if (!NumV) return nullptr;
- bool IsReal = NumV->getType()->isDoubleTy();
- if (NumV->getType()->isIntegerTy(8)) {
- NumV = coerceValueToType(NumV, resolveType("INTEGER"));
- }
- return StrConvHandler->emitNumToStr(NumV, IsReal);
- }
- if (Name == "STR_TO_NUM") {
- Value *Str = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
- if (!Str) return nullptr;
- return StrConvHandler->emitStrToNum(Str, true);
- }
if (Name == "EOF") {
if (isWholeArrayVar(ArgExprs[0].get())) {
reportError("Cannot use a whole array as the EOF filename");
return nullptr;
}
- Value *FileName = emitCoercedExpr(ArgExprs[0].get(), resolveType("STRING"));
+ Value *FileName = emitCStringExpr(ArgExprs[0].get());
if (!FileName) return nullptr;
return Files->emitEof(FileName, Line);
}
diff --git a/lib/CodeGen/FileHandler.cc b/lib/CodeGen/FileHandler.cc
index a7879e0..a2c3492 100644
--- a/lib/CodeGen/FileHandler.cc
+++ b/lib/CodeGen/FileHandler.cc
@@ -39,6 +39,9 @@ void FileHandler::setupExternalFunctions() {
"realloc", FunctionType::get(PtrTy, {PtrTy, I64}, false));
StrcmpFunc = Module.getOrInsertFunction(
"strcmp", FunctionType::get(I32, {PtrTy, PtrTy}, false));
+ StrlenFunc = Module.getOrInsertFunction("strlen", FunctionType::get(I64, {PtrTy}, false));
+ MemcpyFunc = Module.getOrInsertFunction("memcpy", FunctionType::get(PtrTy, {PtrTy, PtrTy, I64}, false));
+ FreeFunc = Module.getOrInsertFunction("free", FunctionType::get(Type::getVoidTy(Context), {PtrTy}, false));
}
void FileHandler::setupGlobals() {
@@ -231,11 +234,22 @@ void FileHandler::buildOpenFn() {
Value *IsRead = Builder.CreateICmpEQ(Mode, ConstantInt::get(I8, uint8_t(FileMode::Read)));
Value *ModeStr = Builder.CreateSelect(IsRead, ModeReadStr, WriteOrAppend, "mode_str");
+ Value *Size = Builder.CreateAdd(Builder.CreateCall(StrlenFunc, {Name}), Builder.getInt64(1));
+ Value *NameCopy = Builder.CreateCall(MallocFunc, {Size}, "owned_filename");
+ Value *AllocationError = Builder.CreateGlobalString("[Fatal] line %d: File name allocation failed\n");
+ Checker.emitCheck(Builder.CreateICmpEQ(NameCopy, ConstantPointerNull::get(PtrTy)), AllocationError, {Line});
+ Builder.CreateCall(MemcpyFunc, {NameCopy, Name, Size});
Value *Handle = Builder.CreateCall(FopenFunc, {Name, ModeStr}, "handle");
Value *OpenFailed = Builder.CreateICmpEQ(Handle, ConstantPointerNull::get(PtrTy), "open_failed");
+ auto *Failed = BasicBlock::Create(Context, "open_failed", OpenFn);
+ auto *Opened = BasicBlock::Create(Context, "opened", OpenFn);
+ Builder.CreateCondBr(OpenFailed, Failed, Opened);
+ Builder.SetInsertPoint(Failed);
+ Builder.CreateCall(FreeFunc, {NameCopy});
Checker.emitCheck(OpenFailed, ErrOpenFail, {Line, Name});
-
- Builder.CreateStore(Name, emitSlotGEP(FileNames, PtrTy, Slot));
+ Builder.CreateBr(Opened);
+ Builder.SetInsertPoint(Opened);
+ Builder.CreateStore(NameCopy, emitSlotGEP(FileNames, PtrTy, Slot));
Builder.CreateStore(Handle, emitSlotGEP(FileHandles, PtrTy, Slot));
Builder.CreateStore(Mode, emitSlotGEP(FileModes, I8, Slot));
Builder.CreateRetVoid();
diff --git a/lib/CodeGen/FileRuntimeIO.cc b/lib/CodeGen/FileRuntimeIO.cc
index ffe5679..c23515d 100644
--- a/lib/CodeGen/FileRuntimeIO.cc
+++ b/lib/CodeGen/FileRuntimeIO.cc
@@ -155,6 +155,7 @@ void FileHandler::buildCloseFn() {
Value *Idx = emitLookupOrFail(Name, Line);
Value *Handle = Builder.CreateLoad(PtrTy, emitSlotGEP(FileHandles, PtrTy, Idx), "handle");
Builder.CreateCall(FcloseFunc, {Handle});
+ Builder.CreateCall(FreeFunc, {Builder.CreateLoad(PtrTy, emitSlotGEP(FileNames, PtrTy, Idx))});
Builder.CreateStore(ConstantInt::get(I8, 0), emitSlotGEP(FileModes, I8, Idx));
Builder.CreateStore(ConstantPointerNull::get(PtrTy), emitSlotGEP(FileNames, PtrTy, Idx));
Builder.CreateStore(ConstantPointerNull::get(PtrTy), emitSlotGEP(FileHandles, PtrTy, Idx));
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index df58f8e..543bd14 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -405,7 +405,7 @@ Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST,
IdentifierMap<SymbolInfo> OldSymbols = Symbols;
Symbols.clear();
for (const auto &[Name, Info] : OldSymbols) {
- if (Info.IsGlobal || (Info.ConstantValue && Info.ScopeID == 0)) Symbols.emplace(Name, Info);
+ if (Info.IsGlobal || (Info.isConstant() && Info.ScopeID == 0)) Symbols.emplace(Name, Info);
}
bool IsMethod = !Proto->getOwnerClass().empty();
diff --git a/lib/CodeGen/GlobalGen.cc b/lib/CodeGen/GlobalGen.cc
index 46b6f0c..b5c39a8 100644
--- a/lib/CodeGen/GlobalGen.cc
+++ b/lib/CodeGen/GlobalGen.cc
@@ -39,10 +39,19 @@ void CodeGen::runGlobalPrePass(const SourceUnit &Unit, int Phase) {
reportError("Global %s conflicts with an existing definition", Name.c_str());
continue;
}
+ const TypeInfo *Type = resolveType(TypeName);
+ if (!Type) {
+ reportError("Unknown type %s", TypeName.c_str());
+ continue;
+ }
auto *Storage = new GlobalVariable(*TheModule, Types->getLLVMType(TypeName), false,
GlobalValue::InternalLinkage, Types->getZeroValue(TypeName), "__cps_global." + Name);
SymbolInfo Info{Storage, TypeName, false, nullptr, 0, false, true};
Exports[CurrentUnit].emplace(Name, Info);
+ if (Type->isString()) {
+ size_t Lifetime = registerStackObject(Storage, Type->Name, DiagnosticLine);
+ if (emitDefaultConstruct(Type->Name, Storage, DiagnosticLine)) activateStackObject(Lifetime);
+ }
} else if (auto *Assign = dynamic_cast<AssignStmtAST*>(S.get())) {
if (Phase != 1) continue;
const SymbolInfo *Info = getSymbolInfo(Assign->getName());
@@ -55,6 +64,11 @@ void CodeGen::runGlobalPrePass(const SourceUnit &Unit, int Phase) {
reportError("GLOBAL initializer must be a literal");
continue;
}
+ if (const auto *Type = resolveType(Info->TypeName); Type && Type->isString()) {
+ emitAssignment({Info->Storage, Info->TypeName}, Assign->getExpr(), Info);
+ cleanupTemporaries();
+ continue;
+ }
auto *Value = dyn_cast_or_null<Constant>(
emitCoercedExpr(Assign->getExpr(), resolveType(Info->TypeName)));
if (!Value) {
diff --git a/lib/CodeGen/NativeTypeSystem.cc b/lib/CodeGen/NativeTypeSystem.cc
index 2dbbc49..7accda7 100644
--- a/lib/CodeGen/NativeTypeSystem.cc
+++ b/lib/CodeGen/NativeTypeSystem.cc
@@ -39,7 +39,6 @@ void TypeSystem::registerBuiltins() {
registerType("INTEGER", Type::getInt64Ty(Context), TypeKind::Integer);
registerType("REAL", Type::getDoubleTy(Context), TypeKind::Real);
registerType("BOOLEAN", Type::getInt1Ty(Context), TypeKind::Boolean);
- registerType("STRING", PointerType::getUnqual(Context), TypeKind::String);
registerType("CHAR", Type::getInt8Ty(Context), TypeKind::Char);
registerType("VOID", Type::getVoidTy(Context), TypeKind::Void);
diff --git a/lib/CodeGen/OperatorGen.cc b/lib/CodeGen/OperatorGen.cc
index b449ba1..67fd1e4 100644
--- a/lib/CodeGen/OperatorGen.cc
+++ b/lib/CodeGen/OperatorGen.cc
@@ -88,12 +88,14 @@ Value *CodeGen::emitOperatorValues(const FuncSig &Signature, Value *Left,
};
if (Member) Args.push_back(Capture(Left, LeftType));
else {
- if (LeftType->IsClass) Left = copyClassValue(LeftType, Capture(Left, LeftType), Transfers);
+ if (LeftType->IsClass || containsString(LeftType))
+ Left = copyClassValue(LeftType, Capture(Left, LeftType), Transfers);
if (!Left) return nullptr;
Args.push_back(Left);
}
const TypeInfo *RightType = resolveType(Signature.Params[Member ? 0 : 1].TypeName);
- if (RightType->IsClass) Right = copyClassValue(RightType, Capture(Right, RightType), Transfers);
+ if (RightType->IsClass || containsString(RightType))
+ Right = copyClassValue(RightType, Capture(Right, RightType), Transfers);
if (!Right) return nullptr;
Args.push_back(Right);
transferObjects(Transfers);
diff --git a/lib/CodeGen/OutputGen.cc b/lib/CodeGen/OutputGen.cc
index c4ceb79..c1ea4b6 100644
--- a/lib/CodeGen/OutputGen.cc
+++ b/lib/CodeGen/OutputGen.cc
@@ -13,6 +13,13 @@ Value *CodeGen::emitStringNullGuard(Value *StrVal) {
void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewline) {
if (!Val || !Info) return;
+ if (Info->isString()) {
+ Value *Text = emitStringCString(Val);
+ if (Text) Builder->CreateCall(PrintfFunc, {
+ AppendNewline ? PrintfStringFormatStr
+ : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()), Text});
+ return;
+ }
std::vector<Value*> Args;
Value *Fmt = nullptr;
@@ -38,16 +45,6 @@ void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewli
break;
}
- case TypeKind::String: {
- Value *StrVal = emitStringNullGuard(Val);
- Fmt = AppendNewline
- ? PrintfStringFormatStr
- : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get());
- Args.push_back(Fmt);
- Args.push_back(StrVal);
- break;
- }
-
case TypeKind::Char: {
Value *CharVal = coerceValueToType(Val, resolveType("CHAR"));
Value *Promoted = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "char_for_printf");
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index c0bc223..5b60d29 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -97,7 +97,7 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
if (auto *Call = dynamic_cast<MemberCallStmtAST*>(Stmt)) {
Value *Result = emitMemberCall(Call->getCall());
const TypeInfo *Type = getExprTypeInfo(Call->getCall());
- if (Result && Type && Type->IsClass) materializeOperatorReceiver(Result, Type);
+ if (Result && Type && (Type->IsClass || containsString(Type))) materializeOperatorReceiver(Result, Type);
return;
}
@@ -106,7 +106,7 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
const FuncSig *Sig = FuncGen->hasFunction(Call->getCallee())
? resolveCall(Call->getCallee(), Call->getArgs()) : nullptr;
const TypeInfo *Type = Sig ? resolveType(Sig->ReturnTypeName) : nullptr;
- if (Result && Type && Type->IsClass) materializeOperatorReceiver(Result, Type);
+ if (Result && Type && (Type->IsClass || containsString(Type))) materializeOperatorReceiver(Result, Type);
return;
}
@@ -195,11 +195,7 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
Value *BoolVal = Builder->CreateICmpNE(Val, ConstantInt::get(*TheContext, APInt(64, 0)), "bool_cast");
Builder->CreateStore(BoolVal, Target.Addr);
} else if (TypeInfo->isString()) {
- Value *Mem = Builder->CreateCall(MallocFunc, ConstantInt::get(*TheContext, APInt(64, 1024)), "input_str");
- Args.push_back(ScanfStringFormatStr);
- Args.push_back(Mem);
- Builder->CreateCall(ScanfFunc, Args);
- Builder->CreateStore(Mem, Target.Addr);
+ emitMethodCall(TypeInfo->Name, Target.Addr, "READ_INPUT", {}, Stmt->getLine());
} else if (TypeInfo->isChar()) {
Value *CharFmt = Builder->CreateGlobalString(" %c", "fmt_in_char", 0, TheModule.get());
Args.push_back(CharFmt);
@@ -233,6 +229,8 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
reportError("Cannot OUTPUT the result of a PROCEDURE call");
continue;
}
+ if (TypeInfo->IsClass && !isObjectLValue(Expr))
+ materializeOperatorReceiver(Val, TypeInfo);
emitOutputValue(Val, TypeInfo, I + 1 == Exprs.size());
}
return;
@@ -243,7 +241,7 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
reportError("Cannot use a whole array as the OPENFILE filename");
return;
}
- Value *Name = emitCoercedExpr(Open->getFileName(), resolveType("STRING"));
+ Value *Name = emitCStringExpr(Open->getFileName());
if (!Name) return;
Files->emitOpen(Name, Open->getMode(), Open->getLine());
return;
@@ -260,7 +258,7 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
reportError("READFILE into entire array %s is not supported", Read->getVarName().c_str());
return;
}
- if (Info->TypeName != "STRING") {
+ if (!sameIdentifier(Info->TypeName, "STRING")) {
reportError("READFILE variable %s must be of type STRING (got %s)",
Read->getVarName().c_str(), Info->TypeName.c_str());
return;
@@ -269,11 +267,23 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
reportError("Cannot use a whole array as the READFILE filename");
return;
}
- Value *Name = emitCoercedExpr(Read->getFileName(), resolveType("STRING"));
+ Value *Name = emitCStringExpr(Read->getFileName());
if (!Name) return;
Value *LineStr = Files->emitRead(Name, Read->getLine());
if (!LineStr) return;
- Builder->CreateStore(LineStr, Info->Storage);
+ const auto *Type = resolveType(Info->TypeName);
+ for (const FuncSig *Sig : FuncGen->getOverloads(Type->Name + ".OPERATOR <-")) {
+ if (Sig->Params.size() == 1 && !Sig->Params[0].IsByRef &&
+ resolveType("C_STRING")->sameType(resolveType(Sig->Params[0].TypeName)) &&
+ checkOperatorAccess(*Sig)) {
+ FuncGen->emitCall(*Sig, {symbolAddress(*Info), LineStr});
+ auto Free = TheModule->getOrInsertFunction("free", FunctionType::get(
+ Type::getVoidTy(*TheContext), {PointerType::getUnqual(*TheContext)}, false));
+ Builder->CreateCall(Free, {LineStr});
+ return;
+ }
+ }
+ reportError("READFILE requires STRING.OPERATOR<-(Text : C_STRING)");
return;
}
@@ -282,8 +292,8 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
reportError("Cannot use a whole array in WRITEFILE");
return;
}
- Value *Name = emitCoercedExpr(Write->getFileName(), resolveType("STRING"));
- Value *Data = emitCoercedExpr(Write->getData(), resolveType("STRING"));
+ Value *Name = emitCStringExpr(Write->getFileName());
+ Value *Data = emitCStringExpr(Write->getData());
if (!Name || !Data) return;
Files->emitWrite(Name, Data, Write->getLine());
return;
@@ -294,7 +304,7 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
reportError("Cannot use a whole array as the CLOSEFILE filename");
return;
}
- Value *Name = emitCoercedExpr(Close->getFileName(), resolveType("STRING"));
+ Value *Name = emitCStringExpr(Close->getFileName());
if (!Name) return;
Files->emitClose(Name, Close->getLine());
return;
diff --git a/lib/CodeGen/StringConversionHandler.cc b/lib/CodeGen/StringConversionHandler.cc
deleted file mode 100644
index 2263ef1..0000000
--- a/lib/CodeGen/StringConversionHandler.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "cps/StringConversionHandler.h"
-#include <limits>
-
-using namespace llvm;
-using namespace cps;
-
-StringConversionHandler::StringConversionHandler(LLVMContext &Ctx, IRBuilder<> &B, llvm::Module &M)
- : Context(Ctx), Builder(B), Module(M) {
- setupExternalFunctions();
-}
-
-void StringConversionHandler::setupExternalFunctions() {
- FunctionType *SnprintfType = FunctionType::get(Type::getInt32Ty(Context), {PointerType::getUnqual(Context), Type::getInt64Ty(Context), PointerType::getUnqual(Context)}, true);
- SnprintfFunc = Module.getOrInsertFunction("snprintf", SnprintfType);
-
- FunctionType *StrtollType = FunctionType::get(Type::getInt64Ty(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context), Type::getInt32Ty(Context)}, false);
- StrtollFunc = Module.getOrInsertFunction("strtoll", StrtollType);
-
- FunctionType *StrtodType = FunctionType::get(Type::getDoubleTy(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context)}, false);
- StrtodFunc = Module.getOrInsertFunction("strtod", StrtodType);
-
- FunctionType *MallocType = FunctionType::get(PointerType::getUnqual(Context), {Type::getInt64Ty(Context)}, false);
- MallocFunc = Module.getOrInsertFunction("malloc", MallocType);
-}
-
-Value *StringConversionHandler::emitNumToStr(Value *Num, bool IsReal) {
- // Allow an extra integer digit, sign, decimal point, six fractional digits and NUL.
- Value *AllocSize = ConstantInt::get(Type::getInt64Ty(Context),
- IsReal ? std::numeric_limits<double>::max_exponent10 + 10 : 64);
- Value *Buffer = Builder.CreateCall(MallocFunc, AllocSize, "num_str_buf");
-
- Value *FormatStr;
- if (IsReal) {
- FormatStr = Builder.CreateGlobalString("%f");
- } else {
- FormatStr = Builder.CreateGlobalString("%lld");
- }
-
- Builder.CreateCall(SnprintfFunc, {Buffer, AllocSize, FormatStr, Num});
- return Buffer;
-}
-
-Value *StringConversionHandler::emitStrToNum(Value *Str, bool AsReal) {
- Value *NullPtr = ConstantPointerNull::get(PointerType::getUnqual(Context));
- if (AsReal) {
- return Builder.CreateCall(StrtodFunc, {Str, NullPtr}, "str_to_real");
- } else {
- Value *Base = ConstantInt::get(Type::getInt32Ty(Context), 10);
- return Builder.CreateCall(StrtollFunc, {Str, NullPtr, Base}, "str_to_int");
- }
-}
-
-Value *StringConversionHandler::emitIsNum(Value *Str) {
- Function *TheFunction = Builder.GetInsertBlock()->getParent();
-
- IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin());
- AllocaInst *EndPtrAlloc = TmpB.CreateAlloca(PointerType::getUnqual(Context), nullptr, "endptr_alloc");
-
- Builder.CreateCall(StrtodFunc, {Str, EndPtrAlloc});
- Value *EndPtrVal = Builder.CreateLoad(PointerType::getUnqual(Context), EndPtrAlloc, "endptr_val");
-
- FunctionType *StrLenType = FunctionType::get(Type::getInt64Ty(Context), {PointerType::getUnqual(Context)}, false);
- FunctionCallee StrLenFunc = Module.getOrInsertFunction("strlen", StrLenType);
- Value *Len = Builder.CreateCall(StrLenFunc, Str, "str_len");
-
- Value *ExpectedEndPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), Str, Len);
-
- Value *IsMatch = Builder.CreateICmpEQ(EndPtrVal, ExpectedEndPtr, "is_full_match");
-
- Value *Zero = ConstantInt::get(Type::getInt64Ty(Context), 0);
- Value *IsNotEmpty = Builder.CreateICmpSGT(Len, Zero, "is_not_empty");
-
- return Builder.CreateAnd(IsMatch, IsNotEmpty, "is_num_bool");
-}
diff --git a/lib/CodeGen/StringHandler.cc b/lib/CodeGen/StringHandler.cc
deleted file mode 100644
index f91539c..0000000
--- a/lib/CodeGen/StringHandler.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-#include "cps/StringHandler.h"
-#include "cps/Lexer.h"
-#include "llvm/IR/Type.h"
-#include "llvm/IR/DerivedTypes.h"
-#include "llvm/IR/Constants.h"
-
-using namespace llvm;
-using namespace cps;
-
-StringHandler::StringHandler(LLVMContext &Ctx, IRBuilder<> &B, llvm::Module &M)
- : Context(Ctx), Builder(B), Module(M) {
- setupExternalFunctions();
-}
-
-void StringHandler::setupExternalFunctions() {
- FunctionType *MallocType = FunctionType::get(PointerType::getUnqual(Context), {Type::getInt64Ty(Context)}, false);
- MallocFunc = Module.getOrInsertFunction("malloc", MallocType);
-
- FunctionType *StrLenType = FunctionType::get(Type::getInt64Ty(Context), {PointerType::getUnqual(Context)}, false);
- StrLenFunc = Module.getOrInsertFunction("strlen", StrLenType);
-
- std::vector<Type*> MemCpyArgs = {PointerType::getUnqual(Context), PointerType::getUnqual(Context), Type::getInt64Ty(Context)};
- FunctionType *MemCpyType = FunctionType::get(PointerType::getUnqual(Context), MemCpyArgs, false);
- MemCpyFunc = Module.getOrInsertFunction("memcpy", MemCpyType);
-
- FunctionType *StrCpyType = FunctionType::get(PointerType::getUnqual(Context),
- {PointerType::getUnqual(Context), PointerType::getUnqual(Context)},
- false);
- StrCpyFunc = Module.getOrInsertFunction("strcpy", StrCpyType);
- StrCatFunc = Module.getOrInsertFunction("strcat", StrCpyType);
- StrCmpFunc = Module.getOrInsertFunction("strcmp",
- FunctionType::get(Type::getInt32Ty(Context),
- {PointerType::getUnqual(Context), PointerType::getUnqual(Context)}, false));
-
- FunctionType *CharCaseType = FunctionType::get(Type::getInt32Ty(Context), {Type::getInt32Ty(Context)}, false);
- ToUpperFunc = Module.getOrInsertFunction("toupper", CharCaseType);
- ToLowerFunc = Module.getOrInsertFunction("tolower", CharCaseType);
-}
-
-Value *StringHandler::createLiteral(const std::string &Val) {
- return Builder.CreateGlobalString(Val);
-}
-
-Value *StringHandler::emitLength(Value *Str) {
- if (!Str) return nullptr;
- return Builder.CreateCall(StrLenFunc, Str, "len");
-}
-
-Value *StringHandler::emitConcat(Value *LHS, Value *RHS) {
- if (!LHS || !RHS) return nullptr;
-
- Value *LLen = Builder.CreateCall(StrLenFunc, {LHS}, "llen");
- Value *RLen = Builder.CreateCall(StrLenFunc, {RHS}, "rlen");
- Value *TotalLen = Builder.CreateAdd(LLen, RLen, "totallen");
- Value *AllocSize = Builder.CreateAdd(TotalLen, ConstantInt::get(Type::getInt64Ty(Context), 1), "allocsize");
-
- Value *NewStr = Builder.CreateCall(MallocFunc, {AllocSize}, "concat_str");
-
- Builder.CreateCall(StrCpyFunc, {NewStr, LHS});
- Builder.CreateCall(StrCatFunc, {NewStr, RHS});
-
- return NewStr;
-}
-
-Value *StringHandler::emitCompare(int Op, Value *LHS, Value *RHS) {
- if (!LHS || !RHS) return nullptr;
-
- CmpInst::Predicate Pred;
- switch (Op) {
- case tok_eq: Pred = CmpInst::ICMP_EQ; break;
- case tok_ne: Pred = CmpInst::ICMP_NE; break;
- case '<': Pred = CmpInst::ICMP_SLT; break;
- case '>': Pred = CmpInst::ICMP_SGT; break;
- case tok_le: Pred = CmpInst::ICMP_SLE; break;
- case tok_ge: Pred = CmpInst::ICMP_SGE; break;
- default: return nullptr;
- }
-
- Value *Cmp = Builder.CreateCall(StrCmpFunc, {LHS, RHS}, "strcmp_result");
- return Builder.CreateICmp(Pred, Cmp, ConstantInt::get(Type::getInt32Ty(Context), 0), "str_compare");
-}
-
-// malloc(Len + 1), copy Len bytes from Src + StartIdx, NUL-terminate.
-// Clamping is each caller's business — MID/RIGHT/LEFT clamp different things.
-Value *StringHandler::allocCopySubstring(Value *Src, Value *StartIdx, Value *Len) {
- Value *One = ConstantInt::get(Context, APInt(64, 1));
- Value *AllocSize = Builder.CreateAdd(Len, One);
- Value *NewStr = Builder.CreateCall(MallocFunc, AllocSize, "substr_mem");
-
- Value *SrcPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), Src, StartIdx);
- Builder.CreateCall(MemCpyFunc, {NewStr, SrcPtr, Len});
-
- Value *NullTermPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), NewStr, Len);
- Builder.CreateStore(ConstantInt::get(Type::getInt8Ty(Context), 0), NullTermPtr);
-
- return NewStr;
-}
-
-Value *StringHandler::emitMid(Value *Str, Value *Start, Value *Len) {
- Value *FullLen = emitLength(Str);
-
- Value *One = ConstantInt::get(Context, APInt(64, 1));
- Value *Zero = ConstantInt::get(Context, APInt(64, 0));
- Value *StartZeroBased = Builder.CreateSub(Start, One, "start_idx");
-
- Value *IsNeg = Builder.CreateICmpSLT(StartZeroBased, Zero);
- StartZeroBased = Builder.CreateSelect(IsNeg, Zero, StartZeroBased);
-
- Value *IsTooBig = Builder.CreateICmpSGT(StartZeroBased, FullLen);
- StartZeroBased = Builder.CreateSelect(IsTooBig, FullLen, StartZeroBased);
-
- Value *RemLen = Builder.CreateSub(FullLen, StartZeroBased);
-
- Value *IsLenTooBig = Builder.CreateICmpSGT(Len, RemLen);
- Value *ActualLen = Builder.CreateSelect(IsLenTooBig, RemLen, Len);
-
- Value *IsLenNeg = Builder.CreateICmpSLT(ActualLen, Zero);
- ActualLen = Builder.CreateSelect(IsLenNeg, Zero, ActualLen);
-
- return allocCopySubstring(Str, StartZeroBased, ActualLen);
-}
-
-Value *StringHandler::emitRight(Value *Str, Value *Len) {
- Value *FullLen = emitLength(Str);
-
- Value *StartIdx = Builder.CreateSub(FullLen, Len, "right_start");
-
- Value *Zero = ConstantInt::get(Context, APInt(64, 0));
- Value *IsNeg = Builder.CreateICmpSLT(StartIdx, Zero);
- StartIdx = Builder.CreateSelect(IsNeg, Zero, StartIdx);
-
- Value *ActualLen = Builder.CreateSub(FullLen, StartIdx);
-
- return allocCopySubstring(Str, StartIdx, ActualLen);
-}
-
-Value *StringHandler::emitLeft(Value *Str, Value *Len) {
- Value *FullLen = emitLength(Str);
-
- Value *Zero = ConstantInt::get(Context, APInt(64, 0));
- Value *IsNeg = Builder.CreateICmpSLT(Len, Zero);
- Value *SafeLen = Builder.CreateSelect(IsNeg, Zero, Len);
-
- Value *IsTooBig = Builder.CreateICmpSGT(SafeLen, FullLen);
- Value *ActualLen = Builder.CreateSelect(IsTooBig, FullLen, SafeLen);
-
- return allocCopySubstring(Str, Zero, ActualLen);
-}
-
-// Shared loop for LCASE/UCASE: malloc a copy of Str with every byte run
-// through CaseFn (tolower/toupper).
-Value *StringHandler::emitCaseConvert(Value *Str, FunctionCallee CaseFn) {
- if (!Str) return nullptr;
- Value *Len = emitLength(Str);
- Value *One = ConstantInt::get(Context, APInt(64, 1));
- Value *AllocSize = Builder.CreateAdd(Len, One);
- Value *NewStr = Builder.CreateCall(MallocFunc, AllocSize, "case_str");
-
- Function *TheFunction = Builder.GetInsertBlock()->getParent();
- BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", TheFunction);
- BasicBlock *AfterBB = BasicBlock::Create(Context, "afterloop", TheFunction);
-
- IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin());
- AllocaInst *IdxVar = TmpB.CreateAlloca(Type::getInt64Ty(Context), nullptr, "idx");
- Builder.CreateStore(ConstantInt::get(Context, APInt(64, 0)), IdxVar);
-
- Builder.CreateBr(LoopBB);
- Builder.SetInsertPoint(LoopBB);
-
- Value *CurIdx = Builder.CreateLoad(Type::getInt64Ty(Context), IdxVar);
- Value *Cond = Builder.CreateICmpSLT(CurIdx, Len);
-
- BasicBlock *BodyBB = BasicBlock::Create(Context, "body", TheFunction);
- Builder.CreateCondBr(Cond, BodyBB, AfterBB);
-
- Builder.SetInsertPoint(BodyBB);
-
- Value *SrcPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), Str, CurIdx);
- Value *CharVal = Builder.CreateLoad(Type::getInt8Ty(Context), SrcPtr);
-
- Value *ExtChar = Builder.CreateSExt(CharVal, Type::getInt32Ty(Context));
- Value *ConvChar = Builder.CreateCall(CaseFn, ExtChar);
- Value *TruncChar = Builder.CreateTrunc(ConvChar, Type::getInt8Ty(Context));
-
- Value *DestPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), NewStr, CurIdx);
- Builder.CreateStore(TruncChar, DestPtr);
-
- Value *NextIdx = Builder.CreateAdd(CurIdx, One);
- Builder.CreateStore(NextIdx, IdxVar);
- Builder.CreateBr(LoopBB);
-
- Builder.SetInsertPoint(AfterBB);
-
- Value *NullPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), NewStr, Len);
- Builder.CreateStore(ConstantInt::get(Type::getInt8Ty(Context), 0), NullPtr);
-
- return NewStr;
-}
-
-Value *StringHandler::emitLCase(Value *Str) {
- return emitCaseConvert(Str, ToLowerFunc);
-}
-
-Value *StringHandler::emitUCase(Value *Str) {
- return emitCaseConvert(Str, ToUpperFunc);
-}
diff --git a/lib/CodeGen/StringValueGen.cc b/lib/CodeGen/StringValueGen.cc
new file mode 100644
index 0000000..ce9694c
--- /dev/null
+++ b/lib/CodeGen/StringValueGen.cc
@@ -0,0 +1,75 @@
+#include "cps/CodeGen.h"
+#include "cps/Operator.h"
+
+using namespace cps;
+using namespace llvm;
+
+const TypeInfo *CodeGen::requireStringType() {
+ const TypeInfo *Type = resolveType("STRING");
+ if (!Type || !Type->IsClass) {
+ reportError("String literals require a visible STRING class; include the string library or omit -no-std");
+ return nullptr;
+ }
+ return Type;
+}
+
+Value *CodeGen::emitStringFromCString(Value *Text) {
+ const TypeInfo *Type = requireStringType();
+ if (!Type || !Text) return nullptr;
+ const FuncSig *Constructor = nullptr;
+ for (const FuncSig *Sig : FuncGen->getOverloads(Type->Name + ".NEW"))
+ if (Sig->Params.size() == 1 && !Sig->Params[0].IsArray && !Sig->Params[0].IsByRef &&
+ resolveType("C_STRING")->sameType(resolveType(Sig->Params[0].TypeName)))
+ Constructor = Sig;
+ if (!Constructor) {
+ reportError("STRING requires a constructor NEW(Text : C_STRING)");
+ return nullptr;
+ }
+ if (!checkOperatorAccess(*Constructor)) return nullptr;
+ auto *Storage = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(),
+ Type->LLVMType, "string_value");
+ Builder->CreateStore(Types->getZeroValue(Type->Name), Storage);
+ for (const auto &[Name, Field] : Type->Record.Fields)
+ if (typeNeedsConstruction(Field.TypeName) &&
+ !emitDefaultConstruct(Field.TypeName,
+ Builder->CreateStructGEP(Type->LLVMType, Storage, Field.Index), DiagnosticLine))
+ return nullptr;
+ if (!FuncGen->emitCall(*Constructor, {Storage, Text})) return nullptr;
+ // The consumer takes ownership, just as with an ordinary class result.
+ return Builder->CreateLoad(Type->LLVMType, Storage, "string");
+}
+
+Value *CodeGen::emitStringCString(Value *Value) {
+ const TypeInfo *Type = requireStringType();
+ if (!Type || !Value) return nullptr;
+ const FuncSig *Method = resolveMethod(Type->Name, "C_STR", {});
+ if (!Method || !resolveType("C_STRING")->sameType(resolveType(Method->ReturnTypeName))) {
+ reportError("STRING requires a method C_STR() RETURNS C_STRING");
+ return nullptr;
+ }
+ auto *Receiver = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(),
+ Type->LLVMType, "string_view");
+ Builder->CreateStore(Value, Receiver);
+ return emitMethodCall(Type->Name, Receiver, "C_STR", {}, DiagnosticLine);
+}
+
+Value *CodeGen::emitCStringExpr(ExprAST *Expr) {
+ const TypeInfo *Type = getExprTypeInfo(Expr);
+ if (!Type || !Type->isString() || isWholeArrayVar(Expr)) {
+ reportError("Expected a STRING value");
+ return nullptr;
+ }
+ Value *Value = emitExpr(Expr);
+ if (!Value) return nullptr;
+ if (!isObjectLValue(Expr)) materializeOperatorReceiver(Value, Type);
+ return emitStringCString(Value);
+}
+
+bool CodeGen::containsString(const TypeInfo *Type) const {
+ if (!Type) return false;
+ if (Type->isString()) return true;
+ if (Type->asRecord())
+ for (const auto &[Name, Field] : Type->Record.Fields)
+ if (containsString(resolveType(Field.TypeName))) return true;
+ return false;
+}
diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc
index 5d014f6..0b66cfc 100644
--- a/lib/CodeGen/TypeCheckGen.cc
+++ b/lib/CodeGen/TypeCheckGen.cc
@@ -46,7 +46,7 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
}
const TypeInfo *Src = getExprTypeInfo(E);
- if (Src && Src->Kind == TypeKind::String && Target->Kind == TypeKind::Boolean) {
+ if (Src && Src->isString() && Target->Kind == TypeKind::Boolean) {
reportError("Cannot implicitly convert STRING to BOOLEAN");
return nullptr;
}
diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc
index 34b8d2d..6cf0f00 100644
--- a/lib/CodeGen/TypeSystem.cc
+++ b/lib/CodeGen/TypeSystem.cc
@@ -41,7 +41,7 @@ bool TypeInfo::isBoolean() const {
}
bool TypeInfo::isString() const {
- return Kind == TypeKind::String;
+ return IsClass && sameIdentifier(Name, "STRING");
}
bool TypeInfo::isChar() const {
@@ -73,17 +73,17 @@ bool TypeInfo::isOutputtable() const {
case TypeKind::Integer:
case TypeKind::Real:
case TypeKind::Boolean:
- case TypeKind::String:
case TypeKind::Char:
case TypeKind::Enum: // prints its ordinal
return true;
case TypeKind::CScalar:
return !isVoid();
case TypeKind::Void:
- case TypeKind::Record:
case TypeKind::Pointer:
case TypeKind::CPointer:
return false;
+ case TypeKind::Record:
+ return isString();
}
return false;
}
@@ -299,8 +299,6 @@ Constant *TypeSystem::getZeroValue(const std::string &TypeName) const {
return ConstantInt::get(Context, APInt(1, 0));
case TypeKind::Char:
return ConstantInt::get(Context, APInt(8, 0));
- case TypeKind::String:
- return ConstantPointerNull::get(cast<PointerType>(Info->LLVMType));
case TypeKind::Void:
return nullptr;
case TypeKind::Enum:
diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc
index f2dafb1..487d61d 100644
--- a/lib/CodeGen/ValueTypeGen.cc
+++ b/lib/CodeGen/ValueTypeGen.cc
@@ -16,7 +16,7 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) {
if (dynamic_cast<BooleanExprAST*>(Expr)) return resolveType("BOOLEAN");
if (dynamic_cast<CharExprAST*>(Expr)) return resolveType("CHAR");
if (auto *String = dynamic_cast<StringExprAST*>(Expr))
- return resolveType(String->isCString() ? "C_STRING" : "STRING");
+ return String->isCString() ? resolveType("C_STRING") : requireStringType();
if (auto *Cast = dynamic_cast<CastExprAST*>(Expr))
return resolveType(Cast->getTypeName());
@@ -200,31 +200,6 @@ Value *CodeGen::coerceValueToType(Value *Val, const TypeInfo *TargetInfo) {
return Builder->CreateTruncOrBitCast(Val, Type::getInt8Ty(*TheContext), "int_to_char");
break;
- case TypeKind::String:
- if (Val->getType()->isPointerTy()) return Val;
- if (Val->getType()->isIntegerTy(1)) {
- return Builder->CreateSelect(Val, TrueStr, FalseStr, "bool_to_str");
- }
- if (Val->getType()->isDoubleTy()) {
- return StrConvHandler->emitNumToStr(Val, true);
- }
- if (Val->getType()->isIntegerTy(8)) {
- Value *Mem = Builder->CreateCall(MallocFunc,
- ConstantInt::get(*TheContext, APInt(64, 2)),
- "char_str");
- Builder->CreateStore(Val, Mem);
- Value *NullPtr = Builder->CreateInBoundsGEP(Type::getInt8Ty(*TheContext),
- Mem,
- ConstantInt::get(*TheContext, APInt(64, 1)));
- Builder->CreateStore(ConstantInt::get(Type::getInt8Ty(*TheContext), 0), NullPtr);
- return Mem;
- }
- if (Val->getType()->isIntegerTy()) {
- Value *AsInt = coerceValueToType(Val, resolveType("INTEGER"));
- return AsInt ? StrConvHandler->emitNumToStr(AsInt, false) : nullptr;
- }
- break;
-
case TypeKind::Void:
return nullptr;
@@ -269,12 +244,7 @@ void CodeGen::emitDeclareStmt(DeclareStmtAST *Stmt) {
AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Info->LLVMType, Stmt->getName());
size_t Lifetime = registerStackObject(Alloca, Info->Name, Stmt->getLine());
- Value *InitVal = nullptr;
- if (Info->isString()) {
- InitVal = EmptyStringStr;
- } else {
- InitVal = Types->getZeroValue(Info->Name);
- }
+ Value *InitVal = Types->getZeroValue(Info->Name);
if (InitVal) {
Builder->CreateStore(InitVal, Alloca);