SlideShare a Scribd company logo
Arbitrary code execution in Rust
or how to roll out custom DSL without getting hurt
Ingvar Stepanyan

@RReverser
Let's build a simple DSL
Let's build a simple DSL
...or use one that everyone knows and loves?
Let's build a simple DSL
...or use one that everyone knows and loves?
z = x * x + y * y - 2 * x * y
AST representation
pub enum BinOp {
Add,
Sub,
Mul,
Div,
}
AST representation
pub enum Variable {
X,
Y,
}
AST representation
pub enum Expr {
Literal(f64),
Variable(Variable),
Binary {
lhs: Box<Expr>,
op: BinOp,
rhs: Box<Expr>,
},
}
Parsing
Parsing
https://github.com/Geal/nom
Parsing
named!(var<CompleteStr, Variable>, alt!(
tag!("x") => { |_| Variable ::X } |
tag!("y") => { |_| Variable ::Y }
));
Parsing
named!(atom<CompleteStr, Expr>, alt!(
double => { Expr ::Literal } |
var => { Expr ::Variable } |
delimited!(char!('('), add_op, char!(')'))
));
Parsing
named!(mul_op<CompleteStr, Expr>, do_parse!(
init: atom >>
res: fold_many0!(
tuple!(
alt!(
char!('*') => { |_| BinOp ::Mul } |
char!('/') => { |_| BinOp ::Div }
),
atom
),
init,
|lhs, (op, rhs)| Expr ::Binary {
lhs: Box ::new(lhs),
op,
rhs: Box ::new(rhs),
}
) >>
(res)
));
Parsing
named!(add_op<CompleteStr, Expr>, do_parse!(
init: mul_op >>
res: fold_many0!(
tuple!(
alt!(
char!('+') => { |_| BinOp ::Add } |
char!('-') => { |_| BinOp ::Sub }
),
mul_op
),
init,
|lhs, (op, rhs)| Expr ::Binary {
lhs: Box ::new(lhs),
op,
rhs: Box ::new(rhs),
}
) >>
(res)
));
Parsing
• nom (combinators) - https://github.com/Geal/nom 

• combine - https://github.com/Marwes/combine

• peg - https://crates.io/crates/peg

• lalrpop (LR / LALR) - https://github.com/lalrpop/lalrpop

