forked from ballerina-platform/nballerina
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bir.bal
873 lines (763 loc) · 28.4 KB
/
bir.bal
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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
import wso2/nballerina.types as t;
import wso2/nballerina.comm.err;
import wso2/nballerina.comm.diagnostic as d;
public type SemType t:SemType;
public type Position d:Position;
public type File d:File;
public type Module object {
public function getId() returns ModuleId;
// A SemType of a potentially recursive type uses integers to refer to definitions
// which are in arrays in this.
public function getTypeContext() returns t:Context;
public function getFunctions() returns Function[];
public function hasCaptureInsn(int i) returns boolean;
public function generateFunctionCode(int i) returns FunctionCode|err:Semantic|err:Unimplemented;
public function symbolToString(int partIndex, Symbol sym) returns string;
// Get the File for a give part index
public function getPartFile(int partIndex) returns File;
public function getPartFiles() returns File[];
public function finish() returns err:Semantic?;
};
public type ModuleId readonly & record {|
string org;
[string, string...] names;
// this omits the version, because programs cannot have two versions of the same module
|};
public type ExternalSymbol readonly & record {|
ModuleId module;
string identifier;
|};
public type ModuleDefn record {|
InternalSymbol symbol;
readonly...;
|};
# A label is an index of a basic block in the basicBlock.
public type Label int;
# A RegionIndex is an index of a region in the regions array.
public type RegionIndex int;
public type Function FunctionDefn | AnonFunction;
public type FunctionBase record {|
FunctionDecl decl;
# The position of the definition
Position position;
# The index of the function in the module
int index;
readonly...;
|};
# The definition of a function.
public type FunctionDefn readonly & record {|
*ModuleDefn;
*FunctionBase;
# Index of source part in which the definition occurs
int partIndex;
|};
public type AnonFunction readonly & record {|
*FunctionBase;
Function parent;
|};
public type InternalSymbol readonly & record {|
boolean isPublic;
string identifier;
|};
public type Symbol InternalSymbol|ExternalSymbol;
public type FunctionRef ExternalFunctionRef | InternalFunctionRef;
public type ExternalFunctionRef readonly & record {|
ExternalSymbol symbol;
t:FunctionSignature erasedSignature;
t:FunctionSignature signature;
|};
public type InternalFunctionRef readonly & record {|
int index;
t:FunctionSignature erasedSignature;
t:FunctionSignature signature;
|};
# A function's code is represented as a factored control flow graph.
# (as described in Choi et al 1999 https://dl.acm.org/doi/abs/10.1145/381788.316171)
# This is like a control flow graph, except that basic blocks
# can contain instructions that can potentially panic.
# Execution starts in basic block 0 with with value of param i
# in register i (0-based). (Not thinking about varargs yet.)
# Control flow between basic blocks is explicit: it does not
# flow implicitly between the members of `blocks`.
public type FunctionCode record {|
# Basic blocks indexed by label
BasicBlock[] blocks = [];
# Registers indexed by number
Register[] registers = [];
# Single-entry single-exit regions
Region[] regions = [];
|};
# This is sufficient until we support named parameters and default values.
public type FunctionDecl t:FunctionSignature;
public type Region record {|
Label entry;
Label? exit = ();
RegionIndex? parent = ();
RegionKind kind;
|};
public enum RegionKind {
# Region whose entry block has a CondBranchInsn and is the destination of a backward branch
REGION_LOOP,
# Region whose entry block has a CondBranchInsn and is not a destination of a backward branch
REGION_COND
}
# A basic block.
# Normal control flow proceeds implicitly through the members of the insns array.
# Basic blocks can contain potentially panicking instructions (PPIs).
# Whether an instruction is a PPI is determined based on the name of the instruction.
# If a basic block does contain a PPI, then onPanic is the label of the
# basic block to which control should flow if any of those instructions panic:
# this basic block must start with a CatchInsn.
public type BasicBlock record {|
# Label for the BB, unique within the function
readonly Label label;
# List of the instructions in this basic block
Insn[] insns = [];
# non-nil if this contains any PPI
Label? onPanic = ();
# Backend can use this to generate human-readable label
string? name = ();
|};
public type InsnRef readonly & record {|
// Label of the block containing the insn
Label block;
// Index in the block of the insn
int index;
|};
public function createBasicBlock(FunctionCode code, string? name = ()) returns BasicBlock {
int label = code.blocks.length();
BasicBlock bb = { label, name };
code.blocks.push(bb);
return bb;
}
public function discardBasicBlocksFrom(FunctionCode code, BasicBlock firstToDiscard) {
code.blocks.setLength(firstToDiscard.label);
}
public function lastInsnRef(BasicBlock bb) returns InsnRef {
return { block: bb.label, index: bb.insns.length() - 1 };
}
public const PARAM_REGISTER_KIND = "param";
public const VAR_REGISTER_KIND = "var";
public const FINAL_REGISTER_KIND = "final";
public const CAPTURED_REGISTER_KIND = "captured";
public const NARROW_REGISTER_KIND = "narrow";
public const TMP_REGISTER_KIND = "tmp";
public const ASSIGN_TMP_REGISTER_KIND = "=tmp";
public type DeclRegisterKind PARAM_REGISTER_KIND|VAR_REGISTER_KIND|FINAL_REGISTER_KIND;
public type RegisterKind DeclRegisterKind|CAPTURED_REGISTER_KIND|NARROW_REGISTER_KIND|TMP_REGISTER_KIND|ASSIGN_TMP_REGISTER_KIND;
public type RegisterBase record {|
RegisterKind kind;
# Unique identifier within a function
# Always >= 0
int number;
SemType semType;
Position? pos;
string? name;
|};
public type DeclRegister ParamRegister|VarRegister|FinalRegister;
public type Register DeclRegister|NarrowRegister|TmpRegister|AssignTmpRegister|CapturedRegister;
public type RegisterScope readonly & record {|
RegisterScope? scope;
Position startPos;
Position endPos;
|};
public type DeclRegisterBase record {|
*RegisterBase;
RegisterScope scope;
Position pos;
string name;
DeclRegisterKind kind;
|};
public type TmpRegister readonly & record {|
*RegisterBase;
TMP_REGISTER_KIND kind = TMP_REGISTER_KIND;
|};
public type AssignTmpRegister readonly & record {|
*RegisterBase;
ASSIGN_TMP_REGISTER_KIND kind = ASSIGN_TMP_REGISTER_KIND;
|};
public type CapturedRegister readonly & record {|
*RegisterBase;
CAPTURED_REGISTER_KIND kind = CAPTURED_REGISTER_KIND;
DeclRegister|CapturedRegister captured;
RegisterScope scope;
string name;
|};
public type NarrowRegister readonly & record {|
*RegisterBase;
// Register that was narrowed. Could be another NarrowRegister.
Register underlying;
// It's name comes from the underlying register.
() name = ();
NARROW_REGISTER_KIND kind = NARROW_REGISTER_KIND;
|};
public type ParamRegister readonly & record {|
*DeclRegisterBase;
PARAM_REGISTER_KIND kind = PARAM_REGISTER_KIND;
|};
public type VarRegister readonly & record {|
*DeclRegisterBase;
VAR_REGISTER_KIND kind = VAR_REGISTER_KIND;
|};
public type FinalRegister readonly & record {|
*DeclRegisterBase;
FINAL_REGISTER_KIND kind = FINAL_REGISTER_KIND;
|};
public function createVarRegister(FunctionCode code, SemType semType, Position pos, string name, RegisterScope scope) returns VarRegister {
VarRegister r = { number: code.registers.length(), semType, pos, name, scope };
code.registers.push(r);
return r;
}
public function createFinalRegister(FunctionCode code, SemType semType, Position pos, string name, RegisterScope scope) returns FinalRegister {
FinalRegister r = { number: code.registers.length(), semType, pos, name, scope };
code.registers.push(r);
return r;
}
public function createNarrowRegister(FunctionCode code, SemType semType, Register underlying, Position? pos = ()) returns NarrowRegister {
NarrowRegister r = { number: code.registers.length(), underlying, semType, pos };
code.registers.push(r);
return r;
}
public function createCapturedRegister(FunctionCode code, SemType semType, DeclRegister|CapturedRegister captured, string name, RegisterScope scope, Position? pos = ()) returns CapturedRegister {
CapturedRegister r = { number: code.registers.length(), captured, semType, pos, scope, name };
code.registers.push(r);
return r;
}
public function createParamRegister(FunctionCode code, SemType semType, Position pos, string name, RegisterScope scope) returns ParamRegister {
ParamRegister r = { number: code.registers.length(), semType, pos, name, scope };
code.registers.push(r);
return r;
}
public function createTmpRegister(FunctionCode code, SemType semType, Position? pos = (), string? name = ()) returns TmpRegister {
TmpRegister r = { number: code.registers.length(), semType, pos, name };
code.registers.push(r);
return r;
}
public function createAssignTmpRegister(FunctionCode code, SemType semType, Position? pos = (), string? name = ()) returns AssignTmpRegister {
AssignTmpRegister r = { number: code.registers.length(), semType, pos, name };
code.registers.push(r);
return r;
}
public function getRegister(FunctionCode code, int number) returns Register {
return code.registers[number];
}
public function unnarrow(Register reg) returns Register {
if reg is NarrowRegister {
return unnarrow(reg.underlying);
}
else {
return reg;
}
}
public type ArithmeticBinaryOp "+" | "-" | "*" | "/" | "%";
public type BitwiseBinaryOp "|" | "^" | "&" | BitwiseShiftOp;
public type BitwiseShiftOp "<<" | ">>" | ">>>";
public type OrderOp "<=" | ">=" | "<" | ">";
public type EqualityOp "==" | "!=" | "===" | "!==";
public enum InsnName {
INSN_INT_ARITHMETIC_BINARY,
INSN_INT_NO_PANIC_ARITHMETIC_BINARY,
INSN_INT_BITWISE_BINARY,
INSN_FLOAT_ARITHMETIC_BINARY,
INSN_FLOAT_NEGATE,
INSN_DECIMAL_ARITHMETIC_BINARY,
INSN_CONVERT_TO_INT,
INSN_CONVERT_TO_FLOAT,
INSN_CONVERT_TO_DECIMAL,
INSN_DECIMAL_NEGATE,
INSN_COMPARE,
INSN_EQUALITY,
INSN_BOOLEAN_NOT,
INSN_LIST_CONSTRUCT_RW,
INSN_LIST_GET,
INSN_LIST_SET,
INSN_MAPPING_CONSTRUCT_RW,
// MappingGetInsn has two potential names
INSN_MAPPING_GET,
INSN_MAPPING_FILLING_GET,
INSN_MAPPING_SET,
INSN_STR_CONCAT,
INSN_ERROR_CONSTRUCT,
INSN_RET,
INSN_ABNORMAL_RET,
INSN_CALL_DIRECT,
INSN_CALL_INDIRECT,
INSN_CAPTURE,
INSN_INVOKE,
INSN_ASSIGN,
INSN_TYPE_CAST,
INSN_TYPE_TEST,
INSN_BRANCH,
INSN_COND_BRANCH,
INSN_TYPE_COND_BRANCH,
INSN_TYPE_MERGE,
INSN_CATCH,
INSN_PANIC
}
# All instructions are a subtype of this.
public type InsnBase record {
# The name of the instruction.
# The name says what kind of instruction it is.
# Whether an instruction is a terminator or a PPI is determined
# just by its name.
InsnName name;
# Position based on the AST node that generated the instruction
Position pos;
};
public type ResultInsnBase record {|
*InsnBase;
TmpRegister result;
|};
public type Insn
IntArithmeticBinaryInsn|IntNoPanicArithmeticBinaryInsn|IntBitwiseBinaryInsn
|FloatArithmeticBinaryInsn|FloatNegateInsn
|DecimalArithmeticBinaryInsn|DecimalNegateInsn
|ConvertToIntInsn|ConvertToFloatInsn|ConvertToDecimalInsn
|BooleanNotInsn|CompareInsn|EqualityInsn
|ListConstructInsn|ListGetInsn|ListSetInsn
|MappingConstructInsn|MappingGetInsn|MappingSetInsn
|StringConcatInsn|RetInsn|AbnormalRetInsn|CallDirectInsn|CallIndirectInsn|CaptureInsn
|AssignInsn|TypeCastInsn|TypeTestInsn|TypeMergeInsn
|BranchInsn|TypeCondBranchInsn|CondBranchInsn|CatchInsn|PanicInsn|ErrorConstructInsn;
public type Operand ConstOperand|Register;
public type ConstOperand readonly & record {|
t:SemType semType;
t:SingleValue|FunctionRef value;
|};
public type SingleValueConstOperand readonly & record {|
t:SemType semType;
t:SingleValue value;
|};
public type NilConstOperand readonly & record {|
t:SemType semType;
() value;
|};
public final NilConstOperand NIL_OPERAND = { value: (), semType: t:NIL };
public type BooleanConstOperand readonly & record {|
t:SemType semType;
boolean value;
|};
public type IntConstOperand readonly & record {|
t:SemType semType;
int value;
|};
public type DecimalConstOperand readonly & record {|
t:SemType semType;
decimal value;
|};
public type FloatConstOperand readonly & record {|
t:SemType semType;
float value;
|};
public type StringConstOperand readonly & record {|
t:SemType semType;
string value;
|};
public type FunctionConstOperand readonly & record {|
t:SemType semType;
FunctionRef value;
|};
public type IntOperand IntConstOperand|Register;
public type FloatOperand FloatConstOperand|Register;
public type DecimalOperand DecimalConstOperand|Register;
public type BooleanOperand BooleanConstOperand|Register;
public type StringOperand StringConstOperand|Register;
public function operandHasType(t:Context tc, Operand operand, t:SemType semType) returns boolean {
return t:isSubtype(tc, operand.semType, semType);
}
# Perform a arithmetic operand on ints with two operands.
# This is a PPI.
public type IntArithmeticBinaryInsn readonly & record {|
*ResultInsnBase;
INSN_INT_ARITHMETIC_BINARY name = INSN_INT_ARITHMETIC_BINARY;
ArithmeticBinaryOp op;
IntOperand[2] operands;
|};
# Concatenate strings, returns a new string
public type StringConcatInsn readonly & record {|
*ResultInsnBase;
INSN_STR_CONCAT name = INSN_STR_CONCAT;
StringOperand[2] operands;
|};
# This is a non-PPI variant of IntArithmeticBinaryInsn.
# It is an optimization to be used only when the compiler can prove that a panic is impossible;
# the NO_PANIC version of % must not be used if first operand is int:MIN_VALUE and second operand is -1.
public type IntNoPanicArithmeticBinaryInsn readonly & record {|
*ResultInsnBase;
INSN_INT_NO_PANIC_ARITHMETIC_BINARY name = INSN_INT_NO_PANIC_ARITHMETIC_BINARY;
ArithmeticBinaryOp op;
IntOperand[2] operands;
|};
public type IntBitwiseBinaryInsn readonly & record {|
*ResultInsnBase;
INSN_INT_BITWISE_BINARY name = INSN_INT_BITWISE_BINARY;
BitwiseBinaryOp op;
IntOperand[2] operands;
|};
# Perform logical not operation on a boolean.
public type BooleanNotInsn readonly & record {|
*ResultInsnBase;
INSN_BOOLEAN_NOT name = INSN_BOOLEAN_NOT;
Register operand;
|};
// This is not a PPI
public type FloatArithmeticBinaryInsn readonly & record {|
*ResultInsnBase;
INSN_FLOAT_ARITHMETIC_BINARY name = INSN_FLOAT_ARITHMETIC_BINARY;
ArithmeticBinaryOp op;
FloatOperand[2] operands;
|};
// This panics for overflows, invalid decimals, divide by zero.
// So this is a PPI.
public type DecimalArithmeticBinaryInsn readonly & record {|
*ResultInsnBase;
INSN_DECIMAL_ARITHMETIC_BINARY name = INSN_DECIMAL_ARITHMETIC_BINARY;
ArithmeticBinaryOp op;
DecimalOperand[2] operands;
|};
public type FloatNegateInsn readonly & record {|
*ResultInsnBase;
INSN_FLOAT_NEGATE name = INSN_FLOAT_NEGATE;
Register operand;
|};
public type DecimalNegateInsn readonly & record {|
*ResultInsnBase;
INSN_DECIMAL_NEGATE name = INSN_DECIMAL_NEGATE;
Register operand;
|};
# If the operand is a float or decimal, then convert it to an int.
# Otherwise leave the operand unchanged.
# The intersection of the operand type with float|decimal must be non-empty.
# The result type must be `(T - (float|decimal))|int`,
# where T is the operand type.
# This panics if the conversion cannot be performed, so is a PPI.
public type ConvertToIntInsn readonly & record {|
*ResultInsnBase;
INSN_CONVERT_TO_INT name = INSN_CONVERT_TO_INT;
Register operand;
|};
# If the operand is an int or decimal, then convert it to a float.
# Otherwise leave the operand unchanged.
# The intersection of the operand type with int|decimal must be non-empty.
# The result type must be `(T - (int|decimal))|float`,
# where T is the operand type.
# This is not a PPI.
public type ConvertToFloatInsn readonly & record {|
*ResultInsnBase;
INSN_CONVERT_TO_FLOAT name = INSN_CONVERT_TO_FLOAT;
Register operand;
|};
# If the operand is an int or float, then convert it to a decimal.
# Otherwise leave the operand unchanged.
# The intersection of the operand type with int|float must be non-empty.
# The result type must be `(T - (int|float))|decimal`,
# where T is the operand type.
# This panics if the conversion cannot be performed, so is a PPI.
public type ConvertToDecimalInsn readonly & record {|
*ResultInsnBase;
INSN_CONVERT_TO_DECIMAL name = INSN_CONVERT_TO_DECIMAL;
Register operand;
|};
# This does ordered comparison
# Equality and inequality are done by equal
public type CompareInsn readonly & record {|
*ResultInsnBase;
INSN_COMPARE name = INSN_COMPARE;
OrderOp op;
Operand[2] operands;
|};
# Constructs a new mutable list value.
public type ListConstructInsn readonly & record {|
*ResultInsnBase;
INSN_LIST_CONSTRUCT_RW name = INSN_LIST_CONSTRUCT_RW;
// The type of the result gives the inherent type of the constructed list
Operand[] operands;
|};
# Gets a member of a list at a specified index.
# This is a PPI (since the index may be out of bounds).
public type ListGetInsn readonly & record {|
*ResultInsnBase;
INSN_LIST_GET name = INSN_LIST_GET;
// fill must be false unless the result type is a subtype of list or mapping
boolean fill = false; // if true do a filling read
[Register, IntOperand] operands;
|};
# Sets a member of a list at a specified index.
# This is a PPI (since the index may be out of bounds).
public type ListSetInsn readonly & record {|
*InsnBase;
INSN_LIST_SET name = INSN_LIST_SET;
[Register, IntOperand, Operand] operands;
|};
# Constructs a new mutable list value.
public type MappingConstructInsn readonly & record {|
*ResultInsnBase;
INSN_MAPPING_CONSTRUCT_RW name = INSN_MAPPING_CONSTRUCT_RW;
// The type of the result gives the inherent type of the constructed list
string[] fieldNames;
Operand[] operands;
|};
# Gets a member of a mapping with a specified key.
# INSN_MAPPING_GET returns nil if there is no such member; this is not a PPI.
# INSN_MAPPING_FILLING_GET fills if there is no such member; this is a PPI
# The filling version
public type MappingGetInsn readonly & record {|
*ResultInsnBase;
INSN_MAPPING_GET|INSN_MAPPING_FILLING_GET name;
[Register, StringOperand] operands;
|};
# Sets a member of a mapping with a specified key.
# This is a PPI.
public type MappingSetInsn readonly & record {|
*InsnBase;
INSN_MAPPING_SET name = INSN_MAPPING_SET;
[Register, StringOperand, Operand] operands;
|};
# Constructs an error value.
# Operand must be of type string.
public type ErrorConstructInsn readonly & record {|
*ResultInsnBase;
INSN_ERROR_CONSTRUCT name = INSN_ERROR_CONSTRUCT;
StringOperand operand;
|};
# This does equality expressions.
# This includes `==`, `!=`, `===` and `!==`
// XXX Complex cases (comparing structures deeply) can use memory in
// a way that cannot be bounded at compile time so may result in memory exhaustion
// Should this mean this is a PPI? Should we distinguish these as different
// kind of instruction.
public type EqualityInsn readonly & record {|
*ResultInsnBase;
INSN_EQUALITY name = INSN_EQUALITY;
EqualityOp op;
Operand[2] operands;
|};
public type FunctionOperand FunctionConstOperand|Register;
# The common supertype of the two call instructions.
# The following applies to both instructions.
# It is not a terminator.
# It will be a PPI. A panic in the called function
# goes to the onPanic label in the basic block.
# (This isn't implemented yet, since we haven't implemented `trap`.)
# Regardless of where the function itself panics,
# any function call could result in a stack overflow panic.
# It can also panic due to memory allocation for uniform function call
# (Panics due to memory allocation are not handled gracefully)
#
# The first operand is the function value to be called;
# this operand must be a subtype of the function basic type.
# The remaining operands are the arguments.
# A function value has an inherent type, which is determined
# by the declared parameter types and return type; these types are
# called atomic types. In an atomic function type, the type of the parameters
# of the function is represented as a tuple type; when the function is declared
# with a rest parameter, the last member descriptor in the tuple type
# will be a rest descriptor.
# In general, function types can be unions and intersections of atomic function types.
# The semantics of function call in Ballerina are that the arguments
# are a list. Static type checking of a call ensures that the list of arguments passed to
# a function value belongs to the tuple type for the parameters of the inherent type
# of the function value.
# In all cases, it is the responsibility of the caller to deal with rest arguments
# in the call expression, by splicing the rest argument into the list of all the arguments;
# the call instructions assume this splicing has already taken place.
# In the normal case, it is similarly the responsibility of the callee to handle
# rest parameters in the function definition, by constructing a list from the corresponding
# trailing part of the list of all the arguments, before binding this list to the rest parameter.
# However, in some cases it is possible to shortcut the callee's handling of the rest
# parameter, by having the caller construct the list to be bound to the rest parameter;
# we call this shortcut `restParamIsList`.
# The two instructions differ in how they distinguish the case `restParamIsList` is in effect.
# A t:FunctionSignature represents the parameter types after the list has been
# constructed for the rest parameter.
# SUBSET We do not yet support functions with a return type of never
public type CallInsnBase record {
*ResultInsnBase;
# The name of call instruction.
INSN_CALL_DIRECT|INSN_CALL_INDIRECT name;
# The operands of the instruction.
# The first member of the list is the function to be called;
# the remaining members are the arguments.
[FunctionOperand, Operand...] operands;
};
# Call a function, when the function value is constant.
# This means that the function to be called is known at compile-time.
# At the machine code level, it can potentially be implemented by
# a branch to a known address.
# It also implies that the type of the function is atomic and so there
# is a single tuple type known at compile time for the arguments.
# With this instruction, we therefore do not need additional information to
# determine whether `restParamIsList` is in effect:
# it is in effect if and only if this tuple type ends with a rest descriptor.
public type CallDirectInsn readonly & record {|
*CallInsnBase;
INSN_CALL_DIRECT name = INSN_CALL_DIRECT;
[FunctionConstOperand, Operand...] operands;
|};
# Call a function value, when the function value is not constant.
# At the machine code level, this will typically be implemented by
# an indirect branch.
# The `restParamIsList` field determines whether `restParamIsList`
# is in effect (as described above in `CallInsnBase`).
# If `restParamIsList` is true, then the type of the first operand,
# which is a function type, must be atomic.
public type CallIndirectInsn readonly & record {|
*CallInsnBase;
INSN_CALL_INDIRECT name = INSN_CALL_INDIRECT;
[Register, Operand...] operands;
boolean restParamIsList;
|};
public type CapturableRegister CapturedRegister|DeclRegister;
# Create a function value from an AnonFunction.
# The operands are the values for the captured registers.
public type CaptureInsn readonly & record {|
*ResultInsnBase;
INSN_CAPTURE name = INSN_CAPTURE;
# Given functionIndex could be used in only one CaptureInsn
int functionIndex;
[CapturableRegister...] operands;
|};
# Assign a value to a register.
# Typing rule:
# typeof(operand) <: typeof(result)
public type AssignInsn readonly & record {|
*InsnBase;
INSN_ASSIGN name = INSN_ASSIGN;
AssignTmpRegister|VarRegister|FinalRegister|CapturedRegister result;
Operand operand;
|};
# A type cast that may fail.
# Don't need to allow for operand to be a const
# Since we can do that at compile-time.
# This is a PPI.
# Typing rules:
# typeof(result) <: semType
# typeof(result) <: typeof(operand)
# semType not empty
public type TypeCastInsn readonly & record {|
*ResultInsnBase;
INSN_TYPE_CAST name = INSN_TYPE_CAST;
Register operand;
SemType semType;
|};
# Tests whether a value belongs to a type
# Used for `is` expressions
# Typing rule:
# typeof(result) <: boolean
# XXX some type tests are potentially complex
# and can require memory allocation and thus can potentially
# panic. Probably need to distinguish these.
public type TypeTestInsn readonly & record {|
*ResultInsnBase;
INSN_TYPE_TEST name = INSN_TYPE_TEST;
# Gets result of test.
# Must be exactly type boolean
# Holds value to be tested.
Register operand;
SemType semType;
boolean negated;
|};
public type TypeCondBranchInsn readonly & record {|
*InsnBase;
INSN_TYPE_COND_BRANCH name = INSN_TYPE_COND_BRANCH;
Register operand;
t:SemType semType;
Label ifTrue;
Label ifFalse;
NarrowRegister ifTrueRegister;
NarrowRegister ifFalseRegister;
|};
public type TypeMergeInsn readonly & record {|
*InsnBase;
INSN_TYPE_MERGE name = INSN_TYPE_MERGE;
NarrowRegister result;
// operands.length() > 1 && operands.length() == predecessors.length()
Register[] operands;
Label[] predecessors;
|};
# Return normally from a function.
# This is a terminator.
# Typing rule:
# typeof(operand) <: typeof(functionReturnType)
public type RetInsn readonly & record {|
*InsnBase;
INSN_RET name = INSN_RET;
Operand operand;
|};
# Return abnormally from the function
# The type of the operand need not belong to the functions return type.
# The associated error value is in the operand register.
# This is a terminator.
public type AbnormalRetInsn readonly & record {|
*InsnBase;
INSN_ABNORMAL_RET name = INSN_ABNORMAL_RET;
# Operand is error value
Register operand;
|};
# Performs a panic.
# This is a PPI.
# Control flow allows follows the onPanic label of its basic block.
# This is a terminator.
# The operand contains the associated error value.
public type PanicInsn readonly & record {|
*InsnBase;
INSN_PANIC name = INSN_PANIC;
# Must be of type error
Register operand;
|};
# A CatchInsn is allowed as the first insn of a block that
# is the target of an onPanic label of a basic block.
# Executing the catch instruction causes the error value associated
# with the panic to be stored in the result register.
# This is a very simplified form of a LLVM landingpad.
public type CatchInsn readonly & record {|
*ResultInsnBase;
INSN_CATCH name = INSN_CATCH;
|};
# Conditionally branch to one of two labels based on a boolean operand.
# If the operand is const, then use a Jump instead.
# This is a terminator.
public type CondBranchInsn readonly & record {|
*InsnBase;
INSN_COND_BRANCH name = INSN_COND_BRANCH;
Register operand;
Label ifTrue;
Label ifFalse;
|};
# Unconditional branch to a label
# This is a terminator.
public type BranchInsn readonly & record {|
*InsnBase;
INSN_BRANCH name = INSN_BRANCH;
Label dest;
boolean backward = false;
|};
public function isBasicBlockPotentiallyPanicking(BasicBlock block) returns boolean {
foreach Insn insn in block.insns {
if isInsnPotentiallyPanicking(insn) {
return true;
}
}
return false;
}
final readonly & map<true> PPI_INSNS = {
// When we implement trap, we will need to treat call as potentially
// panicking but for now we don't
// If we allow this, we need to be careful about not generating
// code from the catch block if the only PPIs in the basic block are calls.
// [INSN_CALL_DIRECT]: true,
// [INSN_CALL_INDIRECT]: true,
[INSN_PANIC]: true,
[INSN_INT_ARITHMETIC_BINARY]: true,
[INSN_DECIMAL_ARITHMETIC_BINARY]: true,
[INSN_CONVERT_TO_INT]: true,
[INSN_CONVERT_TO_DECIMAL]: true,
[INSN_TYPE_CAST]: true,
[INSN_LIST_GET]: true,
[INSN_LIST_SET]: true,
[INSN_MAPPING_FILLING_GET]: true,
[INSN_MAPPING_SET]: true
};
public function isInsnPotentiallyPanicking(Insn insn) returns boolean {
return PPI_INSNS[insn.name] == true;
}