diff options
Diffstat (limited to 'lib/CodeGen/CodeGen.cc')
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 1099 |
1 files changed, 2 insertions, 1097 deletions
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 7e463db..6c6f682 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -1,10 +1,8 @@ #include "cps/CodeGen.h" #include "cps/ArrayHandler.h" -#include "cps/FileAST.h" #include "cps/FileHandler.h" #include "cps/ExceptionHandler.h" -#include "cps/Lexer.h" -#include "cps/TypeAST.h" +#include "Builtins.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Verifier.h" #include <cstdarg> @@ -13,40 +11,7 @@ using namespace llvm; using namespace cps; -namespace { - -// Single source of truth for the built-in functions: name, arity and return -// type. emitExpr checks arity against this table before dispatching, and -// getExprTypeInfo reads the return type from it. -struct BuiltinInfo { - const char *Name; - unsigned Arity; - const char *ReturnTypeName; -}; - -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"}, -}; - -const BuiltinInfo *findBuiltin(const std::string &Name) { - for (const auto &B : Builtins) { - if (Name == B.Name) return &B; - } - return nullptr; -} - -} // namespace +using namespace cps::detail; CodeGen::~CodeGen() = default; @@ -177,362 +142,6 @@ const TypeInfo *CodeGen::resolveType(const std::string &TypeName) const { return Types->resolve(TypeName); } -const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { - if (!Expr) return nullptr; - - if (dynamic_cast<IntegerExprAST*>(Expr)) return resolveType("INTEGER"); - if (dynamic_cast<RealExprAST*>(Expr)) return resolveType("REAL"); - if (dynamic_cast<BooleanExprAST*>(Expr)) return resolveType("BOOLEAN"); - if (dynamic_cast<CharExprAST*>(Expr)) return resolveType("CHAR"); - if (dynamic_cast<StringExprAST*>(Expr)) return resolveType("STRING"); - if (auto *Cast = dynamic_cast<CastExprAST*>(Expr)) - return resolveType(Cast->getTypeName()); - - if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) { - const SymbolInfo *Info = getSymbolInfo(Var->getName()); - if (Info) return resolveType(Info->TypeName); - if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) { - return resolveType(EC->TypeName); - } - return nullptr; - } - - if (auto *Arr = dynamic_cast<ArrayAccessExprAST*>(Expr)) { - const SymbolInfo *Info = getSymbolInfo(Arr->getName()); - return Info ? resolveType(Info->TypeName) : nullptr; - } - - if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) { - const SymbolInfo *Info = getSymbolInfo(D->getBaseName()); - if (!Info) return nullptr; - std::string TypeName = Info->TypeName; - size_t Start = 0; - if (Info->IsArray) { - // SymbolInfo already holds the ELEMENT type name for arrays. - if (D->getAccesses().empty() || D->getAccesses()[0].Kind != AccessKind::Index) - return nullptr; - Start = 1; - } - for (size_t I = Start; I < D->getAccesses().size(); ++I) { - const TypeInfo *Cur = resolveType(TypeName); - if (!Cur) return nullptr; - const DesignatorAccess &A = D->getAccesses()[I]; - if (A.Kind == AccessKind::Field) { - const RecordPayload *RP = Cur->asRecord(); - const RecordFieldInfo *FI = RP ? RP->findField(A.FieldName) : nullptr; - if (!FI) return nullptr; - if (!FI->IsPublic) return nullptr; - TypeName = FI->TypeName; - } else if (A.Kind == AccessKind::Deref) { - const PointerPayload *PP = Cur->asPointer(); - if (!PP) return nullptr; - TypeName = PP->PointeeTypeName; - } else { - return nullptr; // indexing a non-array - } - } - return resolveType(TypeName); - } - - // ^x has no pseudocode type of its own; only the pointer admission in - // emitCoercedExpr may consume it. - if (dynamic_cast<AddrOfExprAST*>(Expr)) return nullptr; - - if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { - if (Unary->getOp() == tok_not) { - return resolveType("BOOLEAN"); - } - const TypeInfo *Operand = getExprTypeInfo(Unary->getOperand()); - if (Operand && Operand->isC()) return Types->promoteCScalar(Operand); - if (Operand && !Operand->isC() && !Operand->isUserKind()) - return Operand->isReal() ? resolveType("REAL") : resolveType("INTEGER"); - return nullptr; - } - - if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) { - switch (Bin->getOp()) { - case tok_eq: - case tok_ne: - case '<': - case '>': - case tok_le: - case tok_ge: - case tok_and: - case tok_or: - return resolveType("BOOLEAN"); - case '&': - return resolveType("STRING"); - case '/': - return resolveType("REAL"); - default: - break; - } - - const TypeInfo *L = getExprTypeInfo(Bin->getLHS()); - const TypeInfo *R = getExprTypeInfo(Bin->getRHS()); - if ((L && L->isC()) || (R && R->isC())) return Types->commonCScalar(L, R); - // Enum arithmetic: E +/- INTEGER (enum on the left) yields the enum; - // every other user-kind combination has no type (emission rejects it). - if ((L && L->isUserKind()) || (R && R->isUserKind())) { - if (L && L->isEnum() && R && R->Kind == TypeKind::Integer && - (Bin->getOp() == '+' || Bin->getOp() == '-')) { - return L; - } - return nullptr; - } - if ((L && L->Kind == TypeKind::Real) || (R && R->Kind == TypeKind::Real)) { - return resolveType("REAL"); - } - return resolveType("INTEGER"); - } - - if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) { - const std::string &Name = Call->getCallee(); - if (const BuiltinInfo *B = findBuiltin(Name)) { - return resolveType(B->ReturnTypeName); - } - if (const FuncSig *Sig = FuncGen->getSignature(Name)) { - return resolveType(Sig->ReturnTypeName); - } - - Function *CalleeF = TheModule->getFunction(Name); - if (!CalleeF) return resolveType("INTEGER"); - - Type *RetTy = CalleeF->getReturnType(); - if (RetTy->isVoidTy()) return resolveType("VOID"); - if (RetTy->isDoubleTy()) return resolveType("REAL"); - if (RetTy->isIntegerTy(1)) return resolveType("BOOLEAN"); - if (RetTy->isIntegerTy(8)) return resolveType("CHAR"); - if (RetTy->isPointerTy()) return resolveType("STRING"); - return resolveType("INTEGER"); - } - - return nullptr; -} - -Value *CodeGen::coerceValueToType(Value *Val, const TypeInfo *TargetInfo) { - if (!Val || !TargetInfo || !TargetInfo->LLVMType) return nullptr; - - if (Val->getType() == TargetInfo->LLVMType) { - return Val; - } - - switch (TargetInfo->Kind) { - case TypeKind::Real: - if (Val->getType()->isDoubleTy()) return Val; - if (Val->getType()->isIntegerTy(1)) - return Builder->CreateUIToFP(Val, Type::getDoubleTy(*TheContext), "bool_to_real"); - if (Val->getType()->isIntegerTy(8)) - return Builder->CreateUIToFP(Val, Type::getDoubleTy(*TheContext), "char_to_real"); - if (Val->getType()->isIntegerTy()) - return Builder->CreateSIToFP(Val, Type::getDoubleTy(*TheContext), "int_to_real"); - break; - - case TypeKind::Integer: - if (Val->getType()->isIntegerTy(64)) return Val; - if (Val->getType()->isDoubleTy()) - return Builder->CreateFPToSI(Val, Type::getInt64Ty(*TheContext), "real_to_int"); - if (Val->getType()->isIntegerTy(1)) - return Builder->CreateZExt(Val, Type::getInt64Ty(*TheContext), "bool_to_int"); - if (Val->getType()->isIntegerTy(8)) - return Builder->CreateZExt(Val, Type::getInt64Ty(*TheContext), "char_to_int"); - if (Val->getType()->isIntegerTy()) - return Builder->CreateSExtOrTrunc(Val, Type::getInt64Ty(*TheContext), "int_cast"); - break; - - case TypeKind::Boolean: - if (Val->getType()->isIntegerTy(1)) return Val; - if (Val->getType()->isIntegerTy()) - return Builder->CreateICmpNE(Val, Constant::getNullValue(Val->getType()), "to_bool"); - if (Val->getType()->isDoubleTy()) - return Builder->CreateFCmpONE(Val, ConstantFP::get(*TheContext, APFloat(0.0)), "to_bool"); - if (Val->getType()->isPointerTy()) - return Builder->CreateICmpNE(Val, - ConstantPointerNull::get(cast<PointerType>(Val->getType())), - "ptr_to_bool"); - break; - - case TypeKind::Char: - if (Val->getType()->isIntegerTy(8)) return Val; - if (Val->getType()->isIntegerTy(1)) - return Builder->CreateZExt(Val, Type::getInt8Ty(*TheContext), "bool_to_char"); - if (Val->getType()->isDoubleTy()) - return Builder->CreateFPToUI(Val, Type::getInt8Ty(*TheContext), "real_to_char"); - if (Val->getType()->isIntegerTy()) - 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; - - // Enum/Record/Pointer identity lives in the type NAME; at this level - // an enum is a plain i64 and a pointer a plain ptr, so nothing can be - // checked here. emitCoercedExpr is the only legitimate door. - case TypeKind::Enum: - case TypeKind::Record: - case TypeKind::Pointer: - case TypeKind::CScalar: - case TypeKind::CPointer: - reportError("Internal: a %s value reached representation coercion without a name-level check", - TargetInfo->Name.c_str()); - return nullptr; - } - - reportError("Cannot coerce value of LLVM type to target type %s", - TargetInfo->Name.c_str()); - return nullptr; -} - -void CodeGen::emitDeclareStmt(DeclareStmtAST *Stmt) { - const TypeInfo *Info = resolveType(Stmt->getType()); - if (!Info || !Info->LLVMType || Info->isVoid()) { - reportError("Unknown type %s", Stmt->getType().c_str()); - return; - } - if (Types->lookupEnumConstant(Stmt->getName())) { - reportError("'%s' is an enum value and cannot be declared as a variable", - Stmt->getName().c_str()); - return; - } - if (Types->resolve(Stmt->getName())) { - reportError("'%s' is a type name and cannot be declared as a variable", - Stmt->getName().c_str()); - return; - } - - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Info->LLVMType, Stmt->getName()); - - Value *InitVal = nullptr; - if (Info->isString()) { - InitVal = EmptyStringStr; - } else { - InitVal = Types->getZeroValue(Info->Name); - } - - if (InitVal) { - Builder->CreateStore(InitVal, Alloca); - } - - registerSymbol(Stmt->getName(), Alloca, Info->Name, false); -} - -Value *CodeGen::emitStringNullGuard(Value *StrVal) { - if (!StrVal || !StrVal->getType()->isPointerTy()) return StrVal; - Value *IsNull = Builder->CreateICmpEQ(StrVal, - ConstantPointerNull::get(cast<PointerType>(StrVal->getType())), - "str_is_null"); - return Builder->CreateSelect(IsNull, EmptyStringStr, StrVal, "safe_str"); -} - -void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewline) { - if (!Val || !Info) return; - - std::vector<Value*> Args; - Value *Fmt = nullptr; - - // Exhaustive over TypeKind (no default): the compiler warns when a new - // kind is added without deciding its OUTPUT story. - switch (Info->Kind) { - case TypeKind::Real: - Fmt = AppendNewline - ? PrintfFloatFormatStr - : Builder->CreateGlobalString("%f", "fmt_flt_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(Val); - break; - - case TypeKind::Boolean: { - Value *BoolVal = coerceValueToType(Val, resolveType("BOOLEAN")); - Fmt = AppendNewline - ? PrintfStringFormatStr - : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(Builder->CreateSelect(BoolVal, TrueStr, FalseStr)); - 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"); - Fmt = AppendNewline - ? PrintfCharFormatStr - : Builder->CreateGlobalString("%c", "fmt_chr_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(Promoted); - break; - } - - case TypeKind::Integer: - case TypeKind::Enum: { // an enum prints its ordinal (user decision) - Value *IntVal = Info->isEnum() ? Val : coerceValueToType(Val, resolveType("INTEGER")); - Fmt = AppendNewline - ? PrintfFormatStr - : Builder->CreateGlobalString("%lld", "fmt_int_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(IntVal); - break; - } - - case TypeKind::Record: - reportError("Cannot OUTPUT a value of record type %s; output its fields individually", - Info->Name.c_str()); - return; - - case TypeKind::Pointer: - reportError("Cannot OUTPUT a value of pointer type %s", Info->Name.c_str()); - return; - - case TypeKind::CScalar: - emitCOutput(Val, Info, AppendNewline); - return; - case TypeKind::CPointer: - reportError("OUTPUT is not supported for type %s", Info->Name.c_str()); - return; - - case TypeKind::Void: - reportError("Cannot OUTPUT a VOID value"); - return; - } - - Builder->CreateCall(PrintfFunc, Args); -} - void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) { FunctionType *FT = FunctionType::get(Type::getInt32Ty(*TheContext), false); Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule.get()); @@ -581,268 +190,6 @@ void CodeGen::print(raw_ostream &Output) { TheModule->print(Output, nullptr); } -Value *CodeGen::emitExpr(ExprAST *Expr) { - if (auto *Cast = dynamic_cast<CastExprAST*>(Expr)) return emitCast(Cast); - if (!Expr) return nullptr; - - if (auto *Num = dynamic_cast<IntegerExprAST*>(Expr)) { - return ConstantInt::get(*TheContext, APInt(64, Num->getVal(), true)); - } - - if (auto *Real = dynamic_cast<RealExprAST*>(Expr)) { - return ConstantFP::get(*TheContext, APFloat(Real->getVal())); - } - - if (auto *Bool = dynamic_cast<BooleanExprAST*>(Expr)) { - return ConstantInt::get(*TheContext, APInt(1, Bool->getVal() ? 1 : 0)); - } - - if (auto *Chr = dynamic_cast<CharExprAST*>(Expr)) { - return ConstantInt::get(Type::getInt8Ty(*TheContext), static_cast<unsigned char>(Chr->getVal())); - } - - if (auto *Str = dynamic_cast<StringExprAST*>(Expr)) { - return StrHandler->createLiteral(Str->getVal()); - } - - if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) { - const SymbolInfo *Info = getSymbolInfo(Var->getName()); - if (!Info) { - // Enum values are bare identifiers; collisions with variables are - // rejected at declaration time, so the order here cannot shadow. - if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) { - return ConstantInt::get(*TheContext, APInt(64, static_cast<uint64_t>(EC->Ordinal), true)); - } - reportError("Unknown variable name %s", Var->getName().c_str()); - return nullptr; - } - if (Info->IsArray) { - return Info->Storage; - } - - const TypeInfo *TypeInfo = resolveType(Info->TypeName); - if (!TypeInfo || !TypeInfo->LLVMType) { - reportError("Unknown type for variable %s", Var->getName().c_str()); - return nullptr; - } - - Value *Loaded = Builder->CreateLoad(TypeInfo->LLVMType, Info->Storage, Var->getName().c_str()); - if (TypeInfo->isString()) { - Loaded = emitStringNullGuard(Loaded); - } - return Loaded; - } - - if (auto *ArrAcc = dynamic_cast<ArrayAccessExprAST*>(Expr)) { - return Arrays->emitArrayAccess(ArrAcc, *this); - } - - if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { - if (Unary->getOp() == tok_not) { - const TypeInfo *Info = getExprTypeInfo(Unary->getOperand()); - Value *Operand = Info && Info->isC() - ? convertNumericValue(emitExpr(Unary->getOperand()), Info, resolveType("BOOLEAN")) - : emitCoercedExpr(Unary->getOperand(), resolveType("BOOLEAN")); - if (Info && Info->Kind == TypeKind::CPointer) { - reportError("NOT does not support C pointer operands"); - return nullptr; - } - if (!Operand) return nullptr; - return Builder->CreateNot(Operand, "nottmp"); - } - if (Unary->getOp() == '-') { - const TypeInfo *Info = getExprTypeInfo(Unary->getOperand()); - if (Info && Info->Kind == TypeKind::CScalar && !Info->isVoid()) { - const TypeInfo *Promoted = Types->promoteCScalar(Info); - Value *Operand = convertNumericValue(emitExpr(Unary->getOperand()), Info, Promoted); - if (!Operand) return nullptr; - return Promoted->isReal() ? Builder->CreateFNeg(Operand, "c_neg") - : Builder->CreateNeg(Operand, "c_neg"); - } - if (!Info || Info->isC() || Info->isUserKind() || Info->isString() || Info->isVoid()) { - reportError("Unary '-' requires a supported numeric operand"); - return nullptr; - } - Value *Operand = emitExpr(Unary->getOperand()); - if (!Operand) return nullptr; - return ArithHandler->emitBinaryOp('-', ConstantInt::get(Type::getInt64Ty(*TheContext), 0), - Operand, 0); - } - return nullptr; - } - - if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) { - // Whole arrays evaluate to raw data pointers and ^x to raw addresses; - // neither is a value any operator may consume. - if (isWholeArrayVar(Bin->getLHS()) || isWholeArrayVar(Bin->getRHS())) { - reportError("A whole array cannot be used as an operand; index it"); - return nullptr; - } - if (dynamic_cast<AddrOfExprAST*>(Bin->getLHS()) || - dynamic_cast<AddrOfExprAST*>(Bin->getRHS())) { - reportError("'^' (address-of) can only be used where a pointer type is expected"); - return nullptr; - } - - // Enum/Record/Pointer operands never reach ArithmeticHandler or the - // '&' path: their admissible operations are decided by name here. - const TypeInfo *LT = getExprTypeInfo(Bin->getLHS()); - const TypeInfo *RT = getExprTypeInfo(Bin->getRHS()); - if ((LT && LT->isC()) || (RT && RT->isC())) { - return emitCBinaryOp(Bin, LT, RT); - } - if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) { - return emitUserKindBinaryOp(Bin, LT, RT); - } - - Value *L = emitExpr(Bin->getLHS()); - Value *R = emitExpr(Bin->getRHS()); - if (!L || !R) return nullptr; - if (Bin->getOp() == '&') { - L = coerceValueToType(L, resolveType("STRING")); - R = coerceValueToType(R, resolveType("STRING")); - if (!L || !R) return nullptr; - return StrHandler->emitConcat(L, R); - } - // STRING and explicit pointers share LLVM's ptr representation; - // string comparisons must dispatch on the pseudocode type. - switch (Bin->getOp()) { - 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 (line %d)", Bin->getLine()); - return nullptr; - } - return StrHandler->emitCompare(Bin->getOp(), L, R); - } - break; - default: - break; - } - return ArithHandler->emitBinaryOp(Bin->getOp(), L, R, Bin->getLine()); - } - - if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) { - std::string Name = Call->getCallee(); - if (const BuiltinInfo *B = findBuiltin(Name)) { - if (Call->getArgs().size() != B->Arity) { - reportError("%s expects %u arg%s", B->Name, B->Arity, B->Arity == 1 ? "" : "s"); - return nullptr; - } - } - if (Name == "LENGTH") { - Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); - if (!Str) return nullptr; - return StrHandler->emitLength(Str); - } - if (Name == "MID") { - Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); - Value *Start = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); - Value *Len = emitCoercedExpr(Call->getArgs()[2].get(), resolveType("INTEGER")); - if (!Str || !Start || !Len) return nullptr; - return StrHandler->emitMid(Str, Start, Len); - } - if (Name == "RIGHT") { - Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); - Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); - if (!Str || !Len) return nullptr; - return StrHandler->emitRight(Str, Len); - } - if (Name == "LEFT") { - Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); - Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); - if (!Str || !Len) return nullptr; - return StrHandler->emitLeft(Str, Len); - } - if (Name == "LCASE") { - Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); - if (!Str) return nullptr; - return StrHandler->emitLCase(Str); - } - if (Name == "UCASE") { - Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); - if (!Str) return nullptr; - return StrHandler->emitUCase(Str); - } - if (Name == "ASC") { - const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[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(Call->getArgs()[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(Call->getArgs()[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(Call->getArgs()[0].get(), resolveType("STRING")); - if (!Str) return nullptr; - return StrConvHandler->emitIsNum(Str); - } - if (Name == "NUM_TO_STR") { - const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[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(Call->getArgs()[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(Call->getArgs()[0].get(), resolveType("STRING")); - if (!Str) return nullptr; - return StrConvHandler->emitStrToNum(Str, true); - } - if (Name == "EOF") { - if (isWholeArrayVar(Call->getArgs()[0].get())) { - reportError("Cannot use a whole array as the EOF filename"); - return nullptr; - } - Value *FileName = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); - if (!FileName) return nullptr; - return Files->emitEof(FileName, Call->getLine()); - } - - std::vector<Value*> Args; - if (!marshalCallArgs(Name, Call->getArgs(), Call->getLine(), Args)) return nullptr; - return FuncGen->emitCallExpr(Call, Args); - } - - if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) { - LValueInfo LV; - if (!emitLValue(D, LV)) return nullptr; - return loadFromLValue(LV); - } - - // ^x produces a raw address; it is only admissible where a pointer type - // is expected, which emitCoercedExpr enforces by pointee name. - if (auto *Addr = dynamic_cast<AddrOfExprAST*>(Expr)) { - LValueInfo LV; - if (!emitLValue(Addr->getTarget(), LV)) return nullptr; - return LV.Addr; - } - - return nullptr; -} - // Collects the arguments for a call to a user FUNCTION/PROCEDURE, dispatching // on the *declared* signature: BYREF parameters take the variable's alloca, // BYVAL parameters are evaluated and coerced to the declared parameter type. @@ -912,445 +259,3 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, } return true; } - -void CodeGen::emitTryStmt(TryStmtAST *Stmt) { - auto *F = Builder->GetInsertBlock()->getParent(); - auto *CatchBB = BasicBlock::Create(*TheContext, "catch", F); - auto *AfterBB = BasicBlock::Create(*TheContext, "try_cont", F); - auto SavedArrays = Arrays->snapshotTable(); - - Exceptions->pushHandler(CatchBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - auto *Exception = Exceptions->popHandler(); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); - Arrays->exchangeTable(SavedArrays); - - Builder->SetInsertPoint(CatchBB); - Exceptions->beginCatch(Exception); - for (const auto &S : Stmt->getCatchBody()) emitStmt(S.get()); - Exceptions->endCatch(); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); - Arrays->exchangeTable(std::move(SavedArrays)); - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitIfStmt(IfStmtAST *Stmt) { - Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); - if (!CondV) return; - - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - BasicBlock *ThenBB = BasicBlock::Create(*TheContext, "then", TheFunction); - BasicBlock *ElseBB = BasicBlock::Create(*TheContext, "else", TheFunction); - BasicBlock *MergeBB = BasicBlock::Create(*TheContext, "ifcont", TheFunction); - - Builder->CreateCondBr(CondV, ThenBB, ElseBB); - - // Arrays are block-scoped: metadata SSA values and the buffer setup live - // inside the branch, so a declaration must not survive it. - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(ThenBB); - for (const auto &S : Stmt->getThenStmts()) emitStmt(S.get()); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); - Arrays->exchangeTable(SavedArrays); - - Builder->SetInsertPoint(ElseBB); - for (const auto &S : Stmt->getElseStmts()) emitStmt(S.get()); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); - Arrays->exchangeTable(std::move(SavedArrays)); - - Builder->SetInsertPoint(MergeBB); -} - -void CodeGen::emitWhileStmt(WhileStmtAST *Stmt) { - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - - BasicBlock *CondBB = BasicBlock::Create(*TheContext, "whilecond", TheFunction); - BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "whileloop", TheFunction); - BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "whilecont", TheFunction); - - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(CondBB); - Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); - if (!CondV) return; - - Builder->CreateCondBr(CondV, LoopBB, AfterBB); - - // Arrays declared in the body are block-scoped (see emitIfStmt). - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(LoopBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - Arrays->exchangeTable(std::move(SavedArrays)); - - if (!Builder->GetInsertBlock()->hasTerminator()) - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitRepeatStmt(RepeatStmtAST *Stmt) { - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - - BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "repeatloop", TheFunction); - BasicBlock *CondBB = BasicBlock::Create(*TheContext, "repeatcond", TheFunction); - BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "repeatcont", TheFunction); - - Builder->CreateBr(LoopBB); - - // Arrays declared in the body are block-scoped (see emitIfStmt). - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(LoopBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - Arrays->exchangeTable(std::move(SavedArrays)); - if (!Builder->GetInsertBlock()->hasTerminator()) - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(CondBB); - Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); - if (!CondV) return; - - Builder->CreateCondBr(CondV, AfterBB, LoopBB); - - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitForStmt(ForStmtAST *Stmt) { - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - std::string VarName = Stmt->getVarName(); - - const SymbolInfo *Symbol = getSymbolInfo(VarName); - if (!Symbol) { - reportError("Unknown variable in FOR loop %s", VarName.c_str()); - return; - } - if (Symbol->TypeName != "INTEGER") { - reportError("FOR loop variable %s must be INTEGER", VarName.c_str()); - return; - } - - Value *StartVal = emitCoercedExpr(Stmt->getStart(), resolveType("INTEGER")); - if (!StartVal) return; - - Value *Alloca = Symbol->Storage; - Builder->CreateStore(StartVal, Alloca); - - // STEP is evaluated exactly once, before the loop. - Value *StepVal = nullptr; - if (Stmt->getStep()) { - StepVal = emitCoercedExpr(Stmt->getStep(), resolveType("INTEGER")); - if (!StepVal) return; - } else { - StepVal = ConstantInt::get(*TheContext, APInt(64, 1)); - } - - BasicBlock *CondBB = BasicBlock::Create(*TheContext, "forcond", TheFunction); - BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "forloop", TheFunction); - BasicBlock *IncBB = BasicBlock::Create(*TheContext, "forinc", TheFunction); - BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "forcont", TheFunction); - - Builder->CreateBr(CondBB); - Builder->SetInsertPoint(CondBB); - - Value *CurVar = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); - Value *EndVal = emitCoercedExpr(Stmt->getEnd(), resolveType("INTEGER")); - if (!EndVal) return; - - // The loop direction depends on the STEP value at run time. - Value *StepNonNeg = Builder->CreateICmpSGE(StepVal, - ConstantInt::get(*TheContext, APInt(64, 0)), - "step_nonneg"); - Value *CondUp = Builder->CreateICmpSLE(CurVar, EndVal, "forcond_le"); - Value *CondDown = Builder->CreateICmpSGE(CurVar, EndVal, "forcond_ge"); - Value *CondV = Builder->CreateSelect(StepNonNeg, CondUp, CondDown, "forcond"); - - Builder->CreateCondBr(CondV, LoopBB, AfterBB); - - // Arrays declared in the body are block-scoped (see emitIfStmt). - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(LoopBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - Arrays->exchangeTable(std::move(SavedArrays)); - if (!Builder->GetInsertBlock()->hasTerminator()) - Builder->CreateBr(IncBB); - - Builder->SetInsertPoint(IncBB); - Value *CurValForInc = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); - Value *NextVal = Builder->CreateAdd(CurValForInc, StepVal, "nextval"); - Builder->CreateStore(NextVal, Alloca); - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitStmt(StmtAST *Stmt) { - if (!Stmt) return; - - if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt)) { - BasicBlock *SavedBlock = Builder->GetInsertBlock(); - // Function bodies get a fresh array scope (metadata SSA values belong - // to the emitting function), mirroring FunctionGen's Symbols handling. - auto SavedArrays = Arrays->exchangeTable({}); - - FuncGen->emitFunctionDef(FuncDef, [this](StmtAST *S) { - this->emitStmt(S); - }); - - Arrays->exchangeTable(std::move(SavedArrays)); - if (SavedBlock) Builder->SetInsertPoint(SavedBlock); - return; - } - - // Keep diagnosing statements after RETURN/THROW without appending code - // to a terminated block or making those statements reachable. - if (Builder->GetInsertBlock()->hasTerminator()) { - auto *F = Builder->GetInsertBlock()->getParent(); - Builder->SetInsertPoint(BasicBlock::Create(*TheContext, "unreachable_code", F)); - } - - if (auto *Try = dynamic_cast<TryStmtAST*>(Stmt)) { - emitTryStmt(Try); - return; - } - if (dynamic_cast<ThrowStmtAST*>(Stmt)) { - Exceptions->emitThrow(); - return; - } - if (auto *ArrDecl = dynamic_cast<ArrayDeclareStmtAST*>(Stmt)) { - Arrays->emitArrayDeclare(ArrDecl, *this); - return; - } - if (auto *ArrAssign = dynamic_cast<ArrayAssignStmtAST*>(Stmt)) { - Arrays->emitArrayAssign(ArrAssign, *this); - return; - } - - if (auto *Call = dynamic_cast<CallStmtAST*>(Stmt)) { - std::vector<Value*> Args; - if (!marshalCallArgs(Call->getCallee(), Call->getArgs(), Call->getLine(), Args)) return; - FuncGen->emitCallStmt(Call, Args); - return; - } - - if (auto *Ret = dynamic_cast<ReturnStmtAST*>(Stmt)) { - Function *F = Builder->GetInsertBlock()->getParent(); - const FuncSig *Sig = FuncGen->getSignature(F->getName().str()); - // Pseudocode cannot name a function "main" (reserved runtime symbol), - // so no signature means we are emitting the top level. - if (!Sig) { - reportError("RETURN is only allowed inside a FUNCTION or PROCEDURE"); - return; - } - if (F->getReturnType()->isVoidTy() && Ret->getRetVal()) { - reportError("RETURN with a value is only allowed in a FUNCTION"); - return; - } - if (!F->getReturnType()->isVoidTy() && !Ret->getRetVal()) { - reportError("RETURN in a FUNCTION requires a value"); - return; - } - Value *RetVal = nullptr; - if (Ret->getRetVal()) { - RetVal = emitCoercedExpr(Ret->getRetVal(), resolveType(Sig->ReturnTypeName)); - if (!RetVal) return; - } - FuncGen->emitReturn(Ret, RetVal); - return; - } - - if (auto *Decl = dynamic_cast<DeclareStmtAST*>(Stmt)) { - emitDeclareStmt(Decl); - return; - } - - if (auto *Assign = dynamic_cast<AssignStmtAST*>(Stmt)) { - const SymbolInfo *Info = getSymbolInfo(Assign->getName()); - if (!Info) { - if (Types->lookupEnumConstant(Assign->getName())) { - reportError("Enum value %s is not an assignable location", Assign->getName().c_str()); - } else { - reportError("Unknown variable name %s", Assign->getName().c_str()); - } - return; - } - if (Info->IsArray) { - reportError("Cannot assign array %s without indices", Assign->getName().c_str()); - return; - } - - Value *Val = emitCoercedExpr(Assign->getExpr(), resolveType(Info->TypeName)); - if (!Val) return; - - Builder->CreateStore(Val, Info->Storage); - return; - } - - if (auto *In = dynamic_cast<InputStmtAST*>(Stmt)) { - const SymbolInfo *Info = getSymbolInfo(In->getName()); - if (!Info) { - reportError("Unknown variable name %s", In->getName().c_str()); - return; - } - if (Info->IsArray) { - reportError("INPUT for entire array %s is not supported", In->getName().c_str()); - return; - } - - const TypeInfo *TypeInfo = resolveType(Info->TypeName); - if (!TypeInfo) { - reportError("Unknown type for INPUT %s", In->getName().c_str()); - return; - } - - std::vector<Value*> Args; - if (TypeInfo->isC()) { - emitCInput(Info, TypeInfo); - } else if (TypeInfo->isReal()) { - Args.push_back(ScanfFloatFormatStr); - Args.push_back(Info->Storage); - Builder->CreateCall(ScanfFunc, Args); - } else if (TypeInfo->isBoolean()) { - AllocaInst *TempInt = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), "tmp_bool_input"); - Args.push_back(ScanfFormatStr); - Args.push_back(TempInt); - Builder->CreateCall(ScanfFunc, Args); - - Value *Val = Builder->CreateLoad(Type::getInt64Ty(*TheContext), TempInt); - Value *BoolVal = Builder->CreateICmpNE(Val, ConstantInt::get(*TheContext, APInt(64, 0)), "bool_cast"); - Builder->CreateStore(BoolVal, Info->Storage); - } 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, Info->Storage); - } else if (TypeInfo->isChar()) { - Value *CharFmt = Builder->CreateGlobalString(" %c", "fmt_in_char", 0, TheModule.get()); - Args.push_back(CharFmt); - Args.push_back(Info->Storage); - Builder->CreateCall(ScanfFunc, Args); - } else if (TypeInfo->Kind == TypeKind::Integer) { - Args.push_back(ScanfFormatStr); - Args.push_back(Info->Storage); - Builder->CreateCall(ScanfFunc, Args); - } else { - // Enum/Record/Pointer and any future kind: scanf'ing raw bytes - // into typed storage would corrupt it — reject explicitly. - reportError("INPUT is not supported for a value of type %s", TypeInfo->Name.c_str()); - } - return; - } - - if (auto *Out = dynamic_cast<OutputStmtAST*>(Stmt)) { - const auto &Exprs = Out->getExprs(); - for (size_t I = 0; I < Exprs.size(); ++I) { - ExprAST *Expr = Exprs[I].get(); - if (Arrays->tryEmitArrayOutput(Expr, *this)) continue; - - Value *Val = emitExpr(Expr); - const TypeInfo *TypeInfo = getExprTypeInfo(Expr); - if (!TypeInfo) { - reportError("Cannot infer the type of the OUTPUT expression"); - continue; - } - if (TypeInfo->isVoid()) { - reportError("Cannot OUTPUT the result of a PROCEDURE call"); - continue; - } - emitOutputValue(Val, TypeInfo, I + 1 == Exprs.size()); - } - return; - } - - if (auto *Open = dynamic_cast<OpenFileStmtAST*>(Stmt)) { - if (isWholeArrayVar(Open->getFileName())) { - reportError("Cannot use a whole array as the OPENFILE filename"); - return; - } - Value *Name = emitCoercedExpr(Open->getFileName(), resolveType("STRING")); - if (!Name) return; - Files->emitOpen(Name, Open->getMode(), Open->getLine()); - return; - } - - if (auto *Read = dynamic_cast<ReadFileStmtAST*>(Stmt)) { - const SymbolInfo *Info = getSymbolInfo(Read->getVarName()); - if (!Info) { - reportError("Unknown variable name %s", Read->getVarName().c_str()); - return; - } - if (Info->IsArray) { - reportError("READFILE into entire array %s is not supported", Read->getVarName().c_str()); - return; - } - if (Info->TypeName != "STRING") { - reportError("READFILE variable %s must be of type STRING (got %s)", - Read->getVarName().c_str(), Info->TypeName.c_str()); - return; - } - if (isWholeArrayVar(Read->getFileName())) { - reportError("Cannot use a whole array as the READFILE filename"); - return; - } - Value *Name = emitCoercedExpr(Read->getFileName(), resolveType("STRING")); - if (!Name) return; - Value *LineStr = Files->emitRead(Name, Read->getLine()); - if (!LineStr) return; - Builder->CreateStore(LineStr, Info->Storage); - return; - } - - if (auto *Write = dynamic_cast<WriteFileStmtAST*>(Stmt)) { - if (isWholeArrayVar(Write->getFileName()) || isWholeArrayVar(Write->getData())) { - reportError("Cannot use a whole array in WRITEFILE"); - return; - } - Value *Name = emitCoercedExpr(Write->getFileName(), resolveType("STRING")); - Value *Data = emitCoercedExpr(Write->getData(), resolveType("STRING")); - if (!Name || !Data) return; - Files->emitWrite(Name, Data, Write->getLine()); - return; - } - - if (auto *Close = dynamic_cast<CloseFileStmtAST*>(Stmt)) { - if (isWholeArrayVar(Close->getFileName())) { - reportError("Cannot use a whole array as the CLOSEFILE filename"); - return; - } - Value *Name = emitCoercedExpr(Close->getFileName(), resolveType("STRING")); - if (!Name) return; - Files->emitClose(Name, Close->getLine()); - return; - } - - if (auto *IfStmt = dynamic_cast<IfStmtAST*>(Stmt)) { - emitIfStmt(IfStmt); - return; - } - if (auto *WhileStmt = dynamic_cast<WhileStmtAST*>(Stmt)) { - emitWhileStmt(WhileStmt); - return; - } - if (auto *RepeatStmt = dynamic_cast<RepeatStmtAST*>(Stmt)) { - emitRepeatStmt(RepeatStmt); - return; - } - if (auto *ForStmt = dynamic_cast<ForStmtAST*>(Stmt)) { - emitForStmt(ForStmt); - return; - } - - // TYPE declarations are registered by compile()'s pre-pass (the parser - // guarantees they are top-level); nothing is emitted here. - if (dynamic_cast<EnumTypeDeclAST*>(Stmt) || dynamic_cast<PointerTypeDeclAST*>(Stmt) || - dynamic_cast<RecordTypeDeclAST*>(Stmt) || dynamic_cast<CTypeDeclAST*>(Stmt)) { - return; - } - if (auto *DA = dynamic_cast<DesignatorAssignStmtAST*>(Stmt)) { - LValueInfo LV; - if (!emitLValue(DA->getTarget(), LV)) return; - Value *Val = emitCoercedExpr(DA->getExpr(), resolveType(LV.TypeName)); - if (!Val) return; - Builder->CreateStore(Val, LV.Addr); - return; - } -} |
