diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-17 09:46:23 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-17 09:46:23 +0000 |
| commit | 0305500e31eda6fe6da84f88748fc20843aebbce (patch) | |
| tree | 0ca372b3337764e8cd44e5477fcee63a371fbd26 | |
| parent | c62473ae0f495c280759e8f285e5366855ad7a52 (diff) | |
refactor(runtime): write runtime diagnostics with fprintf(stderr)
Replace snprintf+write(2) with a single fprintf call on the platform's
stderr: the C library's stderr / __stderrp, or __acrt_iob_func(2) on
Windows, which the embedded runner binds to the host shim.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
| -rw-r--r-- | include/cps/RuntimeCheck.h | 7 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/RuntimeCheck.cc | 39 | ||||
| -rw-r--r-- | tools/driver/EmbeddedRunner.cc | 6 |
4 files changed, 33 insertions, 21 deletions
diff --git a/include/cps/RuntimeCheck.h b/include/cps/RuntimeCheck.h index 8f33e99..30a1910 100644 --- a/include/cps/RuntimeCheck.h +++ b/include/cps/RuntimeCheck.h @@ -14,10 +14,9 @@ class RuntimeCheck { llvm::LLVMContext &TheContext; llvm::IRBuilder<> &Builder; - llvm::FunctionCallee SnprintfFunc; - llvm::FunctionCallee WriteFunc; - llvm::FunctionCallee StrLenFunc; - llvm::GlobalVariable *ErrBuf; + llvm::FunctionCallee FprintfFunc; + llvm::FunctionCallee StderrFunc; // Windows: __acrt_iob_func(2) + llvm::GlobalVariable *StderrVar; // elsewhere: the C library's stderr ExceptionHandler &Exceptions; llvm::Value *DivZeroMsg; llvm::Value *OutOfBoundsMsg; diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 4e70faf..d985bce 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -14,7 +14,7 @@ static bool isReservedRuntimeName(const std::string &Name) { static const char *Reserved[] = { "__cps_main", "printf", "scanf", "malloc", "free", "strlen", "memcpy", "strcpy", "strcat", "toupper", "tolower", "sprintf", "snprintf", "strtol", "strtoll", - "strtod", "memset", "exit", "write", + "strtod", "memset", "exit", "fprintf", "fopen", "fclose", "fgetc", "ungetc", "fputs", "fputc", "realloc", "strcmp", "__cxa_allocate_exception", "__cxa_throw", "__cxa_begin_catch", "__cxa_end_catch", "__gxx_personality_v0", diff --git a/lib/CodeGen/RuntimeCheck.cc b/lib/CodeGen/RuntimeCheck.cc index b1eb677..0515f1e 100644 --- a/lib/CodeGen/RuntimeCheck.cc +++ b/lib/CodeGen/RuntimeCheck.cc @@ -3,8 +3,8 @@ #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" +#include "llvm/IR/GlobalVariable.h" using namespace llvm; using namespace cps; @@ -17,16 +17,21 @@ RuntimeCheck::RuntimeCheck(Module &M, LLVMContext &C, IRBuilder<> &B, ExceptionH void RuntimeCheck::setupExternalFunctions() { 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"); + FprintfFunc = TheModule.getOrInsertFunction( + "fprintf", FunctionType::get(I32, {PtrTy, PtrTy}, true)); + // The stderr FILE* is a platform-specific symbol; host and target match. +#if defined(_WIN32) + StderrFunc = TheModule.getOrInsertFunction( + "__acrt_iob_func", FunctionType::get(PtrTy, {I32}, false)); +#else +#if defined(__APPLE__) + const char *StderrName = "__stderrp"; +#else + const char *StderrName = "stderr"; +#endif + StderrVar = new GlobalVariable(TheModule, PtrTy, false, GlobalValue::ExternalLinkage, + nullptr, StderrName); +#endif 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); @@ -38,12 +43,14 @@ void RuntimeCheck::setupExternalFunctions() { } void RuntimeCheck::emitStderr(Value *Msg, ArrayRef<Value *> FmtArgs) { - // Format into a fixed buffer, then write(2) so diagnostics bypass stdout. - std::vector<Value *> Args = {ErrBuf, ConstantInt::get(Type::getInt64Ty(TheContext), 1024), Msg}; +#if defined(_WIN32) + Value *Stream = Builder.CreateCall(StderrFunc, {ConstantInt::get(Type::getInt32Ty(TheContext), 2)}); +#else + Value *Stream = Builder.CreateLoad(PointerType::getUnqual(TheContext), StderrVar); +#endif + std::vector<Value *> Args = {Stream, 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}); + Builder.CreateCall(FprintfFunc, Args); } void RuntimeCheck::emitCheck(Value *Condition, Value *Msg, int Line) { diff --git a/tools/driver/EmbeddedRunner.cc b/tools/driver/EmbeddedRunner.cc index 7f0b763..8bfeccb 100644 --- a/tools/driver/EmbeddedRunner.cc +++ b/tools/driver/EmbeddedRunner.cc @@ -325,6 +325,12 @@ static llvm::Error defineRuntimeSymbols(llvm::orc::LLJIT &J) { ExecutorSymbolDef::fromPtr(&std::strtoll); #endif #ifdef _WIN32 + // Generated diagnostics write to stderr through __acrt_iob_func(2). The + // msvcrt DLL only exports __iob_func, so bind the host's shim directly. + Symbols[Intern("__acrt_iob_func")] = + ExecutorSymbolDef::fromPtr(&__acrt_iob_func); +#endif +#ifdef _WIN32 // The JIT-generated main references __main on MinGW; provide a no-op so // the host CRT is not initialized a second time. Symbols[Intern("__main")] = |
