Conversation
54184fb to
dc7434e
Compare
|
Awesome, indeed! Congratulations. git clone https://github.com/gburd/pure-lang.git Pure 0.7.1 is now configured for LLVM 20.1.2 on x86_64-pc-linux-gnu. Source directory: . Now run 'make' to build everything, and 'make install' to install this interpreter.cc:14729:13: error: ‘cos’ was not declared in this scope; did you mean ‘llvm::Intrinsic::cos’? |
36bc08c to
9492495
Compare
9492495 to
fec4883
Compare
07815a3 to
d29a882
Compare
|
It works 🥇 Thanks a lot! |
|
@gburd Absolutely awesome! I've been dragging my feet on this for years now, as the prospect of porting to the new JIT just seemed too daunting. I'm also impressed by what is possible with LLMs these days. @nilqed Thanks for testing Greg's work! I'll need some time to test it myself and see whether I can get it to work on the latest LLVM 22.1.8 which recently landed on Arch. |
|
Given that you have seen it and agree there is value I can retest/validate with that newer LLVM version and update the PR. The other target I might try in the next PR is elimination of the GIL for real multithreading using libxtc (https://github.com/gburd/libxtc). Let me know if that is appealing or not. |
That would be much appreciated!
Exactly my first thought when I read the bit about thread-safe code generation! :) I'm not sure about the ramifications this will have on the other library modules, but I think that it would be great if we could get rid of the GIL. It would be nice to have this in a separate PR or at least a separate commit if possible. |
Replace the legacy MCJIT execution engine with ORC JIT v2: - ThreadSafeContext/ThreadSafeModule for thread-safe IR management - LLJIT with delta compilation (submit_module) for incremental JIT - ResourceTrackers for per-module code lifetime management - Opaque pointer migration (LLVM 15+ typed ptr elimination) - Adapt to LLVM 20/21 API changes (setTargetTriple, PassBuilder) Build system and ecosystem: - Add Nix flake for reproducible builds with LLVM 21 - Update configure.ac for LLVM 20/21 detection and shared lib linking - Fix all 20 ecosystem libraries for C++17 compatibility - Update pure-doc lexer for modern flex/bison
…uction Root cause: exit() triggers double-destruction of static objects in libpure.so on Linux (glibc's __run_exit_handlers + _dl_fini both call __cxa_finalize for the same DSO). Replace exit(0) with _exit(0) to skip unnecessary static destructors. Additional defensive fixes for Env reference counting: - FMap::clear(): check refc before deleting child Envs; if closures still reference an Env (refc > 0), call clear() instead of delete - pure_free_clos(): decrement *refp BEFORE deleting env (correct ordering for release_refp) - Add try_free_refp() to clean up orphaned refp pointers when *refp reaches 0 but no Env tracks the pointer anymore - Env::clear(): set f=nullptr at end for idempotency - Add refp_refcounts map and addref_refp()/release_refp() methods for proper refp lifetime tracking across Env copies Result: 96/96 tests pass deterministically on Linux x86_64.
RISC-V sv39 (39-bit virtual address space) causes R_RISCV_PCREL_HI20 relocation overflow when JIT-compiled code references symbols more than ±2GB away. Root cause: globals with InternalLinkage implicitly set dso_local=true, and LLVM doesn't clear it when linkage changes to ExternalLinkage. Fix: Set CodeModel::Medium + Reloc::PIC_ for RISC-V targets, and in submit_module() clear dso_local on all ExternalLinkage functions and globals. This forces the backend to emit R_RISCV_GOT_HI20 (data) and R_RISCV_CALL_PLT (calls), which JITLink resolves via co-located GOT/PLT entries always within ±2GB range.
Multi-platform CI workflow: - Ubuntu 24.04 matrix: LLVM 20 and LLVM 21 (full test suite on both) - macOS 14 (Apple Silicon): LLVM 21 via Homebrew with full test suite - Security scan with Trivy - Artifact upload of test diffs on failure for debugging - Explicitly uses clang-$VERSION as CC/CXX to match reference test output
d29a882 to
57da13b
Compare
|
LLVM 22.1.8 validated. Full
The interpreter.cc preload fix is folded into the migration commit; the CI + Nix work is a separate commit on top. CI is green across all three LLVM versions plus macOS. |
|
Following up on the GIL discussion above: I've opened the separate PR you asked for.
Both are stacked, so please merge in order: this PR (#44), then #47, then #48. Until each parent merges, GitHub shows the parent's commits in the child's diff; that resolves as they land. Happy to rebase or split further if you'd prefer a different shape. |
Pure LLVM Migration: 3.5.2 (2015) → 21.1.8 (2024)
Overview
This PR completes a comprehensive migration of Pure from LLVM 3.5.2 (2015) to LLVM 21.1.8 (2024), replacing the legacy JIT API (removed in LLVM 3.6) with modern ORC JIT v2 infrastructure. The migration spans 9 years of LLVM evolution and maintains full backward compatibility with Pure's language semantics and API.
Claude: (yes, I used "claude code" for this work)
As the overseeing human for this work I tried to review what was done, the result seems sound but you too should be suspect. This was a project I've though about for years, but never had the time. When trying out LLMs for coding tasks this came to mind and here is the result.
Test Results ✅
Core Language: 65/65 Tests PASS
All core Pure language features verified working on LLVM 21.1.8:
Arithmetic & Comparisons (12/12):
42,1+1=2,2*3=6,10-7=3,100 div 3=33,100 mod 3=1,3.14*2=6.281<2,3>5,2==2,"abc"=="abc","abc"<"def"List Operations (8/8):
abs 5,abs (-5),#[1,2,3],head,tail,reverse,nullHigher-Order Functions (9/9):
foldl (+) 0 [1,2,3,4,5]→ 15map (+1) [1,2,3]→ [2,3,4]foldr (+) 0 [1,2,3]→ 6filter (>3) [1,2,3,4,5]→ [4,5]zip [1,2] [3,4]→ [(1,3),(2,4)][x*2 | x = [1,2,3]]→ [2,4,6]C Runtime Functions (8/8):
int 3.7→ 3,double 42→ 42.0str 42→ "42",val "42"→ 42ord "A"→ 65,chr 65→ "A""hello"+" "+"world"→ "hello world"[1,2,3]+[4,5]→ [1,2,3,4,5]Type System (5/5):
typep int 42,listp [1,2,3],thunkp [1]intp 42,doublep 3.14,stringp "hi"Functions & Control Flow (5/5):
fact n = if n<=0 then 1 else n*fact(n-1); fact 10→ 3628800Ranges & Operators (10/10):
1..5→ [1,2,3,4,5]foldl (+) 0 (1..10)→ 55all,any,not,succ,pred,max,min,||,&&Standard Library: All Modules Working ✅
{1,2;3,4}, dimTechnical Architecture
Migration Path: Legacy JIT → ORC JIT v2
Before (LLVM 3.5.2 - 2015):
After (LLVM 21.1.8 - 2024):
Key Implementation Changes
1. ORC JIT v2 Infrastructure (pure/interpreter.cc)
JIT Initialization:
ExecutionEngine::create()withLLJIT::create()ThreadSafeContextfor thread-safe code generationDynamicLibrarySearchGeneratorfor external symbolsPassBuildermodern optimization pipelineSymbol Resolution:
addGlobalMapping()withabsoluteSymbols()libpure.dylibloading viaDynamicLibrarySearchGenerator::Load()mangleAndIntern()Code Lifecycle:
ResourceTrackerfor fine-grained code removal2. Two-Phase Compilation (Commit 4b684a2)
Problem: Functions without terminators being submitted to JIT caused crashes during prelude loading.
Solution: Separate function body generation from JIT symbol lookup:
Impact: Fixes prelude loading crashes, enables proper JIT materialization order.
3. Delta Submission Strategy (Commit f6d9513)
Problem: Full-module replacement invalidated function pointers, causing duplicate symbol crashes on recompilation.
Solution: Track submitted symbols, only submit new/changed code:
Impact: Preserves function pointer stability, enables safe code recompilation, eliminates duplicate symbol errors.
4. C Runtime Symbol Resolution (Commits 960f6f4, 855078b, 1c4639c)
Problem: C functions from
libpure.dylib(likepure_intval,pure_dblval,typep) not resolving in JIT.Root Cause:
DynamicLibrarySearchGenerator::GetForCurrentProcess()usesdlsym(RTLD_DEFAULT)which only searches the global symbol table. On macOS,libpure.dylibisn't in the global table unless linked with-rdynamic.Solution (Multi-layered):
libpure.dylibexplicitly as JIT search generator:std::string libpure_paths[] = { "libpure.dylib", // DYLD_LIBRARY_PATH "./libpure.dylib", // Current directory "../lib/libpure.dylib", // Relative to build dir LIBDIR "/libpure.dylib", // Install location }; for (const auto& path : libpure_paths) { auto LibPureGen = DynamicLibrarySearchGenerator::Load( path.c_str(), DL->getGlobalPrefix()); if (LibPureGen) { MainJD.addGenerator(std::move(*LibPureGen)); break; } }Impact: Fixes all C runtime function calls, enables
int,double,typep, string operations, and higher-order functions that depend on type checking.Architecture Benefits
Thread Safety:
ThreadSafeContextMemory Management:
ResourceTrackerPerformance:
PassBuilderUSE_FASTCC)Maintainability:
llvm::ErrorMigration Statistics
Code Changes
Major file changes:
pure/interpreter.cc: ~650 lines changed (JIT infrastructure rewrite)pure/interpreter.hh: ~110 lines changed (ORC JIT v2 headers, class members)pure/configure.ac: Updated LLVM detection for 20.1+ requirement.github/workflows/: Complete CI/CD infrastructure (4 workflows)Timeline
Testing Effort
CI/CD Infrastructure
This PR includes comprehensive GitHub Actions workflows:
Build & Test (
ci.yml)Code Quality (
code-quality.yml)Documentation (
docs.yml)Upstream Sync (
upstream-sync.yml)Dependabot (
dependabot.yml)All workflow actions use pinned SHA hashes for supply chain security.
Compatibility & Requirements
LLVM Requirements
Build Dependencies
Platform Compatibility
Breaking Changes
None. Pure's language semantics, API, and standard library remain unchanged. This is a pure infrastructure upgrade.
Documentation
Complete migration documentation included in
tmp/:Known Issues & Limitations
Minor Issues (Not Blocking)
!operator: Returns symbolic in some cases (library issue, not JIT)getpid: Returns symbolic (needs extern declaration in prelude)Platform-Specific Notes
LD_LIBRARY_PATHset for non-standard LLVM installsFuture Work (Post-Merge)
Immediate (Before Release)
INSTALLwith LLVM 20.1+ requirementREADMEwith build instructionsRELEASE-NOTESorNEWSShort-term
Long-term
Migration Team
Special recognition to the tester agent for discovering the libpure vs libc symbol resolution difference that led to the final fix.
Commit History Highlights
Key commits in this PR:
Complete commit history available in branch
llvm-migration-orc-jit.Testing Instructions
Quick Smoke Test
Full Test Suite
CI Validation
The CI workflows will automatically:
References
Summary
This PR successfully migrates Pure from LLVM 3.5.2 (2015) to LLVM 21.1.8 (2024), implementing modern ORC JIT v2 infrastructure while maintaining complete backward compatibility. All 65 core tests pass, all standard library modules work, and comprehensive CI/CD infrastructure is included.
The migration: