Академический Документы
Профессиональный Документы
Культура Документы
OMR
Reliable and reusable components for building
high performance language runtimes
2
Eclipse OMR Mission
Build an open reusable language runtime foundation for cloud platforms
3
Eclipse OMR technology components
contributed from IBM SDK for Java
port platform abstraction (porting) library
thread cross platform pthread-like threading library
vm APIs to manage per-interpreter and per-thread contexts
gc garbage collection framework for managed heaps
compiler extensible compiler framework
jitbuilder project to simplify bring up for a new JIT compiler
omrtrace library for publishing trace events for monitoring/diagnostics
fvtest language independent test framework built on the example glue so that
components can be tested outside of a language runtime,
uses Google Test 1.7 framework
+ a few others
~800KLOC at this point, more components coming!
4
Eclipse OMR technology components
contributed from IBM SDK for Java
port platform abstraction (porting) library
thread cross platform pthread-like threading library Roughly
vm APIs to manage per-interpreter and per-thread contexts
Comparable
gc garbage collection framework for managed heaps
compiler extensible compiler framework
to
jitbuilder project to simplify bring up for a new JIT compiler LLVM
omrtrace library for publishing trace events for monitoring/diagnostics
fvtest language independent test framework built on the example glue so that
components can be tested outside of a language runtime,
uses Google Test 1.7 framework
+ a few others
~800KLOC at this point, more components coming!
5
OMR Compiler
• Originates from Testarossa Java Just In Time compiler created circa 1998
• Now used as compiler for static and dynamic languages as well as binary emulation
• Native code generation for X86, POWER, Z, ARM-32 & historically several others
• Hundreds of developer years of effort
• About 70 optimizations and analyses in OMR project
• Complete dataflow analysis engine and other analyses
• Local and global transformations and optimizations (not a complete list)
• Common subexpression elimination, expression simplification, dead tree and dead
store elimination, copy propagation, partial redundancy elimination, On Stack
Replacement (OSR) optimizations, local and global value propagation, global register
allocation, …
• Loop canonicalization, loop unrolling, loop versioning, loop replication, strip mining,
loop striding, ...
• Inlining, block ordering, coldness propagation, async check insertion/removal, store
sinking, …
• Long term focus on code quality, compile time, footprint, adaptive optimization
6
OMR JitBuilder project
• Simplified interface to the OMR compiler technology
• Designed to bootstrap a native code JIT compiler for interpreted methods
• Provides mostly sensible defaults for compiler configuration and setup
• Also a general cross platform native code generation toolkit
• Can be used on OSX as well as Linux for 64-bit X86, POWER, Z and 32-bit ARM
8
First Answer: Project level (hand wavy)
LLVM is a subset of
Eclipse OMR function
e.g. no GC
9
Second Answer: Compiler level
10
Second Answer: Compiler level
This Talk:
Part I: Compare LLVM and Eclipse OMR JitBuilder
implementations of
LLVM’s Kaleidoscope tutorial language
11
Third Answer: Big Picture
12
Third Answer: Big Picture
This Talk:
Part II: Swift + OMR JIT leverages LLVM for static compiler and
Eclipse OMR JitBuilder for JIT compile at runtime
13
Part I
14
Kaleidoscope
• http://llvm.org/docs/tutorial/LangImpl01.html
• From tutorial introduction:
“Kaleidoscope is a procedural language that allows you to define
functions, use conditionals, math, etc. [...including…] if/then/else
construct, a for loop, user defined operators, JIT compilation with a
simple command line interface, etc.”
18
Setting up a JIT compiler: LLVM (page 1)
//===----- KaleidoscopeJIT.h - A simple JIT for Kaleidoscope ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Contains a simple JIT definition for use in the kaleidoscope tutorials.
//
//===----------------------------------------------------------------------===//
19
Setting up a JIT compiler: LLVM (page 2)
#ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H
#define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H
#include "llvm/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/RuntimeDyld.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/LegacyPassManager.h"
20
Setting up a JIT compiler: LLVM (page 3)
#include "llvm/IR/Mangler.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/GVN.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
namespace llvm {
namespace orc {
21
Setting up a JIT compiler: LLVM (page 4)
class KaleidoscopeJIT {
private:
std::unique_ptr<TargetMachine> TM;
const DataLayout DL;
RTDyldObjectLinkingLayer<> ObjectLayer;
IRCompileLayer<decltype(ObjectLayer)> CompileLayer;
typedef std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)>
OptimizeFunction;
22
Setting up a JIT compiler: LLVM (page 5)
public:
typedef decltype(OptimizeLayer)::ModuleSetHandleT ModuleHandle;
KaleidoscopeJIT()
: TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()),
CompileLayer(ObjectLayer, SimpleCompiler(*TM)),
OptimizeLayer(CompileLayer,
[this](std::unique_ptr<Module> M) {
return optimizeModule(std::move(M));
}) {
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
}
23
Setting up a JIT compiler: LLVM (page 6)
TargetMachine &getTargetMachine() { return *TM; }
ModuleHandle addModule(std::unique_ptr<Module> M) {
// Build our symbol resolver:
// Lambda 1: Look back into the JIT itself to find symbols that are part of
// the same "logical dylib".
// Lambda 2: Search for external symbols in the host process.
24
Setting up a JIT compiler: LLVM (page 7)
auto Resolver = createLambdaResolver(
[&](const std::string &Name) {
if (auto Sym = OptimizeLayer.findSymbol(Name, false))
return Sym;
return JITSymbol(nullptr);
},
[](const std::string &Name) {
if (auto SymAddr =
RTDyldMemoryManager::getSymbolAddressInProcess(Name))
return JITSymbol(SymAddr, JITSymbolFlags::Exported);
return JITSymbol(nullptr);
});
25
Setting up a JIT compiler: LLVM (page 8)
// Build a singleton module set to hold our module.
std::vector<std::unique_ptr<Module>> Ms;
Ms.push_back(std::move(M));
// Add the set to the JIT with the resolver we created above and a newly
// created SectionMemoryManager.
return OptimizeLayer.addModuleSet(std::move(Ms),
make_unique<SectionMemoryManager>(),
std::move(Resolver));
}
26
Setting up a JIT compiler: LLVM (page 9)
JITSymbol findSymbol(const std::string Name) {
std::string MangledName;
raw_string_ostream MangledNameStream(MangledName);
Mangler::getNameWithPrefix(MangledNameStream, Name, DL);
return OptimizeLayer.findSymbol(MangledNameStream.str(), true);
}
void removeModule(ModuleHandle H) {
OptimizeLayer.removeModuleSet(H);
}
private:
27
Setting up a JIT compiler: LLVM (page 10)
std::unique_ptr<Module> optimizeModule(std::unique_ptr<Module> M) {
// Create a function pass manager.
auto FPM = llvm::make_unique<legacy::FunctionPassManager>(M.get());
// Run the optimizations over all functions in the module being added to
// the JIT.
for (auto &F : *M)
FPM->run(F);
return M;
}
28
Setting up a JIT compiler: LLVM (page 11)
};
#endif // LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H
29
Setting up a JIT compiler: LLVM (Summary?)
• All that code (137 lines) is in KaleidoscopeJIT.h
30
Setting up a JIT compiler (OMR JitBuilder)
#include “Jit.hpp” // header file from OMR
31
Setting up a JIT compiler (OMR JitBuilder)
#include “Jit.hpp” // header file from OMR
32
Compiling a Kaleidoscope method
• Parser examines string the user typed in
33
Example: Abstract Syntax Tree for fib(x)
Function
if
“fib”
Cond Else
Then
Binary Num Binary
< 1.0 +
Binary Binary
- -
Var Num Var Num
“x” 1.0 “x” 2.0
34
Compiling a Kaleidoscope method
• Each type of AST node has a class associated with it
• NumberExprAST, VarExprAST, CallExprAST, IfExprAST, etc.
• codegen() method generates LLVM IR for that node
• Calls codegen() for children at appropriate places
Value *NumberExprAST::codegen() {
return ConstantFP::get(TheContext, APFloat(Val));
}
Const
3
36
More complicated codegen(): if expression
Value *IfExprAST::codegen() { Builder.CreateBr(MergeBB);
// Codegen of 'Then' can change the current block, update
Value *CondV = Cond->codegen(); ThenBB for the PHI.
if (!CondV) ThenBB = Builder.GetInsertBlock();
return nullptr;
// Emit else block.
TheFunction->getBasicBlockList().push_back(ElseBB);
// Convert condition to a bool by comparing equal to 0.0.
Builder.SetInsertPoint(ElseBB);
CondV = Builder.CreateFCmpONE(
// Create blocks for the then and else cases. Insert the
'then' block at the end of the function. Builder.CreateBr(MergeBB);
// Codegen of 'Else' can change the current block, update
BasicBlock *ThenBB = BasicBlock::Create(TheContext, "then",
ElseBB for the PHI.
TheFunction);
ElseBB = Builder.GetInsertBlock();
BasicBlock *ElseBB = BasicBlock::Create(TheContext, "else");
Builder.SetInsertPoint(ThenBB);
PN->addIncoming(ThenV, ThenBB);
PN->addIncoming(ElseV, ElseBB);
Value *ThenV = Then->codegen(); return PN;
}
if (!ThenV)
return nullptr; 37
More complicated codegen(): if expression
Value *IfExprAST::codegen() { Builder.CreateBr(MergeBB);
// Codegen of 'Then' can change the current block, update
Value *CondV = Cond->codegen(); ThenBB for the PHI.
if (!CondV) ThenBB = Builder.GetInsertBlock();
return nullptr;
// Emit else block.
TheFunction->getBasicBlockList().push_back(ElseBB);
// Convert condition to a bool by comparing equal to 0.0.
Builder.SetInsertPoint(ElseBB);
CondV = Builder.CreateFCmpONE(
// Create blocks for the then and else cases. Insert the
'then' block at the end of the function. Builder.CreateBr(MergeBB);
// Codegen of 'Else' can change the current block, update
BasicBlock *ThenBB = BasicBlock::Create(TheContext, "then",
ElseBB for the PHI.
TheFunction);
ElseBB = Builder.GetInsertBlock();
BasicBlock *ElseBB = BasicBlock::Create(TheContext, "else");
Builder.SetInsertPoint(ThenBB);
PN->addIncoming(ThenV, ThenBB);
PN->addIncoming(ElseV, ElseBB);
Value *ThenV = Then->codegen(); return PN;
}
if (!ThenV)
return nullptr; 38
More complicated codegen(): if expression
{ /* then branch */ }
Value *IfExprAST::codegen() { Builder.CreateBr(MergeBB);
cond = <expression>
// Codegen of 'Then' can change the current block, update
Value *CondV = Cond->codegen(); ThenBB for the PHI.
if (!CondV) ThenBB = Builder.GetInsertBlock();
return nullptr;
// Emit else block.
TheFunction->getBasicBlockList().push_back(ElseBB);
// Convert condition to a bool by comparing equal to 0.0.
Builder.SetInsertPoint(ElseBB);
CondV = Builder.CreateFCmpONE(
{ /* else branch */ }
Function *TheFunction = Builder.GetInsertBlock()->getParent(); return nullptr;
if (cond) { } else { }
// Create blocks for the then and else cases.
'then' block at the end of the function.
Insert the
/* result of if */
PHINode *PN = Builder.CreatePHI(Type::getDoubleTy(TheContext),
2, "iftmp");
// Emit then value.
Builder.SetInsertPoint(ThenBB);
PN->addIncoming(ThenV, ThenBB);
{ /* then branch */ }
PN->addIncoming(ElseV, ElseBB);
Value *ThenV = Then->codegen(); return PN;
}
if (!ThenV)
return nullptr; 39
LLVM-isms: basic blocks, insertion points and SSA
Value *IfExprAST::codegen() { Builder.CreateBr(MergeBB);
// Codegen of 'Then' can change the current block, update
Value *CondV = Cond->codegen(); ThenBB for the PHI.
if (!CondV) ThenBB = Builder.GetInsertBlock();
return nullptr;
// Emit else block.
TheFunction->getBasicBlockList().push_back(ElseBB);
// Convert condition to a bool by comparing equal to 0.0.
Builder.SetInsertPoint(ElseBB);
CondV = Builder.CreateFCmpONE(
// Create blocks for the then and else cases. Insert the
'then' block at the end of the function. Builder.CreateBr(MergeBB);
// Codegen of 'Else' can change the current block, update
BasicBlock *ThenBB = BasicBlock::Create(TheContext, "then",
ElseBB for the PHI.
TheFunction);
ElseBB = Builder.GetInsertBlock();
BasicBlock *ElseBB = BasicBlock::Create(TheContext, "else");
Builder.SetInsertPoint(ThenBB);
PN->addIncoming(ThenV, ThenBB);
PN->addIncoming(ElseV, ElseBB);
Value *ThenV = Then->codegen(); return PN;
}
if (!ThenV)
return nullptr; 40
If Expression code generation with JitBuilder
TR::IlValue *IfExprAST::codegen(TR::IlBuilder *b) {
cond = <expression>
TR::IlValue *CondV = Cond->codegen(b);
if (!CondV)
return nullptr;
if (cond) { } else { }
b->IfThenElse(&elseB, &thenB,
b-> EqualTo(
CondV,
b-> ConstDouble(0.0)));
elseB->StoreOver(ThenV, ElseV);
41
If Expression code generation with JitBuilder
TR::IlValue *IfExprAST::codegen(TR::IlBuilder *b) {
cond = <expression>
TR::IlValue *CondV = Cond->codegen(b);
if (!CondV)
return nullptr;
if (cond) { } else { }
b->IfThenElse(&elseB, &thenB,
b-> EqualTo(
CondV,
b-> ConstDouble(0.0)));
elseB->StoreOver(ThenV, ElseV);
42
Let’s be
a bit more
scientific
43
Comparing Kaleidoscope implementations
Source code lines: compare using “cloc” Final Executable Size (MB)
1000 928 OMR 22.5
19.932236
900 825
JitBuilder 20
800
~11% less
17.5 OMR
700 OMR 15
JitBuilder
600
500
JitBuilder 12.5
~64%
400
373 ~25% less 10
smaller
7.106188
280 7.5
300
200 5
100 2.5
0 0
All Source CodeGen+JIT
LLVM OMR JitBuilder
LLVM OMR JitBuilder
45
Mandelbrot code: 9 functions + final benchmark
def unary!(v) def binary| 5 (LHS RHS)
0; mandelconverge(-2.3,-1.3);
-1.0; def binary& 6 (LHS RHS) for y = ymin, y < ymax, ystep in (
1.0 > 2.0; !!RHS; mandelhelp(-2.3, -2.3+(-1.3)*78, 0.05, -1.3, -1.3+0.07*40, 0.07);
if iters > 255 | (real*real + imag*imag > 4) then imagstart, imagstart+imagmag*40, imagmag);
else
2000
1500
1000
500
0
2500 iterations of mandel
• Some caveats:
• Procedural language, very small functions, most optimizations do little because
there aren’t opportunities (still takes time to discover that :) )
• As with all compilers and performance comparisons: Your Mileage May Vary 49
Part II
50
Swift: modern native language for mobile
Modern Native Language
• “C replacement”
• Inferred typing
• Implicit memory management
51
Swift: also for the server!
Mobile + server can be better than mobile alone
• Centralized data
• Computation heavy algorithms consume battery
• Server-based services enhance mobile experience
52
Chris Lattner at IBM Programming Languages Day 2016
http://researcher.watson.ibm.com/researcher/files/us-lmandel/lattner.pdf
53
Project to prototype a Swift JIT compiler
54
Compiling a Swift program
swiftc
Linker
.swift Object Libraries,
file can be
dynamic
libraries
Familiar “static” language flow:
Executable
Run swiftc to compile Swift source code to object files
Link object files against libraries to create executable
Run executable!
55
Compiling a Swift program (one level deeper)
swiftc
llvm
Linker
.swift .sil .sil LLVM Object Libraries,
(Swift IL) Optimized bitcode file can be
SIL dynamic
libraries
Familiar “static” language flow:
Executable
Run swiftc to compile Swift source code to object files
Link object files against libraries to create executable
Run executable!
70
67.74X
Relative Time Taken v.s. Static
60
50
40.81X 39.64X
40
37.59X
30
20
10
0
Initial functional Caller patching Lazy init patched away Inlined retain/release
JIT Prototype Milestones
2.053X
Relative Time Taken v.s. Static
2.0
1.474X
1.5 1.343X
0.994X
1.0
0.5
0.0
Initial functional Caller patching Lazy init patched away Inlined retain/release
JIT Prototype Milestones
65
Wrap Up
66
Eclipse OMR and LLVM
• Eclipse OMR project built from and for dynamic language environments
• Some overlap with LLVM, natural to ask how the two compare and relate
67
Part I: Compiler vs. Compiler
• For a dynamic compilation scenario, OMR JitBuilder did pretty well:
• 25% less source code that’s more straight-forward to read
• 64% smaller executable
• 3.3X faster compilation time performing 7X the number of optimizations
(which all have compile time impact even though only a few have code impact)
• 3% faster generated code
68
Part II: Static + dynamic compiler synergy
• Hybrid static – dynamic compiler model may have promise for Swift on Server
• Start with high performance statically compiled code using LLVM
• Optimize across module boundaries at runtime where it makes sense
• Remain fully compatible with Swift for mobile platforms
• Look forward to further explore JIT techniques with the Swift community
69
Want to learn more?
https://www.eclipse.org/omr
• Eclipse OMR https://github.com/eclipse/omr
https://developer.ibm.com/code/open-source-ibm/eclipse-omr/
• JitBuilder blog
https://developer.ibm.com/open/2016/07/19/jitbuilder-library-and-eclipse-omr-just-in-time-compilers-made-easy/
https://developer.ibm.com/open/2017/03/08/build-more-complicated-methods-using-the-jitbuilder-library/
https://developer.ibm.com/open/2017/04/14/building-jitbuilder-library-source-code/
• Language ports
• Base9 https://github.com/youngar/Base9
• SOM++ (Smalltalk) https://github.com/charliegracie/SOMpp/tree/jitbuilder_vmstate
• Lua Vermelha https://github.com/Leonardo2718/lua-vermelha
• Rosie Pattern Language JIT https://github.com/mstoodle/rosie-pattern-language
https://github.com/mstoodle/rosie-lpeg
• Ruby https://github.com/rubyomr-preview/rubyomr-preview
https://github.com/rubyomr-preview/rbjitglue
• OpenJ9 (Java) coming soon 70
Questions?
71