From a484591cb95ce5729240394750dd3a47e2d2e727 Mon Sep 17 00:00:00 2001 From: Marek Lipert Date: Tue, 9 Jun 2026 23:05:16 +0200 Subject: [PATCH 1/7] Fix optimised list (remove static vars from runtime). Fix type matching for getType. Optimise var peeks to account for static vs dynamic vars at compile time. --- backend/CMakeLists.txt | 6 +++-- backend/codegen/CodeGen.h | 10 ++++--- backend/codegen/invoke/InvokeManager.h | 15 ++++++----- .../invoke/InvokeManager_InstanceCall.cpp | 6 ++--- .../codegen/invoke/InvokeManager_VarCall.cpp | 11 ++++---- backend/codegen/ops/InvokeNode.cpp | 4 +-- backend/codegen/ops/NewNode.cpp | 4 +-- backend/codegen/ops/StaticCallNode.cpp | 4 +-- backend/jit/JITEngine.cpp | 26 +++++++++++++++++++ backend/runtime/CMakeLists.txt | 13 ++++++---- backend/runtime/PersistentArrayMap.c | 16 ++++++------ backend/runtime/PersistentList.c | 16 ++++++------ backend/runtime/PersistentVector.c | 2 +- backend/runtime/Var.c | 8 +++++- backend/runtime/Var.h | 13 +++++++--- tests/fib.clj | 4 +++ tests/fibjank.clj | 12 +++++++++ 17 files changed, 117 insertions(+), 53 deletions(-) create mode 100644 tests/fibjank.clj diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt index 8709d00b..b7c2e292 100644 --- a/backend/CMakeLists.txt +++ b/backend/CMakeLists.txt @@ -26,9 +26,10 @@ endif() #set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) #set(CMAKE_CXX_FLAGS "-Wall -Wextra") -set(CMAKE_CXX_FLAGS_DEBUG "-g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -fstandalone-debug") -set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -fno-omit-frame-pointer") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -fstandalone-debug") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -fno-omit-frame-pointer -g") set(CMAKE_CXX_STANDARD 20) + add_compile_definitions(BUILD_TYPE="${CMAKE_BUILD_TYPE}") @@ -207,6 +208,7 @@ include_directories(${GMP_INCLUDE_DIRS}) add_library(backend_lib STATIC ${BACKEND_SOURCES} ${PROTO_SRCS} ${PROTO_HDRS}) +set_target_properties(backend_lib PROPERTIES ENABLE_EXPORTS TRUE) # Use target-based linking for Protobuf to handle Abseil dependencies automatically if(TARGET protobuf::libprotobuf) set(PROTOBUF_LINK_TARGET protobuf::libprotobuf) diff --git a/backend/codegen/CodeGen.h b/backend/codegen/CodeGen.h index 993a1f35..8c592637 100644 --- a/backend/codegen/CodeGen.h +++ b/backend/codegen/CodeGen.h @@ -55,11 +55,13 @@ struct RecurTarget { class CodeGen { std::unique_ptr TSContext; - llvm::LLVMContext &TheContext; + +public: std::unique_ptr TheModule; - llvm::IRBuilder<> Builder; +private: + llvm::IRBuilder<> Builder; VariableBindings variableBindingStack; VariableBindings variableTypesBindingsStack; @@ -70,7 +72,9 @@ class CodeGen { DynamicConstructor dynamicConstructor; MemoryManagement memoryManagement; std::vector executionContextStack; - void pushExecutionContext(llvm::Value *ctx) { executionContextStack.push_back(ctx); } + void pushExecutionContext(llvm::Value *ctx) { + executionContextStack.push_back(ctx); + } void popExecutionContext() { executionContextStack.pop_back(); } ThreadsafeCompilerState &compilerState; diff --git a/backend/codegen/invoke/InvokeManager.h b/backend/codegen/invoke/InvokeManager.h index 5283e90f..8942d02c 100644 --- a/backend/codegen/invoke/InvokeManager.h +++ b/backend/codegen/invoke/InvokeManager.h @@ -63,6 +63,7 @@ class InvokeManager { ObjectTypeSet createQ(mpq_ptr val); bool canThrow(const std::string &fname) const; bool hasSwiftSelf(const std::vector &args); + public: explicit InvokeManager(llvm::IRBuilder<> &b, llvm::Module &m, ValueEncoder &v, LLVMTypes &t, ThreadsafeCompilerState &s, CodeGen &cg); @@ -126,11 +127,11 @@ class InvokeManager { const clojure::rt::protobuf::bytecode::Node *node = nullptr, const std::vector &extraCleanup = {}); - - TypedValue generateVarInvoke( - TypedValue varObj, const std::vector &args, - CleanupChainGuard *guard = nullptr, - const clojure::rt::protobuf::bytecode::Node *node = nullptr); + TypedValue + generateVarInvoke(TypedValue varObj, const std::vector &args, + CleanupChainGuard *guard = nullptr, + const clojure::rt::protobuf::bytecode::Node *node = nullptr, + bool forceStaticVar = false); TypedValue generateStaticKeywordInvoke( TypedValue keyword, const std::vector &args, @@ -153,8 +154,8 @@ class InvokeManager { const clojure::rt::protobuf::bytecode::Node *node = nullptr, const std::vector &extraCleanup = {}); - llvm::Value *generateICLookup( - llvm::Value *currentVal, size_t argCount, CleanupChainGuard *guard); + llvm::Value *generateICLookup(llvm::Value *currentVal, size_t argCount, + CleanupChainGuard *guard); const std::vector &getICSlotNames() const { return icSlotNames; } }; diff --git a/backend/codegen/invoke/InvokeManager_InstanceCall.cpp b/backend/codegen/invoke/InvokeManager_InstanceCall.cpp index 2aecca6c..d967ff32 100644 --- a/backend/codegen/invoke/InvokeManager_InstanceCall.cpp +++ b/backend/codegen/invoke/InvokeManager_InstanceCall.cpp @@ -91,11 +91,11 @@ TypedValue InvokeManager::generateDynamicInstanceCall( // 1. Get current instance type TypedValue boxedInstance = valueEncoder.box(instance); FunctionType *getTypeSig = - FunctionType::get(types.wordTy, {types.RT_valueTy}, false); + FunctionType::get(types.i32Ty, {types.RT_valueTy}, false); FunctionCallee getTypeFunc = theModule.getOrInsertFunction("getType", getTypeSig); - Value *currentTypeIdent = - builder.CreateCall(getTypeFunc, {boxedInstance.value}); + Value *currentTypeIdent = builder.CreateZExt( + builder.CreateCall(getTypeFunc, {boxedInstance.value}), types.wordTy); // 2. Check IC hit (Atomic load for consistency) unsigned int pairSizeBits = wordSizeBits * 2; diff --git a/backend/codegen/invoke/InvokeManager_VarCall.cpp b/backend/codegen/invoke/InvokeManager_VarCall.cpp index 7ef830cc..1d45443d 100644 --- a/backend/codegen/invoke/InvokeManager_VarCall.cpp +++ b/backend/codegen/invoke/InvokeManager_VarCall.cpp @@ -12,8 +12,8 @@ namespace rt { TypedValue InvokeManager::generateVarInvoke( TypedValue varObj, const std::vector &args, - CleanupChainGuard *guard, - const clojure::rt::protobuf::bytecode::Node *node) { + CleanupChainGuard *guard, const clojure::rt::protobuf::bytecode::Node *node, + bool forceStaticVar) { size_t argCount = args.size(); @@ -23,10 +23,9 @@ TypedValue InvokeManager::generateVarInvoke( // 2. Load current value FROM VAR (borrowed call - Var_peek) FunctionType *peekSig = FunctionType::get( types.RT_valueTy, {types.ExecutionContextPtrTy, types.ptrTy}, false); - Value *currentVal = - invokeRaw("Var_peek", peekSig, {codeGen.getExecutionContext(), varPtr}, - guard, false, {}, true); - + Value *currentVal = invokeRaw( + forceStaticVar ? "Var_peekStatic" : "Var_peek", peekSig, + {codeGen.getExecutionContext(), varPtr}, guard, false, {}, true); // 3. Unified IC resolution (Handles Functions, Keywords, Maps, Vectors) Value *methodToCall = generateICLookup(currentVal, argCount, guard); diff --git a/backend/codegen/ops/InvokeNode.cpp b/backend/codegen/ops/InvokeNode.cpp index caa9773f..1c378249 100644 --- a/backend/codegen/ops/InvokeNode.cpp +++ b/backend/codegen/ops/InvokeNode.cpp @@ -39,8 +39,8 @@ TypedValue CodeGen::codegen(const Node &node, const InvokeNode &subnode, } // 3. Generate the VarInvoke call - TypedValue result = - invokeManager.generateVarInvoke(varObj, args, &guard, &node); + TypedValue result = invokeManager.generateVarInvoke(varObj, args, &guard, + &node, !v->dynamic); return TypedValue(result.type.restriction(typeRestrictions), result.value); } diff --git a/backend/codegen/ops/NewNode.cpp b/backend/codegen/ops/NewNode.cpp index 681b0530..22d4c1cd 100644 --- a/backend/codegen/ops/NewNode.cpp +++ b/backend/codegen/ops/NewNode.cpp @@ -127,7 +127,7 @@ TypedValue CodeGen::codegen(const Node &node, const NewNode &subnode, if (!argRuntimeTypes[i]) { TypedValue boxed = this->valueEncoder.box(args[i]); FunctionType *getTypeSig = FunctionType::get( - this->types.wordTy, {this->types.RT_valueTy}, false); + this->types.i32Ty, {this->types.RT_valueTy}, false); argRuntimeTypes[i] = this->invokeManager.invokeRaw( "getType", getTypeSig, {boxed.value}, &guard, true); } @@ -136,7 +136,7 @@ TypedValue CodeGen::codegen(const Node &node, const NewNode &subnode, Value *targetVal = ConstantInt::get(this->types.i32Ty, (uint32_t)targetTypeID); Value *isType = this->Builder.CreateICmpEQ( - this->Builder.CreateTrunc(argRuntimeTypes[i], this->types.i32Ty), + argRuntimeTypes[i], targetVal); if (match) diff --git a/backend/codegen/ops/StaticCallNode.cpp b/backend/codegen/ops/StaticCallNode.cpp index 6c058971..0ee6d21b 100644 --- a/backend/codegen/ops/StaticCallNode.cpp +++ b/backend/codegen/ops/StaticCallNode.cpp @@ -168,7 +168,7 @@ TypedValue CodeGen::codegen(const Node &node, const StaticCallNode &subnode, if (!argRuntimeTypes[i]) { TypedValue boxed = this->valueEncoder.box(args[i]); FunctionType *getTypeSig = FunctionType::get( - this->types.wordTy, {this->types.RT_valueTy}, false); + this->types.i32Ty, {this->types.RT_valueTy}, false); argRuntimeTypes[i] = this->invokeManager.invokeRaw( "getType", getTypeSig, {boxed.value}, &guard, true); } @@ -177,7 +177,7 @@ TypedValue CodeGen::codegen(const Node &node, const StaticCallNode &subnode, Value *targetVal = ConstantInt::get(this->types.i32Ty, (uint32_t)targetID); Value *isType = this->Builder.CreateICmpEQ( - this->Builder.CreateTrunc(argRuntimeTypes[i], this->types.i32Ty), + argRuntimeTypes[i], targetVal); if (match) { diff --git a/backend/jit/JITEngine.cpp b/backend/jit/JITEngine.cpp index ab41431e..9b3f517b 100644 --- a/backend/jit/JITEngine.cpp +++ b/backend/jit/JITEngine.cpp @@ -24,8 +24,19 @@ #include #include #include +#include #include +#ifdef __cplusplus +extern "C" { +#endif + +void __asan_version_mismatch_check_v8(void) {} + +#ifdef __cplusplus +} +#endif + namespace rt { JITEngine::Finaliser::~Finaliser() { @@ -187,6 +198,9 @@ JITEngine::compileGeneric(std::function codegenFunc, try { auto codeGenerator = CodeGen(moduleName, threadsafeState, (void *)this); + codeGenerator.TheModule->setTargetTriple( + TM->getTargetTriple().str()); + codeGenerator.TheModule->setDataLayout(TM->createDataLayout()); std::string fName; try { @@ -501,6 +515,9 @@ void JITEngine::registerRuntimeSymbols() { runtimeSymbols.insert( absoluteSymbol("deleteException_C", (void *)deleteException_C)); + runtimeSymbols.insert( + absoluteSymbol("__asan_version_mismatch_check_v8", + (void *)__asan_version_mismatch_check_v8)); cantFail(jit->getMainJITDylib().define( absoluteSymbols(std::move(runtimeSymbols)))); } @@ -672,6 +689,15 @@ void JITEngine::optimize(llvm::Module &M, const std::string &entryPoint) { MPM.addPass(PB.buildPerModuleDefaultPipeline(optLevel)); MPM.addPass(llvm::GlobalDCEPass()); + + if (optLevel == llvm::OptimizationLevel::O0) { + llvm::AddressSanitizerOptions opts; + MPM.addPass(llvm::AddressSanitizerPass(opts, + /*UseGlobalGC=*/true, + /*UseOdrIndicator=*/false, + llvm::AsanDtorKind::Global, + llvm::AsanCtorKind::Global)); + } MPM.run(M, MAM); } diff --git a/backend/runtime/CMakeLists.txt b/backend/runtime/CMakeLists.txt index 21cef739..37fdd761 100644 --- a/backend/runtime/CMakeLists.txt +++ b/backend/runtime/CMakeLists.txt @@ -6,11 +6,11 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -set(CMAKE_CXX_FLAGS_DEBUG "-g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -fexceptions") -set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -fexceptions") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -fexceptions") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -fexceptions -g") -set(CMAKE_C_FLAGS_DEBUG "-g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -fexceptions") -set(CMAKE_C_FLAGS_RELEASE "-Ofast -fexceptions") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -fexceptions") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast -fexceptions -g") add_compile_definitions(BUILD_TYPE="${CMAKE_BUILD_TYPE}") @@ -146,8 +146,10 @@ foreach(src ${SOURCES}) ${CMAKE_CURRENT_SOURCE_DIR}/${src} -iquote ${CMAKE_CURRENT_SOURCE_DIR} -I${GMP_INCLUDE_DIRS} -O3 -gline-tables-only -fPIC -fexceptions -funwind-tables ${EMULATED_TLS_FLAG} ${MCX16_FLAG} + -MD -MF ${bc_output}.d -o ${bc_output} - DEPENDS ${src} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${src} + DEPFILE ${bc_output}.d COMMENT "Generating LLVM Bitcode for ${src}" ) list(APPEND BITCODE_OUTPUTS ${bc_output}) @@ -163,6 +165,7 @@ add_custom_command( # 4. Create a target to trigger the build add_custom_target(runtime_ir ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/runtime_uber.bc) +add_dependencies(runtime runtime_ir) ######################################################################## diff --git a/backend/runtime/PersistentArrayMap.c b/backend/runtime/PersistentArrayMap.c index 13225d4d..965e3646 100644 --- a/backend/runtime/PersistentArrayMap.c +++ b/backend/runtime/PersistentArrayMap.c @@ -5,24 +5,24 @@ #include "defines.h" #include -static PersistentArrayMap *EMPTY = NULL; +PersistentArrayMap * volatile EMPTY_ARRAY_MAP = NULL; /* mem done */ PersistentArrayMap *PersistentArrayMap_empty() { - Ptr_retain(EMPTY); - return EMPTY; + Ptr_retain(EMPTY_ARRAY_MAP); + return EMPTY_ARRAY_MAP; } void PersistentArrayMap_initialise() { - if (EMPTY) + if (EMPTY_ARRAY_MAP) return; - EMPTY = PersistentArrayMap_create(); + EMPTY_ARRAY_MAP = PersistentArrayMap_create(); } void PersistentArrayMap_cleanup() { - if (EMPTY) { - Ptr_release(EMPTY); - EMPTY = NULL; + if (EMPTY_ARRAY_MAP) { + Ptr_release(EMPTY_ARRAY_MAP); + EMPTY_ARRAY_MAP = NULL; } } diff --git a/backend/runtime/PersistentList.c b/backend/runtime/PersistentList.c index 38a1e880..e12481cd 100644 --- a/backend/runtime/PersistentList.c +++ b/backend/runtime/PersistentList.c @@ -4,23 +4,23 @@ #include "RTValue.h" #include -static PersistentList *EMPTY = NULL; +PersistentList * volatile EMPTY_LIST = NULL; // How to mark /* mem done */ void PersistentList_initialise() { - if (EMPTY == NULL) - EMPTY = PersistentList_create(RT_boxNull(), NULL); + if (EMPTY_LIST == NULL) + EMPTY_LIST = PersistentList_create(RT_boxNull(), NULL); } PersistentList *PersistentList_empty() { - Ptr_retain(EMPTY); - return EMPTY; + Ptr_retain(EMPTY_LIST); + return EMPTY_LIST; } void PersistentList_cleanup() { - if (EMPTY) { - Ptr_release(EMPTY); - EMPTY = NULL; + if (EMPTY_LIST) { + Ptr_release(EMPTY_LIST); + EMPTY_LIST = NULL; } } diff --git a/backend/runtime/PersistentVector.c b/backend/runtime/PersistentVector.c index 6cae8a80..1441ca3d 100644 --- a/backend/runtime/PersistentVector.c +++ b/backend/runtime/PersistentVector.c @@ -9,7 +9,7 @@ #include "Transient.h" #include -PersistentVector *EMPTY_VECTOR = NULL; +PersistentVector * volatile EMPTY_VECTOR = NULL; /* mem done */ PersistentVector *PersistentVector_create() { diff --git a/backend/runtime/Var.c b/backend/runtime/Var.c index 906dc862..75956e99 100644 --- a/backend/runtime/Var.c +++ b/backend/runtime/Var.c @@ -200,7 +200,13 @@ RTValue Var_peek(__attribute__((swift_context)) struct ExecutionContext *ctx, } } } - return atomic_load_explicit(&self->root, memory_order_acquire); + return atomic_load_explicit(&self->root, memory_order_relaxed); +} + +RTValue +Var_peekStatic(__attribute__((swift_context)) struct ExecutionContext *ctx, + Var *self) __attribute__((swiftcall)) { + return atomic_load_explicit(&self->root, memory_order_relaxed); } RTValue Var_set(__attribute__((swift_context)) struct ExecutionContext *ctx, diff --git a/backend/runtime/Var.h b/backend/runtime/Var.h index f7ad7269..d75afd4a 100644 --- a/backend/runtime/Var.h +++ b/backend/runtime/Var.h @@ -44,11 +44,18 @@ void Var_destroy(Var *self); Var *Var_setDynamic(Var *self, bool dynamic); // modifies and returns self bool Var_isDynamic(Var *self); bool Var_hasRoot(Var *self); -RTValue Var_deref(__attribute__((swift_context)) struct ExecutionContext *ctx, Var *self) __attribute__((swiftcall)); +RTValue Var_deref(__attribute__((swift_context)) struct ExecutionContext *ctx, + Var *self) __attribute__((swiftcall)); /* the returned reference is not retained and is not guaranteed to even be valid after the call returns. */ -RTValue Var_peek(__attribute__((swift_context)) struct ExecutionContext *ctx, Var *self) __attribute__((swiftcall)); -RTValue Var_set(__attribute__((swift_context)) struct ExecutionContext *ctx, Var *self, RTValue value) __attribute__((swiftcall)); +RTValue Var_peek(__attribute__((swift_context)) struct ExecutionContext *ctx, + Var *self) __attribute__((swiftcall)); +RTValue +Var_peekStatic(__attribute__((swift_context)) struct ExecutionContext *ctx, + Var *self) __attribute__((swiftcall)); + +RTValue Var_set(__attribute__((swift_context)) struct ExecutionContext *ctx, + Var *self, RTValue value) __attribute__((swiftcall)); RTValue Var_bindRoot(Var *self, RTValue object); RTValue Var_unbindRoot(Var *self); diff --git a/tests/fib.clj b/tests/fib.clj index 5cc9363f..f45ddbbb 100644 --- a/tests/fib.clj +++ b/tests/fib.clj @@ -1,3 +1,7 @@ +(in-ns 'clojure.core) +(def list clojure.lang.PersistentList/creator) +(in-ns 'user) + (defn fib [x] (if (= x 1) 1 (if (= x 2) 1 (+ (fib (- x 1)) (fib (- x 2)))))) (fib 42) diff --git a/tests/fibjank.clj b/tests/fibjank.clj new file mode 100644 index 00000000..03ea0cad --- /dev/null +++ b/tests/fibjank.clj @@ -0,0 +1,12 @@ +(in-ns 'clojure.core) +(def list clojure.lang.PersistentList/creator) +(in-ns 'user) + +(defn fibonacci [n] + (if (<= n 1) + n + (+ (fibonacci (- n 1)) + (fibonacci (- n 2))))) + + +(fibonacci 42) \ No newline at end of file From b329af493e1443f7709025928f09e2a157127841 Mon Sep 17 00:00:00 2001 From: Marek Lipert Date: Wed, 10 Jun 2026 09:20:12 +0200 Subject: [PATCH 2/7] Fix build errors. --- backend/jit/JITEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/jit/JITEngine.cpp b/backend/jit/JITEngine.cpp index 9b3f517b..90b09631 100644 --- a/backend/jit/JITEngine.cpp +++ b/backend/jit/JITEngine.cpp @@ -31,7 +31,7 @@ extern "C" { #endif -void __asan_version_mismatch_check_v8(void) {} +__attribute__((weak)) void __asan_version_mismatch_check_v8(void) {} #ifdef __cplusplus } From 534c3c553031a4a4db5a5cd6f4dda37cb685bfe3 Mon Sep 17 00:00:00 2001 From: Marek Lipert Date: Wed, 10 Jun 2026 10:02:52 +0200 Subject: [PATCH 3/7] Fix perf regression. --- backend/runtime/CMakeLists.txt | 7 +++- backend/runtime/Object.h | 59 ++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/backend/runtime/CMakeLists.txt b/backend/runtime/CMakeLists.txt index 37fdd761..d561eada 100644 --- a/backend/runtime/CMakeLists.txt +++ b/backend/runtime/CMakeLists.txt @@ -135,6 +135,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(EMULATED_TLS_FLAG "-femulated-tls") endif() +set(NDEBUG_FLAG "") +if(CMAKE_BUILD_TYPE MATCHES "^(Release|RelWithDebInfo|MinSizeRel)$") + set(NDEBUG_FLAG "-DNDEBUG") +endif() + # 2. Compile each source file to an individual .bc file foreach(src ${SOURCES}) get_filename_component(basename ${src} NAME_WE) @@ -145,7 +150,7 @@ foreach(src ${SOURCES}) COMMAND ${CLANG_EXECUTABLE} -emit-llvm -c ${CMAKE_CURRENT_SOURCE_DIR}/${src} -iquote ${CMAKE_CURRENT_SOURCE_DIR} -I${GMP_INCLUDE_DIRS} - -O3 -gline-tables-only -fPIC -fexceptions -funwind-tables ${EMULATED_TLS_FLAG} ${MCX16_FLAG} + -O3 -gline-tables-only -fPIC -fexceptions -funwind-tables ${EMULATED_TLS_FLAG} ${MCX16_FLAG} ${NDEBUG_FLAG} -MD -MF ${bc_output}.d -o ${bc_output} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${src} diff --git a/backend/runtime/Object.h b/backend/runtime/Object.h index d15f2f7f..82603581 100644 --- a/backend/runtime/Object.h +++ b/backend/runtime/Object.h @@ -5,8 +5,6 @@ struct ExecutionContext; #define RT_OBJECT - - #include "Hash.h" #include "word.h" #ifdef __cplusplus @@ -106,24 +104,50 @@ inline void Object_promoteToSharedShallow(Object *self, uword_t current); inline uword_t Object_getRawRefCount(Object *self); inline void Object_create(Object *restrict self, objectType type) { +#ifdef REFCOUNT_NONATOMIC + self->refCount = COUNT_INC; +#else atomic_store_explicit(&(self->atomicRefCount), COUNT_INC, memory_order_relaxed); +#endif self->type = type; +#ifdef REFCOUNT_TRACING if (self->type > 0 && (int)self->type <= TRACING_LIMIT) { atomic_fetch_add_explicit(&(allocationCount[self->type - 1]), 1, memory_order_relaxed); atomic_fetch_add_explicit(&(objectCount[self->type - 1]), 1, memory_order_relaxed); } +#endif + // printf("--> Allocating type %d addres %p\n", self->type, ); } inline void Object_retain(Object *restrict self) { +// printf("RETAIN!!! %d\n", self->type); +#ifdef REFCOUNT_TRACING if (self->type > 0 && (int)self->type <= TRACING_LIMIT) { atomic_fetch_add_explicit(&(allocationCount[self->type - 1]), 1, memory_order_relaxed); } - atomic_fetch_add_explicit(&(self->atomicRefCount), COUNT_INC, +#endif +#ifdef REFCOUNT_NONATOMIC + self->refCount += COUNT_INC; +#else + uword_t current = + atomic_load_explicit(&(self->atomicRefCount), memory_order_relaxed); + + if (__builtin_expect(!(current & SHARED_BIT), 1)) { + // FAST PATH: It's local. Use a plain non-atomic write. + // We can do this because only THIS thread owns it. + uword_t new_val = current + COUNT_INC; + atomic_store_explicit(&(self->atomicRefCount), new_val, + memory_order_relaxed); + } else { + // SLOW PATH: It's shared. Must use atomic fetch_add. + atomic_fetch_add_explicit(&(self->atomicRefCount), COUNT_INC, memory_order_relaxed); + } +#endif } inline void Ptr_retain(void *ptr); @@ -140,31 +164,30 @@ RTValue RT_withMeta(RTValue v, RTValue meta); #include "BigInteger.h" #include "Boolean.h" #include "BridgedObject.h" -#include "PersistentVectorChunkedSeq.h" -#include "PersistentVectorReverseSeq.h" -#include "ArrayChunk.h" #include "Class.h" #include "ConcurrentHashMap.h" #include "Double.h" #include "Exception.h" +#include "ExecutionContext.h" #include "Function.h" #include "Integer.h" #include "Keyword.h" +#include "Namespace.h" #include "Nil.h" #include "PersistentArrayMap.h" #include "PersistentList.h" #include "PersistentVector.h" +#include "PersistentVectorChunkedSeq.h" #include "PersistentVectorIterator.h" #include "PersistentVectorNode.h" +#include "PersistentVectorReverseSeq.h" #include "Ratio.h" #include "String.h" #include "StringBuilder.h" #include "Symbol.h" #include "Var.h" -#include "ExecutionContext.h" -#include "Namespace.h" -extern void ExecutionContext_destroy(ExecutionContext* self); +extern void ExecutionContext_destroy(ExecutionContext *self); inline void *allocate(size_t size) { #ifndef USE_MEMORY_BANKS @@ -321,13 +344,15 @@ inline void Object_destroy(Object *restrict self, bool deallocateChildren) { StringBuilder_destroy((StringBuilder *)self); break; case persistentVectorChunkedSeqType: - PersistentVectorChunkedSeq_destroy((PersistentVectorChunkedSeq *)self, deallocateChildren); + PersistentVectorChunkedSeq_destroy((PersistentVectorChunkedSeq *)self, + deallocateChildren); break; case arrayChunkType: ArrayChunk_destroy((ArrayChunk *)self, deallocateChildren); break; case persistentVectorReverseSeqType: - PersistentVectorReverseSeq_destroy((PersistentVectorReverseSeq *)self, deallocateChildren); + PersistentVectorReverseSeq_destroy((PersistentVectorReverseSeq *)self, + deallocateChildren); break; case symbolType: Symbol_destroy((Symbol *)self); @@ -370,10 +395,12 @@ inline bool isReusable(RTValue self) { inline bool Object_release_internal(Object *restrict self, bool deallocateChildren) { +#ifdef REFCOUNT_TRACING if (self->type > 0 && (int)self->type <= TRACING_LIMIT) { atomic_fetch_sub_explicit(&(allocationCount[self->type - 1]), 1, memory_order_relaxed); } +#endif #ifdef REFCOUNT_NONATOMIC self->refCount -= COUNT_INC; if ((self->refCount >> 1) == 0) { @@ -482,7 +509,8 @@ inline void Object_promoteToShared(Object *restrict self) { break; case persistentVectorChunkedSeqType: - Object_promoteToShared((Object *)((PersistentVectorChunkedSeq *)self)->it.parent); + Object_promoteToShared( + (Object *)((PersistentVectorChunkedSeq *)self)->it.parent); Object_promoteToSharedShallow(self, count); break; case arrayChunkType: @@ -627,7 +655,9 @@ inline bool Object_equals(Object *self, Object *other) { case stringBuilderType: return StringBuilder_equals((StringBuilder *)self, (StringBuilder *)other); case persistentVectorChunkedSeqType: - return PersistentVectorChunkedSeq_equals((PersistentVectorChunkedSeq *)self, (PersistentVectorChunkedSeq *)other); + return PersistentVectorChunkedSeq_equals( + (PersistentVectorChunkedSeq *)self, + (PersistentVectorChunkedSeq *)other); case symbolType: return Symbol_equals((Symbol *)self, (Symbol *)other); case namespaceType: @@ -696,7 +726,8 @@ inline String *Object_toString(Object *restrict self) { case stringBuilderType: return StringBuilder_toString((StringBuilder *)self); case persistentVectorChunkedSeqType: - return PersistentVectorChunkedSeq_toString((PersistentVectorChunkedSeq *)self); + return PersistentVectorChunkedSeq_toString( + (PersistentVectorChunkedSeq *)self); case symbolType: return Symbol_toString(RT_boxSymbol(self)); case namespaceType: From cb6ba1644bfddb93d9b0e9e5d99eebff2b676a6f Mon Sep 17 00:00:00 2001 From: Marek Lipert Date: Wed, 10 Jun 2026 22:02:39 +0200 Subject: [PATCH 4/7] Module name fix. --- backend/jit/JITEngine.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/jit/JITEngine.cpp b/backend/jit/JITEngine.cpp index 90b09631..77ccd894 100644 --- a/backend/jit/JITEngine.cpp +++ b/backend/jit/JITEngine.cpp @@ -196,8 +196,10 @@ JITEngine::compileGeneric(std::function codegenFunc, compilationPool .enqueue([this, codegenFunc, moduleName]() -> JITResult { try { + static std::atomic moduleCounter{0}; + std::string uniqueModuleId = moduleName + "_" + std::to_string(moduleCounter.fetch_add(1)); auto codeGenerator = - CodeGen(moduleName, threadsafeState, (void *)this); + CodeGen(uniqueModuleId, threadsafeState, (void *)this); codeGenerator.TheModule->setTargetTriple( TM->getTargetTriple().str()); codeGenerator.TheModule->setDataLayout(TM->createDataLayout()); From a4bf51d70aade5ad5857a4e293a7c20e9be94b07 Mon Sep 17 00:00:00 2001 From: Marek Lipert Date: Wed, 10 Jun 2026 22:57:36 +0200 Subject: [PATCH 5/7] Correct for ASAN offsets. --- backend/bridge/Exceptions.cpp | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/backend/bridge/Exceptions.cpp b/backend/bridge/Exceptions.cpp index ff76309c..c97721f4 100644 --- a/backend/bridge/Exceptions.cpp +++ b/backend/bridge/Exceptions.cpp @@ -184,7 +184,22 @@ void symbolizeStackChain(std::stringstream &ss, auto objOrErr = llvm::object::ObjectFile::createObjectFile( buffer->getMemBufferRef()); if (objOrErr) { - uword_t offset = addr - entryStart; + uword_t symbolOffsetInObj = 0; + for (auto const &Sym : objOrErr.get()->symbols()) { + auto nameOrErr = Sym.getName(); + if (nameOrErr) { + std::string symName = nameOrErr.get().str(); + if (symName == entry.name || (symName.starts_with("_") && symName.substr(1) == entry.name)) { + auto valOrErr = Sym.getValue(); + if (valOrErr) { + symbolOffsetInObj = valOrErr.get(); + break; + } + } + } + } + + uword_t offset = symbolOffsetInObj + (addr - entryStart); if (offset >= 4) offset -= 4; @@ -447,7 +462,22 @@ LanguageException::LanguageException(const std::string &name, RTValue message, auto objOrErr = llvm::object::ObjectFile::createObjectFile( buffer->getMemBufferRef()); if (objOrErr) { - uword_t offset = addr - entryStart; + uword_t symbolOffsetInObj = 0; + for (auto const &Sym : objOrErr.get()->symbols()) { + auto nameOrErr = Sym.getName(); + if (nameOrErr) { + std::string symName = nameOrErr.get().str(); + if (symName == entry.name || (symName.starts_with("_") && symName.substr(1) == entry.name)) { + auto valOrErr = Sym.getValue(); + if (valOrErr) { + symbolOffsetInObj = valOrErr.get(); + break; + } + } + } + } + + uword_t offset = symbolOffsetInObj + (addr - entryStart); if (offset >= 4) offset -= 4; From 36044e4f4311e8a5fc008263985af8623c9fdc56 Mon Sep 17 00:00:00 2001 From: Marek Lipert Date: Thu, 11 Jun 2026 08:32:18 +0200 Subject: [PATCH 6/7] ELF fix. --- backend/bridge/Exceptions.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/backend/bridge/Exceptions.cpp b/backend/bridge/Exceptions.cpp index c97721f4..81e0df94 100644 --- a/backend/bridge/Exceptions.cpp +++ b/backend/bridge/Exceptions.cpp @@ -185,6 +185,7 @@ void symbolizeStackChain(std::stringstream &ss, buffer->getMemBufferRef()); if (objOrErr) { uword_t symbolOffsetInObj = 0; + uint64_t sectionIndex = llvm::object::SectionedAddress::UndefSection; for (auto const &Sym : objOrErr.get()->symbols()) { auto nameOrErr = Sym.getName(); if (nameOrErr) { @@ -193,6 +194,10 @@ void symbolizeStackChain(std::stringstream &ss, auto valOrErr = Sym.getValue(); if (valOrErr) { symbolOffsetInObj = valOrErr.get(); + auto secOrErr = Sym.getSection(); + if (secOrErr && secOrErr.get() != objOrErr.get()->section_end()) { + sectionIndex = secOrErr.get()->getIndex(); + } break; } } @@ -208,7 +213,7 @@ void symbolizeStackChain(std::stringstream &ss, llvm::symbolize::LLVMSymbolizer localSymbolizer; auto resOrErr = localSymbolizer.symbolizeInlinedCode( *objOrErr.get(), - {offset, llvm::object::SectionedAddress::UndefSection}); + {offset, sectionIndex}); if (resOrErr) { auto &inlinedInfo = resOrErr.get(); @@ -463,6 +468,7 @@ LanguageException::LanguageException(const std::string &name, RTValue message, buffer->getMemBufferRef()); if (objOrErr) { uword_t symbolOffsetInObj = 0; + uint64_t sectionIndex = llvm::object::SectionedAddress::UndefSection; for (auto const &Sym : objOrErr.get()->symbols()) { auto nameOrErr = Sym.getName(); if (nameOrErr) { @@ -471,6 +477,10 @@ LanguageException::LanguageException(const std::string &name, RTValue message, auto valOrErr = Sym.getValue(); if (valOrErr) { symbolOffsetInObj = valOrErr.get(); + auto secOrErr = Sym.getSection(); + if (secOrErr && secOrErr.get() != objOrErr.get()->section_end()) { + sectionIndex = secOrErr.get()->getIndex(); + } break; } } @@ -484,7 +494,7 @@ LanguageException::LanguageException(const std::string &name, RTValue message, llvm::symbolize::LLVMSymbolizer localSymbolizer; auto resOrErr = localSymbolizer.symbolizeInlinedCode( *objOrErr.get(), - {offset, llvm::object::SectionedAddress::UndefSection}); + {offset, sectionIndex}); if (resOrErr) { auto &inlinedInfo = resOrErr.get(); From 1aef914d70d7baa5a05ad8dd9d9e9a07521e2f38 Mon Sep 17 00:00:00 2001 From: Marek Lipert Date: Thu, 11 Jun 2026 09:43:06 +0200 Subject: [PATCH 7/7] ELF fix #2. --- backend/bridge/Exceptions.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/bridge/Exceptions.cpp b/backend/bridge/Exceptions.cpp index 81e0df94..2cf47cb4 100644 --- a/backend/bridge/Exceptions.cpp +++ b/backend/bridge/Exceptions.cpp @@ -220,10 +220,13 @@ void symbolizeStackChain(std::stringstream &ss, for (uint32_t i = 0; i < inlinedInfo.getNumberOfFrames(); ++i) { auto &info = inlinedInfo.getFrame(i); - std::string fnName = - (info.FunctionName != "") - ? llvm::demangle(info.FunctionName) - : demangled; + std::string funcName = info.FunctionName; + if (funcName == "" || + funcName.find("asan.module_ctor") != std::string::npos || + funcName.find("asan.module_dtor") != std::string::npos) { + funcName = entry.name; + } + std::string fnName = llvm::demangle(funcName); std::stringstream frameSs; frameSs << " " << Colors::INFRA(useColor) << "at "