Skip to content
← Back

src/syntax/process/generate-il/generate_il_definitions.ghul

1
namespace Syntax.Process is
2
use System.Reflection.Metadata.ILOpCode
3
use IO.Std
4
5
use System.Text.StringBuilder
6
7
use Logging
8
use Trees
9
use Source
10
11
use IR
12
use IR.Values
13
14
use Ghul.Pipes
15
16
17
// Type-level walks: namespace, class, partial and impl blocks, trait, struct, union,
18
// variant, enum, plus bodies, properties, pragmas and test attributes.
19
partial GENERATE_IL is
20
enter_class(symbol: Semantic.Symbols.Classy) is
21
ADDRESS.reset_id()
22
TEMP.reset_id()
23
LABEL.reset_id()
24
25
// A body that fails to emit unwinds to `Classy.walk`, which
26
// restores the symbol table and the diagnostics but knows
27
// nothing about emission state. Without this the abandoned
28
// body emitter is still installed when the next class opens,
29
// and everything that class emits is reported against it.
30
_context.current_srm_body_emitter = null
31
si
32
33
leave_class(symbol: Semantic.Symbols.Classy) is
34
si
35
36
pre(`namespace: Definitions.NAMESPACE) -> bool => super.pre(`namespace)
37
38
visit(`namespace: Definitions.NAMESPACE) is
39
let context = _symbol_table.current_closure_context
40
41
gen_closures(context.get_closures())
42
43
emit_pending_state_machines()
44
45
super.visit(`namespace)
46
si
47
48
// A `reset` the type never declared has no body walked from a
49
// tree, so its throw is deposited here.
50
_gen_synthesized_reset(symbol: Semantic.Symbols.Classy) is
51
let found = symbol.find_direct("reset")
52
53
let reset =
54
if let group: Semantic.Symbols.FUNCTION_GROUP = found then
55
group.functions
56
|> Ghul.Pipes.find(f => f.throws_not_supported)
57
elif let single: Semantic.Symbols.Function = found /\ single.throws_not_supported then
58
single
59
else
60
null
61
fi
62
63
if let reset0 = reset then
64
65
let assembly_emitter = _context.srm_assembly_emitter
66
let enclosing = _context.current_srm_body_emitter
67
let body = IR.Emitter.SRM_METHOD_BODY_EMITTER()
68
69
_context.current_srm_body_emitter = body
70
71
try
72
IR.Values.NEW_NOT_SUPPORTED_EXCEPTION().gen(_context)
73
74
body.op(System.Reflection.Metadata.ILOpCode.THROW)
75
76
assembly_emitter.handles.set_body_offset(
77
reset0, body.flush(assembly_emitter))
78
finally
79
_context.current_srm_body_emitter = enclosing
80
yrt
81
fi
82
si
83
84
pre(`class: Definitions.CLASS) -> bool is
85
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`class))!
86
87
enter_class(symbol)
88
89
let owner = cast Semantic.Symbols.Symbol?(symbol.owner)!
90
91
92
enter_scope(`class)
93
94
95
return false
96
si
97
98
visit(`class: Definitions.CLASS) is
99
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`class))!
100
101
let closures = symbol.get_closures()
102
103
104
_gen_synthesized_reset(symbol)
105
106
leave_class(symbol)
107
108
leave_scope(`class)
109
110
gen_closures(closures)
111
112
emit_pending_state_machines()
113
si
114
115
// A `partial` block contributes members to a type declared
116
// elsewhere; the member order collects them onto that type's one
117
// row. The class header (extends/implements/attributes) is the primary
118
// definition's job, so this only walks the block's members; closures
119
// declared here are emitted by visit(partial), after their bodies are
120
// walked.
121
pre(`partial: Definitions.PARTIAL) -> bool is
122
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`partial))!
123
124
enter_class(symbol)
125
126
127
enter_scope(`partial)
128
129
130
return false
131
si
132
133
visit(`partial: Definitions.PARTIAL) is
134
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`partial))!
135
136
// Closures declared in this block are tracked on its injection
137
// scope and emitted here, after the member walk has generated
138
// their bodies - the target's primary definition runs before
139
// this block is walked and would emit them bodyless.
140
let injection = cast Semantic.INJECTION_SCOPE?(scope_for(`partial))
141
let closures = injection?.get_closures()
142
143
144
145
leave_class(symbol)
146
147
leave_scope(`partial)
148
149
gen_closures(closures)
150
151
emit_pending_state_machines()
152
si
153
154
// An `impl` block contributes members to its target exactly as
155
// `partial` does. The interface the target now implements is carried on
156
// the target symbol, so the InterfaceImpl row is written from the
157
// primary definition.
158
pre(`impl: Definitions.IMPL) -> bool is
159
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`impl))!
160
161
enter_class(symbol)
162
163
164
enter_scope(`impl)
165
166
167
return false
168
si
169
170
visit(`impl: Definitions.IMPL) is
171
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`impl))!
172
173
// As visit(partial): closures declared in this block are tracked
174
// on its injection scope and emitted here, after the member walk
175
// has generated their bodies.
176
let injection = cast Semantic.INJECTION_SCOPE?(scope_for(`impl))
177
let closures = injection?.get_closures()
178
179
180
181
leave_class(symbol)
182
183
leave_scope(`impl)
184
185
gen_closures(closures)
186
187
emit_pending_state_machines()
188
si
189
190
pre(`trait: Definitions.TRAIT) -> bool is
191
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`trait))!
192
let owner = cast Semantic.Symbols.Symbol?(symbol.owner)!
193
194
enter_class(symbol)
195
196
197
enter_scope(`trait)
198
199
200
201
return false
202
si
203
204
visit(`trait: Definitions.TRAIT) is
205
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`trait))!
206
207
let closures = symbol.get_closures()
208
209
210
211
leave_class(symbol)
212
213
leave_scope(`trait)
214
215
gen_closures(closures)
216
217
emit_pending_state_machines()
218
si
219
220
pre(`struct: Definitions.STRUCT) -> bool is
221
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`struct))!
222
let owner = cast Semantic.Symbols.Symbol?(symbol.owner)!
223
224
enter_class(symbol)
225
226
227
228
enter_scope(`struct)
229
230
231
return false
232
si
233
234
visit(`struct: Definitions.STRUCT) is
235
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`struct))!
236
237
let closures = symbol.get_closures()
238
239
240
241
_gen_synthesized_reset(symbol)
242
243
leave_class(symbol)
244
245
leave_scope(`struct)
246
247
gen_closures(closures)
248
si
249
250
pre(`union: Definitions.UNION) -> bool is
251
let symbol = cast Semantic.Symbols.Classy?(symbol_for(`union))!
252
253
enter_class(symbol)
254
255
let owner = cast Semantic.Symbols.Symbol?(symbol.owner)!
256
257
258
enter_scope(`union)
259
260
`union.name.walk(self)
261
262
if `union.arguments? then
263
`union.arguments.walk(self)
264
fi
265
266
`union.modifiers.walk(self)
267
268
for member in `union.body |> filter(m => !isa Definitions.VARIANT(m)) do
269
member.walk(self)
270
od
271
272
273
274
275
276
leave_class(symbol)
277
278
leave_scope(`union)
279
280
for member in `union.body |> filter(m => isa Definitions.VARIANT(m)) do
281
member.walk(self)
282
od
283
284
285
return true
286
si
287
288
visit(`union: Definitions.UNION) is
289
// all done in pre
290
si
291
292
pre(variant: Definitions.VARIANT) -> bool is
293
let symbol = cast Semantic.Symbols.Classy?(symbol_for(variant))!
294
295
enter_class(symbol)
296
297
let owner = cast Semantic.Symbols.Symbol?(symbol.owner)!
298
299
300
enter_scope(variant)
301
302
303
return false
304
si
305
306
visit(variant: Definitions.VARIANT) is
307
let symbol = cast Semantic.Symbols.Classy?(symbol_for(variant))!
308
309
let closures = symbol.get_closures()
310
311
gen_unit_variant_singleton(symbol)
312
313
314
leave_class(symbol)
315
316
leave_scope(variant)
317
318
gen_closures(closures)
319
si
320
321
// For a unit variant (no constructor parameters / no
322
// instance fields), emit a static `_instance` field plus a
323
// `.cctor` that allocates exactly one instance. `IR.Values.NEW`
324
// redirects construction sites for unit variants to a `ldsfld`
325
// of this field, so `RED()` (or `NONE[int]()`) returns the
326
// cached singleton instead of allocating each call.
327
gen_unit_variant_singleton(symbol: Semantic.Symbols.Classy) is
328
if let variant_symbol: Semantic.Symbols.VARIANT = symbol then
329
if !variant_symbol.is_unit_variant then
330
return
331
fi
332
333
_gen_unit_variant_singleton_srm(variant_symbol)
334
fi
335
si
336
337
// The rows for the field and this initializer are written by the
338
// structure walk, which cannot encode a body; the body is
339
// deposited here, in the pass that encodes every other body.
340
_gen_unit_variant_singleton_srm(variant: Semantic.Symbols.VARIANT) is
341
let assembly_emitter = _context.srm_assembly_emitter
342
let handles = assembly_emitter.handles
343
344
let instance = handles.unit_variant_instance(variant)
345
346
if !instance? then
347
return
348
fi
349
350
let constructors = cast Semantic.Symbols.FUNCTION_GROUP?(variant.find_direct("init"))
351
352
if !constructors? \/ constructors.functions.count == 0 then
353
return
354
fi
355
356
let body = IR.Emitter.SRM_METHOD_BODY_EMITTER()
357
358
body.new_object(_context.resolve_call_target(constructors.functions[0]))
359
body.stsfld(_context.resolve_unit_variant_instance(variant))
360
body.ret()
361
362
handles.set_unit_variant_initializer_body(variant, body.flush(assembly_emitter))
363
si
364
365
366
367
gen_closures(closures: Collections.Iterable[Semantic.Symbols.Closure]?) is
368
if !closures? then
369
return
370
fi
371
372
for closure in closures |> filter(c => !c.is_anon_func /\ !c.is_delegate) do
373
closure.gen_frame(_context, _symbol_loader)
374
od
375
376
for closure in closures |> filter(c => c.pack_thunk?) do
377
_gen_pack_thunk(closure)
378
od
379
si
380
381
pre(`enum: Definitions.ENUM) -> bool is
382
let symbol = cast Semantic.Symbols.Symbol?(symbol_for(`enum))!
383
384
let owner = cast Semantic.Symbols.Symbol?(symbol.owner)!
385
386
387
enter_scope(`enum)
388
389
390
return false
391
si
392
393
visit(`enum: Definitions.ENUM) is
394
395
leave_scope(`enum)
396
si
397
398
pre(enum_member: Definitions.ENUM_MEMBER) -> bool is
399
return false
400
si
401
pre(body: Bodies.BLOCK) -> bool is
402
add(Values.MAX_STACK(64))
403
404
return super.pre(body)
405
si
406
407
pre(expression_body: Bodies.EXPRESSION) -> bool is
408
add(Values.MAX_STACK(64))
409
410
return super.pre(expression_body)
411
si
412
413
visit(expression_body: Bodies.EXPRESSION) is
414
// An `=> body` whose function awaits is emitted after this
415
// walk, by the state machine's own emitter, which puts its
416
// value in the frame's result. Nothing is emitted here: the
417
// `ret` below would return out of MoveNext without ever
418
// completing the builder.
419
if let casm = _current_async_state_machine, function = current_function then
420
if function == casm.function then
421
super.visit(expression_body)
422
423
return
424
fi
425
fi
426
427
let body_value = expression_body.expression.value
428
if let value = body_value, value_type = value.type then
429
let return_type = current_function!.return_type!
430
431
// A void body discards whatever its expression leaves
432
// standing, the same as a block body's tail: emit it
433
// for its effects, then pop what it pushed.
434
if return_type.is_void /\ !value_type.is_void then
435
add(value)
436
add(Values.INSTRUCTION(ILOpCode.POP))
437
else
438
add(_boxer.box_if_needed(value, return_type))
439
fi
440
elif let
441
self.current_function? /\
442
current_function.return_type? /\
443
!current_function.return_type.is_void
444
then
445
// Diverging body (`=> throw E`): the body's own IL
446
// already terminates control flow. Emit a default-value
447
// trailer so the unreachable fall-off is verifiable,
448
// matching statement-bodied functions that end in throw.
449
add(Values.DEFAULT_RETURN(current_function.return_type!))
450
fi
451
452
add(Values.RET())
453
454
super.visit(expression_body)
455
si
456
457
pre(property: Definitions.PROPERTY) -> bool is
458
return true
459
si
460
461
pre(pragma: Definitions.PRAGMA) -> bool is
462
process_pragma(pragma.pragma, true)
463
return false
464
si
465
466
visit(pragma: Definitions.PRAGMA) is
467
process_pragma(pragma.pragma, false)
468
si
469
470
process_pragma(pragma: Pragmas.PRAGMA, is_enter: bool) is
471
let name = pragma.name.to_string()
472
473
if name =~ "IL.output" then
474
if pragma.arguments.expressions.count != 1 then
475
_logger.error(pragma.arguments.location, "expected 1 argument")
476
return
477
fi
478
479
let argument = pragma.arguments.expressions[0]
480
481
if !isa Expressions.Literals.STRING(argument) then
482
_logger.error(pragma.arguments.location, "expected a string literal argument")
483
return
484
fi
485
486
let file_name = argument.value_string
487
488
if is_enter then
489
_il_output_depth = _il_output_depth + 1
490
491
// Only a statement inside a body has instructions to
492
// measure; a pragma on a declaration marks nothing.
493
if _block_context.is_in_block then
494
current_block.add(Values.ENTER_FILE(file_name))
495
fi
496
else
497
_il_output_depth = _il_output_depth - 1
498
499
if _block_context.is_in_block then
500
current_block.add(Values.LEAVE_FILE(file_name))
501
fi
502
fi
503
fi
504
si
505
506
si
507
si