diff options
Diffstat (limited to 'lib/CodeGen/ExpressionGen.cc')
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc new file mode 100644 index 0000000..0d69408 --- /dev/null +++ b/lib/CodeGen/ExpressionGen.cc @@ -0,0 +1,273 @@ +#include "cps/CodeGen.h" +#include "cps/ArrayHandler.h" +#include "cps/FileHandler.h" +#include "cps/Lexer.h" +#include "cps/TypeAST.h" +#include "Builtins.h" + +using namespace llvm; +using namespace cps; + +using namespace cps::detail; + +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; +} |
