diff options
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); |
