<<

Author Helmut Rohregger, BSc.

Submission Institute for System Software

Thesis Supervisor o.Univ.-Prof. Dipl.-Ing. Dr.Dr.h.. Hanspeter Mössenböck

Assistant Thesis Supervisor Dipl.-Ing. Dr. Lukas Stadler

Month Year May 2016

Smalltalk Interpreter in

MASTER'S THESIS submitted in partial fulfillment of the requirements for the academic degree Diplom-Ingenieur in the Master's Program Computer Science

JOHANNES KEPLER UNIVERSITY LINZ Altenbergerstraße 69 4040 Linz, Austria www.jku.at DVR 0093696 I

Abstract

This thesis presents a Squeak bytecode interpreter based on the Truffle framework. Truffle is used to implement an abstract syntax tree (AST) interpreter for a , which can produce highly optimized code during execution.

The implemented Squeak interpreter can execute methods provided in a Squeak 4.3 image. These methods do not contain the source code that the methods were compiled from. The methods are represented by their corresponding bytecodes. The interpreter converts this bytecode into ASTs by decompiling and executes these ASTs on the Truffle runtime.

The result of this work is a command-line tool, which can load methods from a Squeak image and execute them. The interpreter can transform the complete Squeak bytecode into ASTs. However, only 75 percent of the functionality for execution has been covered.

For performance conclusions, the interpreter has been implemented to the point it can run certain benchmarks. The execution time of these benchmarks on the implemented Squeak interpreter has been compared with the execution time of the same benchmarks on standard Squeak VMs. The result shows that the peak performance of the Squeak interpreter is comparable to the standard Squeak VMs. For one particular benchmark, a speed-up of 5.66 was achieved. II

Kurzfassung

Diese Masterarbeit pr¨asentiert einen Squeak Bytecode Interpreter, welcher auf dem Truffle Framework basiert. Truffle wird verwendet, um einen AST (Abstract Syntax Tree) Inter- preter fur¨ eine Programmiersprache zu implementieren, welcher hoch-optimierten Code w¨ahrend der Ausfuhrung¨ produziert.

Der implementierte Squeak Interpreter kann Methoden ausfuhren,¨ welche in einem Squeak 4.3 Image enthalten sind. Diese Methoden enthalten keinen Source Code, von dem die Methoden compiliert wurden. Die Methoden werden durch ihre zugeh¨origen Bytecodes representiert. Der Interpreter konvertiert diese Bytecodes durch Decompilierung in ASTs und fuhrt¨ diese ASTs auf der Truffle Umgebung aus.

Das Ergebnis dieser Arbeit ist ein Kommandozeilen-Tool, welches Methoden aus einem Squeak Image laden und ausfuhren¨ kann. Der Interpreter kann den kompletten Squeak Bytecode in ASTs umwandeln, jedoch wurden nur ca 75 Prozent des Funktionsumfanges fur¨ die Ausfuhrung¨ dieser ASTs abgedeckt.

Um Leistungsaussagen treffen zu k¨onnen wurde der Interpreter so weit implementiert um bestimmte Benchmarks laufen lassen zu k¨onnen. Die Ausfuhrungszeit¨ dieser Benchmarks auf dem implementierten Squeak Interpreter wurde mit der Ausfuhrungszeit¨ der selben Benchmarks auf Standard Squeak VMs verglichen. Das Ergebnis zeigt, dass die Peak- Performance des Squeak Interpreters vergleichbar mit den Standard Squeak VMs ist. Fur¨ einen bestimmten Benchmark wurde sogar ein Speed-Up von 5,66 erreicht. Contents III

Contents

1 Introduction1 1.1 Goals ...... 1 1.2 Challenges ...... 2

2 Squeak 3 2.1 Components ...... 4 2.2 Characteristics ...... 5 2.2.1 Pseudo Variables ...... 5 2.2.2 Variable Declarations ...... 6 2.2.3 Assignments ...... 7 2.2.4 Literals ...... 7 2.2.5 Answer Expressions ...... 8 2.2.6 Messages ...... 8 2.2.7 Blocks...... 10 2.3 Classes ...... 11 2.4 Method Dictionary ...... 13 2.5 Compiled Method ...... 14 2.6 Special Objects Array ...... 16 2.7 Squeak Image ...... 17 2.7.1 Image Header ...... 18 2.7.2 Object Memory ...... 20 2.7.3 Object Format ...... 23 2.8 Primitives ...... 24 2.9 VMMaker ...... 26

3 Truffle 27 3.1 API ...... 28 3.1.1 Nodes ...... 29 3.1.2 Frames ...... 31 3.2 TruffleDSL...... 31

4 Architecture 34 4.1 Image Reader ...... 35 4.2 Object Heap ...... 36 Contents IV

4.3 Decompiler ...... 36

5 Implementation 37 5.1 The Squeak Image ...... 37 5.2 Squeak Objects ...... 39 5.2.1 Compiled Method ...... 42 5.2.2 Squeak Class ...... 43 5.3 Truffle Runtime Objects ...... 44 5.3.1 Methods ...... 44 5.3.2 Method Context ...... 45 5.3.3 Block Closure ...... 46 5.3.4 Squeak Arguments ...... 46 5.3.5 Squeak Context ...... 46 5.4 Squeak Decompiler ...... 47 5.4.1 Push Bytecodes ...... 49 5.4.2 Store Bytecodes ...... 49 5.4.3 Return Bytecodes ...... 49 5.4.4 Jump Bytecodes ...... 50 5.4.5 Send Bytecodes ...... 50 5.4.6 Closures ...... 51 5.5 Basic Nodes ...... 53 5.6 Control Structure Nodes ...... 55 5.7 Primitive Nodes ...... 56 5.8 Method Lookup Nodes ...... 58 5.9 Message Send Nodes ...... 59 5.9.1 SendNode ...... 60 5.9.2 MultiSendNode ...... 61 5.9.3 SuperSendNode ...... 61 5.10 Closure Nodes ...... 62

6 Case Study 64 6.1 Example Source Code ...... 64 6.2 Interpreter Execution ...... 65 6.2.1 Decompilation of ”testFactorial” ...... 67 6.2.2 Execution of ”testFactorial” ...... 68 6.2.3 Decompilation of ”factorial:” ...... 69 6.2.4 Execution of ”factorial:” ...... 70 6.2.5 Closure AST ...... 70

7 Evaluation 73 7.1 Benchmarks ...... 73 7.2 Completeness ...... 76 Contents V

8 Future Work 78

9 Related Work 80

10 Conclusions 82

Bibliography 87

A Eidesstattliche Erkl¨arung 91 Introduction 1

Chapter 1

Introduction

Building a virtual machine (VM) for a programming language is a complex task. It includes many man-hours to create a high-performance VM for such a language. Maybe this is one reason that some popular programming languages still do not have a high-performance implementation. What if there was a universal VM, which could execute program code written in any programming language? What if there was a high-performance VM, which enables a language implementer to add support for his language with little effort. These are examples of the goals of Truffle: ”One VM to rule them all” [33].

With Truffle, a language implementer has to write a self-rewriting abstract syntax tree (AST) interpreter in Java and the compiled code will run fast on the included Graal VM [11, 12]. There are many prototype language implementations that use the Truffle framework by now, which illustrates that the goal of Truffle is possible to achieve. The best example is the Truffle implementation of JavaScript, which runs benchmarks at speeds similar to V8 and SpiderMonkey.

This thesis presents an implementation of a Squeak interpreter that uses the Truffle framework. This Truffle/Squeak interpreter can execute Squeak methods obtained directly from a Squeak image. Truffle interpreters usually build an AST from source code by parsing the source code. For this thesis, the AST is generated by decompiling Squeak bytecode because the Squeak source code is not part of the Squeak image. The Squeak environment can execute Squeak code located in a Squeak image without the corresponding source code. This is the reason the implemented interpreter decompiles Squeak bytecode instead of parsing the corresponding source code.

1.1 Goals

The goal of this thesis was to implement an interpreter in Java that can execute Squeak/S- malltalk code from a Squeak image. The interpreter should be carried out by using the Introduction 2

Truffle framework; this implies writing an AST interpreter for Squeak/. The fin- ished system should be able to execute some benchmark functionality to provide execution time measurements for the interpreter.

An explicit non-goal was a complete implementation of the standard Squeak distribution. Furthermore, it was not the goal that the performance of the Truffle/Squeak interpreter for executing Squeak bytecode should be comparable with existing Squeak run-time engines.

The implemented interpreter can execute methods obtained directly from a Squeak 4.3 image. The information provided in this thesis is based on this Squeak image format and may be different for other Squeak image versions.

1.2 Challenges

The Squeak image structure is poorly documented. A good starting point was the so-called Blue Book [14]. It contains a description of the Smalltalk-80 system, which is a direct ancestor of the Squeak system. Most parts of Squeak are based on the Smalltalk-80 implementation and have been extended/modified.

The Truffle framework is still under development and changes frequently. During im- plementation, the interpreter has been adapted several times to these Truffle-changes. Furthermore, the author of this thesis is not a member of the Truffle team. That is why some of the Truffle concepts were misinterpreted by the author in the beginning of the Squeak interpreter implementation. Because of these misinterpretations a lot of work has been discarded and therefore has been implemented again in the proper way. Squeak 3

Chapter 2

Squeak

This chapter explains the Squeak dialect and the Squeak development environ- ment. It contains a short introduction to the syntax of Squeak. This outline should enable developers familiar with object oriented design/programming to understand the Squeak code used in this thesis. Further, this chapter describes the format and the properties of certain Squeak objects. These objects are important to understanding the explanations in Chapter 5. Furthermore, this chapter contains information about the structure of a Squeak image, which are essential to load and process objects loaded from it. Section 2.7 contains technical information about the Squeak Image, which is a revised version of the author’s previous work [29] with additional explanations and details. All explanations in the following chapters are based on a 32-bit image.

Squeak [19] is an implementation of the Smalltalk-80 [14] system, which includes a program- ming language description and the programming environment. Squeak is a metacircular VM, which means Squeak is written in itself. Smalltalk was developed in the 1970s by Alan Kay, Dan Ingalls, and Adele Goldberg, among others, and was published as Smalltalk-80. It was influenced by programming languages such as Simula, Lisp, and LOGO. Smalltalk affected many object-oriented programming languages such as Java [9], Objective-C, Python and Ruby1, amongst others. Squeak is a continuation of the Smalltalk-80 system, in which many of the original developers were involved. The idea was to create an open and portable Smalltalk development environment for educational purposes.

Squeak/Smalltalk2 is an untyped, object-oriented programming language and a develop- ment environment written in itself, which is completely open-source. The source code of the Squeak development environment is part of the Squeak system. The development environment and all other components can be extended/modified. Smalltalk is based on

1Python and Ruby support functional and imperative programming paradigms too. 2Squeak is a Smalltalk-80 dialect and many details in Squeak are equivalent to Smalltalk-80. In further consequence references to Smalltalk in this thesis refer to the Squeak implementation of Smalltalk-80. Squeak 4

the principle that everything is an object and every object is an instance of a class (for more details see Section 2.3). In Squeak, everything is dynamically typed and, there- fore, the declaration of variables, arguments and return values does not contain any type information.

2.1 Components

The Squeak system has four main components (Figure 2.1). The Virtual Machine (VM) is the heart of the Squeak system. The sources file contains the Squeak source code of the delivered Squeak system. It is not intended to modify the sources file. All changes done to the Squeak source code are logged in the changes file. The squeak image is a memory dump of a running Squeak system, which has been paused.

User Specific Virtual Machine

Squeak Changes Sources Image

Figure 2.1: Squeak components

The Squeak VM is an OS (Operating System) dependent application. Nevertheless, it is portable because it is open-source. It is available for many different operating systems. Most parts of the Squeak VM are written in SLANG3, which will be converted to C code [17]. SLANG is a subset of Smalltalk, which includes C semantics. OS dependent functionality of the Squeak VM is located in external modules, which contain ”hand-written” C code [20].

The sources file will usually not be modified. All changes are logged in the changes file. The Squeak VM does not need the sources and changes files to work properly. All information necessary to execute the Squeak code is provided via the Squeak image. The image contains all objects with their current state, the class hierarchy and the complete source code, compiled and stored via bytecode. Nevertheless, the Squeak environment can display the source code in a human-readable format even when the sources and changes files are not available. In order to achieve this, the VM decompiles the bytecode into sourcecode similar to the original program4.

3The Smalltalk SLANG will not be handled in this thesis. 4The decompiler uses generated variable names and can not display comments, because they are not part of the bytecode. Squeak 5

2.2 Characteristics

Smalltalk is an entirely object-oriented language, where everything is an object and objects are instances of their corresponding classes. Smalltalk does not have primitive data types like Java does. Values of type Boolean, Integer, Character, etc. are implemented via real objects and, therefore, have a corresponding class. Smalltalk is dynamically typed so that variables, method arguments, etc. do not contain type information.

The Smalltalk syntax [14, 27, 21] is defined by a small and simple set of rules:

• Six reserved keywords (pseudo-variables) • Variable declarations • Assignments • Literals • Answer expressions • Messages • Blocks

Smalltalk does not need a special construct for control structures, as these are implemented via message sends.

Smalltalk does not support multiple inheritance. Every class has exactly one superclass, except for the root class ProtoObject5, which has no superclass. It is possible to implement instance methods and class methods. Class methods are similar to static methods in Java. In Smalltalk instance methods and class methods can be overridden in subclasses. Smalltalk does not have visibility modifiers (private, protected, public in Java). All methods are public and can be accessed from every object by sending the corresponding message.

Expressions and statements are separated via the period character ”.”. It is used to write multiple statements in methods and blocks. Furthermore, it separates elements in Brace Arrays (for more details see Section 2.2.4). Text between double quotes ” is ignored by the compiler and is used to represent comments. Comments can span over multiple lines.

2.2.1 Pseudo Variables

The Squeak syntax contains the following reserved keywords/pseudo-variables: nil, false, true, self, super, thisContext, and Smalltalk. The variables nil, false and true represent singleton objects of the corresponding classes UndefinedObject, False and True.

5In Smalltalk-80, the root class, is Object. In Squeak, the root class is ProtoObject, and the class Object is a subclass of ProtoObject. Squeak 6

The pseudo-variables self, super and thisContext are set by the VM, which vary from the current program state. The keyword Smalltalk is a singleton object of the class SmalltalkImage. It contains the current Squeak image and the runtime environment. Furthermore, it has one instance variable #globals, which is a reference to the system dictionary of all global variables and class names. It is not possible to assign values to these pseudo-variables.

The pseudo-variable nil is similar to the reserved keyword null in Java. It represents an undefined state for objects. false and true represent Boolean values in Squeak. These two singleton objects are results of the evaluation of conditions in Squeak. Furthermore the classes of these two keywords contain methods which implement if-else constructs in Squeak (for more details see Section 2.2.6).

The pseudo-variable self points to the receiver of the current message. It is similar to this in Java. The Squeak VM determines the class of the receiver and does a method lookup in this class when a message is being sent to self. The pseudo-variable super points to the same object like self does. The difference between self is that for evaluating super the VM starts the method lookup in the superclass of the class of the receiver, when sending a message. The pseudo-variable thisContext contains the active context, which is essential for exception handling6 and for implementing development tools.

2.2.2 Variable Declarations

Local Variables are called temporaries in Squeak. They get declared at the beginning of a method. These locals do not contain type information. They are listed and separated by a blank between two pipes (see line 1 in Listing 2.2). In Smalltalk, temporaries usually get declared in lower camelCase notation7. An uninitialized local variable always returns nil.

Smalltalk has Instance Variables which are defined in a Squeak class. These variables are private to the instance, i.e. they cannot be accessed from the outside the class. Moreover, two instances of the same class cannot access each other’s instance variables. Instance variables are similar to protected variables in Java, because subclasses inherit them from their superclasses and can access them directly.

Every class has a corresponding Metaclass (for more details see Section 2.3). These metaclasses have their set of instance variables, called Class Instance Variables. They are similar to usual instance variables and are only accessible from its metaclass and the

6Exception handling in Squeak will not be discussed further in this thesis. 7Camel case is a naming convention where capitalized words get united to a single identifier. An example for a lower camel case identifier is ”lowerCamelCase”. In contrast, an upper camel case looks like ”UpperCamelCase”. Squeak 7

subclasses of this metaclass.

2.2.3 Assignments

Smalltalk has two assignment operators: ”_” and ”:=”. The evaluation result of the expression on the right-hand-side will be assigned to the identifier on the left-hand-side, i.e., the object on the left-hand-side points to the outcome of this evaluated expression. Assignments are expressions too and return the result of the evaluated right-hand-side. Therefore assignments can be cascaded.

2.2.4 Literals

Listing 2.1 shows the syntax of literals in Squeak. The listed Integer values are instances of the class SmallInteger. Float values are double precision floating point values and instances of the class Float. Characters are instances of the class Character. The classes ByteString (8-bit strings) and ByteSymbol (8-bit symbols) are subclasses of String. The class String also contains subclasses for 32-bit strings and symbols (WideString and WideSymbol). Strings with the same content are different instances. On the contrary, symbols with the same content are the same instance. That is why an equality comparison for symbols can be done very efficiently. In Squeak, messages are implemented via symbols.

1 42. "decimal integer value." 2 2r101010. "binary integer value." 3 8r52. "octal integer value." 4 16r2A. "hex integer value." 5 42e2. "integer with exponent." 6 3.14. "float value." 7 3.14e2. "float value with exponent." 8 $a. "character’a’ ." 9 $1. "character ’1’." 10 $$. "character’$’ ." 11 ’text’. "a string." 12 #text. "a symbol." 13 #(123) . "constant array with3 elements: 1, 2,3 ." 14 #(1+2) . "constant array with3 elements: 1,#+,2 ."

Listing 2.1: Literals (Smalltalk)

Line 13 and 14 in Listing 2.1 show one possibility how Constant Arrays get instantiated in Smalltalk. This construct creates an instance of the class Array. Constant arrays are created during compile time, thus do not evaluate expressions (line 14 in Listing 2.1). In contrast, Brace Arrays are computed during runtime. A brace array gets instantiated by specifying expressions between two curly brackets ”{” and ”}”. Elements of a brace Squeak 8

array are separated by a period character (see line 5 in Listing 2.2). The Listing 2.2 shows different possibilities to instantiate arrays in Squeak. Arrays and other collections are one-based, i.e. the first element gets accessed via index one.

1 | arraya new| 2 array := #(1+2) . "a= nil . array=#(1#+ 2)" 3 a := array at:1 . "a=1 . array=#(1#+ 2)" 4 a := array at:2 put:’x’ . "a=’x’ . array=#(1’x’ 2)" 5 array := {1+2 . 2}. "array=#(3 2)" 6 array at:1 put: a . "array=#(’x’ 2)" 7 new := Array new:2 . "new=#(nil nil)" 8 new at:1 put: array . "new=#(#(’x’ 2) nil)"

Listing 2.2: Different possibilities to create an array (Smalltalk)

2.2.5 Answer Expressions

Return values in Smalltalk are defined via the caret/hat character ”ˆ”. This character terminates the execution of the method, evaluates the corresponding expression and answers the result to the sender of the message. Every method in Smalltalk returns an object. When a method does not return a value, the compiler adds a ^self bytecode to the end of the method, but not to the source code itself. Answer expressions in nested blocks (for more details see Section 2.2.7) terminate the enclosing method as well. This is also true when a block object with an answer expression is getting passed to another method, and this method executes this block.

