Hey guys,

So today, I’m going to talk about the modularity of Opal compiler. It comes mostly from a new object, the compilationContext, that Opal move down in the compilation tool chain.

Quick reminder : Opal compilation tool chain

Screen Shot 2013-05-28 at 10.22.46 AM

To sum up, the smalltalk code is parsed with RBParser to the RBAST. Then the OCSemanticAnalyser annotates it. The ASTTranslator visits it, building the IR with IRBuilder at the same time. Lastly, the RByteCodeGenerator generates bytecode out of the IR.

What is Compilation Context ?

When you compile down Smalltalk code, either if it is down to binary code or to byte code, you want to move some kind of data along the compilation chain. For example, the old compiler used to move :

  • the requestor : this object corresponds to the morph(1) that holds the text to compile. It is very useful because it permits for example to write error messages directly in the morph text instead of raising an error.For example, if you compile ‘[ :y | [ :y | y ] ]’, you will get ‘[ :y | [ Name already defined -> :y | y ] ]’ instead of a raised Error.
  • the failedBlock : this block is executed when the compilation fails (usually because the source code was incorrect and an error was raised). Sometimes it is [ ^ #failedDoIt ] or [ ^ nil ] but you can set whatever you want.

This feature is already really nice. But we (Marcus and I) didn’t like to have to give all these things through arguments, resulting in methods with excessive numbers of arguments. For example, you had in the old compiler:

To increase the modularity of Opal, we needed to add even more arguments, most of them being boolean. We decided to add the CompilationContext object, that basically holds all these arguments plus some new ones.

Compilation Context capabilities

Now, let’s have a look at Opal’s compilation context and its capabilities.

Object subclass: #CompilationContext
instanceVariableNames: 'requestor failBlock noPattern class category logged interactive options environment parserClass semanticAnalyzerClass astTranslatorClass bytecodeGeneratorClass'
classVariableNames: ''
poolDictionaries: ''
category: 'OpalCompiler-Core-FrontEnd'

Its instance variables are all the modularity power of Opal. Let’s talk about them one by one.

Basic data

  • requestor : this object corresponds to the morph(1) that holds the text to compile.
  • failBlock : this block is executed when the compilation fails.
  • noPattern : this boolean tells you if the text to compile starts by the method body or by the method selector.
  • class : the class of the compiled object to know how to compile instance variable.
  • category : the category where the compiled method should be put.
  • logged : tells you if the new compiledMethod creation will be logged in the changes file
  • interactive : tells you if this compilation happens in interactive mode (Warnings are raised and stops the compilation) or in non interactive mode (Warnings are shown on console logging and does not stop the compilation).
  • environment : give you the Smalltalk environment (usually an instance of Smalltalk image) where you compile. This is used for example for remote compilation. This is in a beta state

Compiler options

Opal compiler has now options. You can specify them either on a class by overriding #compiler method.
Example :
InstructionStream class>>#compiler
^ super compiler options: #(+ optLongIvarAccessBytecodes)
Or on a method.
Example :
<compilerOptions: #(- optInlineIf)>
^ #myNonBooleanObject ifTrue: [ 1 ] ifFalse: [ 0 ]

List of options :

+ optInlineIf
+ optInlineIfNil
+ optInlineAndOr
+ optInlineWhile
+ optInlineToDo
+ optInlineCase

This set of options permit to disable automatic inlining of some message, as #ifTrue:, #and:, …

– optLongIvarAccessBytecodes
This option permits to force the compiler to generate long bytecode for accessing the object instance variables, it used for objects with a lot of instance variables and MethodContext. (this option is the only one not set by default).

+ optOptimizeIR
This option permit to disable some basic IR optimisations.

Compiler plugins

If you want, you can also replace a full part of the compilation chain. According to the scheme on the top of this article, you can replace :

    • the parserClass : permits to parse Smalltalk code and returns you an RB AST. You could implement a scannerless RBParser.
    • the semanticAnalyzerClass : permits to annotate the RB AST nodes. You could implement one with more annotations that you may use then in a personalized astTranslator for more optimizations.
    • the astTranslatorClass : permits to translate RBAST to Opal IR intermediate representation.
    • the bytecodeGeneratorClass : permits to generate byte code out of the Intermediate representation.

Have fun using Opal modularity capabilities 🙂

(1) a morph is a UI window in Pharo Smalltalk