diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-20 11:57:16 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-20 11:57:16 +0000 |
| commit | 62b605c6c0ea13e9763c0a0115464007b13a8831 (patch) | |
| tree | 53c27965f5a365682e0d79fd96e25b228ecb9dca /lib/CodeGen | |
| parent | bfe2e75d7e3b372e55369677028429990c49bc54 (diff) | |
refactor(strings): replace compiler builtins with the STRING library
Construct literals as STRING values and route string operations through
ordinary library functions and operators. Preserve value ownership in
records, arrays, constants and file I/O.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/ArrayHandler.cc | 3 | ||||
| -rw-r--r-- | lib/CodeGen/ArrayParameters.cc | 22 | ||||
| -rw-r--r-- | lib/CodeGen/Builtins.h | 11 | ||||
| -rw-r--r-- | lib/CodeGen/CTypeGen.cc | 14 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 24 | ||||
| -rw-r--r-- | lib/CodeGen/ConstantGen.cc | 27 | ||||
| -rw-r--r-- | lib/CodeGen/ConstructorGen.cc | 37 | ||||
| -rw-r--r-- | lib/CodeGen/ControlFlowGen.cc | 11 | ||||
| -rw-r--r-- | lib/CodeGen/Designator.cc | 3 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 108 | ||||
| -rw-r--r-- | lib/CodeGen/FileHandler.cc | 18 | ||||
| -rw-r--r-- | lib/CodeGen/FileRuntimeIO.cc | 1 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/GlobalGen.cc | 14 | ||||
| -rw-r--r-- | lib/CodeGen/NativeTypeSystem.cc | 1 | ||||
| -rw-r--r-- | lib/CodeGen/OperatorGen.cc | 6 | ||||
| -rw-r--r-- | lib/CodeGen/OutputGen.cc | 17 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 38 | ||||
| -rw-r--r-- | lib/CodeGen/StringConversionHandler.cc | 74 | ||||
| -rw-r--r-- | lib/CodeGen/StringHandler.cc | 206 | ||||
| -rw-r--r-- | lib/CodeGen/StringValueGen.cc | 75 | ||||
| -rw-r--r-- | lib/CodeGen/TypeCheckGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/TypeSystem.cc | 8 | ||||
| -rw-r--r-- | lib/CodeGen/ValueTypeGen.cc | 34 |
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); |