2.2.6 Messages

In Smalltalk, an operation is performed by sending a message8 to an object. A message send always has a receiver, a message (also known as message selector) and optional some arguments. Smalltalk has three kinds of message sends. Unary messages get sent to an object without an argument. Binary messages consists exactly of the message-receiver, the operator and one argument (see Figure 2.2). A keyword message consists of one or more keywords. Every keyword ends with a colon character ”:”, followed by an argument (see Figure 2.3).

The binary message send in Figure 2.2 shows the message #+, sent to the receiver 1, with the argument 2. To execute the corresponding method, the VM performs a method lookup, executes the method and returns the result. This is only true if the class or any superclass of object 1 contains the method #+. If not, the VM raises a #DoesNotUnderstand: exception.

8Messages are instances of the class Symbol (a subclass of String), which are labelled with # at the beginning (Section 2.2.4). Squeak 9

Message Message a at: 1 put: 2 1 + 2 Argument Arguments Receiver Receiver

Figure 2.2: A binary message Figure 2.3: A keyword message

The keyword message send in Figure 2.3 shows the message #at:put:, sent to the receiver a, with the arguments 1 and 2.

Smalltalk does not have a special construct to implement if-else statements. Smalltalk accomplishes this by sending messages to Boolean objects. The classes True and False contain the messages #ifTrue: and #ifFalse:9, amongst others. Listing 2.3 shows how to implement if statements in Smalltalk. Line 4 and 5 display the implementation of if-else statements, which are processed via keyword-messages. Listing 2.3 does not contain examples with multiple statements in a single branch, which can be implemented by using the block construct (see Section 2.2.7). A block can contain multiple statements, which are separated with a period character.

1 |a| 2 a := (2>1) ifTrue:1 . "a=1" 3 a := (2>1) ifFalse:2 . "a= nil" 4 a := (2>1) ifTrue:1 ifFalse:2 . "a=1" 5 a := (1>2) ifTrue:1 ifFalse:2 . "a=2"

Listing 2.3: Messages for implementing if constructs (Smalltalk)

In Smalltalk, messages can be put in a cascade by separating them with the semicolon character ”;”. These separated messages get sent in sequence from left to right to the same receiver. The result of the cascaded expression is the outcome of the last message. Listing 2.4 shows an example of cascading three messages to the temporary variable a. The last message #yourself10 returns self and, therefore, lets the temporary b point to a.

1 |ab| 2 a := #(135) . "creates array with3 elements" 3 b := a at:1 put:5 ; "a=#(53 5)" 4 at:3 put:1 ; "a=#(53 1)" 5 yourself. "b=#(53 1)"

Listing 2.4: Message cascade example (Smalltalk)

9Current Squeak compiler inline these messages for performance reasons, so that no actual messages are sent. 10The VM executes Object>>#yourself, which consists of one statement: ^self. Squeak 10

2.2.7 Blocks

Blocks are important to building control structures in Smalltalk. In Smalltalk-80, blocks were instances of BlockContext, which had problems with recursive calls. In 2008, the implementation of blocks was changed [24]. Since that time, blocks are instances of the class BlockClosure and are real closure objects. These closure objects are now the standard implementation for blocks in all modern Squeak systems11.

A closure allows to defining a function inside another function and assign it to an identifier. The closure captures the environment in which it was created and therefore has access to the scope of the enclosed function. A closure object can be passed to and executed in another function, which indirectly accesses the scope of the enclosing closure context via the closure object. The concept of closures, also known as lambda-expressions and anonymous functions is a functional construct, which was developed for languages, back in the 1970s.

Smalltalk blocks get instantiated by using square brackets, ”[” and ”]”. The statements in blocks are separated by a period character ”.”. Listing 2.5 illustrates examples for block usage in Squeak. Line 2 shows how to assign a BlockClosure object to a local variable. The statements in a block get not executed until a #value message gets sent to the BlockClosure object. Line 3 shows the evaluation of the BlockClosure object declared in line 2, which returns the last expression of the block and assigns it to the local variable. A BlockClosure contains different ”value” implementations for passing different number of arguments to the block. Blocks can have their local variables, which are only visible in the scope of the block (the identifier ”c” in line 5 in Listing 2.5)12.

1 |i| 2 i :=[ 1 . 2 . 3]. "i=a BlockClosure object" 3 i := i value. "i=3" 4 i :=[ :a | i + a] value:2 . "i=5" 5 i :=[ :a :b ||c|c := i − a − b. c] 6 value:2 value:3 . "i=0"

Listing 2.5: Block examples (Smalltalk)

Listing 2.6 shows examples for implementing loops in Smalltalk. Line 3 and 4 are similar to while loops in Java. Line 5 illustrates a #timesRepeat: message, sent to an Integer object, which executes the block object five times in sequence and returns the result of the last message send. The examples in line 6 and 7 show how to implement for loops in Smalltalk. Line 9 shows a foreach loop, which executes the block for every element of the array. There are many other messages for implementing loops in Smalltalk and of course it is possible

11In further consequence, when talking about blocks in this thesis, the new BlockClosure implementation is intended. 12For a detailed description of blocks and their usage, check the syntax chapters in [14] and [27]. Squeak 11

to define new types of loops by implementing methods in the corresponding classes.

1 |i| 2 i := 0. 3 [ i <5 ] whileTrue: [ i := i +1 ]. "i=5 afterwards" 4 [ i =0 ] whileFalse: [ i := i − 1 ]. "i=0 afterwards" 5 5 timesRepeat: [ i := i +1 ]. "i=5 afterwards" 6 1 to:2 do: [ :a | i := i + a ]. "i=8 afterwards" 7 2 to:1 by: −1 do: 8 [ :a | i := i − a ]. "i=5 afterwards" 9 #(123) do: [ :a | i := i + a ]. "i= 11 afterwards"

Listing 2.6: Examples for implementing loops (Smalltalk)

2.3 Classes

Everything in Smalltalk is an object. An object is an instance of a class. A class is an object too and therefore must be an instance of another class, a so-called Metaclass. Every class has exactly one superclass, except for the root class, which has no superclass; this is also true for metaclasses. New classes get created by sending a message to a class. The class Class contains a method for every object type (see Section 2.7.3) for creating subclasses. By creating a subclass, Squeak creates a corresponding metaclass with the same name (see Figure 2.4). Furthermore, the newly created subclass is an instance of the created metaclass.

Behavior ProtoObject Object Person Student

ProtoObject Class Object class Person class Student class class ClassDescription

Metaclass

Inheritance Realization A B A B B is a subclass of A B Is an instance of A

Figure 2.4: Class diagram of class Student and Person: an example of two related classes and their corresponding metaclasses and their integration in the Squeak class hierarchy.

Figure 2.4 and Figure 2.5 illustrate how classes and their corresponding metaclasses are connected. For the sake of clarity, the metaclasses for Class, Metaclass and their superclasses are displayed in Figure 2.5. Every metaclass in Smalltalk is an instance of Squeak 12

Metaclass. Metaclass is an instance of Metaclass class, which is again an instance of the class Metaclass.

Class and Metaclass inherit the instance variables from their superclasses Behavior and ClassDescription, stated in Figure 2.5. The inherited instance variables are:

• The superclass • The method dictionary • The class format • A collection of instance variables • An organization object

In Squeak, all methods of a certain class are organized in categories. These categories are stored via an instance of the class ClassOrganizer in the variable organization. The class Metaclass extends its instance variable list with the variable thisClass. This is a reference to its only instance, the class itself. The class Class adds the instance variables subclasses and name, among others to its instance variables list.

Behavior ClassDescription Class superclass subclasses instanceVariables methodDict name organization format ... Metaclass thisClass

Metaclass class Inheritance A B ClassDescription Behavior class Class class B is a subclass of A class Realization A B B Is an instance of A

Figure 2.5: Class diagram of Class and Metaclass: for completeness the missing metaclasses for Figure 2.4. Furthermore the figure displays the instance variables of the classes.

The method dictionary (see Section 2.4) of a class contains all its methods. The instance methods are stored in the method dictionary of the class, and the class methods are stored in the method dictionary of the corresponding metaclass. When a message is sent to an instance of a class, the VM has to perform the method lookup in the hierarchy chain of the class. When a message is sent to a class, the VM looks up the hierarchy chain of its metaclass. When the corresponding method was not found, a #doesNotUnderstand: exception is raised. Squeak 13

2.4 Method Dictionary

The class MethodDictionary is a collection of CompiledMethod objects (see Section 2.5), which uses the identity hash values13 of their message selectors to access the corresponding CompiledMethod instances in its method array. In Smalltalk, method names are called message selectors, which are instances of the class ByteSymbol.

A method dictionary has instance variables and indexable fields (format 3 in Table 2.2 on page 22). Figure 2.6 illustrates the basic structure of a method dictionary. The instance variables array and tally are inherited from the class HashedCollection. The variable part of the method dictionary contains the message selectors used as keys for the dictionary. The values of the dictionary are the corresponding CompiledMethod objects, stored in the array field. The index values for the message selectors and for the matching CompiledMethod objects are the same in their corresponding collections.

Array Method Dictionary Base Header Compiled Method Base Header method1 method2 tally Compiled Method Fixed array method3 #method1 Compiled Method #method2 Indexable #method3

Symbol Symbol Symbol

Figure 2.6: A method dictionary and its content

To ensure the hashing of the methods work well, the occupancy of the array and the variable part of the method dictionary is below 75 percent. That is why the method dictionary contains empty slots, tagged with nil. The number of methods in the dictionary is stored in the field tally, which is a SmallInteger object. SmallInteger objects are immediate objects, and their value is encoded in its address (Section 2.7.2).

13The identity hash value of an object is encoded in its base header (see Figure 2.13 on page 21). Squeak 14

2.5 Compiled Method

In Smalltalk, methods are instances of the class CompiledMethod. Usually, a compiled method is created by writing code in the Squeak Class Browser. After saving the code, the Squeak compiler creates a compiled method object and registers it in the method dictionary (Section 2.4) of the corresponding class.

Compiled method objects have a particular format. They contain word-size pointers and sequences of unsigned byte values. The class CompiledMethod is a subclass of ByteArray to avoid duplicating methods, located in the class ByteArray. Figure 2.7 illustrates the content of a compiled method object. The first word always is the Compiled Method Header, an additional header to the usual object base header. Compiled methods do not have a class and a size header. Their instances are very compact; that is why the class CompiledMethod is in the list of the Compact Classes (Section 2.7.2).

Compiled Method

Base Header Object Method Header Literal 1 Object Literal 2

Symbol or Additional Method State Symbol Literal N-1 Literal N Base Header key

... value Class

Figure 2.7: Content of a compiled method object

The Compiled Method Header (Figure 2.8) is an instance of the class SmallInteger and represents information necessary for loading the method content. The 30-bit value is encoded in its memory address (Section 2.7.2). The bit field primitive occupies 10 bits in the method header14, which is split into two parts, because of backward compatibility. A value greater zero indicates the method is a primitive method (Section 2.8). The number of literals stored in the method object is encoded in numLiterals. The frameSize flag indicates whether the method needs a large frame size in its method context when executing the method. A small frame contains 16 words, a large frame contains 56 words15. The

14A bit field of size 10 provides 210 - 1 (= 1023) different primitives, but the comment in Interpreter class>>#initializePrimitiveTable states, that the limit of primitives is 2047, which would be 211 - 1. Newer images likely use the 31st bit of the header value, which is obviously not used in Squeak 4.3, according to CompiledMethod>>#primitive. 15MethodContext is a compact class (see Section 2.7.2), which has six instance variables. The maximum Squeak 15

fields numTemps and numArgs specify the amount of temporaries (local variables) and method arguments. The flag bit is ignored by the VM16.

flag primitive frameSize

numArgs numTemps numLiterals primtive

1 bit 1 bit 4 bit 6 bit 1 bit 8 bit 9 bit 30 bit

Figure 2.8: Content of compiled method header

Every compiled method has a variable amount of literals. These literals are references to objects used in the method, among the standard literals, every method contains. The last literal always points to a key/value object, where the instance variable key refers to the class name and the instance variable value to the class object the method belongs to. Furthermore, this class object contains a reference to its method dictionary (Section 2.4), and again this dictionary contains a reference to the compiled method object.

The message selector (the method name) can be accessed via the second-last literal. If the method has some additional properties or contains pragmas17, this literal points to an AdditionalMethodState object, which has two instance variables: method, the compiled method object and selector, the methods name, an instance of ByteSymbol. If the method contains no pragmas, the second-last literal of the compiled method object points to the method selector, which is an instance of ByteSymbol.

The rest of the data is stored via bytes, including the bytecode and a method trailer. The bytecodes are organized in groups. This groups are represented by the following method categories in the Squeak Interpreter class (see Section 2.9): ”return byte- codes”, ”send bytecodes”, ”stack bytecodes” and ”jump bytecodes”. The Squeak bytecode specification is an extended version of the bytecode specification from Smalltalk-80 [14], and contains new closure (Section 2.2.7) bytecodes [25], amongst others. The bytecode names and ranges implemented in Squeak are listed in the method Interpreter class>> #initializeBytecodeTable, located in the VMMaker package (see Section 2.9).

The method trailer contains additional information regarding the CompiledMethod object. In old Squeak implementations, the method trailer was used to store a pointer to the source

size of a context object with a large frame is 63. 6 instance variables + 1 base header + 56 for the indexable words. 16The comments in CompiledMethod do not contain more information about this flag. It is also identified as ”user-level flag bit”, but never used. 17In Squeak methods can contain additional meta data, which are implemented via pragmas. Primitive methods are realized via pragmas (Section 2.8). Squeak 16

code, located in the changes or source file (see Section 2.1). The trailer had a four byte fixed-size. Now, there are different types of method trailers. The type is encoded in the last byte of the CompiledMethod object18. Figure 2.9 shows how the bits are interpreted in this last byte. The number of bytes allocated by the trailer is defined in the corresponding encode and decode methods in the class CompiledMethodTrailer. Some of these methods use the data bit field (Figure 2.9) to encode additional size information about the method trailer.

kind data

6 bit 2 bit

Figure 2.9: Last byte of a compiled method object

2.6 Special Objects Array

The Special Objects Array contains references to all important objects in the Squeak image. It is an instance of the class Array and contains 56 indexable fields in Squeak 4.3. The class method ObjectMemory class>>#initializeSpecialObjectIndices contains the names for the indexable fields19. The first three fields in the Special Objects Array refer to the singleton objects, nil, false and true (see Section 2.2.1). Furthermore it contains references to all important classes used by the VM.

At index nine (one-based), the Special Objects Array contains the reference to the singleton object Smalltalk, an instance of the class SmalltalkImage. This object contains the instance variable globals, which is a reference to the system dictionary. This dictionary con- tains all global variables and class names and is an instance of the class SystemDictionary (Figure 2.10).

SystemDictionary is a subclass of HashedDictionary and, therefore, inherits the tally and array fields. The instance variable tally contains the number of elements, stored in the dictionary array. The methods of the class HashedDictionary make sure, that the occupancy of the array is below 75 percent. Therefore, the array contains empty slots, tagged with a reference to nil. The indexable fields of the array point to key/value objects, usually instances of Association or ReadOnlyVariableBinding, where key points to the

18The different kinds are explained in the class comment of CompiledMethodTrailer. The class method CompiledMethodTrailer class>>#trailerKinds shows the definitions of all trailer kinds. 19The method #initializeSpecialObjectIndices gets translated into C code. That is why the indices are zero-based. In Squeak the access is one-based. To display the content of the Special Objects Array, explore the message send ”Smalltalk specialObjectsArray” in a workspace window in the Squeak environment. Squeak 17

name of the object and value refers to the object itself.

System Dictionary Array Symbol

Base Header Base Header Base Header tally nil key array value keyValue Object cachedClassNames keyValue cachedNonClassNames Symbol Base Header key Ordered Collection value Object Ordered Collection

Figure 2.10: The system dictionary and its content

The instance variables cachedClassNames and cachedNonClassName are instances of the class OrderedCollection, which contain the references to ByteSymbol instances in ascending order. Every time a global variable or a class gets created, the two caches get flushed. The caches get refilled when an object tries to access the class or non-class collection.

2.7 Squeak Image

Squeak has no separation of program code and program state. Everything the VM needs, is stored in one single image file. The squeak image is a memory dump of all objects used in the Squeak environment. It also contains the class hierarchy and their corresponding methods. Methods implemented in the Squeak Browser, get compiled immediately after saving. The corresponding bytecode is stored in CompiledMethod (see Section 2.5) objects in the image. The code written by the developers is stored in the changes files located on the file system (for more details see Section 2.1).

The byte order of the Squeak image content is stored in big endian or little endian format, depending on the host system the image was created. Most images are 32-bit images. However, it is possible to create 64-bit images as well20. The platform dependent code of a Squeak system is located in the VM or via plugins on the file system, which is accessible via Squeak primitives (for more details see Section 2.8).

2032-bit Images have a 4-gigabyte memory limit, which is usually sufficient for most applications. However, a 64-bit image would enable the ability to define more immediate objects like it is done with SmallInteger objects. Squeak 18

2.7.1 Image Header

The Image Header 21 contains the basic information about the Squeak image used by the VM for reading in the object memory and some additional VM-specific information. Usually, the header starts at the beginning of the image. However, it is possible to put an ”exec” command before the header on Unix systems, filled up to 512 bytes.

To determine the byte ordering of the image content, the VM uses the first word of the image header. This is the so-called Magic Number, the version of the Squeak image. Furthermore, the VM can resolve the used memory address sizes (32-bit or 64-bit) (Table 2.1). To ensure that this mechanism works, version numbers need to be invalid in reversed byte order.

Figure 2.11 shows the content of a Squeak image header. Every image header contains these fields. Additional data can be appended to the end of the header.

Image Version Header Size Image Size Memory Base Address Special Objects Array Hash Header Size Screen Size Flags Extra Memory

32 bit Word Size

Figure 2.11: Squeak image header

Image Version Tells the VM the version of the Squeak image. Table 2.1 shows the current valid version numbers and their interpretation. The Closures column indicates if the image supports real block closure objects (Section 2.2.7). The Cog column tells the VM if the image is a Cog VM image. The Cog VM [26] is a Squeak VM implementation, that provides a JIT (Just in Time) compiler for Squeak.

Header Size Indicates the size of the image header in bytes, including space for additional data.

21Most of the information about the image header described in this chapter are based on the implementation of the Squeak method Interpreter>>#writeImageFileIO:, which is part of the VMMaker package (for more details see Section 2.9). Squeak 19

Version Binary Addresses Closures Cog

6502 1 1001 0110 0110 32-bit no no 6504 1 1001 0110 1000 32-bit yes no 6505 1 1001 0110 1001 32-bit yes yes 68000 1 0000 1001 1010 0000 64-bit no no 68002 1 0000 1001 1010 0010 64-bit yes no 68003 1 0000 1001 1010 0011 64-bit yes yes

Table 2.1: Magic number interpretation

Furthermore, it tells the VM the starting position of the object memory (Section 2.7.2) in the Squeak image.

Image Size Contains the number of bytes occupied by the object memory.

