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
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'
Its instance variables are all the modularity power of Opal. Let’s talk about them one by one.
- 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
Opal compiler has now options. You can specify them either on a class by overriding
^ super compiler options: #(+ optLongIvarAccessBytecodes)
Or on a method.
<compilerOptions: #(- optInlineIf)>
^ #myNonBooleanObject ifTrue: [ 1 ] ifFalse: [ 0 ]
List of options :
This set of options permit to disable automatic inlining of some message, as
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).
This option permit to disable some basic IR optimisations.
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