File: 04-Code_Generation.md

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (296 lines) | stat: -rw-r--r-- 11,197 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
Code Generation
===============

The compiler library contains the functionality required to emit code in the [intermediate
language](md:/Language_Reference/Intermediate_Language), compile it into machine code for the
current platform, and to later execute the code (usually by binding it to a `Function`). The
functionality related to the intermediate language is covered in detail in the [language
reference](md:/Language_Reference/Intermediate_Language). This section covers the types in more
detail.

In addition to the code generation facilities for the intermediate language, the library also
contains a few utilities that can be used to make code generation more convenient.


Operands
--------

The value [stormname:core.asm.Operand] represents an operand for an instruction in the intermediate
language. The different types of operands are described in detail
[here](md:/Language_Reference/Intermediate_Language/Operands).

The `Operand` class has a number of cast constructors that allow implicit conversion from registers
([stormname:core.asm.Reg]), variables ([stormname:core.asm.Var]), labels
([stormname:core.asm.Label]), [references](md:/Language_Reference/Intermediate_Language/References)
([stormname:core.asm.Ref]), and source positions ([stormname:core.lang.SrcPos]). There are also a
number of functions that create operands from various types of literals.

The `Operand` type has the usual operators for comparison, and `toS` for creating a string
representation. The empty operand (created by the default constructor) can also be checked for using
the `empty` and `any` functions as usual. It also has the following operands for accessing the
contents of the operand:

```stormdoc
@core.asm.Operand
- .type()
- .size()
- .readable()
- .writable()
```

The depending on the value returned from `type`, it is possible to extract the contents of the
operand by calling the appropriate members below. These functions check the type internally and
throws an error (`InvalidValue`) if they are used incorrectly.

```stormdoc
@core.asm.Operand
- .constant()
- .reg()
- .var()
- .offset()
- .condFlag()
- .block()
- .label()
- .ref()
- .srcPos()
- .obj()
- .tObj()
```

Instruction
-----------

The [stormname:core.asm.Instr] class represents a single instruction in the intermediate
representation. The instruction format is described in detail
[here](md:/Language_Reference/Intermediate_Language/Instructions). In short, each instruction has
two operands. The first one may be written to, and the second is only ever read from. The `Instr`
class is created by one of many free functions with the same name as the op-codes described in the
language reference. These functions perform basic checking of operand sizes and properties, and
throw an error if they are used incorrectly.

The class itself has only a few members as follows:

```stormdoc
@core.asm.Instr
- .op()
- .src()
- .dest()
- .mode()
- .size()
- .alterSrc(*)
- .alterDest(*)
- .alter(*)
```

For function class, the derived class [stormname:core.asm.TypeInstr] is used. This adds the members
`type` and `member`. The `type` member contains a type description of the parameter or return type.
The `member` member is only used for function call instructions and determines whether the called
function is a member function or not.


Arena
-----

An [stormname:core.asm.Arena] describes the platform-specific bits of code generation. The function
[stormname:full:core.asm.arena()] can be used to create an `Arena` for the current platform.
Creating an `Arena` for another platform allows generating code for another platform. This is,
however, not very useful currently, as it is not possible to save generated code to disk. It does,
however, open up for future cross-platform builds.


Listing
-------

The [stormname:core.asm.Listing] object represents a sequence of instructions, alongside information
about local variables, labels, and blocks. The concepts behind the listing is described in more
detail [here](md:/Language_Reference/Intermediate_Language/Listings). In short, the listing can be
seen as representing a *namespace* for labels, blocks, and variables. As such, it is able to
generate new names for these concepts that are only usable in that particular listing. These names
are represented as instances of the types [stormname:core.asm.Label], [stormname:core.asm.Block],
and [stormname:core.asm.Var] respectively. These created names can then be used as operands to
instructions that are then added to the listing. Labels can also be added directly to the listing to
allow other instructions to reference other instructions easily (e.g. for jump instructions).

### Creation

A listing stores the expected return type of the function it will be bound to. As such, it can be
created in any of the following ways:

- [stormname:core.asm.Listing.__init()]

  Create a listing for a function that returns `void`.

- [stormname:core.asm.Listing.__init(core.Bool, core.asm.TypeDesc)]

  Create a listing for a function that may be a member function (based on `member`), and returns the
  type `result`.

There are also two overloads that accept an `arena`. This is to allow the listing to format
backend-specific things properly. It does not otherwise affect how the `Listing` works.


### Adding and Inspecting Instructions

Instructions and labels are added using the `<<` operator. The `Listing` also provides the usual
`empty`, `any`, `count`, and `[]` members to allow iterating through the instructions in the
listing. Labels that appear before a particular instruction are retrieved using the `labels`
function.


### Labels