• ...https://crates.io/keywords/parsing
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Binary {
    lhs: Binary {
        lhs: Literal(2.0),
        op: Mul,
        rhs: Variable(X),
    },
    op: Mul,
    rhs: Variable(Y),
}
z = 2 * x * y
AST interpretation
Expr ::Literal(lit) => *lit,
Expr ::Variable(var) => var.calc(ctx),
Expr ::Binary { lhs, op, rhs } => {
let lhs = lhs.calc(ctx);
let rhs = rhs.calc(ctx);
op.apply(lhs, rhs)
}
AST interpretation
AST: 28.13 ns
0
500
1,000
1,500
2,000
0 10 20 30 40 50 60 70 80 90 100
Bytecode interpretation
z = 2 * x * y
Bytecode interpretation
z = 2 * x * y
2 x * y *
Bytecode interpretation
z = 2 x * y *
[ ]
Bytecode interpretation
z = 2 x * y *
[ ]
Bytecode interpretation
z = 2 x * y *
[ 2 ]
Bytecode interpretation
z = 2 x * y *
[ 2, x ]
Bytecode interpretation
z = 2 x * y *
[ 2 * x ]
Bytecode interpretation
z = 2 x * y *
[ 2 * x, y ]
Bytecode interpretation
z = 2 x * y *
[ (2 * x) * y ]
Bytecode interpretation
enum BytecodeOp {
Literal(f64),
Variable(Variable),
BinOp(BinOp),
}
Bytecode interpretation
Expr ::Literal(lit) => bc.push(BytecodeOp ::Literal(*lit)),
Expr ::Variable(v) => bc.push(BytecodeOp ::Variable(*v)),
Expr ::Binary { lhs, op, rhs } => {
lhs.compile(bc);
rhs.compile(bc);
bc.push(BytecodeOp ::BinOp(*op));
}
Bytecode interpretation
for op in &self.ops {
match op {
BytecodeOp ::Literal(lit) => stack.push(*lit),
BytecodeOp ::Variable(var) => stack.push(var.calc(ctx)),
BytecodeOp ::BinOp(op) => {
let rhs = stack.pop().unwrap();
let lhs = stack.pop().unwrap();
stack.push(op.apply(lhs, rhs));
}
}
}
Bytecode interpretation
AST: 28.13 ns
Bytecode: 27.79 ns
Bytecode compilation: 207.72 ns
0
500
1,000
1,500
2,000
0 10 20 30 40 50 60 70 80 90 100
AST Bytecode
Closures kind-of-JIT
Closures kind-of-JIT
pub type CompiledExpr = Box<dyn Fn(&Context) -> f64>;
Closures kind-of-JIT
Expr ::Literal(lit) => Box ::new(move |_ctx| lit),
Expr ::Variable(var) => match var {
Variable ::X => Box ::new(move |ctx| ctx.x),
...
},
Expr ::Binary { lhs, op, rhs } => {
let lhs = lhs.compile();
let rhs = rhs.compile();
match op {
BinOp ::Add => Box ::new(move |ctx| lhs(ctx) + rhs(ctx)),
...,
}
}
Closures kind-of-JIT
AST: 28.13 ns
Bytecode: 27.79 ns | Closure: 20.62 ns
Bytecode compilation: 207.72 ns | Closure: 200.68 ns
0
500
1,000
1,500
2,000
0 10 20 30 40 50 60 70 80 90 100
AST Bytecode Closure
JIT compilation
JIT compilation
https://llvm.org/
JIT compilation: SSA form
z = x * y + y * x
JIT compilation: SSA form
z = x * y + y * x
JIT compilation: SSA form
z = x * y + y * x
define double @z(double %x, double %y) unnamed_addr #0 {
%0 = fmul double %x, %y
%1 = fmul double %y, %x
%2 = fadd double %0, %1
ret double %2
}
JIT compilation: SSA form
z = x * y + y * x
define double @z(double %x, double %y) unnamed_addr #0 {
%0 = fmul double %x, %y
%1 = fmul double %y, %x
%2 = fadd double %0, %1
ret double %2
}
JIT compilation: SSA form
z = x * y + y * x
define double @z(double %x, double %y) unnamed_addr #0 {
%0 = fmul double %x, %y
%1 = fadd double %0, %0
ret double %1
}
LLVM from Rust
• Raw C API bindings:

https://github.com/tari/llvm-sys.rs

• llvm-rs (high-level wrappers, unmaintained):

https://github.com/TomBebbington/llvm-rs 

• Inkwell (high-level wrappers, maintained):

