SlideShare a Scribd company logo
Working with
 Bytecodes:

IRBuilder and
 ByteSurgeon

Marcus Denker
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
Overview

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




Marcus Denker
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
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
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
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
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
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
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
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
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
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
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
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

     – Make a instance of IRBuilder




Marcus Denker
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

 numRargs: 1;
     	

 	


     – Define arguments. Note: “self” is default argument




Marcus Denker
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

 numRargs: 1;
     	

 	

 addTemps: #(self); "receiver"
     	

 	

     – define temporary variables. Note: arguments are temps




Marcus Denker
IRBuilder: Step by Step

 •   Number>>asInteger
     
   iRMethod := IRBuilder new
     	

 	

 numRargs: 1;
     	

 	

 addTemps: #(self); "receiver"
     	

 	

 pushTemp: #self

     – push “self” on the stack




Marcus Denker
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
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
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
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
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
IRBuilder: Stack Manipulation

 •   popTop - remove the top of stack
 •   pushDup - push top of stack on the stack
 •   pushLiteral:
 •   pushReceiver - push self
 •   pushThisContext




Marcus Denker
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
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
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
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
IRBuilder: Lessons learned
•   IRBuilder: Easy bytecode generation
    –   Jumps
    –   Instance variable
    –   Temporary variables
    –   Sends

•   Next: Manipulating bytecode
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
Example: Logging

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

example
	

 self test.
	

	



                 example
                 	

 Transcript show: ‘sending #test’.
                 	

 self test.




Marcus Denker
Logging with Bytesurgen

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

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

 send insertBefore:
     	

 	

 ‘Transcript show: ‘’sending #test’’ ‘.
     ]	





Marcus Denker
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
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
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
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
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
Inside ByteSurgeon

 •   Uses IRBuilder internally


                 Decompile        Compile



     Bytecode                IR             Bytecode



 •   Transformation (Code inlining) done on IR