Memory Base Address Specifies the base address of the object heap in the image, when the image was saved. The VM has to adjust all references after reading in an image if the new memory base address, set by the VM is different to the specified address in the image header.

Special Objects Array Refers to the Special Objects array in the object memory. It contains the information that the VM needs to know about all Squeak objects for setting up the environment (for more details see Section 2.6). It contains, amongst others, references to nil, true, false and the class SmallInteger22.

Hash The VM stores the last used identity hash value for the last created object. Every object in Smalltalk has a hash value, encoded in its base header (Figure 2.13).

Screen Size Contains the window size of the Squeak environment, when it was closed. The low 16 bits indicate the width and the high 16 bits indicate the height of the window.

Flags In the past, this was used to determine if the Squeak window was set to full screen, by setting the least significant bit to 1. In the new image format for Cog VMs, it contains some additional VM flags.

Extra Memory

22The content of the Special Objects Array can be displayed by exploring the content of ”Smalltalk specialObjectsArray” in a workspace window in Squeak. Squeak 20

Decreases the amount of bytes reserved for the object heap, occupied by the VM itself.

The rest is optional data used by the VM, filled with zeros if not used.

2.7.2 Object Memory

The Object Memory23 represents a memory dump of all objects in the Squeak environment, saved to the file system. The VM collects garbage before saving, to reduce the size of the image and to provide an object memory with referenced objects only. The Squeak VM uses direct pointers for referencing objects. In contrast, the Smalltalk-80 VM, described in [14], stores object references via object tables.

All objects in the Squeak image are aligned to the word size, which is four byte for 32-bit images. Therefore, the two least significant bits are not used for addressing an object. Consequently, this has been utilized to encode an Integer value directly in its memory address. These so-called Immediate Objects do not have any header and any instance members. The VM uses the least significant bit to distinguish between a SmallInteger value and a normal object. That is why a SmallInteger value is encoded in the remaining 31 bits of an address and has a range of -1.073.741.824 to 1.073.741.823.

Squeak uses a variable-length object header format. Figure 2.12 illustrates a typical Squeak object with all possible headers. Every object has a Base Header, which contains the basic information of an object. Dependent on the object properties, the VM creates an additional Class Header and an additional Size Header.

2 bit Header Type

Size Header T Class Header T Pointer to Object Base Header T

Size Data

32 bit Word Size

Figure 2.12: Squeak object content: Every Squeak object has a base header. The size header and the class header are optional.

23Most information about the object memory was gathered from the ObjectMemory class, which is part of the VMMaker package (for more details see Section 2.9). Squeak 21

Regardless of the number of headers, the memory address of an object gets determined by the location of its base header. To enable a sequentially reading of an image, the Header Type is repeated in the least two significant bits of all three headers and, therefore, the VM can calculate the position of the base header by analysing these two bits.

The essential information of a Squeak object is encoded in its base header, which is illustrated in Figure 2.13.

GC Hash Value ccIndex Format Size Type

3 bit 12 bit 5 bit 4 bit 6 bit 2 bit

Figure 2.13: Base header structure

GC This field is reserved for the garbage collector state machine (mark, old, dirty).

Hash Value Contains the identity hash value for the object and is used for storing the object in a hashed collection.

Compact Class Index (ccIndex) Squeak organizes a collection of classes with instance sizes less than 255 bytes. The references to these compact classes are stored in the Compact Classes Array in the Special Objects Array24. The VM can identify the class of the current object by accessing this array with the value, encoded in the ”ccIndex” field (one-based). If the value is zero the VM has to resolve the address provided via an additional class header.

Format Squeak uses different object types to store data. The format field specifies the format of the given object. Table 2.2 illustrates the possible content of this bit field and describes the corresponding object format25. Section 2.7.3 contains an explicit object format description.

Size The size of an object is declared in words and always includes the size of its base header and, therefore, has a minimum value of one. The maximum value in a 32-bit

24To display the compact classes in the Squeak environment, type in ”Smalltalk specialObjectsArray at: 29” in a workspace window and ”explore it”. 25The information provided in the table can be found in the comments of the method ObjectMemory>> #formatOfHeader:, which is included in the VMMaker package. Squeak 22

Value Binary Description

0 0000 no fields 1 0001 fixed fields only (all containing pointers) 2 0010 indexable fields only (all containing pointers) 3 0011 both fixed and indexable fields (all containing pointers) 4 0100 both fixed and indexable weak fields (all containing pointers) 5 0101 unused 6 0110 indexable word fields only (no pointers) 7 0111 indexable long (64-bit) fields (only in 64-bit images) 8-11 1000-1011 indexable byte fields only (no pointers) (low 2 bits are low 2 bits of size) 12-15 1100-1111 compiled methods: # of literal oops specified in method header, followed by indexable bytes (same interpretation of low 2 bits as above)

Table 2.2: Base header format field

image, encoded in the size field is 254 bytes. If an object allocates more memory, the value of the size field is zero and the size is encoded in an additional size header.

Type Table 2.3 illustrates the three possible object header types, declared in the low two bits of a header and the content used for tagging a free memory chunk (content 10). The VM organizes these free memory chunks for allocating new objects. The header contains a 30-bit long size field, which indicates the size of the free memory available below itself.

Content Words Headers

00 3 Size, Class and Base Header 01 2 Class and Base Header 10 1 Not an Object/Free Memory Chunk 11 1 Base Header

Table 2.3: Object header types

Instances of classes not listed in the compact classes array, need an additional class header. This class header consists of the two bit header type and a 30-bit value, which contains the address of the class. Listing 2.7 shows the method, the VM executes to identify the class of an object. Line 4 returns the SmallInteger class reference via the special objects array. Line 9 shows, how the address from the class header gets determined by masking away the header type. This mechanism works, because of the word alignment of all objects and, therefore, it suffices to fill the two low bits with zeros. Line 11 illustrates, how the address of a compact class gets resolved via the ”ccIndex” field.

Objects with an object size bigger than 254 bytes need an additional size header. This Squeak 23

1 fetchClassOf: oop 2 | ccIndex| 3 (self isIntegerObject: oop) 4 ifTrue: [^ self splObj: ClassInteger]. 5 6 ccIndex := (self baseHeader: oop) >>12 bitAnd:31 . 7 ccIndex =0 8 ifTrue: 9 [^ (self classHeader: oop) bitAnd: self allButTypeMask ] 10 ifFalse: [ "look up compact class" 11 ^ self fetchPointer: ccIndex − 1 ofObject:( self fetchPointer : CompactClasses ofObject: specialObjectsOop) ]

Listing 2.7: ”fetchClassOf” returns a reference to the class of an object. The listed source code has been copied from ObjectMemory>> #fetchClassOf: from the VMMaker package (Smalltalk)

implicates the usage of a class header. The size header consists of the two bit header type and a 30-bit size field used to encode the object size in words.

2.7.3 Object Format

Every object is an instance of a class. The class defines the format of an object. The format field in the base header of an object gets filled by its corresponding class, when instantiated. The content of this format field is illustrated in Table 2.2. In Squeak, objects are classified by its object type, shown in Table 2.4.

Type is variable has pointer has words is weak

normal false true true false bytes true false false false words true false true false variable true true true false weak true true true true

Table 2.4: Squeak object type classification

Normal objects have fixed fields (instance variables). These fields are immutable pointers, which allocate one word each. The fields get accessed via variable names. This category represents format field values 0 and 1. Objects with format 0 have no fields, like nil, true and false.

Objects of type byte contain a variable sequence of bytes, which represent the format field values 8 - 11. Because of the word size alignment of objects, the VM has to store the used number of bytes in the low two bits of the format field. Squeak 24

The word object type can allocate a variable amount of mutable data words. The data gets accessed via an index. This type represents format field values 6 and 7.

Variable objects have fields, which are represented by pointers. Their format field contains the value 2 or 3. Objects with format 3 contain both, fixed and indexable fields. The fixed fields get accessed via their names and the indexable fields get accessed via an index. Objects with format 2 contain indexable pointer fields, like instances of the class Array, which are accessed via an index.

Weak objects, with format field value 4, can have fixed fields (named instance variables) and indexable ”weak” fields. Weak fields get removed from the garbage collector when no other non-weak object refers to it. They have a similar structure like objects with the format field 3.

The format of CompiledMethod objects is different. It is the only class in Squeak, which contains indexable pointers and indexable byte fields (for more details see Section 2.5). The format field for compiled method objects contains additional size information in its low two bits, because of the word size alignment of objects.

2.8 Primitives

Squeak images are used on different operating systems. Anyway, it is possible in Squeak programs to communicate with I/O devices and other OS resources. The platform dependent code is implemented in the VM itself. This OS code is usually written in Smalltalk SLANG26 and converted into C code [20] or gets ”hand-written” in C by developers. This VM code gets executed via primitive methods. Primitives in Squeak exist for several reasons. Some primitives contain basic functionality and cannot be performed via Smalltalk code. Some are optional and exist via VM code and Smalltalk code, mostly because the VM code executes faster. Squeak knows two types of primitives: numbered primitives (Listing 2.8) and named primitives (Listing 2.9).

Listing 2.8 shows the implementation of the #+ method for SmallIntegers. It executes the first primitive in the VM, which is called #primitiveAdd27 and passes the parameter aNumber to the code located in the VM28. The additional Smalltalk code below the primitive-tag will only get executed, when the primitive fails. There are many different reasons, why a primitive can fail, e.g. the type of the parameter is not supported in the primitive. If the primitive is essential because there is no fall-back Squeak implementation, the message #primitiveFailed is sent, and an exception gets raised (Listing 2.9).

26SLANG is a subset of Smalltalk with C semantics, which can be used to convert to real C code. 27The SLANG implementation can be found in InterpreterPrimitives>>#primitiveAdd. 28The numbers and corresponding methods in the VM are listed in Interpreter class>>#initialize Squeak 25

+ aNumber "Primitive. Add the receiver to the argument and answer with the result if it isa SmallInteger . Fail if the argument or the result is nota SmallInteger"

^ super + aNumber

Listing 2.8: A numbered primitive: SmallInteger>>#+ (Smalltalk)

The method tags, declared via angle brackets are implemented via Pragma objects in Squeak29. The developer can tell the VM which primitive it should load, by using the #primitive: keyword and a number for the primitive index. When a primitive method gets compiled, the compiler creates an instance of the class AdditionalMethodState and stores it in the second-last literal of the CompiledMethod object (see Section 2.5). Furthermore, the number of the primitive is encoded in the compiled method header. The pragma object consists of a keyword and arguments, which represents the additional information for the method.

Listing 2.9 shows the implementation of an essential primitive, which will raise a #primi- tiveFailed exception, when not found. The method answers the size of a file, specified by a file id, via the named primitive ”primitiveFileSize” in module ”FilePlugin”. The pragma object for Listing 2.9 contains the keyword #primitive:module: and the arguments ”primitiveFileSize” and ”FilePlugin”.

primSize: id "Answer the size of this file."

self primitiveFailed

Listing 2.9: A named primitive: StandardFileStream>>#primSize: (Small- talk)

Named primitives get loaded via the numbered primitive 117. The corresponding SLANG code can be found in Interpreter>>#primitiveExternalCall. The named primitives can be part of the VM via built-in modules or distributed via dynamic libraries, called external modules. It is possible to extend the VM functionality by implementing external modules, without re-building the VM [16].

PrimitiveTable. 29Pragmas are similar to Java annotations. Squeak 26

2.9 VMMaker

VMMaker is a tool for building a Squeak VM. It contains the interpreter and the object memory, written in SLANG, and the code for converting SLANG to C code. Furthermore, it includes the platform independent code for the Squeak plugins, also written in SLANG. It contains a complete implementation of an extended Smalltalk-80 VM, based on the specification, illustrated in the so-called Blue Book [14]. Most information provided in Section 2.7 and Section 2.8 was gathered from the implementation of the VMMaker classes Interpreter and ObjectMemory.

The method Interpreter>>#writeImageFileIO: shows how the image gets written to a file. The method contains information about the header of a Squeak image file, which is important to read in the image (Section 2.7.1). Furthermore, it shows how to detect the byte order of a Squeak image.

The metaclass Interpreter class contains the methods #initializeBytecodeTable and #initializePrimitiveTable, which illustrates the ranges and the names of bytecodes and primitives used in the Squeak interpreter. The corresponding SLANG code can be found in the instance methods of the class.

The class ObjectMemory describes the 32-bit direct-pointer object memory for Squeak (Section 2.7.2). It contains information about the structure of all objects and their headers. Furthermore, it includes the functionality for allocating objects and garbage collecting them. Truffle 27

Chapter 3

Truffle

This chapter contains relevant information about the Truffle framework, neces- sary to understand the implementations described in Chapter 5. It will illustrate the goal of Truffle and how to use the framework to write an abstract syntax tree (AST) interpreter for a programming language. Truffle is still under de- velopment. The explanations are based on the development snapshot, used for implementing the Squeak Interpreter, which is revision 16024:6b1bd708e254 of the OpenJDK Graal repository [4].

One goal for Truffle is to provide a virtual machine with good performance for all pro- gramming languages [33]. The idea is, choosing the best programming language for a given task, and not choosing the language which generates machine code with the highest performance.

Without Truffle, a language implementer has to perform several tasks to create an engine, which produces machine code with good performance for a particular programming language. This involves writing a parser, an AST interpreter, and a virtual machine. To speed up the execution time, the language usually needs a compact bytecode format, which involves writing a bytecode interpreter and potentially writing a just in time (JIT) compiler.

With Truffle, a language implementer has to write an AST interpreter in Java. By following the Truffle guidelines, the generated machine code will run with good performance. The implementer has to write or re-use an existing parser for the given language and convert the parsed code into AST nodes. There is no need to know or access the underlying compiler to implement an AST interpreter.

Truffle is a framework, written in Java used to implement an AST interpreter for dynamic programming languages. The implementer of a language has to write a tree-rewriting interpreter [34]. This is done by mapping language operations to AST nodes and implement different behaviour for different types. Furthermore, the implementer can add profiling Truffle 28

information to the nodes, which will be used to produce optimized code. The implementation of type specialisations for the Truffle nodes is essential for Truffle. The Truffle interpreter specializes the AST nodes during execution. When the AST reaches a stable state, the compiler can produce optimized machine code for the interpreted AST, which contains additional information gathered during interpretation.

Figure 3.1 shows the basic workflow for an AST interpreter written by using the Truffle framework. The parser creates an AST from the source code of the guest language. The nodes of this AST have an ”uninitialized” state after parsing. During interpretation, the nodes of the AST get specialized. The figure shows a possible specialization to Integer nodes (I) and generic nodes (G). When the tree reaches a stable state, the AST gets compiled to machine code. This machine code is specialized to the resolved types, obtained during interpretation. The produced machine code does not contain the tree rewriting routines. In case, a node specialization fails, the code gets deoptimzed by falling back to the interpreter execution and the interpreter has to perform the necessary rewrites. After tree rewriting, the AST can be compiled again by using partial evaluation.

U G

U U I G Source U U I I

specialize execute Parser Interpreter

deoptimize (Stable AST)

Compiler Machine Code

Figure 3.1: Truffle workflow

Truffle is a front-end to Graal [11, 12], a dynamic compiler for the Java Virtual Machine (JVM), written in Java. Graal produces optimized machine code for the Java bytecode, produced by the execution methods of the AST nodes, using partial evaluation. The Graal VM is an interface to the HotSpot VM [28], which is written in C++. The Graal Project is an OpenJDK project and can be found at [7].

3.1 API

The Truffle API [30] provides all components to implement a Tree-Rewriting AST Inter- preter. It contains the classes and methods for implementing AST nodes and contains the routines for tree rewriting during runtime. The framework includes functionality for Truffle 29

defining method frames, which can be used to process local variables. These frames are accessible via the execution methods of the nodes. The framework provides methods to add profiling information to the AST nodes for creating optimized code. Additionally, the framework contains a runtime environment, which allows executing an AST in interpreted and compiled mode.

3.1.1 Nodes

The AST nodes must be subclasses of the class Node, an abstract Java class, which contains methods for replacing, manipulating and copying nodes. Furthermore, it provides the logic to access the parent node. The class does not define methods for execution. These execute methods must be implemented in the subclasses of the node base class. Listing 3.1 shows an example, how the base class could look like for a guest language. The implementer of the guest language has to define an execute method for his tree nodes, which must contain a VirtualFrame parameter (Section 3.1.2). The virtual frame object gets passed to the execute method via the Truffle runtime and provides a heap-based frame object.

public abstract class MyNode extends Node {

public abstract void executeVoid(VirtualFrame frame); }

Listing 3.1: Example of a Truffle base node (Java)

Based on the base node all truffle nodes must get implemented. The number of child nodes, their types and the logic in the implemented execute methods of all nodes will define the behaviour of the guest language. Every node should be mapped to a simple operation in the guest language. It is important to keep the execute methods of all nodes small and implement specializations for all nodes. These specialized nodes handle different types or operate differently under certain conditions.

Nodes, which evaluate an expression and, therefore, return a value, must implement execute methods for every type, available in the guest language. The class TypedNode in Listing 3.2 shows how a base class could look like for expression nodes. Based on the TypedNode class, the implementer has to implement subclasses for every supported type to define a particular operation. Furthermore, the subclasses must contain the rewriting logic for specialising to different types evaluated at runtime. The method executeGeneric gets implemented in the generic node for an operation and covers the generic case of an expression.

Implementing the rewriting logic for all AST nodes is a repetitive task and implicates writing a lot of boilerplate code. For this reason a Domain Specific Language (DSL) has been developed for the Truffle framework. This DSL is implemented via Java annotations Truffle 30 and generates specialized nodes and the corresponding rewriting logic automatically during the project build process (see Section 3.2).

public abstract class TypedNode extends MyNode {

public abstract Object executeGeneric(VirtualFrame frame);

public abstract boolean executeBoolean(VirtualFrame frame);

public abstract int executeInt(VirtualFrame frame);

... }

Listing 3.2: Example of an expression base node (Java)

The class Node contains two annotation type definitions for defining child nodes: @Child and @Children (see line 3 and 4 in Listing 3.3). Based on these annotations, the DSL processor can generate the rewriting logic for a node. A child annotated with @Child must not be final. Otherwise this child node cannot be rewritten during interpretation. However, an array of children, annotated with @Children must be final to prevent the array-length being modified during execution.