Labels are created using the function [stormname:core.asm.Listing.label()]. It returns a new, unique
label that is valid in the listing. As mentioned above, labels can be used as operands to
instructions, and they can be inserted in between instructions. Each label is assumed to be added to
the listing exactly once.


### Variables and Blocks

Variables in a `Listing` are arranged in a tree of blocks, similarly to many block-scoped languages.
The `Listing` provides the following members to create and manipulate blocks:

```stormdoc
@core.asm.Listing
- .root()
- .createBlock(*)
- .parent(core.asm.Block)
- .isParent(*)
- .allBlocks()
- .addCatch(*)
- .catchInfo(*)
```

Variables can then be added to blocks using any of the `createVar` overloads. Parameters are treated
specially, and are created by the `createParam` overloads. Variables and parameters can then be
inspected and manipulated using any of the following functions:

```stormdoc
@core.asm.Listing
- .accessible(*)
- .isParam(*)
- .paramDesc(*)
- .varInfo(*)
- .parent(core.asm.Var)
- .prev(core.asm.Var)
- .allVars(*)
- .allParams()
- .moveParam(*)
- .moveFirst(*)
```


ExprResult
----------

The value [stormname:core.lang.ExprResult] is a utility class that can be convenient to use for code
generation in various languages. It is, for example, used in Basic Storm.

The `ExprResult` is intended to represent a result from evaluating an expression. It encapsulates
the idea that an expression may either result in some value (represented as a
[stormname:core.lang.Value]), or it may instead stop execution before it evaluates to a value (e.g.
returning from the function or throwing an exception). As such, an `ExprResult` thus has a separate
representation of *will never return*, which is distinct from *returns, but produces no value*.

It provides the following members:

```stormdoc
@core.lang.ExprResult
- .__init(*)
- .type()
- .value()
- .empty()
- .nothing()
- .asRef(*)
```

The free function `noReturn` creates the special value that indicates that the expression will not
return.


CodeGen
-------

The utility class [stormname:core.lang.CodeGen] encapsulates a [stormname:core.asm.Listing] (stored
in the member `l`) that is being used to output instructions to. It also contains additional state
that is typically needed during code generation. This state is a [stormname:core.lang.RunOn] that
indicates the current thread, and a [stormname:core.asm.Block] that represents the current block.
This means that instead of passing three variables to code generation functions, one can instead use
a single `CodeGen` object.

In addition to bundling three concepts together, it provides a few convenience functions:

```stormdoc
@core.lang.CodeGen
- .child(*)
- .createParam(*)
- .createVar(*)
```

The `createVar` functions returns a [stormname:core.lang.VarInfo] object. This object stores a
[stormname:core.asm.Var] alongside a value that records if this variable needs to be activated after
it has been constructed. It is typically not necessary to inspect this value manually, it is enough
to call the [stormname:core.lang.VarInfo.created(core.lang.CodeGen)] member as soon as the variable
has been initialized.


There are also a number of free functions that accomplish some common tasks:

```stormdoc
@core.lang
- .allocObject(*)
- .spillRegisters(*)
- .createFnCall(*)
```


CodeResult
----------

The utility class [stormname:core.lang.CodeResult] is a class that can be used by language
implementations to communicate the location of data during code generation. To illustrate the usage,
imagine you wish to generate code for a particular AST node. The node procudes a result of type `T`.
You wish to use the result eventually, but it is not important where it is located. This wish can be
expressed as a `CodeResult` and passed to the code generation function of the AST node. The AST node
inspects the wish, and updates the `CodeResult` with the location of the result. The caller can then
inspect the location and use it as desired.

The benefit of this approach is that it is in most cases not important where the result from an AST
node is located. So if the AST node represents a local variable, the AST node may just inform the
caller that the result is located in a variable already, and thus avoids a copy if possible.


A `CodeResult` can represent three different types of *wishes*, as indicated by the constructors:

- [stormname:core.lang.CodeResult.__init()]

  The caller is not interested in the result. Only evaluate the AST node for side-effects is
  applicable, and skip creating the result if suitable.

- [stormname:core.lang.CodeResult.__init(core.lang.Value, core.asm.Var)]

  The caller asks for a result of type `type`, and it needs to be stored in the provided variable.
  This means that the AST node is not able to propose another location in order to avoid copies.

- [stormname:core.lang.CodeResult.__init(core.lang.Value, core.asm.Block)]

  The caller asks for a result of type `type`. The variable needs to be visible in `block`, but
  apart from that it does not matter which variable is used. As such, this allows the AST node to
  propose an already existing variable to avoid copying.


The AST node then inspects the `CodeResult` instance to find out which type was asked for, and may
propose a location using the following functions:

```stormdoc
@core.lang.CodeResult
- .type()
- .needed()
- .suggest(*)
- .location(*)
- .created(*)
- .safeLocation(*)
- .split(*)
```

Finally, the caller may call `location` to retrieve the created value.