C++中的JIT是运行时按需将代码(如表达式、IR)动态编译为机器码并在内存中执行,区别于g++等传统编译器的离线静态编译;其实现可手写x86-64机器码或借助LLVM IR实现。
JIT(Just-In-Time)编译器不是在程序启动前把全部代码编译成机器码,而是在运行时,按需把某段代码(比如一段字符串形式的C++表达式、字节码或自定义IR)动态编译为本地机器指令,并立即执行。它不像g++那样生成可执行文件,而是调用操作系统API(如mmap + mprotect)申请可执行内存,把生成的机器码拷进去,再通过函数指针调用。
关键区别在于:传统编译是“离线+静态”,JIT是“在线+动态+内存中执行”。对C++来说,实现一个“真正能编译C++语法”的JIT极其复杂(需完整前端),所以入门级JIT通常聚焦于:表达式求值、简单函数生成(如f(x) = x*x + 2*x + 1)或LLVM IR → 机器码这一环。
不依赖LLVM也能动手——用纯C++手写一个支持加减乘除和括号的浮点表达式JIT(基于栈机思想 + x86-64机器码生成):
"3.14 + x * 2"切分成token(数字、变量名、运算符)movsd、addsd、
mulsd等SSE指令生成代码,写入可执行内存示例片段(简化):
// 假设已分配好 executable_bufferLLVM提供了成熟、跨平台、生产级的JIT基础设施(llvm::orc API)。它不让你手写机器码,而是专注描述计算逻辑(用LLVM IR),由LLVM自动优化并生成机器码。
典型步骤(C++代码):
InitializeNativeTarget()、InitializeNativeTargetAsmPrinter()
orc::KaleidoscopeJIT 或 orc::ThreadSafeContext + orc::ExecutionSession
IRBuilder生成IR(例如创建函数double add(double a, double b))auto sym = jit->lookup("add"); auto f = sym->getAddress();
优势明显:支持循环、条件、函数调用、链接外部符号(如printf)、自动优化(O2)、多模块热重载——这才是工业级JIT的起点。
新手常卡在这几个地方:
mprotect(..., PROT_EXEC);Windows用VirtualProtect(..., PAGE_EXECUTE_READWRITE)
sin或malloc,必须提前用addGlobalMapping注册其地址llvm::sys::PrintStackTrace()捕获崩溃;生成.bc文件用llc离线反汇编验证IR→asm是否合理不复杂但容易忽略。