From 6b332f91c82da6e19efad92b58ac1d9c1bc9eec0 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 09:35:19 +0000 Subject: feat(runtime): write runtime diagnostics to stderr Runtime check messages and the unhandled-exception message were printed to stdout by the generated program, so `cpsc x.cps -r >out.txt` captured them. They are now formatted with snprintf and written with write(2). Test expectations and docs are updated accordingly. Co-Authored-By: Claude Sonnet 5.5 --- lib/CodeGen/RuntimeCheck.cc | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'lib/CodeGen/RuntimeCheck.cc') diff --git a/lib/CodeGen/RuntimeCheck.cc b/lib/CodeGen/RuntimeCheck.cc index 215702e..b1eb677 100644 --- a/lib/CodeGen/RuntimeCheck.cc +++ b/lib/CodeGen/RuntimeCheck.cc @@ -3,6 +3,7 @@ #include "cps/TypeSystem.h" // kEnumOrdinalBase #include "llvm/IR/Function.h" #include "llvm/IR/Type.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Constants.h" using namespace llvm; @@ -14,10 +15,18 @@ RuntimeCheck::RuntimeCheck(Module &M, LLVMContext &C, IRBuilder<> &B, ExceptionH } void RuntimeCheck::setupExternalFunctions() { - std::vector PrintfArgs; - PrintfArgs.push_back(PointerType::getUnqual(TheContext)); - FunctionType *PrintfType = FunctionType::get(Type::getInt32Ty(TheContext), PrintfArgs, true); - PrintfFunc = TheModule.getOrInsertFunction("printf", PrintfType); + Type *PtrTy = PointerType::getUnqual(TheContext); + Type *I32 = Type::getInt32Ty(TheContext); + Type *I64 = Type::getInt64Ty(TheContext); + SnprintfFunc = TheModule.getOrInsertFunction( + "snprintf", FunctionType::get(I32, {PtrTy, I64, PtrTy}, true)); + WriteFunc = TheModule.getOrInsertFunction( + "write", FunctionType::get(I32, {I32, PtrTy, I64}, false)); + StrLenFunc = TheModule.getOrInsertFunction( + "strlen", FunctionType::get(I64, {PtrTy}, false)); + ArrayType *BufTy = ArrayType::get(Type::getInt8Ty(TheContext), 1024); + ErrBuf = new GlobalVariable(TheModule, BufTy, false, GlobalValue::InternalLinkage, + ConstantAggregateZero::get(BufTy), "__cps_errbuf"); DivZeroMsg = Builder.CreateGlobalString("[Fatal] line %d: Division by zero\n", "err_div_zero", 0, &TheModule); OutOfBoundsMsg = Builder.CreateGlobalString("[Fatal] line %d: Array index out of bounds\n", "err_bounds", 0, &TheModule); @@ -28,6 +37,15 @@ void RuntimeCheck::setupExternalFunctions() { "err_arr_arg_bounds", 0, &TheModule); } +void RuntimeCheck::emitStderr(Value *Msg, ArrayRef FmtArgs) { + // Format into a fixed buffer, then write(2) so diagnostics bypass stdout. + std::vector Args = {ErrBuf, ConstantInt::get(Type::getInt64Ty(TheContext), 1024), Msg}; + Args.insert(Args.end(), FmtArgs.begin(), FmtArgs.end()); + Builder.CreateCall(SnprintfFunc, Args); + Value *Len = Builder.CreateCall(StrLenFunc, {ErrBuf}); + Builder.CreateCall(WriteFunc, {ConstantInt::get(Type::getInt32Ty(TheContext), 2), ErrBuf, Len}); +} + void RuntimeCheck::emitCheck(Value *Condition, Value *Msg, int Line) { emitCheck(Condition, Msg, {ConstantInt::get(TheContext, APInt(32, Line))}); } @@ -44,7 +62,7 @@ void RuntimeCheck::emitCheck(Value *Condition, Value *Msg, ArrayRef Fmt std::vector PrintArgs; PrintArgs.push_back(Msg); PrintArgs.insert(PrintArgs.end(), FmtArgs.begin(), FmtArgs.end()); - Builder.CreateCall(PrintfFunc, PrintArgs); + emitStderr(Msg, ArrayRef(PrintArgs).drop_front()); Exceptions.emitThrow(); -- cgit v1.3.1-4-g156e