From febb9e4ac239a15ad8c46575643b53ca66e3f4ac Mon Sep 17 00:00:00 2001 From: _ov4 Date: Wed, 14 Oct 2026 03:36:21 +0000 Subject: feat(codegen): support C scalar arithmetic and input output Co-Authored-By: Claude Fable 5.1 --- lib/CodeGen/CTypeGen.cc | 126 ++++++++++++++++++++++++++++++++++++++++++++ lib/CodeGen/CodeGen.cc | 30 +++++++++-- lib/CodeGen/RuntimeCheck.cc | 2 +- lib/CodeGen/TypeSystem.cc | 38 ++++++++++--- 4 files changed, 184 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc index 03f398f..4d945a7 100644 --- a/lib/CodeGen/CTypeGen.cc +++ b/lib/CodeGen/CTypeGen.cc @@ -77,3 +77,129 @@ Value *CodeGen::emitCast(CastExprAST *Cast) { reportError("Cannot cast %s to %s", Source->Name.c_str(), Target->Name.c_str()); return nullptr; } + +Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const TypeInfo *Right) { + int Op = Bin->getOp(); + if (!Left || !Right || !Left->isC() || !Right->isC()) { + reportError("C and pseudocode operands require an explicit cast (line %d)", Bin->getLine()); + return nullptr; + } + if (Left->isPointer() || Right->isPointer()) { + if (Left->Kind != TypeKind::CPointer || Right->Kind != TypeKind::CPointer || + !Left->sameType(Right) || (Op != tok_eq && Op != tok_ne)) { + reportError("C pointers support only = and <> between the same C pointer type"); + return nullptr; + } + Value *L = emitExpr(Bin->getLHS()); + Value *R = emitExpr(Bin->getRHS()); + if (!L || !R) return nullptr; + return Op == tok_eq ? Builder->CreateICmpEQ(L, R, "c_ptr_eq") + : Builder->CreateICmpNE(L, R, "c_ptr_ne"); + } + const TypeInfo *Common = Types->commonCScalar(Left, Right); + if (!Common) { + reportError("C arithmetic requires scalar operands"); + return nullptr; + } + if (Op == '&' || ((Op == tok_div || Op == tok_mod) && !Common->isIntegral())) { + reportError("Operator is not supported for these C operand types (line %d)", Bin->getLine()); + return nullptr; + } + Value *L = emitExpr(Bin->getLHS()); + Value *R = emitExpr(Bin->getRHS()); + if (!L || !R) return nullptr; + if (Op == tok_and || Op == tok_or) { + L = convertNumericValue(L, Left, resolveType("BOOLEAN")); + R = convertNumericValue(R, Right, resolveType("BOOLEAN")); + return Op == tok_and ? Builder->CreateAnd(L, R, "c_and") + : Builder->CreateOr(L, R, "c_or"); + } + // '/' is a pseudocode operator even with C operands: real division, + // yielding the existing REAL type rather than a native integer quotient. + if (Op == '/') { + L = convertNumericValue(L, Left, resolveType("REAL")); + R = convertNumericValue(R, Right, resolveType("REAL")); + RuntimeChecker->emitDivZeroCheck(R, Bin->getLine()); + return Builder->CreateFDiv(L, R, "c_real_div"); + } + L = convertNumericValue(L, Left, Common); + R = convertNumericValue(R, Right, Common); + if (Common->isReal()) { + switch (Op) { + case '+': return Builder->CreateFAdd(L, R, "c_add"); + case '-': return Builder->CreateFSub(L, R, "c_sub"); + case '*': return Builder->CreateFMul(L, R, "c_mul"); + case tok_eq: return Builder->CreateFCmpOEQ(L, R, "c_eq"); + case tok_ne: return Builder->CreateFCmpUNE(L, R, "c_ne"); + case '<': return Builder->CreateFCmpOLT(L, R, "c_lt"); + case '>': return Builder->CreateFCmpOGT(L, R, "c_gt"); + case tok_le: return Builder->CreateFCmpOLE(L, R, "c_le"); + case tok_ge: return Builder->CreateFCmpOGE(L, R, "c_ge"); + } + } else { + switch (Op) { + case '+': return Builder->CreateAdd(L, R, "c_add"); + case '-': return Builder->CreateSub(L, R, "c_sub"); + case '*': return Builder->CreateMul(L, R, "c_mul"); + case tok_div: case tok_mod: + RuntimeChecker->emitDivZeroCheck(R, Bin->getLine()); + if (Op == tok_div) + return Common->CSigned ? Builder->CreateSDiv(L, R, "c_div") + : Builder->CreateUDiv(L, R, "c_div"); + return Common->CSigned ? Builder->CreateSRem(L, R, "c_mod") + : Builder->CreateURem(L, R, "c_mod"); + case tok_eq: return Builder->CreateICmpEQ(L, R, "c_eq"); + case tok_ne: return Builder->CreateICmpNE(L, R, "c_ne"); + case '<': return Common->CSigned ? Builder->CreateICmpSLT(L, R, "c_lt") + : Builder->CreateICmpULT(L, R, "c_lt"); + case '>': return Common->CSigned ? Builder->CreateICmpSGT(L, R, "c_gt") + : Builder->CreateICmpUGT(L, R, "c_gt"); + case tok_le: return Common->CSigned ? Builder->CreateICmpSLE(L, R, "c_le") + : Builder->CreateICmpULE(L, R, "c_le"); + case tok_ge: return Common->CSigned ? Builder->CreateICmpSGE(L, R, "c_ge") + : Builder->CreateICmpUGE(L, R, "c_ge"); + } + } + reportError("Unsupported operator for C operands (line %d)", Bin->getLine()); + return nullptr; +} + +void CodeGen::emitCOutput(Value *Val, const TypeInfo *Info, bool AppendNewline) { + if (Info->isVoid()) { + reportError("Cannot OUTPUT a void value"); + return; + } + std::string Format = Info->COutputFormat; + if (Info->isBoolean()) { + Format = "%s"; + Val = Builder->CreateSelect(Val, TrueStr, FalseStr); + } else if (Info->CIdentity == "float") { + Val = convertNumericValue(Val, Info, resolveType("C_DOUBLE")); + } else if (Info->isIntegral()) { + Val = convertNumericValue(Val, Info, Types->promoteCScalar(Info)); + } + if (AppendNewline) Format += '\n'; + Value *Fmt = Builder->CreateGlobalStringPtr(Format, "fmt_c_output", 0, TheModule.get()); + Builder->CreateCall(PrintfFunc, {Fmt, Val}); +} + +void CodeGen::emitCInput(const SymbolInfo *Symbol, const TypeInfo *Info) { + if (Info->Kind != TypeKind::CScalar || Info->isVoid()) { + reportError("INPUT is not supported for C pointer or void type %s", Info->Name.c_str()); + return; + } + if (Info->isBoolean()) { + const TypeInfo *NativeInt = resolveType("C_INT"); + AllocaInst *Temp = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), + NativeInt->LLVMType, "c_bool_input"); + Builder->CreateStore(Types->getZeroValue("C_INT"), Temp); + Value *Fmt = Builder->CreateGlobalStringPtr("%d", "fmt_c_input_bool", 0, TheModule.get()); + Builder->CreateCall(ScanfFunc, {Fmt, Temp}); + Value *Input = Builder->CreateLoad(NativeInt->LLVMType, Temp); + Value *Bool = convertNumericValue(Input, NativeInt, Info); + Builder->CreateStore(Bool, Symbol->Storage); + return; + } + Value *Fmt = Builder->CreateGlobalStringPtr(Info->CInputFormat, "fmt_c_input", 0, TheModule.get()); + Builder->CreateCall(ScanfFunc, {Fmt, Symbol->Storage}); +} diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index ce34ebc..a88941b 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -243,6 +243,7 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { 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; @@ -261,13 +262,15 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { 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 nullptr; + 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())) { @@ -516,6 +519,8 @@ void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewli 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; @@ -634,12 +639,26 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { if (auto *Unary = dynamic_cast(Expr)) { if (Unary->getOp() == tok_not) { - Value *Operand = emitCoercedExpr(Unary->getOperand(), resolveType("BOOLEAN")); + 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; @@ -670,8 +689,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { const TypeInfo *LT = getExprTypeInfo(Bin->getLHS()); const TypeInfo *RT = getExprTypeInfo(Bin->getRHS()); if ((LT && LT->isC()) || (RT && RT->isC())) { - reportError("C operators require explicit support for their operand types"); - return nullptr; + return emitCBinaryOp(Bin, LT, RT); } if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) { return emitUserKindBinaryOp(Bin, LT, RT); @@ -1184,7 +1202,9 @@ void CodeGen::emitStmt(StmtAST *Stmt) { } std::vector Args; - if (TypeInfo->isReal()) { + if (TypeInfo->isC()) { + emitCInput(Info, TypeInfo); + } else if (TypeInfo->isReal()) { Args.push_back(ScanfFloatFormatStr); Args.push_back(Info->Storage); Builder->CreateCall(ScanfFunc, Args); diff --git a/lib/CodeGen/RuntimeCheck.cc b/lib/CodeGen/RuntimeCheck.cc index ee270e5..2954af0 100644 --- a/lib/CodeGen/RuntimeCheck.cc +++ b/lib/CodeGen/RuntimeCheck.cc @@ -23,7 +23,7 @@ void RuntimeCheck::emitCheck(Value *Condition) { void RuntimeCheck::emitDivZeroCheck(Value *Divisor, int Line) { Value *IsZero; - if (Divisor->getType()->isDoubleTy()) { + if (Divisor->getType()->isFloatingPointTy()) { IsZero = Builder.CreateFCmpOEQ(Divisor, ConstantFP::get(Divisor->getType(), 0.0), "is_zero"); } else { IsZero = Builder.CreateICmpEQ(Divisor, ConstantInt::get(Divisor->getType(), 0), "is_zero"); diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc index 73d4136..9906050 100644 --- a/lib/CodeGen/TypeSystem.cc +++ b/lib/CodeGen/TypeSystem.cc @@ -53,19 +53,21 @@ const RecordFieldInfo *RecordPayload::findField(const std::string &Name) const { } bool TypeInfo::isNumeric() const { - return Kind == TypeKind::Integer || Kind == TypeKind::Real; + return Kind == TypeKind::Integer || Kind == TypeKind::Real || + (Kind == TypeKind::CScalar && !isVoid()); } bool TypeInfo::isIntegral() const { - return Kind == TypeKind::Integer || Kind == TypeKind::Char; + return Kind == TypeKind::Integer || Kind == TypeKind::Char || + (Kind == TypeKind::CScalar && LLVMType->isIntegerTy()); } bool TypeInfo::isReal() const { - return Kind == TypeKind::Real; + return Kind == TypeKind::Real || (Kind == TypeKind::CScalar && LLVMType->isFloatingPointTy()); } bool TypeInfo::isBoolean() const { - return Kind == TypeKind::Boolean; + return Kind == TypeKind::Boolean || CIdentity == "_Bool"; } bool TypeInfo::isString() const { @@ -73,7 +75,7 @@ bool TypeInfo::isString() const { } bool TypeInfo::isChar() const { - return Kind == TypeKind::Char; + return Kind == TypeKind::Char || CIdentity == "char"; } bool TypeInfo::isVoid() const { @@ -105,10 +107,11 @@ bool TypeInfo::isOutputtable() const { 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::CScalar: case TypeKind::CPointer: return false; } @@ -209,6 +212,29 @@ bool TypeSystem::sameType(const std::string &Left, const std::string &Right) con return L && L->sameType(resolve(Right)); } +const TypeInfo *TypeSystem::promoteCScalar(const TypeInfo *Info) const { + if (!Info || Info->Kind != TypeKind::CScalar || Info->isVoid()) return nullptr; + if (Info->CRank >= 4) return Info; + const TypeInfo *Int = resolve(cTypeName("int")); + unsigned Width = Info->LLVMType->getIntegerBitWidth(); + unsigned IntWidth = Int->LLVMType->getIntegerBitWidth(); + return Width < IntWidth || (Width == IntWidth && Info->CSigned) + ? Int : resolve(cTypeName("unsigned int")); +} + +const TypeInfo *TypeSystem::commonCScalar(const TypeInfo *Left, const TypeInfo *Right) const { + const TypeInfo *L = promoteCScalar(Left); + const TypeInfo *R = promoteCScalar(Right); + if (!L || !R) return nullptr; + if (L->isReal() || R->isReal() || L->CSigned == R->CSigned) + return L->CRank >= R->CRank ? L : R; + const TypeInfo *S = L->CSigned ? L : R; + const TypeInfo *U = L->CSigned ? R : L; + if (U->CRank >= S->CRank) return U; + if (S->LLVMType->getIntegerBitWidth() > U->LLVMType->getIntegerBitWidth()) return S; + return resolve(cTypeName("unsigned " + S->CIdentity)); +} + const TypeInfo *TypeSystem::importCType(const std::string &Expression, std::string &Err) { // Whitespace is immaterial around stars; C's multiword base type is // validated separately. Stars must all precede or all follow the base. -- cgit v1.3.1-4-g156e