Marcus Denker
ByteSurgeon Usage

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

         (MyClass>>#myMethod) instrument: [.... ].


 •   Different instrument methods:
     –   instrument:
     –   instrumentSend:
     –   instrumentTempVarRead:
     –   instrumentTempVarStore:
     –   instrumentTempVarAccess:
     –   same for InstVar


Marcus Denker
ByteSurgeon: Lessons learned
•   ByteSurgeon: Tool for editing bytecode
    – Simple example
    – Based on IRBuilder


•   Next: Advanced ByteSurgeon
Advanced ByteSurgeon:

 •   Goal: extend a send with after logging


example
	

 self test.
	

	



                 example
                 	

 	

 self test.
                 	

 	

 Logger logSendTo: self.




Marcus Denker
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
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
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
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
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
End


•   Short overview of Squeak bytecode
•   Introduction to bytecode generation with IRBuilder
•   Manipulating bytecode with ByteSurgeon


•   Questions?

More Related Content

PPT
Stoop 390-instruction stream
PDF
Virtual Machines Lecture
PPT
Behavioral Reflection
PPT
Stoop 305-reflective programming5
KEY
Grand Central Dispatch Design Patterns
PDF
Blocks & GCD
PDF
An Introduction to Twisted
PDF
Objective-C Blocks and Grand Central Dispatch
Stoop 390-instruction stream
Virtual Machines Lecture
Behavioral Reflection
Stoop 305-reflective programming5
Grand Central Dispatch Design Patterns
Blocks & GCD
An Introduction to Twisted
Objective-C Blocks and Grand Central Dispatch

What's hot (20)

PDF
EKON 25 Python4Delphi_mX4
PPTX
Grand Central Dispatch
PDF
WTF is Twisted?
PDF
From Zero To Production (NixOS, Erlang) @ Erlang Factory SF 2016
PPT
Java Performance Tuning
KEY
Twisted: a quick introduction
PPTX
Node.js System: The Approach
PDF
Dennis Benkert & Matthias Lübken - Patterns in a containerized world? - code....
KEY
Runtime
PDF
NodeJS: an Introduction
PPT
8 - OOP - Syntax & Messages
PDF
Ekon 25 Python4Delphi_MX475
PDF
PDF
What Makes Objective C Dynamic?
PPTX
Asynchronous Python with Twisted
PDF
Other Approaches (Concurrency)
PDF
JVM Mechanics: When Does the JVM JIT & Deoptimize?
PPTX
Network programming in java - PPT
PDF
Patterns for JVM languages JokerConf
PDF
2008 07-24 kwpm-threads_and_synchronization
EKON 25 Python4Delphi_mX4
Grand Central Dispatch
WTF is Twisted?
From Zero To Production (NixOS, Erlang) @ Erlang Factory SF 2016
Java Performance Tuning
Twisted: a quick introduction
Node.js System: The Approach
Dennis Benkert & Matthias Lübken - Patterns in a containerized world? - code....
Runtime
NodeJS: an Introduction
8 - OOP - Syntax & Messages
Ekon 25 Python4Delphi_MX475
What Makes Objective C Dynamic?
Asynchronous Python with Twisted
Other Approaches (Concurrency)
JVM Mechanics: When Does the JVM JIT & Deoptimize?
Network programming in java - PPT
Patterns for JVM languages JokerConf
2008 07-24 kwpm-threads_and_synchronization
Ad

Similar to RBuilder and ByteSurgeon (20)

PPT
Working with Bytecode
PDF
VMs, Interpreters, JIT
PDF
Refactoring
PDF
Lecture: Reflection
PDF
Reflection
PDF
Presentation about my Research
PDF
Lecture: Reflection
PDF
Beyond Text - Methods as Objects
PDF
Ruby & Machine Vision - Talk at Sheffield Hallam University Feb 2009
PDF
A JIT Smalltalk VM written in itself
PDF
Runtime Bytecode Transformation for Smalltalk
PDF
Pharo: Syntax in a Nutshell
PDF
Talk: Practical, Pluggable Types
PDF
Sub-Method Reflection
PDF
Smalltalk Bar Camp Hanoi 2009
PDF
Unanticipated Partial Behavioral Reflection
PDF
The Pharo Programming Language
PDF
Reflectivity Demo
PDF
Emerging Languages: A Tour of the Horizon
PDF
Runtime Tools
Working with Bytecode
VMs, Interpreters, JIT
Refactoring
Lecture: Reflection
Reflection
Presentation about my Research
Lecture: Reflection
Beyond Text - Methods as Objects
Ruby & Machine Vision - Talk at Sheffield Hallam University Feb 2009
A JIT Smalltalk VM written in itself
Runtime Bytecode Transformation for Smalltalk
Pharo: Syntax in a Nutshell
Talk: Practical, Pluggable Types
Sub-Method Reflection
Smalltalk Bar Camp Hanoi 2009
Unanticipated Partial Behavioral Reflection
The Pharo Programming Language
Reflectivity Demo
Emerging Languages: A Tour of the Horizon
Runtime Tools
Ad

More from Marcus Denker (20)

PDF
Soil And Pharo
PDF
ConstantBlocks in Pharo11
PDF
Demo: Improved DoIt
PDF
First Class Variables as AST Annotations
PDF
Supporting Pharo / Getting Pharo Support
PDF
Lecture: "Advanced Reflection: MetaLinks"
PDF
thisContext in the Debugger
PDF
Variables in Pharo
PDF
Lecture. Advanced Reflection: MetaLinks
PDF
Improving code completion for Pharo
PDF
VUB Brussels Lecture 2019: Advanced Reflection: MetaLinks
PDF
Slot Composition
PDF
Lecture: Advanced Reflection. MetaLinks
PDF
PHARO IOT
PDF
Open-Source: An Infinite Game
PDF
Lecture: MetaLinks
PDF
PharoTechTalk: Contributing to Pharo
PDF
Feedback Loops in Practice
PDF
Pharo6 - ESUG17
PDF
Soil And Pharo
ConstantBlocks in Pharo11
Demo: Improved DoIt
First Class Variables as AST Annotations
Supporting Pharo / Getting Pharo Support
Lecture: "Advanced Reflection: MetaLinks"
thisContext in the Debugger
Variables in Pharo
Lecture. Advanced Reflection: MetaLinks
Improving code completion for Pharo
VUB Brussels Lecture 2019: Advanced Reflection: MetaLinks
Slot Composition
Lecture: Advanced Reflection. MetaLinks
PHARO IOT
Open-Source: An Infinite Game
Lecture: MetaLinks
PharoTechTalk: Contributing to Pharo
Feedback Loops in Practice
Pharo6 - ESUG17

Recently uploaded (20)

PDF
cuic standard and advanced reporting.pdf
PPTX
A Presentation on Artificial Intelligence
PDF
Unlocking AI with Model Context Protocol (MCP)
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PPT
Teaching material agriculture food technology
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Encapsulation theory and applications.pdf
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Machine learning based COVID-19 study performance prediction
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Empathic Computing: Creating Shared Understanding
PDF
Electronic commerce courselecture one. Pdf
cuic standard and advanced reporting.pdf
A Presentation on Artificial Intelligence
Unlocking AI with Model Context Protocol (MCP)
“AI and Expert System Decision Support & Business Intelligence Systems”
MYSQL Presentation for SQL database connectivity
Reach Out and Touch Someone: Haptics and Empathic Computing
Mobile App Security Testing_ A Comprehensive Guide.pdf
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Dropbox Q2 2025 Financial Results & Investor Presentation
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Teaching material agriculture food technology
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Encapsulation theory and applications.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Machine learning based COVID-19 study performance prediction
Advanced methodologies resolving dimensionality complications for autism neur...
NewMind AI Weekly Chronicles - August'25 Week I
Empathic Computing: Creating Shared Understanding
Electronic commerce courselecture one. Pdf

RBuilder and ByteSurgeon

  • 1. Working with Bytecodes: IRBuilder and ByteSurgeon Marcus Denker
  • 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. Overview 1. Introduction to Squeak Bytecodes 2. Generating Bytecode with IRBuilder 3. Introduction to ByteSurgeon Marcus Denker
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new – Make a instance of IRBuilder Marcus Denker
  • 16. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; – Define arguments. Note: “self” is default argument Marcus Denker
  • 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. IRBuilder: Step by Step • Number>>asInteger iRMethod := IRBuilder new numRargs: 1; addTemps: #(self); "receiver" pushTemp: #self – push “self” on the stack Marcus Denker
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. IRBuilder: Lessons learned • IRBuilder: Easy bytecode generation – Jumps – Instance variable – Temporary variables – Sends • Next: Manipulating bytecode
  • 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. 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. 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. 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. 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. 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. 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. 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. Inside ByteSurgeon • Uses IRBuilder internally Decompile Compile Bytecode IR Bytecode • Transformation (Code inlining) done on IR Marcus Denker
  • 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. ByteSurgeon: Lessons learned • ByteSurgeon: Tool for editing bytecode – Simple example – Based on IRBuilder • Next: Advanced ByteSurgeon
  • 41. Advanced ByteSurgeon: • Goal: extend a send with after logging example self test. example self test. Logger logSendTo: self. Marcus Denker
  • 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. 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. 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. 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. 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. End • Short overview of Squeak bytecode • Introduction to bytecode generation with IRBuilder • Manipulating bytecode with ByteSurgeon • Questions?