vmm2016-eclipse omr jitbuilder for better performance
TRANSCRIPT
1
Eclipse OMREclipse OMR JITBuilder for Better Performance
Charlie Gracie, Project LeadIBM Advisory Software Developer
The Legal Stuff
2
IBM’s statements regarding its plans, directions, and intent are subject to change or withdrawal without notice at IBM’s sole discretion. Information regarding potential future products is intended to outline our general product direction and it should not be relied on in making a purchasing decision. The information mentioned regarding potential future products is not a commitment, promise, or legal obligation to deliver any material, code or functionality. Information about potential future products may not be incorporated into any contract. The development, release, and timing of any future features or functionality described for our products remains at our sole discretion.
IBM, the IBM logo, and ibm.com are trademarks or registered trademarks of International Business Machines Corp., registered in many jurisdictions worldwide. Other product and service names might be trademarks of IBM or other companies. A current list of other IBM trademarks is available on the web at "Copyright and trademark information" at http://www.ibm.com/legal/copytrade.shtml
Other company, product, or service names may be trademarks or service marks of others.
THE INFORMATION CONTAINED IN THIS PRESENTATION IS PROVIDED FOR INFORMATIONAL PURPOSES ONLY. WHILE EFFORTS WERE MADE TO VERIFY THE COMPLETENESS AND ACCURACY OF THE INFORMATION CONTAINED IN THIS PRESENTATION, IT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN ADDITION, THIS INFORMATION IS BASED ON IBM’S CURRENT PRODUCT PLANS AND STRATEGY, WHICH ARE SUBJECT TO CHANGE BY IBM WITHOUT NOTICE. IBM SHALL NOT BE RESPONSIBLE FOR ANY DAMAGES ARISING OUT OF THE USE OF, OR OTHERWISE RELATED TO, THIS PRESENTATION OR ANY OTHER DOCUMENTATION. NOTHING CONTAINED IN THIS PRESENTATION IS INTENDED TO, NOR SHALL HAVE THE EFFECT OF, CREATING ANY WARRANTIES OR REPRESENTATIONS FROM IBM (OR ITS SUPPLIERS OR LICENSORS), OR ALTERING THE TERMS AND CONDITIONS OF ANY AGREEMENT OR LICENSE GOVERNING THE USE OF IBM PRODUCTS OR SOFTWARE.
© Copyright International Business Machines Corporation 2016. All rights reserved.
More Legal Stuff (!)
THE INFORMATION CONTAINED IN THIS PRESENTATION IS PROVIDED FOR INFORMATIONAL PURPOSES ONLY.
WHILST EFFORTS WERE MADE TO VERIFY THE COMPLETENESS AND ACCURACY OF THE INFORMATION CONTAINED IN THIS PRESENTATION, IT IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
ALL PERFORMANCE DATA INCLUDED IN THIS PRESENTATION HAVE BEEN GATHERED IN A CONTROLLED ENVIRONMENT. YOUR OWN TEST RESULTS MAY VARY BASED ON HARDWARE, SOFTWARE OR INFRASTRUCTURE DIFFERENCES.
ALL DATA INCLUDED IN THIS PRESENTATION ARE MEANT TO BE USED ONLY AS A GUIDE.
IN ADDITION, THE INFORMATION CONTAINED IN THIS PRESENTATION IS BASED ON IBM’S CURRENT PRODUCT PLANS AND STRATEGY, WHICH ARE SUBJECT TO CHANGE BY IBM, WITHOUT NOTICE.
IBM AND ITS AFFILIATED COMPANIES SHALL NOT BE RESPONSIBLE FOR ANY DAMAGES ARISING OUT OF THE USE OF, OR OTHERWISE RELATED TO, THIS PRESENTATION OR ANY OTHER DOCUMENTATION.
NOTHING CONTAINED IN THIS PRESENTATION IS INTENDED TO, OR SHALL HAVE THE EFFECT OF:
- CREATING ANY WARRANT OR REPRESENTATION FROM IBM, ITS AFFILIATED COMPANIES OR ITS OR THEIR SUPPLIERS AND/OR LICENSORS
3
This Talk1. Eclipse OMR project
• Created March 2016• OMR JIT to be open source Fall 2016
2. Introduction to OMR JitBuilder• Native code generation toolkit using OMR JIT
3. Experiments with adding a JIT to SOM++ using JitBuilder• Design decisions and limitations• Walkthrough of some of the code• Performance results
4
5
Eclipse OMR MissionBuild an open reusable language runtime foundation for cloud platforms
• To accelerate advancement and innovation
• In full cooperation with existing language communities
• Engaging a diverse community of people interested in language runtimes• Professional developers• Researchers• Students• Hobbyists
6
1. OMR has no language semantics
2. OMR is for building language runtimes but is not itself a language runtime
3. OMR components can be independently integrated into any language runtime, new or existing, without influencing language semantics
Key Goals for Eclipse OMR
http://www.eclipse.org/omrhttps://github.com/eclipse/omr
https://developer.ibm.com/open/omr/
Dual License:Eclipse Public License V1.0
Apache 2.0
Contributors very welcome https://github.com/eclipse/omr/blob/master/CONTRIBUTING.md
Eclipse OMRCreated March 2016
7
8
Eclipse OMR Components Currently Availableport platform abstraction (porting) librarythread cross platform pthread-like threading libraryvm APIs to manage per-interpreter and per-thread contextsgc garbage collection framework for managed heapsomrtrace library for publishing trace events for monitoring/diagnosticsomrsigcompat signal handling compatibility libraryexample demonstration code to show how a language runtime might consume OMR components, also used for testingfvtest 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
…400KLOC at this point, more components coming!
9
JitBuilder Library
Just In Time (JIT) CompilersMade Easy(er)
10
What is JitBuilder?• Prototype interface to the OMR JIT compiler technology: Prototype Alert!
• Designed to simplify work to bootstrap a JIT compiler to generate native instructions for interpreted methods• Really a general native code generation toolkit
• OMR JIT is not open source just yet (coming this fall!)• That means JitBuilder is not available as part of Eclipse OMR project just yet• Can be used independently of Eclipse OMR
• JitBuilder Docker image available via Box.com download:• https://ibm.box.com/v/JitBuilder-x86-64-image
• Ease into it with this first blog article below, more coming shortlyhttps://developer.ibm.com/open/2016/07/19/jitbuilder-library-and-eclipse-omr-just-in-time-compilers-made-easy/
11
Simple API• InitializeJit() gets the OMR JIT ready to go
• Among other things, allocates a code cache into which compiled methods go
• ShutdownJit() when you’re done with your native code• It will free the code cache, so compiled methods go away after this call
• Then there’s the compiling part• Need to write a MethodBuilder
What is a MethodBuilder?• MethodBuilder corresponds to a method callable with system linkage
taking whatever parameters you want, returning a value if you want
• 2 basic parts to this C++ class:• Constructor describes return and parameter types• ::buildIL() describes the method’s code
• MethodBuilder class provides services to inject code operations
12
13
What can you generate with JitBuilder?• Data Types
• Primitives: Int8, Int16, Int32, Int64, Float, Double• Arbitrary structs of primitives• Arrays and pointers, but aliasing isn’t yet perfect
• Arithmetic• Add, Sub, Mul, Div, And, Xor, ShiftL, ShiftR, UnsignedShiftR
• Conditional• EqualTo, NotEqualTo, LessThan, GreaterThan
• Type Conversion• ConvertTo
• Memory• Load, Store, IndexAt, LoadAt, StoreAt, LoadField, StoreField• VectorLoad, VectorStore, VectorLoadAt, VectorStoreAt
• Call (use DefineFunction to enable arbitrary C functions to be called)• Control flow
• IfThen, IfThenElse, Switch, ForLoopUp, ForLoopDown, DoWhile, WhileDo, variants with break, continue, etc.• Return
14
Operations are (mostly) typeless• You say “Add” not “Add 32-bit integers”
• Jit Builder will derive type from operands• Leaves of expression trees are typically Load or Const operations
• Current exceptions are things like: LoadAt, StoreAt, IndexAt
• Types of params and/or locals described in constructor• Every Store to a new variable assigns its type (cannot change)
• Store to a new name creates a new slot in the native stack frame
Simple MethodBuilder ExampleSimpleMB::SimpleMB(TR::TypeDictionary *d)
: MethodBuilder(d)
{
DefineName("increment");
DefineParameter("value", Int32);
DefineReturnType(Int32);
}
boolSimpleMB::buildIL(){ Return( Add( Load("value"), ConstInt32(1))); return true;}
15
Simple MethodBuilder ExampleSimpleMB::SimpleMB(TR::TypeDictionary *d)
: MethodBuilder(d)
{
DefineName("increment");
DefineParameter("value", Int32);
DefineReturnType(Int32);
}
boolSimpleMB::buildIL(){ Return( Add( Load("value"), ConstInt32(1))); return true;}
Set the name of the function
16
17
Simple MethodBuilder ExampleSimpleMB::SimpleMB(TR::TypeDictionary *d)
: MethodBuilder(d)
{
DefineName("increment");
DefineParameter("value", Int32);
DefineReturnType(Int32);
}
boolSimpleMB::buildIL(){ Return( Add( Load("value"), ConstInt32(1))); return true;}
Define a 32-bit integer parameter called “value”
18
Simple MethodBuilder ExampleSimpleMB::SimpleMB(TR::TypeDictionary *d)
: MethodBuilder(d)
{
DefineName("increment");
DefineParameter("value", Int32);
DefineReturnType(Int32);
}
boolSimpleMB::buildIL() { Return( Add( Load("value"), ConstInt32(1))); return true; }
increment will return a 32-bit integer
19
Simple MethodBuilder ExampleSimpleMB::SimpleMB(TR::TypeDictionary *d)
: MethodBuilder(d)
{
DefineName("increment");
DefineParameter("value", Int32);
DefineReturnType(Int32);
}
boolSimpleMB::buildIL(){ Return( Add( Load("value"), ConstInt32(1))); return true;}
Used to record and lookup types (e.g. Int32)
20
Simple MethodBuilder ExampleSimpleMB::SimpleMB(TR::TypeDictionary *d)
: MethodBuilder(d)
{
DefineName("increment");
DefineParameter("value", Int32);
DefineReturnType(Int32);
}
boolSimpleMB::buildIL(){ Return( Add( Load("value"), ConstInt32(1))); return true;}
Describes what code to generate for incrementConstruct expression trees from simple operations
21
Simple MethodBuilder ExampleSimpleMB::SimpleMB(TR::TypeDictionary *d)
: MethodBuilder(d)
{
DefineName("increment");
DefineParameter("value", Int32);
DefineReturnType(Int32);
}
boolSimpleMB::buildIL(){ Return( Add( Load("value"), ConstInt32(1))); return true;}
Reference parameters by name e.g. “value”You can also create new locals by name
22
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// code after merge goes into “this” builderReturn( Load(“T”));
23
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// code after merge goes into “this” builderReturn( Load(“T”));
Creates two new code paths (thenPath, elsePath)
24
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// code after merge goes into “this” builderReturn( Load(“T”));
IfThenElse connects the new code paths in a diamond,Decide based on (a < b)
25
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// code after merge goes into “this” builderReturn( Load(“T”));
Inject operations directly onto the code path where they should execute
26
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// code after merge goes into “this” builderReturn( Load(“T”));
On then path, T=1
27
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// code after merge goes into “this” builderReturn( Load(“T”));
On else path, T=0
28
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// code after merge goes into “this” builderReturn( Load(“T”));
Operations are appended to the code path you specifyReturn(Load(“T”)) happens after the merge for IfThenElse
29
Create IlBuilders for new code paths// if-then-else constructIlBuilder *thenPath=OrphanBuilder();IlBuilder *elsePath=OrphanBuilder();IfThenElse(&thenPath, &elsePath, LessThan(Load(“a”), Load(”b”));
// put then operations into thenPath builderthenPath->Store(“T”,thenPath-> ConstInt32(1));
// put else operations into elsePath builderelsePath->Store(“T”,elsePath-> ConstInt32(0));
// else path returns, but then path does notelsePath->Return(elsePath-> Load(“T”));
Return(Load(”T”)) directly from the elsePath
30
TypeDictionary for managing types• Primitive types: NoType, Int8, Int16, Int32, Int64, Float, Double• Pointer (array) types: pInt8, pInt16, pInt32, pInt64, pFloat, pDouble• Can define structures with fields
• Then LoadIndirect() or StoreIndirect() to access fields offset from a base pointer
TR::IlType *elementType = DefineStruct(“Element”);
DefineField(“Element”, “next”, PointerTo(elementType));
DefineField(“Element”, “key”, Int32);
DefineField(“Element”, “value”, Double);
// load ((Element *)ptr)->key
TR::IlValue *result = LoadIndirect(“Element”, “key”, Load(“ptr”));
31
BytecodeBuilders for easy(er) JIT writing• Write a handler for each type of bytecode to inject code to execute that type
of bytecode• Create a BytecodeBuilder object for every bytecode index• Iterate over bytecodes in a method:
• Call the right handler on BytecodeBuilder object for this bytecode index• Tell the object how control flows out from this bytecode
bytecodeBuilder->AddFallThroughBuilder(nextBuilderObject);bytecodeBuilder->AddSuccessorBuilder(branchesToBuilderObject);
• MethodBuilder must call AppendBuilder(bytecodeBuilder[0]);• More explanation in developerworksOpen talk from July 20:
https://developer.ibm.com/open/videos/eclipse-omr-tech-talk/
32
• Group of implementations written in different languages• C, C++, Java, Python, Truffle/Graal, RPython, etc.• SOM++ is the C++ implementation
• Spent 2-3 weeks implementing a JitBuilder for SOM++• Benchmark improvements of 2.5-15x
http://som-st.github.io
33
JitBuilder Design for SOM++• On N executions of a method add it to queue to be JIT’d• Create a JIT thread to compile methods asynchronously• Call the JIT’d version of a method on sends if it exists
• JitBuilder methods are generated as C callable functions
• Keep shape of the interpreter frame• This allows easy fallback to the interpreter
• Make the interpreter loop callable so it can be called from JIT’d code• Assume objects do not move
34
SOM++ Interpreter Basics• Every Smalltalk method’s source code is compiled to an array of bytecodes• Interpreter iterates over those bytecodes• Every bytecode has a bytecode handler that executes actions for that
bytecode• Most bytecodes pop and/or push values to the operand stack
• E.g. BC_PUSH_CONSTANT pushes a constant value onto the operand stack• E.g. BC_DUP conceptually pops a value from the operand stack, then pushes two
copies of that value back onto the operand stack
• Some bytecodes cause interpreter to read at another location in bytecode array
• E.g. BC_JUMP or BC_JUMP_IF_TRUE
35
JitBuilder for Bytecode Basics• Just like interpreter needs a handler for every bytecode, so does JitBuilder
• Every bytecode needs a BytecodeBuilder handler• Handler describes how to generate code for that bytecode
• Also like interpreter, need to iterate over bytecode array• For each bytecode, create a BytecodeBuilder object and call BytecodeBuilder
handler on it• BytecodeBuilder object represents the native code to execute a particular bytecode
• Also need to indicate where control goes after each bytecode:• Most bytecodes just fall through to next bytecode• Some (e.g. BC_JUMP) branch to some other bytecode
• Best to create all BytecodeBuilder objects up front, then call handler to generate code
36
SOM++ MethodBuilder SOMppMethodSOMppMethod::SOMppMethod(TR::TypeDictionary *types, VMMethod *vmMethod)
: MethodBuilder(types),
method(vmMethod)
{ DefineName(methodName); //methodName is class>>#signature
DefineReturnType(NoType);
defineStructures(types);
DefineParameter("interpreter", Int64);
DefineParameter("frame", pVMFrame);
}
37
SOM++ MethodBuilder SOMppMethodSOMppMethod::SOMppMethod(TR::TypeDictionary *types, VMMethod *vmMethod)
: MethodBuilder(types),
method(vmMethod)
{ DefineName(methodName); //methodName is class>>#signature
DefineReturnType(NoType);
defineStructures(types);
DefineParameter("interpreter", Int64);
DefineParameter("frame", pVMFrame);
}
38
SOM++ MethodBuilder SOMppMethodSOMppMethod::SOMppMethod(TR::TypeDictionary *types, VMMethod *vmMethod)
: MethodBuilder(types),
method(vmMethod)
{ DefineName(methodName); //methodName is class>>#signature
DefineReturnType(NoType);
defineStructures(types);
DefineParameter("interpreter", Int64);
DefineParameter("frame", pVMFrame);
}
39
SOMppMethod defined structuresvoid
SOMppMethod::defineStructures(TR::TypeDictionary *types)
{
defineVMObjectStructure(types);
defineVMFrameStructure(types);
}
40
SOMppMethod defined structuresvoid
SOMppMethod::defineStructures(TR::TypeDictionary *types)
{
defineVMObjectStructure(types);
defineVMFrameStructure(types);
}
41
SOMppMethod VMFramevoid
SOMppMethod::defineVMFrameStructure(TR::TypeDictionary *types)
{
vmFrame = types->DefineStruct("VMFrame");
pVMFrame = types->PointerTo("VMFrame");
types->DefineField("VMFrame", "vTable", Int64);
....
types->DefineField("VMFrame", "arguments", pVMObject);
types->DefineField("VMFrame", "locals", pVMObject);
types->DefineField("VMFrame", "stack_ptr", pVMObject);
types->CloseStruct("VMFrame");
}
42
SOMppMethod::buildIL()bool SOMppMethod::buildIL()
{
TR::BytecodeBuilder **bytecodeBuilderTable = allocBytecodeBuilderTable(method);
if (nullptr == bytecodeBuilderTable) {
return false;
}
createBytecodeBuilders(bytecodeBuilderTable, method);
AppendBuilder(bytecodeBuilderTable[0]); //append the first bytecode builder
bool generated = generateIlForBytecodes(bytecodeBuilderTable, method)
free((void *)bytecodeBuilderTable);
return generated;
}
43
SOMppMethod::buildIL()bool SOMppMethod::buildIL()
{
TR::BytecodeBuilder **bytecodeBuilderTable = allocBytecodeBuilderTable(method);
if (nullptr == bytecodeBuilderTable) {
return false;
}
createBytecodeBuilders(bytecodeBuilderTable, method);
AppendBuilder(bytecodeBuilderTable[0]); //append the first bytecode builder
bool generated = generateIlForBytecodes(bytecodeBuilderTable, method)
free((void *)bytecodeBuilderTable);
return generated;
}
44
SOM++ MethodBuildervoid createBytecodeBuilders(TR::BytecodeBuilder **table, VMMethod *method)
{
long bytecodeIndex = 0;
while (bytecodeIndex < method->GetNumberOfBytecodes()) {
uint8_t bc = method->GetBytecode(bytecodeIndex);
table[bytecodeIndex] = OrphanBytecodeBuilder(bytecodeIndex, Bytecode::GetBytecodeName(bc));
bytecodeIndex += Bytecode::GetBytecodeLength(bc);
}
}
45
BytecodeBuilder table for example bytecodes
PUSH_CONSTANT0
PUSH_LOCAL15
JUMP_IF_TRUE000
15PUSH_CONSTANT
1PUSH_LOCAL
25
OrphanBytecodeBuilder(0, “PUSH_CONSTANT”)
OrphanBytecodeBuilder(2, “PUSH_LOCAL”)
OrphanBytecodeBuilder(5, “JUMP_IF_TRUE”)
0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:
0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:
OrphanBytecodeBuilder(10, “PUSH_CONSTANT”)
OrphanBytecodeBuilder(12, “PUSH_LOCAL”)
bytecodes builders
46
SOMppMethod::buildIL()bool SOMppMethod::buildIL()
{
TR::BytecodeBuilder **bytecodeBuilderTable = allocBytecodeBuilderTable(method);
if (nullptr == bytecodeBuilderTable) {
return false;
}
createBytecodeBuilders(bytecodeBuilderTable, method);
AppendBuilder(bytecodeBuilderTable[0]); //append the first bytecode builder
bool generated = generateIlForBytecodes(bytecodeBuilderTable, method);
free((void *)bytecodeBuilderTable);
return generated;
}
47
Call Builder Handlers for each bytecodeboolgenerateIlForBytecodes(TR::BytecodeBuilder **table, VMMethod *method) { while (bcIndex < method->GetNumberOfBytecodes()) { uint8_t bc = method->GetBytecode(bcIndex); switch(bc) { case BC_PUSH_CONSTANT : { doPushConstant(builders[bcIndex], method->GetBytecode(bcIndex+1)); bcIndex += 2; // this bytecode has a length of 2 break; } case BC_PUSH_LOCAL : …
48
BC_ PUSH_CONSTANT <constant index>void Interpreter::doPushConstant(long bytecodeIndex) { vm_oop_t constant = method->GetConstant(bytecodeIndex); GetFrame()->Push(constant);}
Boils down to:uint8_t index = bytecodes[bytecodeIndex + 1];vm_oop_t constant = method->indexableFields[index];++frame->stack_ptr;*frame->stack_ptr = constant;
49
BC_ PUSH_CONSTANT <constant index>bool
doPushConstant(TR::BytecodeBuilder *b, uint8_t index) {
b->Store(“constant”, // can load/store native locals by name
b-> LoadAt(pInt64, // pInt64 is a type: pointer to Int64
b-> IndexAt(pInt64, // for simplicity, treating vm_oop_t as pInt64
b-> LoadIndirect(“VMMethod”, “indexableFields”,
b-> Load(“method”)),
b-> ConstInt64(index))));
vm_oop_t constant = method->indexableFields[index];++frame->stack_ptr;*frame->stack_ptr = constant;
50
BC_ PUSH_CONSTANT <constant index>bool
doPushConstant(TR::BytecodeBuilder *b, uint8_t bc)
…
b->Store("newSP", // store the value of frame->stack_ptr + 8 into a temp
b-> Add( // using 8 to simply the code and make it more readable
b-> LoadIndirect("VMFrame", "stack_ptr",
b-> Load(frame)),
b-> ConstInt64(8)));
b->StoreIndirect("VMFrame", "stack_ptr", // store newSP into frame->stack_ptr
b-> Load(frame),
b-> Load("newSP"));
b->StoreAt(pInt64, // store the constant value into the frame->stack_ptr
b-> Load("newSP"),
b-> Load("constant"));
return true;
}
vm_oop_t constant = method->indexableFields[index];++frame->stack_ptr;*frame->stack_ptr = constant;
51
What should happen after bytecode at 0?Control should fall through to bytecode at 2!
PUSH_CONSTANT0
PUSH_LOCAL15
JUMP_IF_TRUE000
15PUSH_CONSTANT
1PUSH_LOCAL
25
0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:
bytecodeswhile (bcIndex < method->GetNumberOfBytecodes()) { switch(bc) { case BC_PUSH_CONSTANT : { doPushConstant(builders[bcIndex], method->GetBytecode(bcIndex+1)); builders[bcIndex]->AddFallthroughBuilder(builders[bcIndex+2]); bcIndex += 2; // this bytecode has a length of 2 break; } case BC_PUSH_LOCAL …
52
Branches: add successor builder for destination
bytecodes while (bc < method->GetNumberOfBytecodes()) { switch(method->GetBytecode(bc)) { case BC_JUMP_IF_TRUE : { long destIndex = calculateBranchDestinationForJump(bc); doJumpIfTrue(builders[bc], destIndex); builders[bc]->AddFallthroughBuilder(builders[bc+5]); builders[bc]->AddSuccessorBuilder(builders[destIndex]); bc += 5; // this bytecode has a length of 5 break; } case BC_PUSH_LOCAL : …
PUSH_CONSTANT0
PUSH_LOCAL15
JUMP_IF_TRUE000
15PUSH_CONSTANT
1PUSH_LOCAL
25
0:1:2:3:4:5:6:7:8:9:10:11:12:13:14:
53
Generating and Calling JIT’d methodsvoid VMMethod::Invoke(Interpreter* interp, VMFrame* frame) { VMFrame* frm = interp->PushNewFrame(this); frm->CopyArgumentsFrom(frame); if(NULL != compiledMethod) { compiledMethod((int64_t)interp, (int64_t)frm); } else if (invokedCount > 0) { if ((0 == --invokedCount) && enableJIT) { TR::TypeDictionary types; SOMppMethod methodBuilder(&types, this); rc = (*compileMethodBuilder)(&methodBuilder, &compiledMethod); if (0 != rc) compiledMethod = (SOMppFunctionType *)NULL;………..}
54
SOM++ JitBuilder performance
Fibonacci
Dispatch
BounceLoop
Permute
QueensLis
t
Recurse
StorageSieve
BubbleSort
QuickSort
SumTowers
TreeSort
IntegerLoop
FieldLoop0
20
40
60
80
100
120
140
160
180
SOM++ Benchmarks (lower is better)
default "-jit"
55
Future improvements to SOM++ JIT• Do not keep frame->stack_ptr up to date
• Use a JIT operand stack• If required write to the actual stack (for sends)
• Modify VM so that inline allocation is possible• Allocate frame objects on JIT stack for sends from the JIT• Improve JIT to JIT calls / returns
56
Wrap Up• Eclipse OMR mission to create an open reusable language runtime foundation• Building an open community for everyone to share and discuss ideas,
technology, and best practices to build language runtimes• JitBuilder is available now for you to play with
• Includes the current OMR JIT just not in source form (coming in the Fall!)• Write your own JIT compiler• Part of a layered strategy to incrementally improve performance via JIT technology
• The SOM++ JitBuilder strategy and code could easily be used by any C/C++ runtime to add JIT support in a very short period of time
• Everyone is welcome to join us at Eclipse OMR and we hope you will !
57
Where to contact us• Charlie Gracie [email protected] @crgracie• Mailing List [email protected]
Subscribe at https://dev.eclipse.org/mailman/listinfo/omr-dev
• Eclipse OMR Web Site https://www.eclipse.org/omr
• Eclipse OMR pages on DeveloperWorks Open https://developer.ibm.com/open/omr/
• Eclipse OMR Github project https://github.com/eclipse/omr
• JitBuilder Docker image https://ibm.box.com/v/JitBuilder-x86-64-image
• SOM++ with OMR and JitBuilder (omr_jitbuilder) https://github.com/charliegracie/SOMpp
• OMR developers will be at:• Ruby Kaigi in September• Java One in September• ReUsable Modular Programming Language Environments (RUMPLE) workshop in October