aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 09:46:23 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 09:46:23 +0000
commit0305500e31eda6fe6da84f88748fc20843aebbce (patch)
tree0ca372b3337764e8cd44e5477fcee63a371fbd26
parentc62473ae0f495c280759e8f285e5366855ad7a52 (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.h7
-rw-r--r--lib/CodeGen/FunctionGen.cc2
-rw-r--r--lib/CodeGen/RuntimeCheck.cc39
-rw-r--r--tools/driver/EmbeddedRunner.cc6
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")] =