Skip to content
← Back

src/ir/innate_operation_generator.ghul

1
namespace IR is
2
use System.Reflection.Metadata.ILOpCode
3
use System.Exception
4
5
use Values.Call
6
use Values.Value
7
8
use Values.BLOCK
9
10
class INNATE_OPERATION_GENERATOR(
11
_logger: Logging.Logger,
12
_boxer: VALUE_BOXER,
13
_brancher: IR.BRANCHER
14
) is
15
_type_and_operation_handlers: Collections.MAP[string,(INNATE,BLOCK) -> void]
16
_operation_handlers: Collections.MAP[string,(INNATE,BLOCK) -> void]
17
_type_handlers: Collections.MAP[string,(INNATE,BLOCK) -> void]
18
19
init(..) is
20
_type_and_operation_handlers = Collections.MAP[string,(INNATE,BLOCK) -> void]()
21
_operation_handlers = Collections.MAP[string,(INNATE,BLOCK) -> void]()
22
_type_handlers = Collections.MAP[string,(INNATE,BLOCK) -> void]()
23
24
_type_and_operation_handlers["compare.order"] = (value: INNATE, context: BLOCK) -> void is gen_compare_order(value, false, context); si
25
_type_and_operation_handlers["compare.uorder"] = (value: INNATE, context: BLOCK) -> void is gen_compare_order(value, true, context); si
26
_type_and_operation_handlers["compare.value"] = (value: INNATE, context: BLOCK) -> void is gen_compare_value(value, context); si
27
_type_and_operation_handlers["compare.reference"] = (value: INNATE, context: BLOCK) -> void is gen_compare_reference(value, context); si
28
29
30
_type_and_operation_handlers["bool.not"] = (value: INNATE, context: BLOCK) -> void is gen_bool_not(value, context); si
31
_type_and_operation_handlers["reference.read"] = (value: INNATE, context: BLOCK) -> void is gen_reference_read(value, context); si
32
_type_and_operation_handlers["reference.assign"] = (value: INNATE, context: BLOCK) -> void is gen_reference_assign(value, context); si
33
34
_type_handlers["bool"] = (value: INNATE, context: BLOCK) -> void is gen_short_circuit_bool(value, context); si
35
_type_handlers["arithmetic"] = (value: INNATE, context: BLOCK) -> void is gen_arithmetic(value, context); si
36
_type_handlers["arithmetic_decimal"] = (value: INNATE, context: BLOCK) -> void is gen_arithmetic_decimal(value, context); si
37
_type_handlers["compare_decimal"] = (value: INNATE, context: BLOCK) -> void is gen_compare_decimal(value, context); si
38
_type_handlers["string"] = (value: INNATE, context: BLOCK) -> void is gen_string(value, context); si
39
_type_handlers["range"] = (value: INNATE, context: BLOCK) -> void is gen_range(value, context); si
40
41
// _type_handlers["object"] = (value: INNATE, context: CONTEXT) -> void is gen_object(value, context); si;
42
si
43
44
lower(value: INNATE, block: BLOCK) is
45
if !IoC.CONTAINER.instance.build_flags.want_assembler then
46
return
47
fi
48
49
let innate_name = value.innate_name
50
51
if try_handle(innate_name, _type_and_operation_handlers, value, block) then
52
return
53
fi
54
55
let operation_name = value.op_name
56
57
if try_handle(operation_name, _operation_handlers, value, block) then
58
return
59
fi
60
61
let type_name = value.type_name
62
63
if try_handle(type_name, _type_handlers, value, block) then
64
return
65
fi
66
67
throw System.Exception("unknown innate operation: {value.function.innate_name}")
68
si
69
70
try_handle(
71
key: string,
72
handlers: Collections.Map[string,(INNATE,BLOCK) -> void],
73
value: INNATE,
74
block: BLOCK) -> bool is
75
76
if handlers.contains_key(key) then
77
let handler = handlers[key]
78
79
handler(value, block)
80
return true
81
fi
82
return false
83
si
84
85
gen_arithmetic(value: INNATE, block: BLOCK) is
86
gen_arguments(value, block)
87
88
// FIXME: do we need to convert the inputs or outputs?
89
block.add(Values.INSTRUCTION(_arithmetic_op_code(value.op_name)))
90
si
91
92
// The operation half of the intrinsic's declared name, which is
93
// what the declaring assembly writes in its INTRINSIC_ATTRIBUTE.
94
_arithmetic_op_code(operation: string) -> ILOpCode static =>
95
if operation =~ "add" then ILOpCode.ADD
96
elif operation =~ "sub" then ILOpCode.SUB
97
elif operation =~ "mul" then ILOpCode.MUL
98
elif operation =~ "div" then ILOpCode.DIV
99
elif operation =~ "div_un" then ILOpCode.DIV_UN
100
elif operation =~ "rem" then ILOpCode.REM
101
elif operation =~ "rem_un" then ILOpCode.REM_UN
102
elif operation =~ "neg" then ILOpCode.NEG
103
elif operation =~ "not" then ILOpCode.NOT
104
elif operation =~ "and" then ILOpCode.AND
105
elif operation =~ "or" then ILOpCode.OR
106
elif operation =~ "xor" then ILOpCode.XOR
107
elif operation =~ "shl" then ILOpCode.SHL
108
elif operation =~ "shr" then ILOpCode.SHR
109
elif operation =~ "ushr" then ILOpCode.SHR_UN
110
else throw System.Exception("unexpected arithmetic operation: {operation}")
111
fi
112
113
gen_bool_not(value: INNATE, block: BLOCK) is
114
assert value.arguments.count == 1 else "not bool missing arguments"
115
116
gen_arguments(value, block)
117
118
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
119
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
120
si
121
122
gen_short_circuit_bool(value: INNATE, block: BLOCK) is
123
assert value.arguments.count == 2 else "short circuit bool missing arguments"
124
125
let brancher = _brancher.get_for(block)
126
127
let exit = IR.LABEL()
128
let operation = value.op_name
129
130
// push speculative result:
131
if operation =~ "and_then" then
132
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
133
else
134
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_1))
135
fi
136
137
// branch to exit if speculative result is correct based on evaluating left argument:
138
if operation =~ "and_then" then
139
brancher.branch(IR.BRANCH.Z, value.arguments[0], exit)
140
else
141
brancher.branch(IR.BRANCH.NZ, value.arguments[0], exit)
142
fi
143
144
// speculative result may not be correct, discard it:
145
block.add(Values.INSTRUCTION(ILOpCode.POP))
146
147
// result is value of right side:
148
gen_argument(value, 1, block)
149
150
brancher.label(exit)
151
si
152
153
gen_compare_order(value: INNATE, is_unsigned: bool, block: BLOCK) is
154
assert value.arguments.count == 2 else "compare order missing arguments"
155
156
let actual_operation = value.actual_operation
157
158
// A direct `<>` — written as the operator or reached as an
159
// ordinary member call, so carrying no rewritten operation —
160
// wants the ordering value itself. The relational operators
161
// arrive here only via the parser's rewrite onto `<>`, with
162
// the surface operator in actual_operation.
163
if !actual_operation? \/ actual_operation =~ "<>" then
164
gen_order(value, is_unsigned, block)
165
return
166
fi
167
168
gen_arguments(value, block)
169
170
let needs_not mut = false
171
let is_greater_than mut = false
172
173
if actual_operation =~ ">" then
174
is_greater_than = true
175
elif actual_operation =~ ">=" then
176
needs_not = true
177
elif actual_operation =~ "<" then
178
// less than: the default of both flags
179
elif actual_operation =~ "<=" then
180
is_greater_than = true
181
needs_not = true
182
else
183
throw System.Exception("unexpected compare order operation: {actual_operation}")
184
fi
185
186
let op_code =
187
if is_greater_than then
188
if is_unsigned then ILOpCode.CGT_UN else ILOpCode.CGT fi
189
else
190
if is_unsigned then ILOpCode.CLT_UN else ILOpCode.CLT fi
191
fi
192
193
block.add(Values.INSTRUCTION(op_code))
194
195
if needs_not then
196
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
197
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
198
fi
199
si
200
201
// `a <> b` as the operator itself: the ordering value, composed
202
// from the two comparison instructions so it agrees with them at
203
// every operand width and in both signednesses — `a - b` would
204
// wrap to the wrong sign at the type's extremes. The operands
205
// are spilled to temps so their side effects happen once.
206
gen_order(value: INNATE, is_unsigned: bool, block: BLOCK) is
207
let less_op = if is_unsigned then ILOpCode.CLT_UN else ILOpCode.CLT fi
208
let greater_op = if is_unsigned then ILOpCode.CGT_UN else ILOpCode.CGT fi
209
210
let left = TEMP(block, "order", value.arguments[0])
211
let right = TEMP(block, "order", value.arguments[1])
212
213
// greater - less: negative when a < b, positive when a > b,
214
// zero when neither
215
block.add(left.load())
216
block.add(right.load())
217
block.add(Values.INSTRUCTION(greater_op))
218
219
block.add(left.load())
220
block.add(right.load())
221
block.add(Values.INSTRUCTION(less_op))
222
223
block.add(Values.INSTRUCTION(ILOpCode.SUB))
224
si
225
226
gen_string(value: INNATE, block: BLOCK) is
227
let innate_name = value.function.innate_name
228
229
if innate_name =~ "string.equals" then
230
gen_arguments(value, block)
231
232
block.add(Values.CALL_STRING_EQUALITY())
233
234
if value.actual_operation =~ "!~" then
235
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
236
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
237
fi
238
else
239
throw System.Exception("unexpected string innate operation: {innate_name}")
240
fi
241
si
242
243
gen_compare_reference(value: INNATE, block: BLOCK) is
244
assert value.arguments.count == 2 else "compare reference missing arguments"
245
246
let left mut = value.arguments[0]
247
let right mut = value.arguments[1]
248
249
// A pointer against null. A pointer is a value type, so the
250
// mismatch below would box it, and the CLI has no boxed
251
// form for a pointer: what came out was an object reference
252
// that is never null, so a null pointer read as present.
253
// A null pointer is a zero native integer, which is what
254
// the null side pushes instead.
255
if _is_pointer(left) \/ _is_pointer(right) then
256
if _is_null(left) then
257
left = _native_zero(right.type!)
258
fi
259
260
if _is_null(right) then
261
right = _native_zero(left.type!)
262
fi
263
elif left.is_value_type != right.is_value_type then
264
left = _boxer.box_if_value(left)
265
right = _boxer.box_if_value(right)
266
fi
267
268
block.add(left)
269
block.add(right)
270
271
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
272
273
if value.actual_operation =~ "!=" then
274
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
275
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
276
fi
277
si
278
279
gen_compare_value(value: INNATE, block: BLOCK) is
280
assert value.arguments.count == 2 else "compare value missing arguments"
281
282
gen_arguments(value, block)
283
284
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
285
286
// `!=` is the negation of `==`; `!~` is the negation of `=~`.
287
// An enum's `=~` reaches this innate (see ENUM_EQUALITY_OPERATOR),
288
// so both spellings of inequality negate the `ceq` here.
289
if value.actual_operation =~ "!=" \/ value.actual_operation =~ "!~" then
290
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
291
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
292
fi
293
si
294
295
_is_pointer(value: Value) -> bool static =>
296
if let type = value.type then type.is_pointer else false fi
297
298
_is_null(value: Value) -> bool static =>
299
if let type = value.type then type.is_null else false fi
300
301
// The zero of a pointer type, typed as the pointer it is being
302
// compared against so the block it goes into agrees about what
303
// is on the stack.
304
_native_zero(pointer_type: Semantic.Types.Type) -> Value static is
305
let block = BLOCK(pointer_type)
306
307
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
308
block.add(Values.INSTRUCTION(ILOpCode.CONV_U))
309
310
return block
311
si
312
313
gen_range(value: INNATE, block: BLOCK) is
314
assert value.arguments.count == 2 else "create range incorrect arguments"
315
316
gen_arguments(value, block)
317
318
let prefix = "['ghul-runtime']"
319
320
if value.op_name !~ "inclusive" /\ value.op_name !~ "exclusive" then
321
throw Exception("unexpected range innate operation: {value.innate_name}")
322
fi
323
324
// The range's own type names the struct being constructed,
325
// so the constructor is reachable from a symbol rather than
326
// from the type's spelling.
327
block.add(Values.NEW_RANGE(value.type))
328
329
return
330
si
331
332
gen_reference_read(value: INNATE, context: BLOCK) is
333
// TODO
334
si
335
336
gen_reference_assign(value: INNATE, context: BLOCK) is
337
// TODO
338
si
339
340
gen_arithmetic_decimal(value: INNATE, block: BLOCK) is
341
gen_arguments(value, block)
342
343
let op = value.op_name
344
345
let method_name: string mut
346
let is_unary mut = false
347
348
if op =~ "neg" then
349
method_name = "op_UnaryNegation"
350
is_unary = true
351
elif op =~ "add" then
352
method_name = "op_Addition"
353
elif op =~ "sub" then
354
method_name = "op_Subtraction"
355
elif op =~ "mul" then
356
method_name = "op_Multiply"
357
elif op =~ "div" then
358
method_name = "op_Division"
359
else
360
throw System.Exception("unexpected arithmetic_decimal operation: {op}")
361
fi
362
363
let decimal_type = IoC.CONTAINER.instance.innate_symbol_lookup.get_decimal_type()
364
365
block.add(
366
Values.DECIMAL_CALL(
367
decimal_type,
368
method_name,
369
_decimal_parameters(decimal_type, if is_unary then 1 else 2 fi)))
370
si
371
372
_decimal_parameters(decimal_type: Semantic.Types.Type, count: int) -> Collections.List[Semantic.Types.Type] is
373
let result = Collections.LIST[Semantic.Types.Type]()
374
375
for i in 0..count do
376
result.add(decimal_type)
377
od
378
379
return result
380
si
381
382
gen_compare_decimal(value: INNATE, block: BLOCK) is
383
gen_arguments(value, block)
384
385
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
386
let decimal_type = lookup.get_decimal_type()
387
let bool_type = lookup.get_bool_type()
388
let operands = _decimal_parameters(decimal_type, 2)
389
390
let op = value.op_name
391
392
if op =~ "equality" then
393
block.add(Values.DECIMAL_CALL(bool_type, "op_Equality", operands))
394
395
if value.actual_operation =~ "!=" then
396
block.add(Values.INSTRUCTION(ILOpCode.LDC_I4_0))
397
block.add(Values.INSTRUCTION(ILOpCode.CEQ))
398
fi
399
400
return
401
fi
402
403
if op =~ "order" then
404
let actual = value.actual_operation
405
let method_name: string mut
406
407
if actual =~ ">" then
408
method_name = "op_GreaterThan"
409
elif actual =~ ">=" then
410
method_name = "op_GreaterThanOrEqual"
411
elif actual =~ "<" then
412
method_name = "op_LessThan"
413
elif actual =~ "<=" then
414
method_name = "op_LessThanOrEqual"
415
else
416
throw System.Exception("unexpected compare_decimal.order operation: {actual}")
417
fi
418
419
block.add(Values.DECIMAL_CALL(bool_type, method_name, operands))
420
421
return
422
fi
423
424
throw System.Exception("unexpected compare_decimal operation: {op}")
425
si
426
427
gen_argument(value: INNATE, index: int, block: BLOCK) is
428
block.add(value.arguments[index])
429
si
430
431
gen_arguments(value: INNATE, block: BLOCK) is
432
for a in value.arguments do
433
block.add(a)
434
od
435
si
436
si
437
si