Working with
 Bytecodes:

IRBuilder and
 ByteSurgeon

Marcus Denker
Reasons for working with Bytecode


 •   Generating Bytecode
     – Implementing compilers for other languages
     – Expe...
Overview

 1. Introduction to Squeak Bytecodes
 2. Generating Bytecode with IRBuilder
 3. Introduction to ByteSurgeon




...
The Squeak Virtual Machine

 •   From last lecture:
     – Virtual machine provides a virtual processor
     – Bytecode: T...
Bytecode in the CompiledMethod

 •   CompiledMethods format:

                                        (Number>>#asInteger)...
Example: Number>>asInteger

 •   Smalltalk code:
      Number>>asInteger
      	

 "Answer an Integer nearest the receiver...
Example: Step by Step

 •   9 <70> self
     – The receiver (self) is pushed on the stack
 •   10 <D0> send: truncated
   ...
Squeak Bytecodes

 •   256 Bytecodes, four groups:

     – Stack Bytecodes
         • Stack manipulation: push / pop / dup...
Stack Bytecodes

 •   Push values on the stack, e.g., temps, instVars, literals
     – e.g: 16 - 31: push instance variabl...
Sends and Returns

 •   Sends: receiver is on top of stack
     – Normal send
     – Super Sends
     – Hard-coded sends f...
Jump Bytecodes

 •   Control Flow inside one method
 •   Used to implement control-flow efficiently
 •   Example:
          ...
What you should have learned...

•   ... dealing with bytecodes directly is possible, but
    very boring.
•   We want reu...
Generating Bytecodes


 •   IRBuilder: A tool for generating bytecode
 •   Part of the new compiler for Squeak 3.9

 •   I...
IRBuilder: Simple Example

 •   Number>>asInteger
     
     iRMethod := IRBuilder new
     	

   	

 numRargs: 1;
     	
...
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

     – Make a instance of I...
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

 numRargs: 1;
     	

 	


...
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

 numRargs: 1;
     	

 	

 ...
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

 numRargs: 1;
     	

 	

 ...
IRBuilder: Step by Step

 •   Number>>asInteger
     
      iRMethod := IRBuilder new
     	

    	

 numRargs: 1;
     	
...
IRBuilder: Step by Step

 •   Number>>asInteger
     
     iRMethod := IRBuilder new
     	

   	

 numRargs: 1;
     	

 ...
IRBuilder: Step by Step

 •   Number>>asInteger
     
     iRMethod := IRBuilder new
     	

   	

 numRargs: 1;
     	

 ...
IRBuilder: Step by Step

 •   Number>>asInteger
     
     iRMethod := IRBuilder new
     	

   	

 numRargs: 1;
     	

 ...
IRBuilder: Step by Step

 •   Number>>asInteger
     
     iRMethod := IRBuilder new
     	

   	

 numRargs: 1;
     	

 ...
IRBuilder: Stack Manipulation

 •   popTop - remove the top of stack
 •   pushDup - push top of stack on the stack
 •   pu...
IRBuilder: Symbolic Jumps

 •   Jump targets are resolved:
 •   Example: false ifTrue: [’true’] ifFalse: [’false’]
 iRMeth...
IRBuiler: Instance Variables

 •   Access by offset
 •   Read: getField:
      – receiver on top of stack
 •   Write: setF...
IRBuilder: Temporary Variables

 •   Accessed by name
 •   Define with addTemp: / addTemps:
 •   Read with pushTemp:
 •   W...
IRBuilder: Sends

 •   normal send
            builder pushLiteral: ‘hello’
            builder send: #size;


 •   super ...
IRBuilder: Lessons learned
•   IRBuilder: Easy bytecode generation
    –   Jumps
    –   Instance variable
    –   Tempora...
ByteSurgeon

 •   Library for bytecode transformation in Smalltalk
 •   Full flexibility of Smalltalk Runtime
 •   Provides...
Example: Logging

 •   Goal: logging message send.
 •   First way: Just edit the text:

example
	

 self test.
	

	



   ...
Logging with Bytesurgen

 •   Goal: Change the method without changing program
     text
 •   Example:

     (Example>>#ex...
Logging: Step by Step

      (Example>>#example)instrumentSend: [:send |
      	

 send insertBefore:
      	

 	

 ‘Trans...
Logging: Step by Step

        (Example>>#example)instrumentSend: [:send |
        	

 send insertBefore:
        	

 	

 ...
Logging: Step by Step

        (Example>>#example)instrumentSend: [:send |
        	

 send insertBefore:
        	

 	

 ...
Logging: Step by Step

        (Example>>#example)instrumentSend: [:send |
        	

 send insertBefore:
        	

 	

 ...
Logging: Step by Step

        (Example>>#example)instrumentSend: [:send |
        	

 send insertBefore:
        	

 	

 ...
Inside ByteSurgeon

 •   Uses IRBuilder internally


                 Decompile        Compile



     Bytecode           ...
ByteSurgeon Usage

 •   On Methods or Classes:
         MyClass instrument: [.... ].	

         (MyClass>>#myMethod) instr...
ByteSurgeon: Lessons learned
•   ByteSurgeon: Tool for editing bytecode
    – Simple example
    – Based on IRBuilder


• ...
Advanced ByteSurgeon:

 •   Goal: extend a send with after logging


example
	

 self test.
	

	



                 examp...
Advanced ByteSurgeon

 •   With Bytesurgeon, something like:


        (Example>>#example)instrumentSend: [:send |
       ...
Advanced ByteSurgeon

 •   With Bytesurgeon, something like:


        (Example>>#example)instrumentSend: [:send |
       ...
Implementation Metavariables

 •   Stack during send:

           receiver

           arg1

           arg2              ...
Metavariables: Implementation


 •   Solution: ByteSurgeon generates preamble

     –   Pop the arguments into temps
     ...
Metavariables: Implementation



         25 <70> self
         26 <81 40> storeIntoTemp: 0   Preamble
         28 <D0> se...
End


•   Short overview of Squeak bytecode
•   Introduction to bytecode generation with IRBuilder
•   Manipulating byteco...
Upcoming SlideShare
Loading in...5
×

RBuilder and ByteSurgeon

519

Published on

Lecture "RBuilder and ByteSurgeon", DCC University of Chile, October 2005

Published in: Technology, Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
519
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

RBuilder and ByteSurgeon

  1. 1. Working with Bytecodes: IRBuilder and ByteSurgeon Marcus Denker
  2. 2. Reasons for working with Bytecode • Generating Bytecode – Implementing compilers for other languages – Experimentation with new language features • Bytecode Transformation – Adaptation of running Systems – Tracing / Debugging – New language features Marcus Denker
  3. 3. Overview 1. Introduction to Squeak Bytecodes 2. Generating Bytecode with IRBuilder 3. Introduction to ByteSurgeon Marcus Denker
  4. 4. The Squeak Virtual Machine • From last lecture: – Virtual machine provides a virtual processor – Bytecode: The ‘machine-code’ of the virtual machine – Smalltalk (like Java): Stack machine • Today: – Closer look at Squeak bytecode Marcus Denker
  5. 5. Bytecode in the CompiledMethod • CompiledMethods format: (Number>>#asInteger)inspect Header Number of temps, literals... Array of all Literals Literal Objects Bytecode Pointer to Trailer Source Marcus Denker
  6. 6. Example: Number>>asInteger • Smalltalk code: Number>>asInteger "Answer an Integer nearest the receiver toward zero." ^self truncated • Symbolic Bytecode 9 <70> self 10 <D0> send: truncated 11 <7C> returnTop Marcus Denker
  7. 7. Example: Step by Step • 9 <70> self – The receiver (self) is pushed on the stack • 10 <D0> send: truncated – Bytecode 208: send literal selector 1 – Get the selector from the first literal – start message lookup in the class of the object that is top of the stack – result is pushed on the stack • 11 <7C> returnTop – return the object on top of the stack to the calling method Marcus Denker
  8. 8. Squeak Bytecodes • 256 Bytecodes, four groups: – Stack Bytecodes • Stack manipulation: push / pop / dup – Send Bytecodes • Invoke Methods – Return Bytecodes • Return to caller – Jump Bytecodes • Control flow inside a method Marcus Denker
  9. 9. Stack Bytecodes • Push values on the stack, e.g., temps, instVars, literals – e.g: 16 - 31: push instance variable • Push Constants (False/True/Nil/1/0/2/-1) • Push self, thisContext • Duplicate top of stack • Pop Marcus Denker
  10. 10. Sends and Returns • Sends: receiver is on top of stack – Normal send – Super Sends – Hard-coded sends for efficiency, e.g. +, - • Returns – Return top of stack to the sender – Return from a block – Special bytecodes for return self, nil, true, false (for efficiency) Marcus Denker
  11. 11. Jump Bytecodes • Control Flow inside one method • Used to implement control-flow efficiently • Example: ^ 1<2 ifTrue: ['true'] 9 <76> pushConstant: 1 10 <77> pushConstant: 2 11 <B2> send: < 12 <99> jumpFalse: 15 13 <20> pushConstant: 'true' 14 <90> jumpTo: 16 15 <73> pushConstant: nil 16 <7C> returnTop Marcus Denker
  12. 12. What you should have learned... • ... dealing with bytecodes directly is possible, but very boring. • We want reusable abstractions that hide the details (e.g. the different send bytecodes) • We would like to have frameworks for – Generating bytecode easily – Transforming bytecode
  13. 13. Generating Bytecodes • IRBuilder: A tool for generating bytecode • Part of the new compiler for Squeak 3.9 • Idea: a symbolic Assembler for Squeak Marcus Denker
  14. 14. IRBuilder: Simple Example • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self; send: #truncated; returnTop; ir. aCompiledMethod := iRMethod compiledMethod. aCompiledMethod valueWithReceiver:3.5 arguments: #() Marcus Denker
  15. 15. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new – Make a instance of IRBuilder Marcus Denker
  16. 16. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; – Define arguments. Note: “self” is default argument Marcus Denker
  17. 17. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" – define temporary variables. Note: arguments are temps Marcus Denker
  18. 18. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self – push “self” on the stack Marcus Denker
  19. 19. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self send: #truncated; – call method truncated on “self” Marcus Denker
  20. 20. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self send: #truncated; returnTop; – return Top of Stack Marcus Denker
  21. 21. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self send: #truncated; returnTop; ir. – tell IRBuilder to generate Intermediate Representation (IR) Marcus Denker
  22. 22. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self send: #truncated; returnTop; ir. aCompiledMethod := iRMethod compiledMethod. – Generate method from IR Marcus Denker
  23. 23. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self send: #truncated; returnTop; ir. aCompiledMethod := iRMethod compiledMethod. aCompiledMethod valueWithReceiver:3.5 arguments: #() – Execute the method with reveiver 3.5 and no arguments. – “3.5 truncated” Marcus Denker
  24. 24. IRBuilder: Stack Manipulation • popTop - remove the top of stack • pushDup - push top of stack on the stack • pushLiteral: • pushReceiver - push self • pushThisContext Marcus Denker
  25. 25. IRBuilder: Symbolic Jumps • Jump targets are resolved: • Example: false ifTrue: [’true’] ifFalse: [’false’] iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushLiteral: false; jumpAheadTo: #false if: false; pushLiteral: 'true'; "ifTrue: ['true']" jumpAheadTo: #end; jumpAheadTarget: #false; pushLiteral: 'false'; "ifFalse: ['false']" jumpAheadTarget: #end; returnTop; ir. Marcus Denker
  26. 26. IRBuiler: Instance Variables • Access by offset • Read: getField: – receiver on top of stack • Write: setField: – receiver and value on stack • Example: set the first instance variable to 2 iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushLiteral: 2; pushTemp: #self; setField: 1; pushTemp: #self; returnTop; ir. aCompiledMethod := iRMethod compiledMethod. aCompiledMethod valueWithReceiver: 1@2 arguments: #() Marcus Denker
  27. 27. IRBuilder: Temporary Variables • Accessed by name • Define with addTemp: / addTemps: • Read with pushTemp: • Write with storeTemp: • Examle: set variables a and b, return value of a iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" addTemps: #(a b); pushLiteral: 1; storeTemp: #a; pushLiteral: 2; storeTemp: #b; pushTemp: #a; returnTop; ir. Marcus Denker
  28. 28. IRBuilder: Sends • normal send builder pushLiteral: ‘hello’ builder send: #size; • super send .... builder send: #selector toSuperOf: aClass; – The second parameter specifies the class were the lookup starts. Marcus Denker
  29. 29. IRBuilder: Lessons learned • IRBuilder: Easy bytecode generation – Jumps – Instance variable – Temporary variables – Sends • Next: Manipulating bytecode
  30. 30. ByteSurgeon • Library for bytecode transformation in Smalltalk • Full flexibility of Smalltalk Runtime • Provides high-level API • For Squeak, but portable • Runtime transformation needed for • Adaptation of running systems • Tracing / debugging • New language features (MOP, AOP) Marcus Denker
  31. 31. Example: Logging • Goal: logging message send. • First way: Just edit the text: example self test. example Transcript show: ‘sending #test’. self test. Marcus Denker
  32. 32. Logging with Bytesurgen • Goal: Change the method without changing program text • Example: (Example>>#example)instrumentSend: [:send | send insertBefore: ‘Transcript show: ‘’sending #test’’ ‘. ] Marcus Denker
  33. 33. Logging: Step by Step (Example>>#example)instrumentSend: [:send | send insertBefore: ‘Transcript show: ‘’sending #test’’ ‘. ] Example >> #example Class Name of Method >>: - takes a name of a method - returns the CompiledMethod object Marcus Denker
  34. 34. Logging: Step by Step (Example>>#example)instrumentSend: [:send | send insertBefore: ‘Transcript show: ‘’sending #test’’ ‘. ] • instrumentSend: – takes a block as an argument – evaluates it for all send bytecodes Marcus Denker
  35. 35. Logging: Step by Step (Example>>#example)instrumentSend: [:send | send insertBefore: ‘Transcript show: ‘’sending #test’’ ‘. ] • The block has one parameter: send • It is executed for each send bytecode in the method Marcus Denker
  36. 36. Logging: Step by Step (Example>>#example)instrumentSend: [:send | send insertBefore: ‘Transcript show: ‘’sending #test’’ ‘. ] • Objects describing bytecode understand how to insert code – insertBefor – insertAfter – replace Marcus Denker
  37. 37. Logging: Step by Step (Example>>#example)instrumentSend: [:send | send insertBefore: ‘Transcript show: ‘’sending #test’’ ‘. ] • The code to be inserted. • Double quoting for string inside string –Transcript show: ’sending #test’ Marcus Denker
  38. 38. Inside ByteSurgeon • Uses IRBuilder internally Decompile Compile Bytecode IR Bytecode • Transformation (Code inlining) done on IR Marcus Denker
  39. 39. ByteSurgeon Usage • On Methods or Classes: MyClass instrument: [.... ]. (MyClass>>#myMethod) instrument: [.... ]. • Different instrument methods: – instrument: – instrumentSend: – instrumentTempVarRead: – instrumentTempVarStore: – instrumentTempVarAccess: – same for InstVar Marcus Denker
  40. 40. ByteSurgeon: Lessons learned • ByteSurgeon: Tool for editing bytecode – Simple example – Based on IRBuilder • Next: Advanced ByteSurgeon
  41. 41. Advanced ByteSurgeon: • Goal: extend a send with after logging example self test. example self test. Logger logSendTo: self. Marcus Denker
  42. 42. Advanced ByteSurgeon • With Bytesurgeon, something like: (Example>>#example)instrumentSend: [:send | send insertAfter: ‘Logger logSendTo: ?’ . ] • How can we access the receiver of the send? • Solution: Metavariable Marcus Denker
  43. 43. Advanced ByteSurgeon • With Bytesurgeon, something like: (Example>>#example)instrumentSend: [:send | send insertAfter: ‘Logger logSendTo: <meta: #receiver>’ . ] • How can we access the receiver of the send? • Solution: Metavariable Marcus Denker
  44. 44. Implementation Metavariables • Stack during send: receiver arg1 arg2 result before after • Problem 1: After send, receiver is not available • Problem II: Before send, receiver is deep in the stack Marcus Denker
  45. 45. Metavariables: Implementation • Solution: ByteSurgeon generates preamble – Pop the arguments into temps – Pop the receiver into temps – Rebuild the stack – Do the send – Now we can acces the receiver even after the send Marcus Denker
  46. 46. Metavariables: Implementation 25 <70> self 26 <81 40> storeIntoTemp: 0 Preamble 28 <D0> send: test 29 <41> pushLit: Transcript 30 <10> pushTemp: 0 Inlined Code 31 <E2> send: show: 32 <87> pop 33 <87> pop 34 <78> returnSelf Marcus Denker
  47. 47. End • Short overview of Squeak bytecode • Introduction to bytecode generation with IRBuilder • Manipulating bytecode with ByteSurgeon • Questions?
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×