https://github.com/TheDan64/inkwell
LLVM from Rust
let context = Context ::create();
let f64type = context.f64_type();
let builder = context.create_builder();
LLVM from Rust
let module = context.create_module("module");
let func = module.add_function(
"jit_compiled",
f64type.fn_type(&[
f64type.into(),
f64type.into()
], false),
None,
);
LLVM from Rust
let block = func.append_basic_block("entry");
builder.position_at_end(&block);
let result = ...;
builder.build_return(Some(&result));
LLVM from Rust
Expr ::Literal(lit) => f64type.const_float(*lit),
Expr ::Variable(var) => func.get_nth_param( ...).into_float_value(),
Expr ::Binary { lhs, op, rhs } => {
let lhs = lhs.compile( ...);
let rhs = rhs.compile( ...);
match op {
BinOp ::Add => builder.build_float_add(lhs, rhs, "add"),
...
}
}
LLVM from Rust
let engine = module.create_jit_execution_engine(
OptimizationLevel ::None
)?;
unsafe {
engine.get_function("jit_compiled")?
}
LLVM from Rust
EXC_BAD_ACCESS (code=1, address=0x6d)
0x0000000100324790 jit`llvm ::verifyModule(llvm ::Module const&,
llvm ::raw_ostream*, bool*) + 121
0x0000000100180266 jit`LLVMVerifyModule + 119
0x000000010000b030 jit`verify(self =<unavailable>) at module.rs:634
0x000000010000b11a jit`clone(self =<unavailable>) at module.rs:1323
0x000000010000ad06 jit`create_jit_execution_engine(self =<unavailable>,
opt_level =<unavailable>) at module.rs:502
LLVM from Rust
EXC_BAD_ACCESS (code=1, address=0x0)
0x000000010085c19e jit`llvm ::LLVMContext ::removeModule(llvm ::Module*) + 4
0x00000001008840f3 jit`llvm ::Module ::~Module() + 31
0x0000000100407123
jit`llvm ::MCJIT ::OwningModuleContainer ::freeModulePtrSet(llvm ::SmallPtrSet<l
lvm ::Module*, 4u>&) + 97
0x0000000100407044
jit`llvm ::MCJIT ::OwningModuleContainer ::~OwningModuleContainer() + 18
0x0000000100404452 jit`llvm ::MCJIT ::~MCJIT() + 214
0x0000000100404654 jit`llvm ::MCJIT ::~MCJIT() + 14
0x0000000100008090
jit`_$LT$inkwell ..execution_engine ..ExecEngineInner$u20$as$u20$core ..ops ..dr
op ..Drop$GT$ ::drop ::h86f518daac700ebf(self=0x00007ffeefbff310) at
execution_engine.rs:421
0x0000000100001a05
jit`core ::ptr ::drop_in_place ::h91efe68b20f1058b((null)=0x00007ffeefbff310)at
ptr.rs:59
0x0000000100001783
jit`core ::ptr ::drop_in_place ::h198dbc589dc5920e((null)=0x00007ffeefbff310)at
ptr.rs:59
LLVM JIT
AST: 28.13 ns
LLVM: 1.79 ns
LLVM JIT
AST: 28.13 ns
LLVM: 1.79 ns
LLVM compilation: 820.98 us
LLVM JIT
AST: 28.13 ns
LLVM: 1.79 ns
1
10
100
1,000
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
1 10 100 1,000 10,000 100,000 1,000,000 10,000,000
AST LLVM
LLVM compilation: 820.98 us
LLVM JIT (optimised)
AST: 28.13 ns
LLVM: 1.79 ns / 1.50 ns
LLVM compilation: 820.98 us / 1.79 ms
1
10
100
1,000
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
1 10 100 1,000 10,000 100,000 1,000,000 10,000,000
AST LLVM LLVM (opt)
JIT compilation
JIT compilation
https://github.com/CraneStation/cranelift:
JIT compilation
https://github.com/CraneStation/cranelift:


Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be
useful elsewhere too. The initial planned uses that affected its design are:
JIT compilation
https://github.com/CraneStation/cranelift:


Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be
useful elsewhere too. The initial planned uses that affected its design are:
• WebAssembly compiler for the SpiderMonkey engine in Firefox.
JIT compilation
https://github.com/CraneStation/cranelift:


Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be
useful elsewhere too. The initial planned uses that affected its design are:
• WebAssembly compiler for the SpiderMonkey engine in Firefox.
• Backend for the IonMonkey JavaScript JIT compiler in Firefox.
JIT compilation
https://github.com/CraneStation/cranelift:


Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be
useful elsewhere too. The initial planned uses that affected its design are:
• WebAssembly compiler for the SpiderMonkey engine in Firefox.
• Backend for the IonMonkey JavaScript JIT compiler in Firefox.
• Debug build backend for the Rust compiler.
JIT compilation
https://github.com/CraneStation/cranelift:


Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be
useful elsewhere too. The initial planned uses that affected its design are:
• WebAssembly compiler for the SpiderMonkey engine in Firefox.
• Backend for the IonMonkey JavaScript JIT compiler in Firefox.
• Debug build backend for the Rust compiler.
...

