aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-14 03:36:21 +0000
committer_ov4 <ov4@ov4.org>2026-10-14 03:36:21 +0000
commitfebb9e4ac239a15ad8c46575643b53ca66e3f4ac (patch)
tree59436aa9897e44cc5f1cea1442755063ad4e4c20 /lib/CodeGen
parent2147449f4d6f37b4624990ff26bce58ebf3cfb4b (diff)
feat(codegen): support C scalar arithmetic and input output
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
-rw-r--r--lib/CodeGen/CTypeGen.cc126
-rw-r--r--lib/CodeGen/CodeGen.cc30
-rw-r--r--lib/CodeGen/RuntimeCheck.cc2
-rw-r--r--lib/CodeGen/TypeSystem.cc38
4 files changed, 184 insertions, 12 deletions
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<UnaryExprAST*>(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<Value*> 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.