1 public class TestNode extends MyNode { 2 3 @Child private MyNode aNode; // must not be final 4 @Children private final MyNode[] nodes; // must be final 5 6 public TestNode (MyNode aNode, MyNode[] nodes){ 7 this.aNode = aNode; 8 this.nodes = nodes; 9 } 10 11 @ExplodeLoop // triggers full unrolling of the loop during compilation 12 @Override 13 public void executeVoid(VirtualFrame frame) { 14 aNode.executeVoid(frame); 15 for(MyNode node : nodes) { 16 node.executeVoid(frame); 17 } 18 } 19 }

Listing 3.3: Example of a node implementation with children (Java)

Listing 3.3 shows the definition and execution of children nodes. To execute every child in an array, one has to iterate through this array. For partial evaluation, loops must get unrolled during compilation. This process is done by annotating the method with the @ExplodeLoop annotation (Line 11 in Listing 3.3). Truffle 31

3.1.2 Frames

Truffle provides an implementation of frames, which can be highly optimized in compiled code. These frames can be used to define and access local variables and to pass values via arguments to AST trees. Section 3.1.1 illustrates that execute methods for Truffle nodes contain a VirtualFrame parameter. This frame object is instantiated and passed to by the Truffle runtime. VirtualFrame is a Java interface, which extends the interface Frame. The implementation of these Frame interfaces is dependent on the used truffle runtime.

VirtualFrame objects are only accessible in interpretation mode. The compiler eliminates the allocation of VirtualFrame objects and optimizes the frame access for local variables of the guest language and provides the same performance as for Java local variables. That is why VirtualFrame objects must not be stored in fields, returned to or passed to other nodes. This restriction can be bypassed by converting the VirtualFrame object to a MaterializedFrame object by calling the materialize() method. This allows the guest language implementer to pass a frame object to another node, e.g. for implementing closures.

The layout of a frame gets defined during parsing, by instantiating a FrameDescriptor object. This frame descriptor is used to define local variables by specifying an identifier and the corresponding type information. Truffle creates a FrameSlot object for every local variable, defined in the descriptor and stores this information in a Java hash map. This frame slot description is used during execution to access the local variables via the VirtualFrame object.

The interface Frame contains methods for Java primitives and Object, which gives access to the local variables of the guest language. It provides methods for checking the type and for setting or getting the value of a local variable, by passing the corresponding FrameSlot object. The access to local variables in a guest language implementation can be specialized via AST nodes for reading and writing, using the Truffle DSL.

3.2 Truffle DSL

Writing specializations and the corresponding rewriting logic for AST nodes is a repetitive task, which yields in writing much code. The Truffle framework provides a domain specific language (DSL), which enables writing specializations for nodes without implementing the tree-rewriting logic. The DSL is implemented via Java annotations. The included Java annotation processor generates the corresponding node classes during compilation. These generated classes contain all specializations, specified via the DSL, and the rewriting logic. As a consequence, the interpreter uses the generated classes to represent the AST nodes in Truffle 32 the guest language. Another advantage of using the DSL is that the framework can apply optimizations to the generated code.

Listing 3.41 illustrates how a Truffle node gets defined using Truffle DSL. The node maps the operation of the guest language and defines specializations for the types long, BigInteger and String. The definition of children nodes in line 1 is different to the definition shown in Listing 3.3, which uses the @Child and @Children annotations for defining its children. The Truffle DSL creates subclasses for the AddNode and adds a leftNode and a rightNode child by adding the Java annotation @Child to these fields. Furthermore, it creates the necessary constructors and the logic to set these child fields.

1 @NodeChildren({@NodeChild("left"), @NodeChild("right")}) 2 public abstract class AddNode extends BaseNode { 3 4 @Specialization(rewriteOn = ArithmeticException.class) 5 long add(long left, long right) { 6 return ExactMath.addExact(left, right); 7 } 8 9 @Specialization 10 BigInteger add(BigInteger left, BigInteger right) { 11 return left.add(right); 12 } 13 14 @Specialization(guards ="isString") 15 String add(Object left, Object right) { 16 return left.toString() + right.toString(); 17 } 18 19 boolean isString(Object a, Object b) { 20 return a instanceof String || b instanceof String; 21 } 22 }

Listing 3.4: Example of an AddNode using Truffle DSL (Java)

For generating the rewriting logic, the Truffle DSL needs the type information, used in the guest language. This type information must be implemented in a class, annotated with @TypeSystem. Furthermore, the type system class provides an ordering of all types used by the DSL processor to generate the specialized nodes. Furthermore, the implementer can define possible type casts for the supported types in this class.

With the Truffle DSL it is possible to define rewrites based on Java exceptions (see line 4 in Listing 3.4). This ”rewriteOn” attribute triggers a node rewrite when an ArithmeticEx- ception has been thrown in the method. In this example the node rewrites itself to a BigInteger add node when the result of the addition creates an overflow. The ExactMath class, used in the long specialization, contains methods included in JDK 8, which throw an

1The example has been copied from the Graal OpenJDK project [7], located in the package com.oracle.truffle.sl. The package describes the implementation of a Truffle interpreter using Truffle DSL. The implementation is based on an example programming language, called Simple Language (SL). Truffle 33

ArithmeticException, when the arithmetic operation overflows.

For specializations based on a custom condition, the Truffle DSL uses the attribute ”guards” (line 14 in Listing 3.4). The guard isString shown in line 19, defines the condition to rewrite to a string add node based on the Boolean value returned via this guard. In this example, the method returns true if either the left or the right child node returns a String object.

The Truffle DSL annotation processor generates subclasses for the implemented node class definition, supplied with the DSL Java annotations. The specializations and guards, implemented by the language implementer, get called in the generated subclasses. Figure 3.2 shows the class hierarchy for the AddNode listed in Listing 3.4 and its generated subclasses. Additionally, the DSL processor generates an AddNodeFactory class, an implementation of the interface NodeFactory. This factory can be used to create the generated nodes dynamically without using Java reflection.

generated classes

AddUninitializedNode

AddLongNode

AddBigIntegerNode AddNode AddBaseNode <> <> AddStringObjectObjectNode

AddPolymorphicNode

AddGenericNode

Inheritance A B B is a subclass of A

Figure 3.2: Subclasses generated by the DSL processor

During parsing the AST gets constructed and uses AddUnitializedNode instances for representing ”add” operations. When an uninitialized node gets executed, the node will rewrite itself to a specialized node by evaluating the dynamic type. When the specialized add node gets executed again, and the evaluation returns another type, the node gets rewritten again. This rewriting process gets repeated until the node reaches a stable state. The AddPolymorphicNode can handle multiple input conditions, performed by chaining specializations. This polymorphic node prevents a toggling between two or more states. The AddGenericNode covers all specializations and represents the generic case, by creating a chain of all nodes. Architecture 34

Chapter 4

Architecture

This chapter will explain the architecture of the Truffle/Squeak Interpreter written for this thesis. It will give an overview of the components and their relations to each other.

The Truffle/Squeak Interpreter is a command-line tool for executing methods of Squeak classes, read from a Squeak image. The written interpreter can load a 32-bit Squeak 4.3 image [8]. The interpreter implementation is based on the Truffle framework (see Chapter 3), which is part of the OpenJDK Graal [7] project.

The Truffle/Squeak Interpreter generates abstract syntax trees (AST) from Squeak bytecode and executes these ASTs via the truffle runtime. Furthermore, it loads all corresponding Squeak objects from the Squeak image and processes these objects during execution. This approach is unusual for Truffle. Usually, Truffle interpreters parse source code into ASTs and execute them. Normally these interpreters do not process existent objects. They create objects during execution and process them.

Figure 4.1 shows an overview of the architecture of the implemented Truffle/Squeak Interpreter. To execute Squeak program code, the tool requires the path to the Squeak image, the Squeak class, and the name of the method. The command-line parser translates the class/method information into a real object by performing a method lookup in the object heap. The object heap contains the Squeak objects loaded from the Squeak image, stored in a Java hash map, using the corresponding heap addresses as keys.

After the method lookup was successful, the object heap returns a CompiledMethod object. This is the Java representation of the Squeak CompiledMethod object, introduced in Section 2.5. The bytecode of this object gets decompiled into a Truffle AST. The Truffle/Squeak Interpreter can execute this AST using the runtime provided by the Truffle framework. Because, a usual Squeak image contains more than 50.000 compiled method objects, the decompilation of a method takes place when executed. The generated AST Architecture 35

Squeak Image Reader Image

register object read object

resolve/lookup Truffle/Squeak execute Object Heap Truffle Runtime Interpreter

lookup method decompile method

execute method Command Line Command Decompiler Parser decompile method

Figure 4.1: Truffle/Squeak architecture overview

gets cached into the compiled method object after decompiling.

4.1 Image Reader

The Image Reader creates real Java objects from Squeak objects, loaded from the Squeak image. The image reader needs the information provided by the Squeak image header (see Section 2.7.1) before creating the object heap. The image header contains the header size, the memory base address and a reference to the Special Objects Array (see Section 2.6), amongst other information.

The start of the object memory can be determined by the header size. The memory base address is used to convert the Squeak object addresses, read from the image, to zero-based addresses, which are used to store them into the Java hash map. The location of the Special Objects Array enables the creation of singleton objects for all important Squeak objects, such as nil, true, false and the SystemDictionary.

A usual Squeak image contains more than 500.000 Squeak objects. Therefore, the tool has the option to lazy-load the Squeak image. This option can be enabled via the command- line. When activated, the image reader only reads the important objects referenced in the Special Objects Array. Otherwise, the image reader loads the complete object memory sequentially and creates Java objects, based on the object format encoded in the object header (Section 2.7.3) and registers them on the object heap. Architecture 36

4.2 Object Heap

The object heap represents the Squeak object memory, read from the Squeak image. The objects, created during image reading, are stored in a Java hash map associated with their object address. The image reader ensures that the first object is associated with address zero. After reading in the Squeak image, the objects can be accessed via its address.

If the Squeak image has been lazy-loaded, the access of an object from the object heap requires an additional check if the object has been loaded already. If not and the object heap returns null for the specified address, the image reader has to load the data from the Squeak image and has to create the corresponding object. This object gets registered in the object heap via its associated address for prospective access.

4.3 Decompiler

The implemented decompiler generates Truffle ASTs by decompiling the bytecode loaded from CompiledMethod objects. It iterates through the bytecode array and creates corre- sponding Truffle nodes. Some Squeak bytecodes process literals, which are stored in the compiled method object (see Section 2.5). These literals are converted to typed literal nodes by the decompiler. The message send bytecodes are converted to send nodes. The method lookup is performed during AST interpretation and not during decompilation. If successful, the resulting compiled method object gets decompiled to an AST and cached in the method object.

The implemented decompiler is based on the Squeak Decompiler implementation, which is part of the Squeak source code. The Java version of the decompiler can decompile all available methods stored in a Squeak 4.3 image, which are more than 50.000 compiled method objects. Implementation 37

Chapter 5

Implementation

This chapter will explain the components implemented for this thesis. It will illustrate the Squeak image loading process and the Java representation of the Squeak objects included in the image. The chapter contains implementation de- tails of the basic Truffle/Squeak nodes and how they get generated by decompiling Squeak bytecode.

5.1 The Squeak Image

The implemented Squeak image loader is based on the Squeak Image Tool 1, developed at the Hasso Plattner Institute in Potsdam. The tool was used to display the content of a Squeak Image in a tree view and for navigating through the Squeak objects. For this thesis, it has been adopted and extended to meet the needs for the Truffle/Squeak Interpreter. The new implementation allows the modification of Squeak objects. Furthermore, it is possible either to load a complete Squeak image or to lazy load a Squeak Image in the object heap.

Figure 5.1 illustrates a schematic view of the class diagram for loading a Squeak image. The class Image represents the Squeak image loaded from the file system. The heap field in Image contains the Squeak object memory (see Section 2.7.2) loaded from the Squeak image and converted to their corresponding Java representations. The object heap gets constructed via the ObjectHeap member reader. The static type of reader is the abstract class ImageReader, which contains the loading functionality for all Squeak object types. The class ImageHeader represents the Squeak image header introduced in Section 2.7.1. It provides information for the ImageReader used for interpreting the data loaded from the Squeak image.

1The Squeak Image Tool was developed during the implementation of the master thesis of Arian Treffer and Tobias Pape. It is available under MIT license. There exists no public information, neither an official link to this tool. Implementation 38

ImageReader <> ImageHeader ObjectHeap bytesToShift : long Image hasOffset : boolean heap <> reader header abstract read(ObjectHeap) : void byteOrder : ByteOrder path : String objects : Map readObjectHeader(long) : ObjectHeader formatVersion : ImageVersion readPointer(ObjectHeader) : long[] headerSize : long read() : boolean register(long, object) : Object readWords(ObjectHeader) : long[] imageSize : long getHeap() : ObjectHeap abstract resolve(long) : Object readBytes(ObjectHeader) : long[] memoryBaseAddress : long readSpecialObjects(ObjectHeap) : void specialObjectsOop : long createObject(ObjectHeap, ObjectHeader, long) : Object

LazyHeap reader MinimumReader

resolve(long) : Object read(ObjectHeap) : void Inheritance A B reader CompleteHeap CompleteReader B is a subclass of A

resolve(long) : Object read(ObjectHeap) : void Dependency A B B depends on / uses A

Figure 5.1: Class diagram for the Java implementation for loading a Squeak image

The image reader loads raw data arrays from the Squeak image, converts the data to Java objects and registers them on the object heap. The addresses read from the Squeak image are based on the memoryBaseAddress stored in the image header. The image reader adapts these addresses to generate an object heap, which starts at address zero.

The address of the Special Objects Array (Section 2.6) is stored in the image header. The slots in the Special Objects Array are well-documented and, therefore, can be used to generate individual types for them. The image reader creates singleton objects for the Special Objects Array itself and for the special objects, which are important for the Truffle/Squeak Interpreter. These special objects are nil, true, false, SpecialSelectors, CompactClasses, SmalltalkImage, and SystemDictionary.

To distinguish between the different object formats (Section 2.7.3), the image reader loads the object header information first. Based on the format field and the compact class index in the base header (Figure 2.13 on page 21), the reader can generate a corresponding Squeak object (Section 5.2). The Squeak image does not contain SmallInteger objects because they are immediate objects and, therefore, encoded in the memory address itself (Section 2.7.2).

All Squeak objects are reference types and, therefore, get resolved via their addresses. These references get stored in Java long fields during object heap construction. In a lazy-loaded object heap, implemented in the class LazyHeap, the addresses get resolved when accessed. In contrast, in the CompleteHeap implementation all references get resolved after loading the complete Squeak image.

Objects created by the image reader get registered on the object heap. The internal Implementation 39

representation of the heap is a Java hash map. The created objects get registered via their associated heap addresses. To access an object in a complete loaded object memory is illustrated in Listing 5.1.

public class CompleteHeap extends ObjectHeap {

@Override public Object resolve(long address) { return objects.get(address); } }

Listing 5.1: Resolving addresses in a fully Loaded object heap (Java)

The access to an object in a lazy-loaded object heap requires an additional check (Listing 5.2). If the Java hash map does not contain an object for the specified address, the object has not been loaded yet. Consequently, the image reader must load the object when required and registers the interpreted Squeak data on the object heap.

public class LazyHeap extends ObjectHeap {

@Override public Object resolve(long address) { final Object object = objects.get(address); if (object == null){ try { return reader.readObject(this, address); } catch (IOException e) { throw new RuntimeException("could not resolve object at"+ address); } } return object; } }

Listing 5.2: Resolving addresses in a lazy loaded object heap (Java)

5.2 Squeak Objects

The image reader loads data from the Squeak image and generates corresponding Java objects and Java primitives by interpreting the Squeak object headers. The Squeak object format and the compact class index is encoded in the base header (see Figure 2.13 on page 21). Based on these values the image reader instantiates the corresponding Java types. Table 5.1 shows the Java representation for the corresponding base header content. The interpretation of the ccIndex column is described in Table 5.2.

The implemented image reader relies on the compact class index, which should be changed Implementation 40

format ccIndex Java Object

0 - EmptyObject 1 - FixedObject 2 - IndexableObject 3, 4 - HybridObject 6, 7 6 double 6, 7 - WordArray 8 - 11 4 long or BigInteger (negative value) 8 - 11 5 long or BigInteger (positive value) 8 - 11 - ByteArray 12 - 15 - CompiledMethod

Table 5.1: Java representation of Squeak objects for future implementations. One could modify the content of the compact class array in the Squeak environment, which would break the current implementation of the Truffle/Squeak Interpreter.

ccIndex Squeak Class

4 LargeNegativeInteger 5 LargePositiveInteger 6 Float

Table 5.2: Compact class index (one-based)

Squeak objects with a compact class index of six are instances of the Squeak class Float, which get converted to double values. A Squeak Float is an indexable word array, which represents an IEEE-754 [6] floating point double precision value. This value is encoded in the first two 32-bit words of the word array.

Squeak objects with a compact class index of four and five represent Integer values, which do not fit in a 31-bit SmallInteger object. These big Integer values are instances of the Squeak classes LargePositiveInteger and LargeNegativeInteger. Values represented by these two classes are encoded in indexable byte arrays. The size of this array is dependent on the binary representation of the Integer value. For example a 32-bit Integer is encoded in a byte array of length four. Big Integer values do not allocate an extra bit for negative values. The byte array content of a LargeNegativeInteger value is equal to the unsigned representation of this value stored in a LargePositiveInteger object.

Figure 5.2 shows the class hierarchy of the implemented Squeak object representation. The figure does not show the complete hierarchy. It contains the implemented classes, which get instantiated by the image reader for building the object heap, based on the Squeak image content. Implementation 41

SObject <> ByteArray CompiledMethod

header : ObjectHeader data : byte[] methodHeader : CompiledMethodHeader heap : ObjectHeap literals : Object[] getByte(int) : byte bytecode : Bytecode resolve() : void setByte(int, byte) : void trailer : byte[] getSqueakClass() : PointerObject truffleMethod : Method resolve() : void sClass installedIn() : PointerObject PointerObject decompile(SContext) : Method WordArray <> installedIn data : long[] content : Object[] getWord(int) : long ptrContent : long[] setWord(int, long) : void resolve() : void

EmptyObject FixedObject IndexableObject HybridObject

fixedFieldCount : int resolve() : void getFixedField(int) : Object get(int) : Object setFixedField(int, Object) : void set(int, Object) : void resolve() : void getFixedField(int) : Object setFixedField(int, Object) : void get(int) : Object set(int, Object) : void Inheritance Dependency A B A B B is a subclass of A B depends on / uses A

Figure 5.2: Squeak object class hierarchy

The abstract class SObject is the base class for all Squeak object types implemented for the interpreter. The Squeak class of an SObject instance is stored in the sClass field. The static type of sClass is PointerObject. PointerObject is the base class for all Squeak objects, which contain references to other objects. In contrast, the classes ByteArray and WordArray process data represented via byte and long values.

The type EmptyObject represents Squeak objects without content, like nil, false and true. Instances of FixedObject contain references to a fixed amount of named instance variables, which get internally accessed via an index. The names used for accessing the variables get resolved to the corresponding indices during runtime. IndexableObject represents objects with a variable amount of pointers using integer values to access their content. For example, Squeak arrays get stored via IndexableObject instances.

The class HybridObject represents Squeak objects with fixed and indexable pointer fields. These Squeak objects contain a fixed amount of named instance variables and a variable amount of references. The instance variables get accessed via their names and the indexable fields via indices.

The class PointerObject stores references to other objects in the ptrContent member. The ptrContent array gets filled by the image reader during object heap construction. When the image reader has finished loading the data from the Squeak image, the addresses must get resolved to Java objects. The resolved objects get stored in the content array of the PointerObject instance. Implementation 42

If the least significant bit of an address is one, the remaining 31-bits get converted to a Java integer. Squeak encodes SmallInteger values in their memory addresses. If the least significant bit is zero, the object gets resolved via the object heap. Listing 5.3 shows the resolve process of addresses, stored in the ptrContent array of a PointerObject instance. If the specified address is an immediate object, it returns the encoded Integer value by right-shifting the address.

public abstract class Pointer {

public static Object resolve(ObjectHeap heap, long address) { if (isIntegerReference(address)) return resolveToInteger(address); return heap.resolve(address); }

private static int resolveToInteger(long address) { return (int) address >> 1; }

private static boolean isIntegerReference(long address) { return (address & 1) > 0; } }

Listing 5.3: Resolving addresses using the object heap (Java)

For Truffle, it is important that the code for accessing objects on the object heap is not included in the execute methods of the Truffle nodes, which get compiled. Because Truffle inlines all method calls, it would inline the hash map access in the compiled code, which would hurt the performance of the resulting compiled machine code. That is why the implemented Truffle nodes must ensure, that references are resolved in the uninitialized state of the nodes.

5.2.1 Compiled Method

The Java class CompiledMethod represents the compiled method objects read from the Squeak image (Section 2.5). Instances of CompiledMethod have a special format. They contain word-size pointers and byte values. The pointers contain references to literals which get accessed in the bytecode stored via byte values.

The number of pointers is stored in the bit field numLiterals in the compiled method header (Figure 2.8 on page 15). The method header is the first word of the compiled method object, encoded in a SmallInteger object. The value of numLiterals can be used to calculate the location of the byte value section of the corresponding compiled method object. Implementation 43

The byte value section is split into two parts: the bytecode compiled from Squeak source code and an additional method trailer. The method trailer contains information about the method itself. The provided information is dependent on the type of the method trailer. The method trailer type and its size is encoded in the last byte of the compiled method object (see Figure 2.9 on page 16).

To execute the program logic stored via bytecode in a CompiledMethod object, the bytecode gets decompiled into ASTs (see Section 5.4). The decompilation is done during runtime. When a message gets sent to an object, the implemented Truffle/Squeak interpreter performs a message lookup. If successful, the message lookup returns the desired compiled method object. The decompiled bytecode gets stored in an instance of the implemented class Method or a subclass and a reference to this instance gets cached in the CompiledMethod object itself.

5.2.2 Squeak Class

Every SObject instance contains the reference to its Squeak class. Squeak classes have fixed fields only (see Table 2.3 on page 22) and, therefore, get stored in FixedObject instances by the implemented image reader. In Squeak, classes are objects and, therefore, are instances of so-called metaclasses (Section 2.3). A Squeak class is an instance of FixedObject. FixedObject is a subclass of SObject and, therefore, has a reference to its Squeak class. This reference points to the metaclass, which is again an instance of FixedObject.

The implemented Java class SqueakClass is a collection of static methods for processing the squeak class objects, stored in FixedObject instances. SqueakClass contains, amongst others methods for the following functionality:

• Accessing the name of a Squeak class • Finding the superclass for a class • Doing a method lookup • Interpreting the ”instance specification” word, which contains information about instances created by their classes

Listing 5.4 shows the Java implementation for a method lookup for a selector in the Squeak class sClass. This lookup is performed by a recursive method call. Truffle cannot inline such recursive method calls and, therefore, must not be part of the compiled code. That is why the method is marked via the @SlowPath annotation, which tells Truffle not to inline the code. Implementation 44

@SlowPath public static CompiledMethod lookup(PointerObject sClass, Selector selector) { final CompiledMethod method = selector.findInMethodDict(sClass); if (method == null){ final PointerObject superclass = getSuperClass(sClass); if (superclass == Nil.getInstance()) // method not found in class hierarchy return null; return lookup(superclass, selector); } return method; }

Listing 5.4: Recursive method lookup (Java)

5.3 Truffle Runtime Objects

During AST execution, the implemented interpreter generates objects, which contain information essential for performing certain tasks. These objects are not instances of the class SObject or any subclass introduced in Section 5.2. Nevertheless, instances of these classes can have references to these runtime objects and process their data they contain.

5.3.1 Methods

The implemented class Method is used to store information gathered from a corresponding CompiledMethod object. It contains the decompiled bytecode used by the Truffle interpreter to execute the AST representation of the corresponding CompiledMethod object. An instance of Method can be executed by calling its call method, passing a MethodContext (Section 5.3.2) object and an array of arguments. The class Method has three subclasses illustrated in Figure 5.3. These subclasses represent three special variations of Squeak methods used by the Squeak VM.

The Truffle framework uses instances of the class RootCallTarget for executing ASTs. A RootCallTarget gets instantiated by calling TruffleRuntime.createCallTarget(Root Node) (see Section 5.5). Because of primitive methods in Squeak (see Section 2.8), the class Method has two members of type RootCallTarget. One for executing the primitive method code and one for the fall-back Squeak code.

In Squeak basic functionality is performed by calling primitive methods, introduced in Section 2.8. A primitive method executes code, which is part of the Squeak VM and therefore not available in the Squeak image. The Squeak primitives are implemented via primitive nodes, managed in the primitive registry described in Section 5.3.5. If a primitive fails, or does not exist, the Squeak VM executes the Smalltalk code implemented in the Implementation 45

QuickSelfMethod

Method call(MethodContext, Object[]) : Object

callTarget : RootCallTarget next : RootCallTarget QuickReturnMethod compiledMethod : CompiledMethod call(MethodContext, Object[]) : Object call(MethodContext, Object[]) : Object getCallTarget() : RootCallTarget nextCallTarget() : RootCallTarget getSClass() : PointerObject PrimitiveMethod

call(MethodContext, Object[]) : Object Inheritance A B B is a subclass of A

Figure 5.3: Implemented types of Truffle methods for the Truffle/Squeak interpreter

method body.

Squeak has special primitives, which just return a value and, therefore, do not have a method body. These primitives are implemented via QuickSelfMethod and QuickReturnMethod. The method call in QuickSelfMethod just returns the receiver of the message, which is accessible via the prior passed method context. The receiver of a message usually can be referenced by using the Squeak keyword self in a normal method body. In QuickReturnMethod, the call method returns a constant, dependent on the primitive index.

5.3.2 Method Context

A method call in Truffle/Squeak can only be performed by passing a corresponding method context object. Usually, an instance of MethodContext contains the receiver of the method and a reference to the method object itself. When executing a Squeak block (Section 2.2.7), represented by a BlockClosure object (Section 5.3.3), MethodContext stores the reference to the executing block closure object.

A method context object gets passed to the call method of a Truffle CallTarget. Therefore, it can be accessed in the execute method of a node, by accessing the VirtualFrame object (Section 3.1.2). This enables the access to the receiver of a message when using the keyword self in a Squeak method. Furthermore, the method context can be used to implement . This feature has not been applied in the current version of the Truffle/Squeak Interpreter, but could be part of a future implementation. Implementation 46

5.3.3 Block Closure

In Squeak, a block closure (Section 2.2.7) is an object, which represents an . A block closure has access to the surrounded method context and, therefore, can manipulate the local variables of the method, the closure was defined in. It can have arguments and its locals. A closure object can be passed to other methods by sending a message and still has access to the original method context.

The Truffle/Squeak implementation of the class BlockClosure gets created during decom- pilation. The resulting AST gets stored in the closure object by creating a call target, using the truffle runtime method createCallTarget(RootNode). Furthermore, the number of arguments and the number of locals, the closure accesses from the outer method context, get saved in the block closure object.

During execution, the VirtualFrame object gets materialized (Section 3.1.2) and stored in the block closure object. This enables the access to the local variables of the outer frame in other Squeak methods.

5.3.4 Squeak Arguments

In Truffle, arguments can be passed to an AST, by calling the call method of the corresponding call target. The passed arguments can be accessed in the VirtualFrame object in an execute method of the Truffle nodes in the corresponding AST. For the Truffle/Squeak Interpreter, the passed arguments array is always the length of two. The first slot of this array always holds the corresponding method context (see Section 5.3.2). The second contains the real arguments passed to the AST, stored in an Object array. Listing 5.5 shows the static methods implemented in the SArguments class for accessing the method context and the passed arguments array.

5.3.5 Squeak Context

The implemented Squeak Context is used to manage all decompiled methods and all available primitive methods. The method registry, realized with a hash map, contains all executed Method objects, converted from CompiledMethod objects. This collection is used to enable the analysis of all executed ASTs in a Smalltalk program, by dumping the created nodes to the Ideal Graph Visualizer (IGV), developed by Thomas Wuertinger for his master’s thesis [32]. The IGV is part of the OpenJDK Graal project [7].

The primitive registry contains all primitives available in the Truffle/Squeak Interpreter, implemented via Truffle nodes and converted to call targets. They are stored in a hash Implementation 47

public class SArguments {

private final static int mcIndex = 0; private final static int argumentsIndex = 1;

public static MethodContext getMethodContext(VirtualFrame frame){ return (MethodContext) frame.getArguments()[mcIndex]; }

public static Object[] get(VirtualFrame frame) { return (Object[]) frame.getArguments()[argumentsIndex]; } }

Listing 5.5: Accessing the method context and the passed arguments array from a VirtualFrame (Java)

map, using the associated primitive number as key. The primitive registry gets filled during Truffle/Squeak Interpreter initialization and does not change during execution.

The primitive registry gets accessed, when a compiled method is a primitive, which was returned via method lookup in the Squeak class of the message receiver. Also, the method body gets decompiled. The call target returned from the primitive registry and the call target generated through decompiling get passed to a PrimitiveMethod object, introduced in Section 5.3.1. If the primitive has not been installed in the registry, a normal Method object gets created by decompiling the method body. In some cases, the method body contains a Squeak implementation of the primitive routine. If not, the Truffle/Squeak Interpreter fails and throws an exception.

5.4 Squeak Decompiler

The implemented decompiler is a stack-based state machine, which creates an AST of Truffle/Squeak nodes for Squeak bytecode and the corresponding literals provided in a CompiledMethod object read from a Squeak image. The implementation is based on the Squeak Decompiler class, available in the Squeak environment.

The Squeak bytecodes are based on the description of the Smalltalk-80 system in the so-called Blue Book [14]. In Squeak some extended bytecodes for new implementations have been introduced, which are not part of the Smalltalk-80 system. Most bytecodes are encoded in a single byte. More complex bytecode instructions consist of byte sequences of two to four bytes.

The bytecode instructions are categorized in Push, Store, Return, Jump and Send Bytecodes. These bytecode instructions can have additional information encoded in the bytecode value Implementation 48 itself. These bytecodes cover a range, which is a multiple of 16. A on the bytecode value with the argument 16 returns the index used for further bytecode interpretation.

Listing 5.6 shows how the type and the index, encoded in the bytecode is interpreted. The interpretation of bytecode instructions is implemented in a big single switch statement. The listing is an abstract of the implementation and shows the interpretation of a Push Constant Bytecode, which pushes a Squeak constant on the stack in the Squeak VM. The constant gets loaded from the literals array, fetched from a CompiledMethod object.

1 protected void interpretInstruction(int currentBc) { 2 3 final int type = currentBc / 16; 4 final int offset = currentBc % 16; 5 6 switch (type) { 7 case pushConstant: 8 pushConstant(literals[offset]); 9 break; 10 ... 11 } 12 }

Listing 5.6: An abstract of interpreting Squeak bytecode (Java)

The corresponding method is listed in Listing 5.7. It creates a literal node for the literal object loaded from the CompiledMethod object and adds the created node on the internal decompiler stack. The factory member is an instance of the class SNodeFactory, responsible for creating Truffle nodes. Furthermore, it contains the logic for creating frame descriptors and frame slots, introduced in Section 3.1.2.

protected void pushConstant(Object value) { stack.add(factory.createLiteralNode(value)); }

Listing 5.7: Literal node creation for a Push Constant Bytecode (Java)

The nodes pushed to the decompiler stack during decompilation (Listing 5.7) are used to create more complex nodes, which operate on their result during execution. An operation, represented by the corresponding node gets pushed on the internal decompiler statements collection. After decompiling the complete method bytecode, the statements collection is used to create a block node (Section 5.5), which contains all statements of the decompiled method.

The following chapters contain an abstract of the bytecodes utilized in the decompiler implementation for the Truffle/Squeak Interpreter. Every chapter introduces a Squeak bytecode category, which will explain the function of the category and the corresponding Implementation 49

implementation of the interpreter, written for this thesis.

5.4.1 Push Bytecodes

Push Bytecodes push an object to the top of the stack of the Squeak VM. The decompiler simulates this stack by pushing corresponding nodes on top of its internal stack representa- tion (Listing 5.7). Squeak has Push Bytecodes for instance variables, temporary variables, Squeak constants, literal constants, literal variables and the keyword self. Squeak constants are nil, true, false and the values minus one, zero, one and two. Constants passed via literals are instances of SmallInteger, Float, Symbol, Array and similar. Literal Variables are references, stored in the literal array, which point to an instance of Association. These associations are key/value pairs, which consists of a reference to an object in the value field, associated with its textual representation, referenced in the key field, stored in an instance of the class Symbol.

Squeak has no Push Bytecodes for arguments. Arguments in Squeak are represented via temporary variables. The implemented decompiler has to distinguish between local variables and arguments, when interpreting a Push Temporary Variable Bytecode, because locals and arguments are handled differently in the Truffle/Squeak Interpreter.

Cascaded Messages, introduced in Section 2.2.6, are realized with the Duplication Bytecode, which is always combined with a Send Bytecode. The bytecode duplicates the top of the stack, which usually is the receiver of the cascaded message send. When decompiling the corresponding send bytecode, a MultiSendNode (Section 5.9) gets created.

5.4.2 Store Bytecodes

A Store Bytecode stores the result of an expression, pushed on top of the stack, in a Squeak variable. They are implemented in two different realisations. Some of the bytecodes leave the created objects on top of the stack and some remove them afterward. In the implemented decompiler, the expression is realized with a Truffle node, which gets pushed on the stack by a Push Bytecode. This node gets passed to the corresponding node, which will execute the expression node during runtime and write the result in the variable/object, dependent of the bytecode type.

5.4.3 Return Bytecodes

Squeak has Return Bytecodes for the constants nil, true, false, for the values minus one, zero, one, two, for the keyword self and for returning the top of the stack. In Implementation 50

Squeak, every method returns a value. If not declared, the compiler adds a bytecode, which returns self, the receiver of the corresponding message. The decompiler realizes these return bytecodes, by creating a ReturnNode (Section 5.5). The node, which represents the constant or expression the method will return, is on top of the stack when decompiling the return bytecode and can be passed to the return node by popping the node off the decompiler stack.

5.4.4 Jump Bytecodes

Jump Bytecodes encode a distance in their bytecode instruction. This value tells the Squeak VM the distance in bytes to the next bytecode instruction, where the execution will continue. If the distance is bigger than eight bytes, the jump instruction is encoded in two bytes. Jump bytecodes are used by the Squeak compiler to optimize message sends, which are used to define control structures.

Squeak has two kinds of jumps, unconditional and conditional jumps. An unconditional jump always skips the number of bytes, specified by the bytecode instruction. Conditional jumps only skip bytes if the condition on top of the stack is fulfilled.

The implemented decompiler translates this jump bytecodes into Truffle control nodes, dependent on certain conditions. For example, the body of a gets decompiled into a BlockNode (Section 5.5), which consists of multiple Truffle nodes, decompiled from the corresponding bytecode instructions. In conjunction with the condition node, the WhileNode can be generated.

The creation of if-then-else control structures is similar to the creation of while control structures. An IfNode gets generated by providing a control node and two BlockNodes; one block for the then-branch and one for the else-branch.

5.4.5 Send Bytecodes

Send Bytecodes represent message sends to objects (Section 2.2.6). A message send consists of a message, a variable amount of arguments and a receiver. The receiver of a message tells the VM, where to start the method lookup. In Squeak, it is possible to send a message to super. This provokes the VM to begin the method lookup in the superclass of current class.

Squeak has different kinds of bytecode instructions for sending messages. One kind uses a set of 32 special selectors and, therefore, do not need to specify the message selector in the literals array. The Special Selector Array is referenced in the Special Objects Array Implementation 51

(Section 2.6). A special selector is split into two slots in the array, one for the message selector, and one for the number of arguments the message consists of.

Another kind loads the message selector from the literal array, located in the Compiled- Method object. There exist different bytecode instructions for encoding the number of arguments. One uses the type of the bytecode calculated by dividing with 16 (see line 3 in Listing 5.6). The value of the variable type is:

• 13 for unary sends, • 14 for binary sends, and • 15 for ternary sends.

Another kind is implemented via extended send bytecode instructions, which use one to two additional bytes for defining a message send.

The implemented decompiler creates Truffle nodes dependent on the bytecode instruction. A SendNode (Section 5.9.1) represents a normal message send to a receiver object. A SuperSendNode (Section 5.9.3) is used, when a message is sent to the keyword super.A MultiSendNode (Section 5.9.2) gets created, when cascaded messages are sent to an object, described in Section 5.4.1. The receiver node and the argument nodes of a message send are pushed on the stack by the corresponding push bytecodes, which get passed to the generated send node. The message selector, an instance of the class ByteArray, is located either in the literal array or the Special Selector Array.

5.4.6 Closures

The implementation of Block Closures (Section 2.2.7) is relatively new in Squeak. The Smalltalk-80 implementation of blocks had problems with access to temporary variables in the enclosing method context in recursive calls. This issue had been fixed in the new closure implementation [24]. As a consequence, new bytecode instructions had been implemented for this new feature [25].

The closure implementation is independent of its enclosing context, by copying the tempo- rary variables from the outer context, instead of direct accessing them. This can be done, when these variables do not get modified in the closure. All temporary variables accessed in the outer context and modified in the closure are put in an indirection vector, which is copied into the closure. This vector is a simple array, one slot per variable, allocated on the heap. Changes in this indirection vector affect the temporary variables of the enclosing context. Implementation 52

Push Array Bytecode

The Push Array bytecode instruction is an extension, which consists of two bytes. Squeak has two types of Push Array bytecode instructions. If the second byte of the instruction is smaller than 128, an empty array gets allocated, with a size, specified by the byte value. Otherwise, an array of size ”byte value - 128” gets allocated and initialized with elements popped off the stack. Listing 5.8 shows the Java implementation of the Push Array bytecode. The method listed in line 1 creates an empty array of Object. The method listed in line 6, creates a BraceArrayNode by passing the expressions, represented via nodes, to it. A BraceArrayNode represents Squeak brace arrays, introduced in Section 2.2.4.

1 protected void pushNewArrayOfSize(int size) { 2 Object[] array = new Object[size]; 3 stack.add(factory.createLiteralNode(array)); 4 } 5 6 protected void pushConstArrayWithElements(int numElements) { 7 TypedNode[] array = new TypedNode[numElements]; 8 for (int i = numElements − 1; i >= 0; i−−){ 9 array[i] = (TypedNode) stack.pop(); 10 } 11 stack.add(factory.createBraceArray(array)); 12 }

Listing 5.8: Java implementation of the Push Array Bytecode (Java)

The Squeak compiler uses the Push Array bytecode for empty array creation, for allocating an indirection vector used in a block closure. Squeak has bytecode instructions for pushing the vector and storing into the vector. The corresponding functionality is implemented via Truffle frame slots, introduced in Section 3.1.2. The decompiler creates ReadVector- Nodes and WriteVectorNodes for accessing the indirection vector in a normal method context. For access in a closure context, the decompiler creates ClosureReadVectorNodes and ClosureWriteVectorNodes, which access the outer frame through a materialized VirtualFrame object, described in Section 3.1.2.

Push Closure Copy Bytecode

The Push Closure Copy bytecode instruction consists of four bytes. The instruction contains information about the number of copied values from the outer context, the number of arguments, the closure accepts and the block size. The block size indicates the number of bytes the closure allocates in the complete method bytecode. The Push Closure Copy bytecode instruction creates a BlockClosure object and pushes it on the stack. The instruction does not execute the code, defined by the closure. A BlockClosure object gets evaluated by sending a #value message to it (Section 2.2.7). Implementation 53

The implemented decompiler creates a BlockClosure object, by decompiling the number of bytecodes, specified by the block size argument encoded in the bytecode instruction. The decompiler has to determine the number of temporary variables of the block closure, which will be converted to ReadLocalNodes and WriteLocalNodes for accessing them in the closure AST.

The number of temporary variables gets calculated by the implemented BlockLocals- Counter. The closure bytecode contains a Push Nil bytecode at the beginning of the closure for every temporary variable, used in the closure. It is not sufficient to count these bytecodes because there could be more Push Nil bytecodes after the definition of the local variables. That is why the counter has to calculate the number of locals by interpreting the bytecodes for the corresponding block closure. This is done by increasing the counter for every push bytecode and decreasing it for every pop bytecode. The remaining value tells the decompiler the number of nil pushes and, therefore, the number of temporary variables.

The decompiler creates a CallTarget object, described in Section 5.3.1, on top of the closure statement nodes packed in a BlockNode. This call target, the number of copied values from the outer context and the number of closure arguments is used to create the BlockClosure object. This closure object and the copied value nodes, popped off the stack, get stored in a BlockClosureNode, which gets pushed on the decompiler stack for further processing.

5.5 Basic Nodes

The base node for normal Truffle/Squeak nodes is SNode. It just contains an abstract method executeVoid, which does not return a value. The base node for returning values is TypedNode, listed in Listing 5.9. It contains a Java annotation (line 1), which defines the type system used for the interpreter. Based on the type system definition, the Truffle DSL annotation processor creates the class STypesGen. This class contains standard and user-defined type checks and type casts for the defined types.

The Listing 5.9 is incomplete. It just shows the execute method for returning Integer values (line 11). The real implementation contains an execute method for every defined type in the type system. The type system definition and the execute methods for all types enable the usage of the Truffle DSL, introduced in Section 3.2.

Methods and blocks have multiple statements, which get decompiled into nodes. A BlockNode represents an array of child nodes. The execution of a BlockNode is simple. It iterates over the array of children and executes every single child. Implementation 54

1 @TypeSystemReference(STypes.class) 2 public abstract class TypedNode extends SNode { 3 4 public abstract Object executeGeneric(VirtualFrame frame); 5 6 @Override 7 public void executeVoid(VirtualFrame frame) { 8 executeGeneric(frame); 9 } 10 11 public int executeInteger(VirtualFrame frame) throws UnexpectedResultException { 12 return STypesGen.STYPES.expectInteger(executeGeneric(frame)); 13 } 14 }

Listing 5.9: Basic node for returning a value (Java)

Returning a value from a child node to the caller of a method, represented by an AST, is done by throwing a Java exception. The value of the return exception is passed to the instantiated exception object. The Truffle/Squeak node for this functionality is ReturnNode. It has one child node for representing the return expression, which gets evaluated by executing the child during runtime.

The return exception thrown by a ReturnNode gets caught in a BodyNode. The BodyNode has one child node, which usually holds an instance of a BlockNode. The execute method of the body node contains a try block for executing the child and a catch block, which returns the evaluated result, thrown by the ReturnNode.

The method TruffleRuntime.createCallTarget(RootNode) must be called to create a call target for an AST, The class RootNode is included in the Truffle framework and stores the frame descriptor of a method and the call target itself. The Truffle/Squeak root node is implemented with the subclass SRootNode. SRootNode has one child, usually the body of a method. When creating an instance of SRootNode, a copy of the uninitialized body node gets created. This copy can be used by the Truffle framework to inline the AST and perform call-specific optimizations to each copy.

Figure 5.4 shows the structure of a basic AST for a Squeak method. In this figure, the block node contains three child nodes. These children do not have a special order. One of these child nodes represents the ReturnNode, which returns the result of its child via a ReturnException, which is caught in the BodyNode.

The class ArgumentNode returns argument values passed to the AST via the VirtualFrame, introduced in Section 5.3.4. The argument values are evaluated in the caller, by executing the corresponding nodes. ArgumentNode just accesses these evaluated values. The same technique is used to access the receiver of a method and the method context itself. The Implementation 55

SRootNode

uninitializedBody BodyNode

BlockNode

ReturnNode

TypedNode

Figure 5.4: Basic Truffle/Squeak AST

corresponding classes are SelfNode and ThisContextNode. SelfNode returns the receiver stored in the method context. ThisContextNode returns the method context object and appends the materialized VirtualFrame object to it.

5.6 Control Structure Nodes

In Squeak control structures like if-then-else and loops are realized with message sends, described in Section 2.2.6 and Section 2.2.7. In reality the compiler optimizes this message sends by generating conditional and unconditional jump bytecode instructions. These bytecodes get decompiled into IfNodes and WhileNodes by the implemented decompiler, described in Section 5.4.4.

IfNode WhileNode

evaluate evaluate

condition then else condition body

Figure 5.5: Structure of an IfNode Figure 5.6: Structure of a WhileNode

Figure 5.5 shows the structure of an IfNode. An IfNode contains three child nodes, which all return a value, when being executed. The execute method of the IfNode evaluates the boolean value of the condition, executes the corresponding node and returns the resulting value. Listing 5.10 illustrates the reason, why thenNode and elseNode return values. In Squeak, it is possible to assign a value, based on a condition. If the condition is true, the Implementation 56

variable c is assigned to result. If the condition is false, nil gets assigned to result. The evaluation of a block always returns the last statement of this block. In this example, the expression a + b gets discarded.

result := condition ifTrue: [ a + b. c ]

Listing 5.10: Condition-based assignment (Smalltalk)

Figure 5.6 shows the structure of a WhileNode. It contains two child nodes. The body child node of the WhileNode does not return a value when being executed. The body node gets executed as long as the evaluation of the condition node returns true. Squeak allows defining a ”while false” loop by sending the message #whileFalse: to an expression. This is realized by inverting the Boolean value of the evaluated condition node.

5.7 Primitive Nodes

Numbered primitives, introduced in Section 2.8, are implemented via primitive nodes. Most of these primitive nodes are implemented via the Truffle DSL (Section 3.2). The functionality of these nodes has been reproduced from the SLANG implementation of the primitive methods in the InterpreterPrimitives class, included in the VMMaker package (Section 2.9).

The Squeak VM contains a few hundred numbered primitives. For this thesis about 70 primitives have been implemented to provide the basic functionality for the Truffle/Squeak interpreter. The implementation includes primitives for SmallIntegers, LargeIntegers, Floats, Strings, array access, object instantiation and others. The interpreter implementation does not contain any named primitives, which have platform dependent functionality.

The primitive nodes are managed via the primitive registry referenced in an SContext object, introduced in Section 5.3.5. The primitive registry returns a CallTarget object, created during interpreter initialization, by providing the primitive index, stored in the method header of a CompiledMethod object.

Listing 5.11 shows an abstract for the Java representation of #primitiveAdd, the Squeak primitive with index one. The primitive is called in the #+ method of SmallInteger, listed in Listing 2.8 on page 25. AddSmallNode is a binary node, which has two child nodes. The first child is always a SelfNode, which returns the receiver of the message. The second child is an ArgumentNode, which returns the argument by accessing the current method frame (Section 5.5).

The specializations for AddSmallNode cover the int type of the left operand. The right Implementation 57

1 public abstract class AddSmallNode extends BinaryNode { 2 3 @Specialization(order=5, rewriteOn=ArithmeticException.class) 4 protected int add(int left, int right) { 5 // possible small integer overflow 6 return Convert.toSmallInt(ExactMath.addExact(left, right)); 7 } 8 9 @Specialization(order=6) 10 protected Object addPossibleOverflow(int left, int right) { 11 try { 12 // create integer value, if possible 13 return Convert.toSmallInt(ExactMath.addExact(left, right)); 14 } catch (ArithmeticException e) { 15 // small integer overflow, create long value 16 return (long) ExactMath.addExact((long) left, (long) right); 17 } 18 } 19 20 @SuppressWarnings("unused") 21 @Generic 22 protected Object failed(Object left, Object right) { 23 throw new PrimitiveFailException(); 24 } 25 }

Listing 5.11: Abstract of AddSmallNode implementation (Java)

operand can be of any type, the add operation for SmallIntegers allows. If a type is not supported by the primitive node, the node rewrites itself to a generic node, which throws a PrimitiveFailException (line 22 in Listing 5.11). This will force the interpreter to execute the fall-back implementation of the Squeak method, implemented under the primitive tag in the method body (Listing 2.8 on page 25).

In Squeak, the result of an arithmetic message send gets ”normalized”. This means the system creates an object for the result that fits best. An add operation of two Integers, which would cause an overflow in Java, returns a large integer object in Squeak. This behaviour has been reproduced by creating specializations, which handle a Java overflow by rewriting itself. The specializations in line 4 and 10 in Listing 5.11 imitate the Squeak behaviour for an add operation of SmallInteger objects. The code in line 6 in Listing 5.11 throws an ArithmeticException, if the operation does not fit into a 31-bit SmallInteger object. This forces the node to rewrite itself to an AddSmallNode, which can handle possible overflows, listed in line 11 to 17.

Similar specializations for LargeIntegers and Floats have been implemented in AddLargeN- ode and AddFloatNode. Implementation 58

5.8 Method Lookup Nodes

The method lookup for the Truffle/Squeak interpreter is implemented via a polymorphic inline cache [18]. This inline cache caches Method objects (see Section 5.3.1) which get resolved by doing a method lookup in the class of a message receiver. The Squeak class of this receiver and the corresponding method gets stored in a DirectLookupNode. When executed, this node returns the cached method object, as long as the Squeak class of the receiver is equal to the cached class object.

If the Squeak class of the message receiver changes during execution and, therefore, require another method lookup, the child node in the DirectLookupNode gets executed. This is a UninitializedLookupNode, which rewrites itself to another DirectLookupNode, by doing a method lookup in the new Squeak class. This chain of nodes represents the inline cache. If the maximum of inline nodes has been reached, the complete chain rewrites itself to a GenericLookupNode. Figure 5.7 illustrates this transformation of the method lookup node.

UninitializedLookupNode DirectLookupNode DirectLookupNode GenericLookupNode

1 1

UninitializedLookupNode DirectLookupNode

2

UninitializedLookupNode

Figure 5.7: Transformation of the Lookup Node

Figure 5.8 shows the class hierarchy for method lookup nodes. The decompiler always creates a UninitializedLookupNode, when a method lookup must be performed. This node contains the rewriting logic for the method lookup. During AST creation, the Unini- tializedLookupNode does not contain any information. The information necessary for the method lookup gets evaluated during execution.

When the node gets executed the first time, a temporary GenericLookupNode gets created and executed. This returns the corresponding Method object, used to create a Direct- LookupNode. The DirectLookupNode gets initialized with the method, the Squeak class of the message receiver and a UninitializedLookupNode for the child node. The executed UninitializedLookupNode gets replaced with the created DirectLookupNode and returns the found method object.

The method lookup for a message selector in a given Squeak class is implemented in Implementation 59

nextNode DirectLookupNode

cachedClass : PointerObject AbstractLookupNode cachedMethod : Method <> executeMethod(VirtualFrame, ... ) : Method abstract executeMethod(VirtualFrame, ... ) : Method

TruffleDSL UninitializedLookupNode

DecompiledMethodNode executeMethod(VirtualFrame, ... ) : Method <> abstract executeMethod(VirtualFrame, CompiledMethod) : Method GenericLookupNode

decompileNode executeMethod(VirtualFrame, ... ) : Method

......

generated classes Inheritance Dependency A B A B B is a subclass of A B depends on / uses A

Figure 5.8: Class hierarchy for Method Lookup Nodes

the class GenericLookupNode. The only child of this node is a DecompiledMethodNode, which is implemented via the Truffle DSL and, therefore, has subclasses generated by the Truffle DSL annotation processor. The execute method of GenericLookupNode calls the method lookup(PointerObject, Selector), implemented in the class SqueakClass (see Listing 5.4 in Section 5.2.2 on page 44).

The lookup method returns a CompiledMethod object, which gets passed to the decompile node during execution. The decompile node has two specializations for a CompiledMethod object, which will be applied based on a guard (Section 3.2). This guard checks if the bytecode of the compiled method has been decompiled already (Section 5.2.1). If so, the execute method of the node returns the stored Method object. Otherwise, the bytecode gets decompiled to an AST and the resulting Method object gets stored in the CompiledMethod object. Then the cached Method object gets returned.

5.9 Message Send Nodes

In Squeak, sending a message is the basic operation for executing the corresponding method of an object, described in Section 2.2.6. A message send is defined by the receiving object, the message selector, and the passed arguments. The method being executed is evaluated during runtime by determining the Squeak class of the receiving object. Based on this Squeak class and the message selector, the method lookup takes place and returns (if found) the CompiledMethod object. Otherwise, the system raises a DoesNotUnderstand exception. Implementation 60

The decompiler creates send nodes by decompiling Send Bytecode instructions, introduced in Section 5.4.5. Squeak has bytecode instructions for a usual messages send, for a message send to a superclass and for a cascaded message send. The corresponding nodes are SendNode, SuperSendNode and MultiSendNode.

5.9.1 SendNode

Figure 5.9 shows an abstract of the structure of a send node and the flow of its information when executed. The figure does not show the argument children. The class node is implemented with the Truffle DSL and evaluates the Squeak class of an object, in this case, the class of the message receiver. The lookup node, introduced in Section 5.8, assesses the method by doing a method lookup. SendNode passes the receiver, the method and the evaluated arguments to the invoke node and executes the method.

receiver

receiver class UninitializedClass

SendNode class lookup UninitializedLookup

method invoke UninitializedInvoke

Figure 5.9: Information flow in a Send Node

The invocation of a method is implemented via a polymorphic inline cache, introduced in Section 5.8. The DirectInvokeNode, which caches a corresponding Method object, has two child nodes. One child is a UninitializedInvokeNode, which contains the rewriting logic. The other child is a dispatch node, which specializes for the different method types, illustrated in Section 5.3.1.

Figure 5.10 shows the various dispatch nodes for the different method types. DispatchNode executes Method objects, decompiled from the corresponding bytecode in a CompiledMethod object. DispatchPrimitiveMethod handles instances of the class PrimitiveMethod, which represent Squeak primitives. The class DispatchQuickMethodNode handles ”quick return” methods, which do not have a method body and just return an object like QuickSelfMethod and QuickReturnMethod do (Section 5.3.1).

DispatchNode and DispatchPrimitiveNode have children nodes that are instances of the Implementation 61

DispatchNode

cachedCallNode : DirectCallNode

executeDispatch(VirtualFrame, ... ) : Object

AbstractDispatchNode DispatchPrimitiveMethodNode <> cachedPrimitiveCallNode : DirectCallNode cachedMethodBodyCallNode : DirectCallNode abstract executeDispatch(VirtualFrame, ... ) : Object executeDispatch(VirtualFrame, ... ) : Object

DispatchQuickMethodNode

executeDispatch(VirtualFrame, ... ) : Object

Figure 5.10: Class diagram of Dispatch Nodes

class DirectCallNode. This is a node class included in the Truffle framework, which performs optimizations on direct calls to CallTarget objects. A direct call implies that the call target remains the same. One optimization uses the uninitializedBody member of an SRootNode, introduced in Section 5.5, to collect call site sensitive profiling information for this particular call.

5.9.2 MultiSendNode

MultiSendNode represents cascaded messages, where multiple message selectors get sent to one receiver. The children are a receiver node and multiple SendNodes. During execution, the receiver gets evaluated and passed to every send node by iterating over the complete children array of SendNodes. The result of the last send node gets returned to the parent node.

5.9.3 SuperSendNode

SuperSendNode represents a message send to the superclass of a Squeak class of the message receiver. In Squeak, this is performed by sending a message to the keyword super. The superclass of a class cannot change during runtime. This enables the caching of the evaluated Method object after a method lookup.

During decompilation, the super send is represented via a UninitializedSuperSendNode. The decompiler passes a receiver node, the message selector and the argument nodes to the UninitializedSuperSendNode.

During the first execution of the UninitializedSuperSendNode, the Squeak class of the currently executed method gets determined by accessing the current method context. The Implementation 62

method lookup takes place by passing the superclass of the evaluated Squeak class. If successful, the lookup returns the corresponding CompiledMethod object. The compiled method gets decompiled to a Method object, if this has not been done already. Depending on the method type, a corresponding dispatch node gets created, described in Section 5.9.1. The UninitializedSuperSendNode replaces itself with a DirectSuperSendNode by passing the dispatch node and the Method object, executes the DirectSuperSendNode and returns the resulting object to the parent node.

There is no need for further node rewriting for a DirectSuperSendNode because the corresponding Method object does not change during runtime.

5.10 Closure Nodes

The implemented closure nodes represent Squeaks ability to define and execute anonymous functions, called Block Closures (Section 2.2.7). The decompiler generates a BlockClo- sureNode for the Push Closure Copy bytecode instruction, introduced in Section 5.4.6.A BlockClosureNode represents the definition of an anonymous function. It contains the BlockClosure object (Section 5.3.3) and a children array of copied value nodes. A copied value node represents the access of a local variable from the enclosing method context, i.e. from the materialized outer frame.

When executing a BlockClosureNode, a reference to the materialized VirtualFrame object and to the corresponding outer method context gets saved in the BlockClosure object. Furthermore, all copied value nodes get executed, and their results get saved in the copiedValues array of the BlockClosure object. In the end, the execute method of the BlockClosureNode returns the modified BlockClosure object.

If a block closure does not modify a local variable from the enclosing method context, the access is realized via a ClosureCopiedValueNode. This node returns the corresponding object, stored in the copiedValues array of the BlockClosure object. This is done by accessing the closure member of the current method context, described in Section 5.3.2.

If a block closure modifies a local variable from the enclosing method context, the internal representation of the local is realized via an indirection vector, introduced in Section 5.4.6. This also influences the access to this local in the original method context. Usually, a local variable gets accessed via ReadLocalNodes and WriteLocalNodes. When using an indirection vector, the access is done via ReadVectorNodes and WriteVectorNodes. The access to this indirection vector in the closure itself is realized via ClosureReadVector- Nodes and ClosureWriteVectorNodes, which is done by accessing the materialized frame object, stored in the method context. Implementation 63

To execute a block closure, a #value message must be sent to it, as described in Section 2.2.7. The ”value” methods in the Squeak class BlockClosure are implemented via primitive methods. The corresponding node in the Truffle/Squeak interpreter is ClosureValueNode. This node has two children: a SelfNode and an InvokeClosureNode. The SelfNode contains the receiver of the #value message, which returns the BlockClosure object, filled with runtime information in the execute method of BlockClosureNode.

The InvokeClosureNode represents a polymorphic inline cache, introduced in Section 5.8, which caches the the BlockClosure object. This inline cache is implemented via Di- rectInvokeClosureNodes.A DirectInvokeClosureNode has two children. One child points to the next node in the node chain. The other child is a DispatchNode, introduced in Section 5.9.1, which represents a direct call node of the CallTarget, stored in the BlockClosure object. Case Study 64

Chapter 6

Case Study

This chapter contains a small Smalltalk example and illustrates the steps; the Truffle/Squeak interpreter has to perform, to execute the example code. The example was taken from Eliot Mirandas Cog blog [24]. The Smalltalk code contains a recursive block closure activation, which did not work properly in Smalltalk-80 and therefore Eliot Miranda implemented the new closure implementation for Squeak.

6.1 Example Source Code

The example consists of two methods, implemented on the class side of the class CaseStudy. Listing 6.1 shows the algorithm for calculating the factorial of the argument value. In line 3, the block closure gets defined and stored into the temporary variable factorial. This block closure has one argument, declared as n. The closure consists of one #ifTrue:ifFalse: message send, sent to the expression n = 1 (line 4 to 6) and returns one if n equals one. Otherwise, it makes the recursive call with n - 1 and multiplies the result by n. Line 8 contains the block closure activation with the argument, passed to the Smalltalk method, which returns the result to the message sender.

1 CaseStudy class>>factorial: value 2 | factorial| 3 factorial :=[ :n | 4 n =1 5 ifTrue: [ 1 ] 6 ifFalse: [ (factorial value: n − 1) ∗ n ] 7 ]. 8 ^factorial value: value

Listing 6.1: Factorial algorithm with a recursive block closure activation (Smalltalk) Case Study 65

Listing 6.2 shows the method, which calculates the factorial of 11 by sending factorial: 11 to self and returns the result. Because this is a class method, the evaluation of self returns CaseStudy class, which will end up by executing the method in Listing 6.1.

CaseStudy class>>testFactorial ^self factorial:11

Listing 6.2: Factorial of 11 (Smalltalk)

The Truffle/Squeak interpreter is a bytecode interpreter and has no access to the Squeak source code. The methods listed in Listing 6.1 and in Listing 6.2 get compiled to Compiled- Method objects and stored in the method dictionary of the metaclass CaseStudy class, which is the class of the class CaseStudy.

Listing 6.3 shows the content of the CompiledMethod object for the method factorial: which is listed in Listing 6.1. Line 1 show the compiled method header content, which is encoded in an Integer value (see Figure 2.8 on page 15). The information in the header tells the VM; the method object has one argument, two temporary variables, three literals and a frame size of 16. Line 2 to 4 show the content of the literals.

The information between line 5 and 27 in Listing 6.3 contains the bytecode of the method. The first column displays the program counter, the second shows the bytecode in hexadec- imal format and the third shows a human readable representation of the corresponding bytecode. The second column of this three-column bytecode representation is the real data stored in the CompiledMethod object. Line 28 to 31 represents the method trailer. The last byte tells the VM the size of the trailer. The rest contains a number, which tells the VM the location of the corresponding source code in the ”sources” or ”changes” file (Section 2.1).

Listing 6.4 shows the content of the CompiledMethod object for the method testFactorial, listed in Listing 6.2. The method header contains following information: the method object has no arguments, no temporary variables, four literals and a frame size of 16. The rest is split into literals, bytecode and method trailer similar to the content in Listing 6.3.

6.2 Interpreter Execution

The written Truffle/Squeak interpreter is a command-line tool. It can execute class methods without arguments in a Squeak image by specifying the Squeak class name and the method name. The interpreter gets started by using the mx tool, a command-line tool included in the OpenJDK Graal project, which manages the development of Java code organized as suites of projects. Case Study 66

1 header: 17303040 2 literal1: #ifTrue:ifFalse: 3 literal2: #factorial: 4 literal3: nil−>CaseStudy class 5 17 <8A 01> push: (Array new: 1) 6 19 <69> popIntoTemp: 1 7 20 <11> pushTemp: 1 8 21 <8F 11 00 11> closureNumCopied: 1 numArgs: 1 bytes 25 to 41 9 25 <10> pushTemp: 0 10 26 <76> pushConstant: 1 11 27 send: = 12 28 <9A> jumpFalse: 32 13 29 <76> pushConstant: 1 14 30 jumpTo: 41 15 32 <8C 00 01> pushTemp: 0 inVectorAt: 1 16 35 <10> pushTemp: 0 17 36 <76> pushConstant: 1 18 37 send: − 19 38 send: value: 20 39 <10> pushTemp: 0 21 40 send: ∗ 22 41 <7D> blockReturn 23 42 <8E 00 01> popIntoTemp: 0 inVectorAt: 1 24 45 <8C 00 01> pushTemp: 0 inVectorAt: 1 25 48 <10> pushTemp: 0 26 49 send: value: 27 50 <7C> returnTop 28 51 176 29 52 53 30 53 216 31 54 253

Listing 6.3: Content of the CompiledMethod object for method factorial: listed in Listing 6.1

The example code discussed in this chapter will be executed in interpretation mode. No Graal compilation takes place. To execute the example in Listing 6.2, the following command must be typed in a command-line: > mx sq "/path/to/squeak.image" "CaseStudy>>testFactorial"

The corresponding output is: > 39916800

When starting the interpreter with the argument "CaseStudy>>testFactorial", the tool has to find the corresponding CompiledMethod object in the specified Squeak image. The string gets interpreted as [Squeak class]>>[Method]. The string "CaseStudy" is used to find the Squeak class object in the SystemDictionary. The system dictionary is a hashed collection, which contains all classes and global objects of the Squeak system. A reference to the system dictionary is stored in the SpecialObjectsArray, which gets created when loading the Squeak image.

When the Squeak class has been found successfully, a method lookup takes place in the class Case Study 67

1 header: 2048 2 literal1: #factorial: 3 literal2: 11 4 literal3: #testFactorial 5 literal4: nil−>CaseStudy class 6 21 <70> self 7 22 <21> pushConstant: 11 8 23 send: factorial: 9 24 <7C> returnTop 10 25 99 11 26 56 12 27 216 13 28 253

Listing 6.4: Content of the CompiledMethod object for method testFactorial listed in Listing 6.2

of this Squeak class with the string "testFactorial". The returned CompiledMethod ob- ject gets decompiled, illustrated in Section 6.2.1, and stored in a Method object. The method invocation is done by calling its call method, which starts the AST interpretation.

At this point, only the testFactorial method and all primitive methods have been decom- piled. The primitives get initialized during start-up and stored in the PrimitiveRegistry (Section 5.3.5). The method invocation in testFactorial can only be resolved during runtime, by evaluating the Squeak class of the message receiver.

6.2.1 Decompilation of ”testFactorial”

The decompiler creates an AST, as illustrated in Figure 6.1, based on the CompiledMethod content, listed in Listing 6.4. The bytecode in line 6 creates a SelfNode and pushes it on the decompiler stack. The bytecode 0x21 is a Push Literal Constant bytecode, which pushes the literal at index one on the stack. The decompiler creates an IntegerLiteralNode for the Integer value "11", located in the literal array of the CompiledMethod object.

The bytecode 0xE0 in line 8 is a Send bytecode, which creates a message send with the message #factorial: located in the literal array at index zero. The bytecode also contains the number of arguments the message send will include, which is one. The decompiler creates a SendNode for this bytecode. The argument node gets popped off the stack, which is the IntegerLiteralNode. Now the top of the stack contains the SelfNode, which will be the receiver node of the send node. The SendNode gets created by passing the SelfNode, the message selector #factorial: and the IntegerLiteralNode. The decompiler pushes this send node on the decompiler stack.

The last bytecode 0x7C is a Return Top bytecode. The decompiler pops the only element left, off the stack, which is the SendNode and creates a ReturnNode with it. Case Study 68

object CaseStudy Self

value 11

IntegerLiteral

SRoot Body Return Send ClassUninitialized

selector #factorial: UninitializedLookup

UninitializedInvoke

Figure 6.1: AST of "testFactorial" after decompilation

The resulting statements array contains one node; that is why the decompiler does not create a block node. The ReturnNode gets stored in a BodyNode and furthermore the body gets stored in an SRootNode. Based on the SRootNode, the Truffle call target gets created, which will be executed by the Truffle runtime. The call target gets stored in a Method object, which is the representation of Truffle/Squeak methods in the interpreter.

The three ”uninitialized” children in Figure 6.1 get created during SendNode creation and are part of every SendNode. These nodes will be replaced during AST execution (see Figure 6.2).

6.2.2 Execution of ”testFactorial”

After decompilation, the interpreter starts executing the AST. It goes down the tree, creates specializations for the executed nodes and replaces the child nodes with these specialized nodes.

Figure 6.2 shows the AST from Figure 6.1 after execution. The ClassUninitializedNode gets replaced by a ClassSObjectNode by determining the Squeak class of the SelfNode. Based on the Squeak class the UninitializedLookupNode rewrites itself to a Direct- LookupNode by doing a method lookup with the message #factorial: in the CaseStudy class. This method lookup forces the decompiler to decompile the CompiledMethod object, representing the "factorial:" method. This process is described in Section 6.2.3.

The SendNode executes the argument nodes and passes the receiver node (CaseStudy), the decompiled Method object and the evaluated arguments array to the execute method of UninitializedInvokeNode. This node rewrites itself to a DirectInvokeNode for the prior passed Method object and creates a DispatchNode, with an OptimizedDirectCallNode, Case Study 69

Self

IntegerLiteral

CaseStudy class SRoot Body Return Send class ClassSObject

DirectLookup UninitializedLookup method „CaseStudy>>factorial:“ CaseStudy receiver

UninitializedInvoke DirectInvoke arguments callTarget [0] = 11 „CaseStudy>>factorial:“

Dispatch OptimizedDirectCall

Figure 6.2: AST of "testFactorial" after execution

provided by the Truffle framework. The OptimizedDirectCallNode represents the actual call of the "factorial:" method, listed in Listing 6.1.

6.2.3 Decompilation of ”factorial:”

The decompilation of the CompiledMethod content, listed in Listing 6.3, is similar to the decompilation, explained in Section 6.2.1. The AST, which represents the Squeak method, listed in Listing 6.1, is shown in Figure 6.3. The BlockNode has three children nodes. WriteLocalUninitializedNode represents the initialization of the indirection vector for the temporary variable "factorial". The UninitializedWriteVectorNode processes the assignment of the block closure definition to the indirection vector.

The ReturnNode represents the answer expression in line 8 in Listing 6.1, which returns the result of the block closure activation. It contains the SendNode, which represents the message send of #value: to a UninitializedReadVectorNode and an ArgumentNode with index zero. The ArgumentNode represents the access of the method argument "value", passed to the method.

The bytecode between line 8 and line 22 in Listing 6.3, which represents the block closure code listed in Listing 6.1, gets decompiled to an AST and stored in the BlockClosure Case Study 70

UninitializedWriteVector ReadLocalUninitialized

BlockClosure

SRoot Body Block ObjectArrayLiteral UninitializedReadVector

index 0 (="value") WriteLocalUninitialized Argument

Send Return ClassUninitialized

selector value: UninitializedLookup

UninitializedInvoke

Figure 6.3: AST of "factorial:" after decompilation

object itself. The corresponding AST is illustrated in Section 6.2.5.

6.2.4 Execution of ”factorial:”

Figure 6.4 shows the AST from Figure 6.3 after execution. The execution of the "facto- rial:" AST is similar to the execution described in Section 6.2.2. There is one difference in the invocation of the Method object, returned through the method lookup. The "val- ue:" method of the Squeak class BlockClosure is a primitive method. That is why the DirectInvokeNode contains a DispatchPrimitiveNode, which has two OptimizedDi- rectCallNodes: One for the primitive, loaded from the primitive registry and the other for the fall-back method body of the "value:" method.

6.2.5 Closure AST

The AST for the block closure in the Squeak method "factorial:", listed in Listing 6.1 contains many send nodes. The structure is relatively complex and, therefore, the execution of this AST is not going to be discussed in this thesis. The execution of the "testFac- torial" and "factorial:" ASTs should suffice to understand the node rewriting in the Truffle/Squeak interpreter.

Figure 6.5 shows the closure AST after decompilation. The AST consists of one IfNode (Section 5.6) with its three children nodes: condition, then and else. The condition Case Study 71

DirectWriteVector ReadLocalObject0

BlockClosure [0] = a BlockClosure object vector

SRoot Body Block ObjectArrayLiteral DirectReadVector

WriteLocalObject Argument

BlockClosure class ClassBlockClosure Return Send DirectLookup UninitializedLookup method „BlockClosure>>value:“ a BlockClosure object receiver UninitializedInvoke DirectInvoke arguments callTarget [0] = 11 „primitive value:“

OptimizedDirectCall

DispatchPrimitiveMethod callTarget „BlockClosure>>value:“

OptimizedDirectCall

Figure 6.4: AST of "factorial:" after execution node is represented by a SendNode, which sends the message #= to an ArgumentNode with an IntegerLiteralNode as an argument. The ArgumentNode contains the index zero and, therefore, accesses the closure argument "n". The IntegerLiteralNode contains the Integer value "1".

The then part of the IfNode was decompiled to an IntegerLiteralNode, which returns the Integer value "1" when executed. The else part represents the Squeak expression in line 6 in Listing 6.1.

The resulting method objects for the message sends of #=, #*, #value: and #- are all primitive method objects. When executing the AST, all these primitive method invocations represented by UninitializedInvokeNodes, would rewrite themselves to DirectInvokeN- odes with a DispatchPrimitiveMethodNode child, like this was done in Section 6.2.4. Case Study 72

0 (="n") Argument index 1 value

IntegerLiteral

selector Send #= ClassUninitialized

UninitializedLookup Argument 0 (="n") condition index 1 UninitializedInvoke value

1 selector value IntegerLiteral SRoot If #- IntegerLiteral thenPart 0 (="n") Argument Send ClassUninitialized index ClosureReadVector #value: elsePart UninitializedLookup selector

UninitializedInvoke Send ClassUninitialized selector #* UninitializedLookup Send ClassUninitialized

UninitializedInvoke UninitializedLookup

UninitializedInvoke

Figure 6.5: AST of block closure after decompilation Evaluation 73

Chapter 7

Evaluation

This chapter illustrates the execution time of benchmarks on the Truffle/Squeak Interpreter compared to the same benchmarks measured in a Squeak environ- ment. The used Truffle/Squeak interpreter is based on the OpenJDK Graal repository [4], revision 16024:6b1bd708e254. All benchmarks, presented in this chapter, were executed on an Intel Core 2 Quad Processor Q8300 with 2.5 GHz and 4 GB of RAM running a 64-bit Windows 7 Professional with Service Pack 1. Benchmarks not included in this discussion may not run or may not run fast.

7.1 Benchmarks

The benchmarks presented in this chapter are all part of The Computer Language Bench- marks Game, which can be found at [1]. The corresponding Squeak implementation of these benchmarks can be downloaded at [10]. The following benchmarks have been compared in this thesis1:

binary-tree: allocation and deallocation of binary trees.

fasta: generates DNA sequences by copying from a given sequence.

n-body: calculates the individual motions between planets and the sun.

spectral-norm: calculates the spectral norm of an infinite matrix.

The benchmarks have been executed on a Squeak VM 4.1.1 and a CogVM 13.33.2776 to compare the execution time of the Truffle/Squeak interpreter to real Squeak VMs. The statement, written by Eliot Miranda in his cog blog, explains best what Cog is:

1The description of every benchmark was gathered from [1]. Evaluation 74

”Cog is a virtual machine designed for Smalltalk and other similar dynamic languages. Cog builds on the Squeak virtual machine adding a stack-to-register- mapping just-in-time compiler, aggressive in-line message caching and effective optimization of Smalltalk’s first-class activation records. Cog is the virtual machine underlying 3D ICC’s Croquet-based enterprise virtual collaboration spaces software, and is the fastest virtual machine for Squeak.” [23]

A Squeak image version 4.3 was used to run the benchmarks on the Squeak VM 4.1.1. The same image was used to execute the benchmarks on the written Truffle/Squeak interpreter. For the CogVM, the benchmarks were executed on a Squeak image version 4.4, because the format of the used 4.3 image is not supported on a CogVM.

Table 7.1 shows the execution time in milliseconds for every benchmark, executed on the different systems. The table contains two rows for the Truffle/Squeak interpreter. The first row contains measurements for the benchmarks, executed in ”interpreted” mode. In this mode, Truffle executes the nodes without compiling them to machine code. Usually, Truffle interpretation is used for development and debugging. The values in the first row are average values, calculated for ten measurements.

The second row shows the values measured in ”compiled” mode. These values are peak performance measurements. Truffle needs some time to warm-up, to generate compiled code for an AST. The values have been calculated by taking the average of execution time for ten iterations after a warm-up phase of 30 iterations.

VM binary-tree fasta n-body spectral-norm

TruffleSq interpreted 25.222 45.104 16.512 158.358 TruffleSq compiled 1.850 1.619 454 687 Cog VM 861 1.303 711 3.887 Squeak VM 3.922 3.307 4.182 14.968

Table 7.1: Execution time of benchmarks in milliseconds

The values for the Cog VM and the Squeak VM were taken, running the benchmarks in the corresponding environment. The values are average values, calculated for ten executions. It must be pointed out, that Cog VM uses a just-in-time compiler, in contrast the Squeak VM executes the benchmarks by interpreting bytecode.

Figure 7.1 shows the ”speed up” for the Truffle/Squeak interpreter in ”interpreted mode”, compared to the Cog VM. The interpreter runs about 25-50 times slower in this mode. This diagram should illustrate the gained speed up, when running Truffle in ”compiled mode”, shown in Figure 7.2. Evaluation 75

Speed Up 1 1 1 1 1 0,9 0,8 0,7 0,6 Squeak VM 0,5 0,39 Cog VM 0,4 Truffle/Squeak in 0,3 0,26 interpreted mode 0,22 0,20 0,2 0,1 0,03 0,03 0,04 0,02 0 binary-tree fasta n-body spectral-norm

Figure 7.1: Speed up for the Truffle/Squeak Interpreter in interpreted mode, compared to Cog VM

Figure 7.2 displays the speed up of the Truffle/Squeak Interpreter when allowing Truffle to compile the ASTs to machine code, compared to the Cog VM. For every executed benchmark, the diagram shows that the written interpreter is faster than the standard Squeak VM. The Cog VM is about two times faster than Truffle/Squeak for executing the binary-tree benchmark. Maybe these can be improved by changing the object model for the Truffle/Squeak interpreter in future extensions.

Speed Up 6 5,66

5

4 Squeak VM 3 Cog VM Truffle/Squeak in 2 1,50 compiled mode 1 1 1 1 1 0,80 0,47 0,39 0,22 0,20 0,26 0 binary-tree fasta n-body spectral-norm

Figure 7.2: Speed up for the Truffle/Squeak Interpreter in compiled mode, compared to Cog VM

The Truffle/Squeak Interpreter runs the spectral-norm benchmark 5,5 times faster than the Cog VM. The reason for this is the underlying algorithm for this benchmark. The algorithm performs matrix multiplications with instances of Array. The interpreter represents these Evaluation 76

instances of Array via nodes with real Java arrays. Obviously, Truffle can apply optimiza- tions on these Java arrays.

7.2 Completeness

The written interpreter can load a complete Squeak 4.3 image and process the included objects. It can access all objects of a Squeak image. Furthermore, the implemented decompiler can decompile all methods in a Squeak 4.3 image to Truffle/Squeak ASTs. These are more than 50.000 CompiledMethod objects. However, it is not able to execute all these ASTs in the Truffle/Squeak interpreter. One reason for this is Squeaks handling of platform dependent code, located in external modules. These modules contain C code, which get called via named primitives to access platform dependent code, like accessing the file system.

The second reason is that the implemented Truffle/Squeak interpreter does not support all numbered primitives, available in a Squeak VM. Figure 7.3 illustrates the number of implemented primitives in blue bars. The primitives are grouped by their functionality. The yellow bars indicate the absence of primitive implementations.

implemented Primitive Groups not implemented

External VM Implementor Unwind Cog File Misc Input/Output Control System Storage Closures Stream Quick Push Float Large Integer Integer

0 5 10 15 20 25 30 35 40 45

Number of Primitives

Figure 7.3: Number of implemented primitives Evaluation 77

The decision, which primitives to implement, has been arisen by providing basic functionality and by enabling the execution of benchmarks, introduced in Section 7.1. Furthermore, it is uncertain, that the system indeed uses all primitives included in the Squeak VM. For instance, the LargeInteger primitive group contains seven primitives, which are no longer called by the Squeak methods. These seven primitives have been removed from Figure 7.3 and, therefore, count 11 primitives for this group, instead of 18.

Squeak reserves 255 primitives for loading instance variables from an object, which are not included in Figure 7.3. The written Truffle/Squeak interpreter supports these primitives. Consequently, the implemented Truffle/Squeak interpreter supports about 75 percent of the numbered Squeak primitives, including the primitives for loading instance variables. Future Work 78

Chapter 8

Future Work

The implemented Truffle/Squeak Interpreter supports Squeak code, which contains the basic functionality. Furthermore, it is possible to execute benchmarks, introduced in Section 7.1, in acceptable execution time. The Truffle implementation of JavaScript [30, 34, 33] shows that one can still apply many potential improvements, in consideration of execution time, for the Truffle/Squeak interpreter. Following extensions/improvements are conceivable for the implemented interpreter:

Compact Class Index Some Squeak Objects get converted to Java primitives, based on their compact class index during object heap creation, introduced in Section 5.2. The compact class index can be changed in the Squeak environment, which would break the current interpreter implementation. The Special Objects Array contains references to this special treated Squeak classes. A new implementation could compare the Squeak class of an object, with the references in the Special Objects Array.

Exception Handling The implemented interpreter does not support the Squeak exception handling mech- anism, implemented via five primitives categorized in the Unwind primitive group (Figure 7.3). The implementation of these primitives via primitive nodes would enable the correct handling of exceptions in Squeak methods in the Truffle/Squeak interpreter.

Numbered Primitives There are still many numbered primitives available (Figure 7.3), which have not been implemented yet. One has to clarify, which primitives still get executed via Squeak methods and implement these primitives via primitive nodes.

Squeak Object Model One has to adapt the current implemented Squeak object model for the lazy-loading Future Work 79

mechanism for Squeak objects read from a Squeak image, which has been implemented subsequently. Furthermore, the interpreter could get some performance boost by adapting the underlying object model on the needs of the Graal compiler. One has to verify if the developed common object storage model (OSM), introduced in [31], would be applicable for the Truffle/Squeak interpreter.

Named Primitives Squeak executes named primitives for providing platform dependent functionality, such as file system access. These primitives are written in C and are not part of the Squeak image. They get loaded via dynamic libraries. The loading mechanism of these modules is realized via five numbered primitives, categorized in the External primitive group (Figure 7.3), which can be implemented via five primitive nodes. The developed TruffleC interpreter [15] could produce relief for executing the native Squeak code, provided via dynamic C libraries. Related Work 80

Chapter 9

Related Work

Many prototype language implementations have been implemented using Truffle, which can keep up with the performance of the corresponding Interpreters/VMs created for these languages. Furthermore, there are some Squeak VMs written in other languages. The following list will give a short description of alternatives:

TruffleJS [30, 34, 33] A Truffle implementation of JavaScript. Supports ECMAScript 5.1 and executes JavaScript code with a peak performance, similar to leading JavaScript engines.

TruffleC [15] A Truffle C interpreter with the dynamic execution of C code, which executes benchmarks from the Computer Benchmarks Game [1], similar to the performance of the GCC.

JRuby+Truffle [2] A Truffle implementation of JRuby. The current implementation does not cover the complete Ruby ecosystem. It can run some micro-benchmarks and passes about half of the language specification of RubySpec [5].

TruffleSOM [22] A Truffle implementation for a Smalltalk dialect, called SOM (Simple Object Machine). In contrast to the Truffle/Squeak interpreter, written for this thesis, TruffleSOM parses SOM source code instead of decompiling Squeak bytecode.

JSqueak [3] A complete Smalltalk development environment written in Java, which runs a Squeak Mini2.1 image. This runs between 10 and 30 times slower than the standard Squeak VM.

SqueakJS [13] Related Work 81

A Squeak VM written in JavaScript and runs entirely in a web browser without the need of any plugin. Conclusions 82

Chapter 10

Conclusions

This thesis describes the implementation of a Squeak bytecode interpreter which can execute Squeak code, loaded from a Squeak image. The interpreter can execute this Squeak bytecode by lazy-loading the Squeak image, which reduces the time for initialization to a minimum. The benchmark results show that the Graal compiler included in the framework can produce fast machine code when following some simple rules, implementing an AST interpreter. However, the performance can still be improved by applying changes/optimizations.

This thesis shows that it is possible to implement an interpreter, based on the Truffle framework, which interprets data produced by another system. In this case, objects created and stored via the Squeak environment. Furthermore, this work illustrates another way for generating ASTs; by decompiling bytecode instead of parsing source code.

It is conceivable to extend the interpreter to an entirely operational high-performance Squeak VM with a graphical user interface. One has to develop a way to execute the named primitives, provided by dynamic C libraries. This would enable the Truffle/Squeak interpreter to execute the complete Squeak code, provided in the Squeak image. List of Figures 83

List of Figures

2.1 Squeak components ...... 4 2.2 A binary message ...... 9 2.3 A keyword message ...... 9 2.4 Classes and Their Metaclasses Part I ...... 11 2.5 Classes and Their Metaclasses Part II ...... 12 2.6 A method dictionary and its content ...... 13 2.7 Content of a compiled method object ...... 14 2.8 Content of compiled method header ...... 15 2.9 Last byte of a compiled method object ...... 16 2.10 The system dictionary and its content ...... 17 2.11 Squeak image header ...... 18 2.12 Squeak Object Content ...... 20 2.13 Base header structure ...... 21

3.1 Truffle workflow ...... 28 3.2 Subclasses generated by the DSL processor ...... 33

4.1 Truffle/Squeak architecture overview ...... 35

5.1 Class diagram for the Java implementation for loading a Squeak image . . . 38 5.2 Squeak object class hierarchy ...... 41 5.3 Truffle/Squeak Methods ...... 45 5.4 Basic Truffle/Squeak AST ...... 55 5.5 Structure of an IfNode ...... 55 5.6 Structure of a WhileNode ...... 55 5.7 Transformation of the Lookup Node ...... 58 5.8 Class hierarchy for Method Lookup Nodes ...... 59 5.9 Information flow in a Send Node ...... 60 5.10 Class diagram of Dispatch Nodes ...... 61

6.1 AST of "testFactorial" after decompilation ...... 68 6.2 AST of "testFactorial" after execution ...... 69 6.3 AST of "factorial:" after decompilation ...... 70 6.4 AST of "factorial:" after execution ...... 71 6.5 AST of block closure after decompilation ...... 72 List of Figures 84

7.1 Truffle/Squeak in Interpreted Mode ...... 75 7.2 Truffle/Squeak in Compiled Mode ...... 75 7.3 Number of implemented primitives ...... 76 List of Tables 85

List of Tables

2.1 Magic number interpretation ...... 19 2.2 Base header format field ...... 22 2.3 Object header types ...... 22 2.4 Squeak object type classification ...... 23

5.1 Java representation of Squeak objects ...... 40 5.2 Compact class index (one-based) ...... 40

7.1 Execution time of benchmarks in milliseconds ...... 74 List of Listings 86

List of Listings

2.1 Literals ...... 7 2.2 Smalltalk Arrays ...... 8 2.3 Smalltalk #ifTrue:/#ifFalse: ...... 9 2.4 Message cascade example ...... 9 2.5 Block examples ...... 10 2.6 Examples for implementing loops ...... 11 2.7 ObjectMemory>>#fetchClassOf: ...... 23 2.8 SmallInteger>>#+ ...... 25 2.9 StandardFileStream>>#primSize: ...... 25

3.1 Example of a Truffle base node ...... 29 3.2 Example of an expression base node ...... 30 3.3 Example of a node implementation with children ...... 30 3.4 Truffle SL AddNode ...... 32

5.1 CompleteHeap class...... 39 5.2 LazyHeap class ...... 39 5.3 Pointer class...... 42 5.4 lookup(PointerObject, Selector):CompiledMethod ...... 44 5.5 SArguments class...... 47 5.6 interpretInstruction(int):void ...... 48 5.7 pushConstant(Object):void ...... 48 5.8 Push Array Bytecodes ...... 52 5.9 TypedNode class...... 54 5.10 Condition-based assignment ...... 56 5.11 AddSmallNode class...... 57

6.1 CaseStudy class>>#factorial: ...... 64 6.2 CaseStudy class>>#testFactorial ...... 65 6.3 Compiled Method Content of CaseStudy class>>#factorial: ...... 66 6.4 Compiled Method Content of CaseStudy class>>#testFactorial ..... 67 Bibliography 87

Bibliography

[1] The Computer Language Benchmarks Game. http://benchmarksgame.alioth. debian.org/.

[2] JRuby+Truffle Wiki. https://github.com/jruby/jruby/wiki/Truffle.

[3] JSqueak - a Smalltalk Interpreter written in Java. http://news.squeak.org/2008/ 06/21/jsqueak-smalltalk-interpreter-written-in-java/.

[4] OpenJDK Graal Repository. http://hg.openjdk.java.net/graal/graal.

[5] RubySpec: a project to write an executable specification for the Ruby Programming Language. http://rubyspec.org/.

[6] IEEE Standard for Floating-Point Arithmetic. IEEE Std 754-2008, pages 1–70, Aug 2008.

[7] Graal Project, 2014. http://openjdk.java.net/projects/graal/.

[8] Squeak 4.3 VM, 2014. http://ftp.squeak.org/4.3/.

[9] Ken Arnold, James Gosling, and David Holmes: Java(TM) Programming Language, The (4th Edition). Addison-Wesley Professional, 2005, ISBN 0321349806.

[10] Nicolas Cellier: Squeak Implementation of Shootout Benchmarks. http://ss3. gemstone.com/ss/Shootout.html/Overview.

[11] Gilles Duboscq, Lukas Stadler, Thomas Wurthinger,¨ Doug Simon, Christian Wimmer, and Hanspeter M¨ossenb¨ock: Graal IR: An extensible declarative intermediate repre- sentation. In Proceedings of the Asia-Pacific Programming Languages and Compilers Workshop, 2013.

[12] Gilles Duboscq, Thomas Wurthinger,¨ Lukas Stadler, Christian Wimmer, Doug Simon, and Hanspeter M¨ossenb¨ock: An Intermediate Representation for Speculative Optimiza- tions in a Dynamic Compiler. In Proceedings of the 7th ACM Workshop on Virtual Ma- Bibliography 88

chines and Intermediate Languages, VMIL ’13, pages 1–10, New York, NY, USA, 2013. ACM, ISBN 978-1-4503-2601-8. http://doi.acm.org/10.1145/2542142.2542143.

[13] Bert Freudenberg, Dan H.H. Ingalls, Tim Felgentreff, Tobias Pape, and Robert Hirschfeld: SqueakJS: A Modern and Practical Smalltalk That Runs in Any Browser. In Proceedings of the 10th ACM Symposium on Dynamic Languages, DLS ’14, pages 57–66, New York, NY, USA, 2014. ACM, ISBN 978-1-4503-3211-8. http://doi.acm. org/10.1145/2661088.2661100.

[14] Adele Goldberg and David Robson: Smalltalk-80: The Language and Its Implemen- tation. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1983, ISBN 0-201-11371-6.

[15] Matthias Grimmer, Manuel Rigger, Roland Schatz, Lukas Stadler, and Hanspeter M¨ossenb¨ock: TruffleC: Dynamic Execution of C on a Java Virtual Machine. In Proceedings of the 2014 International Conference on Principles and Practices of Programming on the Java Platform: Virtual Machines, Languages, and Tools, PPPJ ’14, pages 17–26, New York, NY, USA, 2014. ACM, ISBN 978-1-4503-2926-2. http: //doi.acm.org/10.1145/2647508.2647528.

[16] Mark Guzdial: Squeak: Object-Oriented Design with Multimedia Applications. Prentice Hall PTR, Upper Saddle River, NJ, USA, 1st edition, 2000, ISBN 0130280283.

[17] Mark Guzdial and Kimberly Rose: Squeak: Open Personal Computing and Multimedia. Prentice Hall PTR, Upper Saddle River, NJ, USA, 1st edition, 2001, ISBN 0130280917.

[18] Urs H¨olzle, Craig Chambers, and David Ungar: Optimizing Dynamically-Typed Object- Oriented Languages With Polymorphic Inline Caches. In Proceedings of the European Conference on Object-Oriented Programming, ECOOP ’91, pages 21–38, London, UK, UK, 1991. Springer-Verlag, ISBN 3-540-54262-0. http://dl.acm.org/citation. cfm?id=646149.679193.

[19] Dan Ingalls, Ted Kaehler, John Maloney, Scott Wallace, and Alan Kay: Back to the Future: The Story of Squeak, a Practical Smalltalk Written in Itself. In Proceedings of the 12th ACM SIGPLAN Conference on Object-oriented Programming, Systems, Languages, and Applications, OOPSLA ’97, pages 318–326, New York, NY, USA, 1997. ACM, ISBN 0-89791-908-4. http://doi.acm.org/10.1145/263698.263754.

[20] Brian W. Kernighan: The C Programming Language. Prentice Hall Professional Technical Reference, 2nd edition, 1988, ISBN 0131103709.

[21] Wilf R. LaLonde and John R. Pugh: Inside Smalltalk: Vol. 1. Prentice-Hall, Inc., Upper Saddle River, NJ, USA, 1990, ISBN 0-13-468414-1. Bibliography 89

[22] Stefan Marr: TruffleSOM - a SOM Smalltalk implemented on top of Oracle’s Truffle Framework. https://github.com/smarr/TruffleSOM.

[23] Eliot Miranda: Cog Blog: ”About Cog”. http://www.mirandabanda.org/cogblog/ about-cog/.

[24] Eliot Miranda: Closures Part I, June 2008. http://www.mirandabanda.org/ cogblog/2008/06/07/closures-part-i/.

[25] Eliot Miranda: Closures Part II - The Bytecodes, July 2008. http://www. mirandabanda.org/cogblog/2008/07/22/closures-part-ii-the-bytecodes/.

[26] Eliot Miranda: The Cog Smalltalk Virtual Machine: writing a JIT in a high-level dynamic language. In VMIL 2011 - The 5th workshop on Virtual Machines and In- termediate Languages, 2011. http://design.cs.iastate.edu/vmil/2011/papers/ p03-miranda.pdf.

[27] Oscar Nierstrasz, Stephane Ducasse, and Damien Pollet: Squeak by Example. Square Bracket Associates, 2009, ISBN 3952334103, 9783952334102.

[28] Michael Paleczny, Christopher Vick, and Cliff Click: The Java Hotspot Server Compiler. In Proceedings of the 2001 Symposium on Java(TM) Virtual Machine Research and Technology Symposium - Volume 1, JVM’01, pages 1–1, Berkeley, CA, USA, 2001. USENIX Association. http://dl.acm.org/citation.cfm?id=1267847.1267848.

[29] Helmut Rohregger: Building Blocks for an AST Interpreter of Smalltalk Bytecode, 2013.

[30] Andreas W¨oß: Self-Optimizing AST Interpreter for JavaScript. Master’s thesis, Jo- hannes Kepler University, Linz, 2013.

[31] Andreas W¨oß, Christian Wirth, Daniele Bonetta, Chris Seaton, Christian Humer, and Hanspeter M¨ossenb¨ock: An Object Storage Model for the Truffle Language Im- plementation Framework. In Proceedings of the 2014 International Conference on Principles and Practices of Programming on the Java Platform: Virtual Machines, Languages, and Tools, PPPJ ’14, pages 133–144, New York, NY, USA, 2014. ACM, ISBN 978-1-4503-2926-2. http://doi.acm.org/10.1145/2647508.2647517.

[32] Thomas Wurthinger:¨ Visualization of Program Dependence Graphs. Master’s thesis, Johannes Kepler University, Linz, 2007.

[33] Thomas Wurthinger,¨ Christian Wimmer, Andreas W¨oß, Lukas Stadler, Gilles Duboscq, Christian Humer, Gregor Richards, Doug Simon, and Mario Wolczko: One VM to Rule Them All. In Proceedings of the 2013 ACM International Symposium on Bibliography 90

New Ideas, New Paradigms, and Reflections on Programming & Software, Onward! 2013, pages 187–204, New York, NY, USA, 2013. ACM, ISBN 978-1-4503-2472-4. http://doi.acm.org/10.1145/2509578.2509581.

[34] Thomas Wurthinger,¨ Andreas W¨oß, Lukas Stadler, Gilles Duboscq, Doug Simon, and Christian Wimmer: Self-Optimizing AST Interpreters. In Proceedings of the 8th Symposium on Dynamic Languages, DLS ’12, pages 73–82, New York, NY, USA, 2012. ACM, ISBN 978-1-4503-1564-7. http://doi.acm.org/10.1145/2384577.2384587. Eidesstattliche Erkl¨arung 91

Appendix A

Eidesstattliche Erkl¨arung

Ich erkl¨are an Eides statt, dass ich die vorliegende Masterarbeit selbstst¨andig und ohne fremde Hilfe verfasst, andere als die angegebenen Quellen und Hilfsmittel nicht benutzt bzw. die w¨ortlich oder sinngem¨aß entnommenen Stellen als solche kenntlich gemacht habe. Die vorliegende Masterarbeit ist mit dem elektronisch ubermittelten¨ Textdokument identisch.

Linz, April 2016

Helmut Rohregger, BSc. Curriculum Vitae

Personal Information

Name Helmut Rohregger Adress Baumbachstraße 25 / 25, 4020 Linz, Austria Email [email protected]

Work

2014-present Software developer at Softwaretechnik GesmbH in Traun, writing logistic software in C#/.NET for an international commercial enterprise. 2007-2014 Croupier for Casinos Austria in Linz, dealing roulette, black jack and poker. 2006-2007 Supervisor of the students bar at the Johannes Kepler Hostel in Linz 2003-2006 Bartender in the students bar at the Johannes Kepler Hostel in Linz. Bartender on different events and music festivals.

Education

2013-present MSc in Software Engineering/Computer Science at the Johannes Kepler University in Linz. 2010-2013 BSc in Software Engineering at the Johannes Kepler University in Linz. 2003-2005 Computer Science at the Johannes Kepler University in Linz with a premature withdrawal from my studies. 2000-2003 Computer Science at the Johannes Kepler University in Linz with a positive graduation of the first diploma examination. 1994-1999 Higher technical school for telecommunication engineering with specialization in Computer Science at the HTL in Braunau.