Cranelift's APIs are not yet stable.
Cranelift
let builder = SimpleJITBuilder ::new();
let mut module: Module<SimpleJITBackend> = Module ::new(builder);
let mut ctx = module.make_context();
let mut func_builder_ctx = FunctionBuilderContext ::new();
Cranelift
let sig = &mut ctx.func.signature;
sig.params.push(AbiParam ::new(types ::F64)); // X
sig.params.push(AbiParam ::new(types ::F64)); // Y
sig.returns.push(AbiParam ::new(types ::F64));
Cranelift
let mut builder = FunctionBuilder ::new(
&mut ctx.func,
&mut func_builder_ctx
);
Cranelift
let entry_ebb = builder.create_ebb();
builder.append_ebb_params_for_function_params(entry_ebb);
builder.switch_to_block(entry_ebb);
builder.seal_block(entry_ebb);
let result = ...;
builder.ins().return_(&[result]);
builder.finalize();
Cranelift
Expr ::Literal(lit) => {
let ins = builder.ins();
ins.f64const(Ieee64 ::with_float(*lit))
}
Expr ::Variable(var) => builder.ebb_params(ebb)[ ...],
Expr ::Binary { lhs, op, rhs } => {
let lhs = lhs.compile( ...);
let rhs = rhs.compile( ...);
let ins = builder.ins();
match op {
BinOp ::Add => ins.fadd(lhs, rhs),
...
}
}
Cranelift
let id = module.declare_function(
"jit_compiled",
Linkage ::Local,
&ctx.func.signature
)?;
module.define_function(id, &mut ctx)?;
module.clear_context(&mut ctx);
module.finalize_definitions();
Ok(unsafe {
::std ::mem ::transmute(module.get_finalized_function(id))
})
Cranelift JIT
Cranelift JIT
AST: 28.131 ns
Cranelift: 2.36 ns
Cranelift JIT
AST: 28.131 ns
Cranelift: 2.36 ns
Cranelift compilation: 71.75 us
Cranelift JIT
AST: 28.131 ns
Cranelift: 2.36 ns
1
10
100
1,000
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
1 10 100 1,000 10,000 100,000 1,000,000 10,000,000
AST Cranelift
Cranelift compilation: 71.75 us
Cranelift JIT
AST: 28.131 ns
LLVM: 1.79 ns | Cranelift: 2.36 ns
1
10
100
1,000
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
1 10 100 1,000 10,000 100,000 1,000,000 10,000,000
AST LLVM Cranelift
LLVM compilation: 820.98 us | Cranelift: 71.75 us
Full comparison
1
10
100
1,000
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
1 10 100 1,000 10,000 100,000 1,000,000 10,000,000
AST LLVM Cranelift Closure

More Related Content

Building fast interpreters in Rust

  • 1. Arbitrary code execution in Rust or how to roll out custom DSL without getting hurt Ingvar Stepanyan @RReverser
  • 2. Let's build a simple DSL
  • 3. Let's build a simple DSL ...or use one that everyone knows and loves?
  • 4. Let's build a simple DSL ...or use one that everyone knows and loves? z = x * x + y * y - 2 * x * y
  • 5. AST representation pub enum BinOp { Add, Sub, Mul, Div, }
  • 6. AST representation pub enum Variable { X, Y, }
  • 7. AST representation pub enum Expr { Literal(f64), Variable(Variable), Binary { lhs: Box<Expr>, op: BinOp, rhs: Box<Expr>, }, }
  • 10. Parsing named!(var<CompleteStr, Variable>, alt!( tag!("x") => { |_| Variable ::X } | tag!("y") => { |_| Variable ::Y } ));
  • 11. Parsing named!(atom<CompleteStr, Expr>, alt!( double => { Expr ::Literal } | var => { Expr ::Variable } | delimited!(char!('('), add_op, char!(')')) ));
  • 12. Parsing named!(mul_op<CompleteStr, Expr>, do_parse!( init: atom >> res: fold_many0!( tuple!( alt!( char!('*') => { |_| BinOp ::Mul } | char!('/') => { |_| BinOp ::Div } ), atom ), init, |lhs, (op, rhs)| Expr ::Binary { lhs: Box ::new(lhs), op, rhs: Box ::new(rhs), } ) >> (res) ));
  • 13. Parsing named!(add_op<CompleteStr, Expr>, do_parse!( init: mul_op >> res: fold_many0!( tuple!( alt!( char!('+') => { |_| BinOp ::Add } | char!('-') => { |_| BinOp ::Sub } ), mul_op ), init, |lhs, (op, rhs)| Expr ::Binary { lhs: Box ::new(lhs), op, rhs: Box ::new(rhs), } ) >> (res) ));
  • 14. Parsing • nom (combinators) - https://github.com/Geal/nom • combine - https://github.com/Marwes/combine • peg - https://crates.io/crates/peg • lalrpop (LR / LALR) - https://github.com/lalrpop/lalrpop • ...https://crates.io/keywords/parsing
  • 15. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 16. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 17. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 18. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 19. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 20. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 21. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 22. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 23. AST interpretation Binary {     lhs: Binary {         lhs: Literal(2.0),         op: Mul,         rhs: Variable(X),     },     op: Mul,     rhs: Variable(Y), } z = 2 * x * y
  • 24. AST interpretation Expr ::Literal(lit) => *lit, Expr ::Variable(var) => var.calc(ctx), Expr ::Binary { lhs, op, rhs } => { let lhs = lhs.calc(ctx); let rhs = rhs.calc(ctx); op.apply(lhs, rhs) }
  • 25. AST interpretation AST: 28.13 ns 0 500 1,000 1,500 2,000 0 10 20 30 40 50 60 70 80 90 100
  • 27. Bytecode interpretation z = 2 * x * y 2 x * y *
  • 30. Bytecode interpretation z = 2 x * y * [ 2 ]
  • 31. Bytecode interpretation z = 2 x * y * [ 2, x ]
  • 32. Bytecode interpretation z = 2 x * y * [ 2 * x ]
  • 33. Bytecode interpretation z = 2 x * y * [ 2 * x, y ]
  • 34. Bytecode interpretation z = 2 x * y * [ (2 * x) * y ]
  • 35. Bytecode interpretation enum BytecodeOp { Literal(f64), Variable(Variable), BinOp(BinOp), }
  • 36. Bytecode interpretation Expr ::Literal(lit) => bc.push(BytecodeOp ::Literal(*lit)), Expr ::Variable(v) => bc.push(BytecodeOp ::Variable(*v)), Expr ::Binary { lhs, op, rhs } => { lhs.compile(bc); rhs.compile(bc); bc.push(BytecodeOp ::BinOp(*op)); }
  • 37. Bytecode interpretation for op in &self.ops { match op { BytecodeOp ::Literal(lit) => stack.push(*lit), BytecodeOp ::Variable(var) => stack.push(var.calc(ctx)), BytecodeOp ::BinOp(op) => { let rhs = stack.pop().unwrap(); let lhs = stack.pop().unwrap(); stack.push(op.apply(lhs, rhs)); } } }
  • 38. Bytecode interpretation AST: 28.13 ns Bytecode: 27.79 ns Bytecode compilation: 207.72 ns 0 500 1,000 1,500 2,000 0 10 20 30 40 50 60 70 80 90 100 AST Bytecode
  • 40. Closures kind-of-JIT pub type CompiledExpr = Box<dyn Fn(&Context) -> f64>;
  • 41. Closures kind-of-JIT Expr ::Literal(lit) => Box ::new(move |_ctx| lit), Expr ::Variable(var) => match var { Variable ::X => Box ::new(move |ctx| ctx.x), ... }, Expr ::Binary { lhs, op, rhs } => { let lhs = lhs.compile(); let rhs = rhs.compile(); match op { BinOp ::Add => Box ::new(move |ctx| lhs(ctx) + rhs(ctx)), ..., } }
  • 42. Closures kind-of-JIT AST: 28.13 ns Bytecode: 27.79 ns | Closure: 20.62 ns Bytecode compilation: 207.72 ns | Closure: 200.68 ns 0 500 1,000 1,500 2,000 0 10 20 30 40 50 60 70 80 90 100 AST Bytecode Closure
  • 45. JIT compilation: SSA form z = x * y + y * x
  • 46. JIT compilation: SSA form z = x * y + y * x
  • 47. JIT compilation: SSA form z = x * y + y * x define double @z(double %x, double %y) unnamed_addr #0 { %0 = fmul double %x, %y %1 = fmul double %y, %x %2 = fadd double %0, %1 ret double %2 }
  • 48. JIT compilation: SSA form z = x * y + y * x define double @z(double %x, double %y) unnamed_addr #0 { %0 = fmul double %x, %y %1 = fmul double %y, %x %2 = fadd double %0, %1 ret double %2 }
  • 49. JIT compilation: SSA form z = x * y + y * x define double @z(double %x, double %y) unnamed_addr #0 { %0 = fmul double %x, %y %1 = fadd double %0, %0 ret double %1 }
  • 50. LLVM from Rust • Raw C API bindings:
 https://github.com/tari/llvm-sys.rs • llvm-rs (high-level wrappers, unmaintained):
 https://github.com/TomBebbington/llvm-rs • Inkwell (high-level wrappers, maintained):
 https://github.com/TheDan64/inkwell
  • 51. LLVM from Rust let context = Context ::create(); let f64type = context.f64_type(); let builder = context.create_builder();
  • 52. LLVM from Rust let module = context.create_module("module"); let func = module.add_function( "jit_compiled", f64type.fn_type(&[ f64type.into(), f64type.into() ], false), None, );
  • 53. LLVM from Rust let block = func.append_basic_block("entry"); builder.position_at_end(&block); let result = ...; builder.build_return(Some(&result));
  • 54. LLVM from Rust Expr ::Literal(lit) => f64type.const_float(*lit), Expr ::Variable(var) => func.get_nth_param( ...).into_float_value(), Expr ::Binary { lhs, op, rhs } => { let lhs = lhs.compile( ...); let rhs = rhs.compile( ...); match op { BinOp ::Add => builder.build_float_add(lhs, rhs, "add"), ... } }
  • 55. LLVM from Rust let engine = module.create_jit_execution_engine( OptimizationLevel ::None )?; unsafe { engine.get_function("jit_compiled")? }
  • 56. LLVM from Rust EXC_BAD_ACCESS (code=1, address=0x6d) 0x0000000100324790 jit`llvm ::verifyModule(llvm ::Module const&, llvm ::raw_ostream*, bool*) + 121 0x0000000100180266 jit`LLVMVerifyModule + 119 0x000000010000b030 jit`verify(self =<unavailable>) at module.rs:634 0x000000010000b11a jit`clone(self =<unavailable>) at module.rs:1323 0x000000010000ad06 jit`create_jit_execution_engine(self =<unavailable>, opt_level =<unavailable>) at module.rs:502
  • 57. LLVM from Rust EXC_BAD_ACCESS (code=1, address=0x0) 0x000000010085c19e jit`llvm ::LLVMContext ::removeModule(llvm ::Module*) + 4 0x00000001008840f3 jit`llvm ::Module ::~Module() + 31 0x0000000100407123 jit`llvm ::MCJIT ::OwningModuleContainer ::freeModulePtrSet(llvm ::SmallPtrSet<l lvm ::Module*, 4u>&) + 97 0x0000000100407044 jit`llvm ::MCJIT ::OwningModuleContainer ::~OwningModuleContainer() + 18 0x0000000100404452 jit`llvm ::MCJIT ::~MCJIT() + 214 0x0000000100404654 jit`llvm ::MCJIT ::~MCJIT() + 14 0x0000000100008090 jit`_$LT$inkwell ..execution_engine ..ExecEngineInner$u20$as$u20$core ..ops ..dr op ..Drop$GT$ ::drop ::h86f518daac700ebf(self=0x00007ffeefbff310) at execution_engine.rs:421 0x0000000100001a05 jit`core ::ptr ::drop_in_place ::h91efe68b20f1058b((null)=0x00007ffeefbff310)at ptr.rs:59 0x0000000100001783 jit`core ::ptr ::drop_in_place ::h198dbc589dc5920e((null)=0x00007ffeefbff310)at ptr.rs:59
  • 58. LLVM JIT AST: 28.13 ns LLVM: 1.79 ns
  • 59. LLVM JIT AST: 28.13 ns LLVM: 1.79 ns LLVM compilation: 820.98 us
  • 60. LLVM JIT AST: 28.13 ns LLVM: 1.79 ns 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 100,000,000 1,000,000,000 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 AST LLVM LLVM compilation: 820.98 us
  • 61. LLVM JIT (optimised) AST: 28.13 ns LLVM: 1.79 ns / 1.50 ns LLVM compilation: 820.98 us / 1.79 ms 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 100,000,000 1,000,000,000 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 AST LLVM LLVM (opt)
  • 64. JIT compilation https://github.com/CraneStation/cranelift: 
 Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be useful elsewhere too. The initial planned uses that affected its design are:
  • 65. JIT compilation https://github.com/CraneStation/cranelift: 
 Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be useful elsewhere too. The initial planned uses that affected its design are: • WebAssembly compiler for the SpiderMonkey engine in Firefox.
  • 66. JIT compilation https://github.com/CraneStation/cranelift: 
 Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be useful elsewhere too. The initial planned uses that affected its design are: • WebAssembly compiler for the SpiderMonkey engine in Firefox. • Backend for the IonMonkey JavaScript JIT compiler in Firefox.
  • 67. JIT compilation https://github.com/CraneStation/cranelift: 
 Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be useful elsewhere too. The initial planned uses that affected its design are: • WebAssembly compiler for the SpiderMonkey engine in Firefox. • Backend for the IonMonkey JavaScript JIT compiler in Firefox. • Debug build backend for the Rust compiler.
  • 68. JIT compilation https://github.com/CraneStation/cranelift: 
 Cranelift is designed to be a code generator for WebAssembly, but it is general enough to be useful elsewhere too. The initial planned uses that affected its design are: • WebAssembly compiler for the SpiderMonkey engine in Firefox. • Backend for the IonMonkey JavaScript JIT compiler in Firefox. • Debug build backend for the Rust compiler. ...
 Cranelift's APIs are not yet stable.
  • 69. Cranelift let builder = SimpleJITBuilder ::new(); let mut module: Module<SimpleJITBackend> = Module ::new(builder); let mut ctx = module.make_context(); let mut func_builder_ctx = FunctionBuilderContext ::new();
  • 70. Cranelift let sig = &mut ctx.func.signature; sig.params.push(AbiParam ::new(types ::F64)); // X sig.params.push(AbiParam ::new(types ::F64)); // Y sig.returns.push(AbiParam ::new(types ::F64));
  • 71. Cranelift let mut builder = FunctionBuilder ::new( &mut ctx.func, &mut func_builder_ctx );
  • 72. Cranelift let entry_ebb = builder.create_ebb(); builder.append_ebb_params_for_function_params(entry_ebb); builder.switch_to_block(entry_ebb); builder.seal_block(entry_ebb); let result = ...; builder.ins().return_(&[result]); builder.finalize();
  • 73. Cranelift Expr ::Literal(lit) => { let ins = builder.ins(); ins.f64const(Ieee64 ::with_float(*lit)) } Expr ::Variable(var) => builder.ebb_params(ebb)[ ...], Expr ::Binary { lhs, op, rhs } => { let lhs = lhs.compile( ...); let rhs = rhs.compile( ...); let ins = builder.ins(); match op { BinOp ::Add => ins.fadd(lhs, rhs), ... } }
  • 74. Cranelift let id = module.declare_function( "jit_compiled", Linkage ::Local, &ctx.func.signature )?; module.define_function(id, &mut ctx)?; module.clear_context(&mut ctx); module.finalize_definitions(); Ok(unsafe { ::std ::mem ::transmute(module.get_finalized_function(id)) })
  • 76. Cranelift JIT AST: 28.131 ns Cranelift: 2.36 ns
  • 77. Cranelift JIT AST: 28.131 ns Cranelift: 2.36 ns Cranelift compilation: 71.75 us
  • 78. Cranelift JIT AST: 28.131 ns Cranelift: 2.36 ns 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 100,000,000 1,000,000,000 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 AST Cranelift Cranelift compilation: 71.75 us
  • 79. Cranelift JIT AST: 28.131 ns LLVM: 1.79 ns | Cranelift: 2.36 ns 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 100,000,000 1,000,000,000 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 AST LLVM Cranelift LLVM compilation: 820.98 us | Cranelift: 71.75 us
  • 80. Full comparison 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 100,000,000 1,000,000,000 1 10 100 1,000 10,000 100,000 1,000,000 10,000,000 AST LLVM Cranelift Closure