Skip to content

slumlordsym/simple-jit

Repository files navigation

Simple JIT Compiler (SJC)

A simple Just-In-Time (JIT) written in C that compiles bytecode instructions to native machine code using template-driven dispatch. It executes the compiled code directly in memory and supports basic math, comparison and bitwise operations with register manipulation.

Note

This project is not production-ready and should be used as a learning resource. It intended for educational purposes to demonstrate JIT compilation concepts as part of an upcoming tutorial I'm creating.

Features

  • Bytecode Compilation: Converts virtual machine bytecode to native x86-64 instructions
  • Register Management: Handles virtual register to native register mapping
  • Constant Pool: Supports loading constants into registers
  • Basic Operations: Implements a variety of arithmetic, comparison, and bitwise operations
  • Memory Management: Handles executable code generation and memory allocation
  • Cross-Platform Build: Supports multiple compilers (GCC, clang, Cosmopolitan cosmocc)

Example compilation

Supported Operations

The current implementation supports the following bytecode operations:

  • OP_LOADK - Load constant into register
  • OP_MOV - Move value between registers
  • OP_ADD - Add two registers
  • OP_SUB - Subtract two registers
  • OP_MUL - Multiply two registers
  • OP_DIV - Divide two registers
  • OP_MOD - Floating point remainder
  • OP_NEG - Negate a value
  • OP_EQ - Equality comparison
  • OP_NE - Inequality comparison
  • OP_LT - Less than comparison
  • OP_LE - Less than or equal to comparison
  • OP_GT - Greater than comparison
  • OP_GE - Greater than or equal to comparison
  • OP_AND - Bitwise AND
  • OP_OR - Bitwise OR
  • OP_XOR - Bitwise XOR
  • OP_NOT - Bitwise NOT
  • OP_SHL - Bitwise shift left
  • OP_SHR - Bitwise shift right
  • OP_CALL - Function call
  • OP_RETURN - Return from function
  • OP_JMP - Unconditional jump
  • OP_TEST - Conditional jump
  • OP_GETPROP - Property access
  • OP_SETPROP - Property assignment

Quickstart

Build Options

The Makefile defaults to clang but can be configured to use others:

# Use Clang
make TOOLCHAIN=clang

# Use GCC
make TOOLCHAIN=gcc

# Use Cosmocc
make TOOLCHAIN=cosmo

# Auto-detect compiler
make TOOLCHAIN=auto

Basic Build

# Build the project
make

# Clean build artifacts
make clean

# Rebuild from scratch
make rebuild

# Run the compiled program
make run

# Show build configuration
make info

Installation

# Install to default location (~/.local/bin)
make install

# Install to custom location
make PREFIX=/usr/local install

# Uninstall
make uninstall

Usage Example

The main program demonstrates a simple JIT compilation by adding 10.00 and 20.0 with a result of 30.0:

// Sample bytecode: LOADK R0, #0; LOADK R1, #1; ADD R2, R0, R1
Instruction bytecode[] = {
    (OP_LOADK) | (0 << 8) | (0 << 16),           // LOADK R0, constant[0]
    (OP_LOADK) | (1 << 8) | (1 << 16),           // LOADK R1, constant[1]
    (OP_ADD) | (2 << 8) | (0 << 16) | (1 << 24)  // ADD R2, R0, R1
};

// Constants
Value constants[2];
constants[0].number = 10.0;
constants[1].number = 20.0;

// Compile and execute
void* native_code = Compile(bytecode, 3, registers, constants, 2);

Output

Before JIT compilation:
R0: 0.000000
R1: 0.000000
R2: 0.000000

JIT compilation successful, executing...

After execution:
R0: 10.000000
R1: 20.000000
R2: 30.000000 (should be 30.0)

Architecture

Bytecode Format

Instructions are encoded as 32-bit values:

  • Bits 0-7: Opcode
  • Bits 8-15: Register A
  • Bits 16-23: Register B
  • Bits 24-31: Register C

Value Representation

Uses NaN-boxing for efficient value storage:

  • Numbers stored as double
  • Other types tagged in unused NaN bits
  • Supports numbers, strings, objects, booleans, null, and undefined

Register Management

  • Maps virtual registers (0-255) to native x86-64 registers
  • Tracks register usage to prevent conflicts
  • Supports 16 native registers (RAX, RCX, RDX, etc.)

Development

Debugging

# Build with clang and debug with LLDB
make TOOLCHAIN=clang debug

# Build with GCC and debug with GDB
make TOOLCHAIN=gcc debug

Adding New Operations

  1. Define the opcode in src/bytecode.h
  2. Implement the template function in src/bytecode.c
  3. Add the template to the opcode table in src/compiler.c
  4. Test with sample bytecode

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A simple Just-In-Time (JIT) written in C that compiles bytecode instructions to native machine code using template-driven dispatch.

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors