forked from newspeaklanguage/newspeak
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Newspeak2SqueakCompilation.ns
3010 lines (2724 loc) · 97.8 KB
/
Newspeak2SqueakCompilation.ns
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
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Newspeak3
'Root'
class Newspeak2SqueakCompilation usingPlatform: platform
asts: asts
newspeakParser: ns3Parser
intermediates: i = NewspeakCompilation
usingPlatform: platform
asts: asts (
(* The Newspeak compilation module for Squeak and PrimordialSoup. For a detailed overview of compilation, see Compiler.
Relation to parsing:
Note that this module does not include a parser or AST classes. These have utility independent of compilation, and so are available as a separate parsing module. This module requires such a parsing module as parameter. It uses this parameter to create a parser for compilation, and to subclass certain AST classes and tools for its own purposes.
The parser is instantiated upon module creation and stored in a module slot. We expect a Newspeak parser to be purely functional; hence we may share it among all compiler instances. It's not clear if this is a valid assumption in the long term.
Separating the parsing module makes it easy to change parsing strategies (though one could also define parsing here and override it in a subclass).
Copyright 2008 Cadence Design Systems, Inc.
Copyright 2010 Gilad Bracha, Felix Geller and Ryan Macnak
Copyright 2011 Matthias Kleine, Ryan Macnak and Cadence Design Systems
Copyright 2012-2013 Gilad Bracha, Ryan Macnak and Cadence Design Systems
Licensed under the Apache License, Version 2.0 (the ''License''); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 *)
|
private Map = platform collections Map.
private List = platform collections List.
private Set = platform collections Set.
private ASTTool = asts ASTTool.
private AST = asts AST.
private BlockAST = asts BlockAST.
private CascadedSendAST = asts CascadedSendAST.
private CodeBodyAST = asts CodeBodyAST.
private MessageAST = asts MessageAST.
private MessagePatternAST = asts MessagePatternAST.
private MethodAST = asts MethodAST.
private MutableSlotDefAST = asts MutableSlotDefAST.
private UnresolvedSendAST = asts UnresolvedSendAST.
private NumberAST = asts NumberAST.
private ReturnStatAST = asts ReturnStatAST.
private SendAST = asts SendAST.
private SetterSendAST = asts SetterSendAST.
private StringAST = asts StringAST.
private SymbolAST = asts SymbolAST.
private TupleAST = asts TupleAST.
private VarDeclAST = asts VarDeclAST.
private VariableAST = asts VariableAST.
private Pragma = [platform squeak Pragma] on: Error do: [:ex | nil].
private ProcessorAssociation = [platform squeak Smalltalk associationAt: #Processor] on: Error do: [:ex | nil].
private IntermediateClassDeclaration = i IntermediateClassDeclaration.
private IntermediateSlotDeclaration = i IntermediateSlotDeclaration.
private IntermediateMethod = i IntermediateMethod.
private IntermediateMixin = i IntermediateMixin.
private IntermediateTransientSlot = i IntermediateTransientSlot.
protected parser <CommonParser> = ns3Parser CommonParser new.
|) (
public class Compiler = super Compiler (
(* The compiler has three main entry points: compileClassSource:Within:, compileClassHeader: within: and compileMethodSource:within:. These compile an entire class declaration, a class header, and a method, respectively.
The compiler returns low level (e.g., VM level) objects representing the results of compilation.
These results are described in some detail in the individual methods. The results are never installed by the compiler. The compiler does not know or care whether it is running in the service of a live system. If the compiler is invoked from such a system, the caller may choose to install the results. This should be done as an atomic modification, since the results may be interdependent (e.g., a nested class and its enclosing class).
Compilation begins with parsing to produce an AST. As that tree is walked, each class side creates a scope with its immediate members, before compiling those members within the scope.
When compiling an individual method, we also rewrite its AST into a lower level AST, and submit that to the AST2ByteCodeCompiler, which produces byte code. If we are compiling a class declaration or a class header, we compute the overall structure of the resulting mixin, including any required synthetic members.
The NS3 implementation involves a considerable amount of synthetic code, dealing with accessors, nested classes, slot initialization, superclass initialization and the primary constructor.
Nested classes are distinct for every instance of an outer class. Hence, every outer class has synthetic fields that hold the class objects for its nested classes. These fields are always accessed by a getter method which lazily initializes the field. The name of the getter method is the simple name of the inner class. The field, in contrast, is named X`slot, where X is the fully qualified name of the inner class' mixin. This is necessary to prevent it from clashing with nested classes with the same simple name elsewhere in the hierarchy. See createNestedClassAccessorFrom:.
The creation of classes is based on producing an IntermediateClassDeclaration for the class.
Nested classes also have an enclosing object slot pointing at the object that created them. See Behavior>>enclosingObjectSlot.
The primary constructor induces a synthetic class method of the same name, which calls a synthetic instance method prefixed with initializer` me on a freshly created instance. Within that instance method is the code for all the slot initializers, as well as the superclass constructor call. See astForPrimaryFactory: and astForClassDeclarationInitializer:. *)
|
currentSource <String>
currentScope <Scope>
currentDepth <Integer> (* The nesting level of the current class; top level classes are at level 0. *)
literalTable <IdentityMap[Symbol, Integer]>
rewriter = Rewriter new.
methodCompiler = AST2ByteCodeCompiler new.
(* We do not use the quick send bytecode for #== or #class because they hardwire the definitions appropriate for Object, but we expect Values and proxies to override #== and #class respectively. *)
specialSelectors =
{#+. #-. #<. #>. #<=. #>=. #=. #~=. #*. #/. #\\. #@.
#bitShift:. #//. #bitAnd:. #bitOr:.
#at:. #at:put:. #size. #next. #nextPut:. #atEnd.
nil. (* #==. *)
nil. (* #class. *)
#blockCopy:. #value. #value:. #do:. #new. #new:. #x. #y}.
|) (
class AST2ByteCodeCompiler = ASTTool (
(* The compiler is a visitor on an AST that has been processed by the rewriter.
It is a subtype of ASTTool[Self] - that is, its methods do not return individual results, but instead
side effect state within the compiler.
The compiler decides what instructions to generate at a somewhat abstract level, and asks
a code generator object to actually produce the instruction stream.
The code generator deals with issues like whether to use regular or wide instructions,
what the actual instruction codes are, what the actual offsets for branches are etc.,
so the compiler can abstract from these details. One can almost think of this as producing
assembly.
Closure code appears inline (regardless of whether execution is inlined!). Code in a non-inlined closure starts executing with an empty operand stack, but the depth of the method must be sufficient for the block.
To complicate matters further, the actual size of the activation is simply the sum of the number of parameters, number of locals and maximal operand stack size.
We must ensure sufficient space for any given closure defined in the method. The space required for a closure is the sum of its arity, the number of values copied into it, and its maximal operand stack size. Because the closure must explicitly initialize its locals to nil on startup, its operand stack will include sufficient space for its temporaries.
Ideally, we'd track the operand stack depth for the method and for each closure within it separately. Though it seems that the actual required depth would be the maximum of all these, that is simplistic.
We really need to compute the required context size which is the maximum of:
#method formals + #method locals + method operand stack size
#closure formals + #closure copy downs + closure operand stack size
assuming that the locals of the closure are accounted for in the stack size.
Then, the actual operand stack size we specify for the method is
context size - #method formals - #method locals
We don't do this yet. What we do instead is often suboptimal, but sometimes insufficient. We compute the cumulative maximum operand stack size for all code in the method, including closures. The idea was that this would be conservative and simple, but in fact it may fail if a closure has a large number of arguments and copy-downs, and the actual operand stack usage is small. *)
|
cgen <CodeGenerator>
debugInfo <DebugInfo>
valueExpected <Boolean>
maxClosureContextSize <Integer>
|) (
class CodeGenerator = (|
public code = IntermediateMethod new.
public numTemps ::= 1. (* temp0 always allocated *) (*why?*)
public maxStackDepth ::= 0.
public currentStackDepth ::= 0.
|) (
public accessModifier: am <Symbol> = (
code accessModifier: am.
)
public argCount: n <Integer> = (
code argCount: n
)
public bci ^<Integer> = (
(* Answers the current bytecode index. Used for calculating jump lengths. Note this is NOT the pc. *)
^code bci
)
public close ^<LowLevelMethodMirror> = (
(* To be called at the end of code generation. *)
| methodContextSize = maxStackDepth + code argCount + code maxLocals. |
code maxStack: (methodContextSize max: maxClosureContextSize).
^code
)
public decrementStackDepthBy: n = (
(* Reduce the current height of the operand stack *)
currentStackDepth:: currentStackDepth - n.
assert: [currentStackDepth >= 0]
message: 'Stack depth must not be negative'.
)
public incrementStackDepthBy: n = (
(* Increase the current height of the operand stack. Keep track of the maximum operand stack height seen in this method (that's the whole point of tracking the stack height - we need this to create a CompiledMethod. *)
currentStackDepth:: currentStackDepth + n.
maxStackDepth:: maxStackDepth max: currentStackDepth.
)
public literals: ls = (
code literals: ls.
)
public maxLocals ^<Integer> = (
^code maxLocals
)
public maxLocals: n <Integer> = (
code maxLocals: n
)
public methodName: n <Symbol> = (
code selector: n
)
public pushMixin = (
(* The mixin will be stored in the final literal -added after the end of the literal array *)
pushLiteralWithIndex: literalTable size + 1
)
) : (
)
class CodeGeneratorV4 = CodeGenerator (
) (
public createClosureOfArity: numArgs copying: numCopied length: jumpSize = (
| numExtensions numCopiedMod8 numArgsMod8 extA |
decrementStackDepthBy: numCopied.
incrementStackDepthBy: 1.
assert: [jumpSize between: 0 and: 65535]
message: 'Closure jump out of range'.
assert: [numCopied between: 0 and: 127]
message: 'Too many copied values in closure'.
assert: [numArgs between: 0 and: 127]
message: 'Too many args in closure'.
extA:: numExtensions:: 0.
(numArgsMod8:: numArgs) > 7 ifTrue:
[extA:: numArgs // 8.
numArgsMod8:: numArgsMod8 \\ 8].
(numCopiedMod8:: numCopied) > 7 ifTrue:
[extA:: extA + (numCopied // 8 * 16).
numCopiedMod8:: numCopiedMod8 \\ 8].
0 = extA ifFalse:
[self unsignedSingleExtendA: extA.
numExtensions:: 1].
(*jumpSize > 255 ifTrue: *)
numExtensions:: numExtensions + 1.
unsignedSingleExtendB: jumpSize // 256.
code
byte: 253;
byte: (numExtensions << 6) + (numCopiedMod8 << 3) + numArgsMod8;
byte: (jumpSize bitAnd: 16rFF)
)
public createEmptyArray: size = (
(* 231 11100111 jkkkkkkk Push (Array new: kkkkkkk) (j = 0) *)
assert: [size between: 0 and: 127] message: 'Array size must be < 128'.
incrementStackDepthBy: 1.
code byte: 231; byte: size.
)
public createFullArray: size = (
assert: [size between: 0 and: 127] message: 'Array size must be < 128'.
decrementStackDepthBy: size.
incrementStackDepthBy: 1.
code byte: 231; byte: 128 + size.
)
public dup = (
code byte: 219.
incrementStackDepthBy: 1.
)
public implicitReceiverSend: selector numArgs: numArgs = (
|
selectorIndex = indexForLiteral: selector.
extendedIndex extendedNArgs
|
decrementStackDepthBy: numArgs.
incrementStackDepthBy: 1.
assert: [selectorIndex between: 0 and: 32767]
message: 'Selector index out of range'.
assert: [numArgs between: 0 and: 31]
message: 'Message arity out of range'.
(selectorIndex < 16 and: [numArgs = 0]) ifTrue:
[code byte: 160 + selectorIndex. ^self].
(extendedIndex:: selectorIndex) > 31 ifTrue:
[self unsignedSingleExtendA: extendedIndex // 32.
extendedIndex:: extendedIndex \\ 32].
(extendedNArgs:: numArgs) > 7 ifTrue:
[self unsignedSingleExtendB: extendedNArgs // 8.
extendedNArgs:: extendedNArgs \\ 8].
code
byte: 240;
byte: extendedNArgs + (extendedIndex * 8)
)
public indexForLiteral: literal = (
^literalTable at: literal
)
public jumpBy: distance <Integer> = (
distance < 0 ifTrue:
[assert: [distance between: -32768 and: -1]
message: 'Unconditional backjump out of range'.
signedSingleExtendB: (distance >> 8).
code byte: 242; byte: (distance bitAnd: 255).
^self].
assert: [distance between: 0 and: 32767]
message: 'Unconditional jump out of range'.
signedSingleExtendB: (distance >> 8).
code byte: 242; byte: (distance bitAnd: 255).
)
public jumpIf: bool <Boolean> by: distance <Integer> = (
(* Always generates unextended jumps *)
assert: [distance between: 0 and: 32767]
message: 'Conditional jump out of range'.
decrementStackDepthBy: 1.
unsignedSingleExtendB: (distance >> 8).
code
byte: (bool ifTrue: [243] ifFalse: [244]);
byte: (distance bitAnd: 255).
)
public nop = (
code byte: 221
)
public normalSend: selector numArgs: numArgs = (
|
selectorIndex
extendedIndex extendedNArgs
|
decrementStackDepthBy: numArgs + 1 (* the receiver *).
incrementStackDepthBy: 1.
(specialSelectors includes: selector) ifTrue:
[code byte: 79 + (specialSelectors indexOf: selector).
^self].
selectorIndex:: indexForLiteral: selector.
assert: [selectorIndex between: 0 and: 32767]
message: 'Selector index out of range'.
assert: [numArgs between: 0 and: 31]
message: 'Message arity out of range'.
(selectorIndex < 16 and: [numArgs < 3]) ifTrue:
[code byte: 112 + (numArgs * 16) + selectorIndex.
^self].
(extendedIndex:: selectorIndex) > 31 ifTrue:
[self unsignedSingleExtendA: extendedIndex // 32.
extendedIndex:: extendedIndex \\ 32].
(extendedNArgs:: numArgs) > 7 ifTrue:
[self unsignedSingleExtendB: extendedNArgs // 8.
extendedNArgs:: extendedNArgs \\ 8].
code
byte: 238;
byte: extendedNArgs + (extendedIndex * 8)
)
public outerSend: selector numArgs: numArgs depth: depth = (
|
selectorIndex = indexForLiteral: selector.
extendedIndex extendedNArgs
|
assert: [selectorIndex between: 0 and: 32767]
message: 'Selector index out of range'.
assert: [numArgs between: 0 and: 31]
message: 'Message arity out of range'.
assert: [depth between: 0 and: 255]
message: 'Lexical depth out of range'.
decrementStackDepthBy: numArgs.
incrementStackDepthBy: 1.
(extendedIndex:: selectorIndex) > 31 ifTrue:
[unsignedSingleExtendA: extendedIndex // 32.
extendedIndex:: extendedIndex \\ 32].
(extendedNArgs:: numArgs) > 7 ifTrue:
[self unsignedSingleExtendB: extendedNArgs // 8.
extendedNArgs:: extendedNArgs \\ 8].
code
byte: 254;
byte: extendedNArgs + (extendedIndex * 8);
byte: depth
)
public patchClosureJumpAt: blockBci <Integer> with: distance <Integer> = (
assert: [distance between: 0 and: 65535] message: 'Closure jump out of range'.
assert: [(code byteAt: blockBci - 5) = 225] message: 'Not really a closure/extb?'.
code byteAt: blockBci - 4 put: (distance >> 8).
assert: [(code byteAt: blockBci - 3) = 253] message: 'Not really a closure?'.
code byteAt: blockBci - 1 put: (distance bitAnd: 255)
)
public patchJumpAt: jumpBci <Integer> with: distance <Integer> = (
| extendedIndex |
assert: [distance between: 0 and: 32767]
message: 'Unconditional jump out of range'.
assert: [(code byteAt: jumpBci - 4) = 225] message: 'Not really a jump/extb?'.
extendedIndex:: distance >> 8.
code byteAt: jumpBci - 3 put: (extendedIndex >= 0
ifTrue: [extendedIndex]
ifFalse: [extendedIndex + 256]). (*patch the extend b*)
assert: [(code byteAt: jumpBci - 2) = 242] message: 'Not really a jump?'.
code byteAt: jumpBci - 1 put: (distance bitAnd: 255).
)
public patchJumpIfAt: jumpBci <Integer> with: distance <Integer> = (
assert: [distance between: 0 and: 32767]
message: 'Conditional jump out of range'.
assert: [(code byteAt: jumpBci - 4) = 225] message: 'Not really a branch/extb?'.
code byteAt: jumpBci - 3 put: (distance >> 8).
assert: [(code byteAt: jumpBci - 2) between: 243 and: 244] message: 'Not really a branch?'.
code byteAt: jumpBci - 1 put: (distance bitAnd: 255)
)
public pop = (
code byte: 220.
decrementStackDepthBy: 1.
)
public popIntoRemoteTemporary: index inVector: vectorIndex = (
assert: [index between: 0 and: 255]
message: 'Temp index out of range'.
assert: [vectorIndex between: 0 and: 255]
message: 'Temp vector index out of range'.
code byte: 252; byte: index; byte: vectorIndex.
decrementStackDepthBy: 1.
)
public popIntoTemporary: index = (
assert: [index between: 0 and: 63] message: 'Temp index out of range'.
(* 184-191 10111 i i i Pop and Store Temporary Variable #iii *)
index < 8 ifTrue:
[code byte: 184 + index.
decrementStackDepthBy: 1.
^self].
(* 237 11101101 i i i i i i i i Pop and Store Temporary Variable #iiiiiiii *)
code byte: 237; byte: index.
decrementStackDepthBy: 1.
)
public pushEnclosingObject: depth = (
assert: [depth between: 0 and: 127]
message: 'Enclosing object depth out of range'.
depth = 0 ifTrue: [^pushSelf].
incrementStackDepthBy: 1.
signedSingleExtendB: depth negated.
code byte: 77
)
public pushFalse = (
code byte: 77.
incrementStackDepthBy: 1.
)
public pushInteger: n <Integer> = (
incrementStackDepthBy: 1.
n = 0 ifTrue: [code byte: 78. ^self].
n = 1 ifTrue: [code byte: 79. ^self].
assert: [n between: -32768 and: 32767]
message: 'Quick integer out of range'.
(n < 0 or: [n > 255]) ifTrue: [signedSingleExtendB: (n >> 8)].
code byte: 229.
code byte: (n bitAnd: 255).
)
public pushLiteral: value = (
true = value ifTrue: [^pushTrue].
false = value ifTrue: [^pushFalse].
nil = value ifTrue: [^pushNil].
(value isKindOfInteger and: [value between: -32768 and: 32767])
ifTrue: [^pushInteger: value].
^pushLiteralWithIndex: (indexForLiteral: value).
)
public pushLiteralVariable: association = (
| index = indexForLiteral: association. extendedIndex |
assert: [index between: 0 and: 32767]
message: 'Literal index out of range'.
incrementStackDepthBy: 1.
index < 16 ifTrue: [code byte: 16 + index. ^self].
(extendedIndex:: index) > 255 ifTrue:
[unsignedSingleExtendA: extendedIndex // 256.
extendedIndex:: extendedIndex \\ 256].
code byte: 227; byte: extendedIndex.
)
public pushLiteralWithIndex: index = (
| extendedIndex |
assert: [index between: 0 and: 32767]
message: 'Literal index out of range'.
incrementStackDepthBy: 1.
index < 32 ifTrue: [code byte: 32 + index. ^self].
(extendedIndex:: index) > 255 ifTrue:
[unsignedSingleExtendA: extendedIndex // 256.
extendedIndex:: extendedIndex \\ 256].
code byte: 228; byte: extendedIndex.
)
public pushNil = (
unsignedSingleExtendB: 2.
code byte: 77.
incrementStackDepthBy: 1.
)
public pushRemoteTemporary: index inVector: vectorIndex = (
assert: [index between: 0 and: 255]
message: 'Temp index out of range'.
assert: [vectorIndex between: 0 and: 255]
message: 'Temp vector index out of range'.
code byte: 250; byte: index; byte: vectorIndex.
incrementStackDepthBy: 1.
)
public pushSelf = (
code byte: 76.
incrementStackDepthBy: 1.
)
public pushTemporary: index = (
assert: [index between: 0 and: 63] message: 'Temp index out of range'.
(* 64-71 01000 i i i Push Temporary Variable #iii *)
(* 72-75 010010 i i Push Temporary Variable #ii + 8 *)
index < 12 ifTrue:
[incrementStackDepthBy: 1.
code byte: 64 + index.
^self].
(* 230 11100110 i i i i i i i i Push Temporary Variable #iiiiiiii *)
incrementStackDepthBy: 1.
code byte: 230; byte: index.
)
public pushTrue = (
unsignedSingleExtendB: 1.
code byte: 77.
incrementStackDepthBy: 1.
)
public returnFromBlock = (
code byte: 218.
decrementStackDepthBy: 1.
)
public returnFromMethod = (
code byte: 217.
decrementStackDepthBy: 1.
)
public returnSelfFromMethod = (
code byte: 216
)
public selfSend: selector numArgs: numArgs = (
|
selectorIndex = indexForLiteral: selector.
extendedIndex extendedNArgs
|
assert: [selectorIndex between: 0 and: 32767]
message: 'Selector index out of range'.
assert: [numArgs between: 0 and: 31]
message: 'Message arity out of range'.
decrementStackDepthBy: numArgs.
incrementStackDepthBy: 1.
(extendedIndex:: selectorIndex) > 31 ifTrue:
[unsignedSingleExtendA: extendedIndex // 32.
extendedIndex:: extendedIndex \\ 32].
(extendedNArgs:: numArgs) > 7 ifTrue:
[self unsignedSingleExtendB: extendedNArgs // 8.
extendedNArgs:: extendedNArgs \\ 8].
code
byte: 245;
byte: extendedNArgs + (extendedIndex * 8)
)
signedSingleExtendB: extendedIndex = (
assert: [extendedIndex between: -128 and: 127]
message: 'Single extend B out of range'.
code
byte: 225;
byte: (extendedIndex >= 0
ifTrue: [extendedIndex]
ifFalse: [extendedIndex + 256])
)
public sizeOfBackJump = (
^4
)
public storeIntoRemoteTemporary: index inVector: vectorIndex = (
assert: [index between: 0 and: 255]
message: 'Temp index out of range'.
assert: [vectorIndex between: 0 and: 255]
message: 'Temp vector index out of range'.
code byte: 251; byte: index; byte: vectorIndex.
)
public storeIntoTemporary: index = (
assert: [index between: 0 and: 63] message: 'Temp index out of range'.
code byte: 234; byte: index.
)
public superSend: selector numArgs: numArgs = (
|
selectorIndex = indexForLiteral: selector.
extendedIndex extendedNArgs
|
assert: [selectorIndex between: 0 and: 32767]
message: 'Selector index out of range'.
assert: [numArgs between: 0 and: 31]
message: 'Message arity out of range'.
decrementStackDepthBy: numArgs.
incrementStackDepthBy: 1.
(extendedIndex:: selectorIndex) > 31 ifTrue:
[unsignedSingleExtendA: extendedIndex // 32.
extendedIndex:: extendedIndex \\ 32].
(extendedNArgs:: numArgs) > 7 ifTrue:
[self unsignedSingleExtendB: extendedNArgs // 8.
extendedNArgs:: extendedNArgs \\ 8].
code
byte: 241;
byte: extendedNArgs + (extendedIndex * 8)
)
unsignedSingleExtendA: extendedIndex = (
assert: [extendedIndex between: 0 and: 255]
message: 'Single extend A out of range'.
code byte: 224; byte: extendedIndex
)
unsignedSingleExtendB: extendedIndex = (
assert: [extendedIndex between: 0 and: 255]
message: 'Single extend B out of range'.
code byte: 225; byte: extendedIndex
)
) : (
)
class DebugInfo = (
(* Debugger information. *)
|
public bciSourceMapping <MutableMap[Integer, Interval]> = Map new.
public localVariables <MutableList[LocalVariableDebugInfo]> = List new.
public source <String>
|) (
public addInfoForLocal: var <AllocatedLocal> = (
| info |
info: LocalVariableDebugInfo new.
info name: var original name.
info zeroOriginOffset: var offset.
info remoteVector: nil.
info contextDepth: var closureDepth.
^localVariables addLast: info.
)
public addInfoForRemote: varInfo <AllocatedLocal> in: remoteVarInfo <SemanticVarDecl> = (
| info |
info: LocalVariableDebugInfo new.
info name: varInfo original name.
info zeroOriginOffset: varInfo original remoteOffset.
info remoteVector: remoteVarInfo original name.
info contextDepth: varInfo closureDepth.
^localVariables addLast: info.
)
public isKindOfDebugInfo ^<Boolean> = (
^true
)
public mapBCI: bci <Integer> to: src <interval> = (
bciSourceMapping at: bci put: src
)
public mapperForContext: ctxt = (
^DebugMapper forContext: ctxt usingDebugInfo: self.
)
) : (
)
class DebugMapper forContext: ctxt usingDebugInfo: dbgInfo = (|
context <MethodContext> = ctxt.
debugInfo <DebugInfo> = dbgInfo.
myDepth <Integer> = self depthOfContext: ctxt.
|) (
astForContextAtDepth: targetDepth <Integer> ^<AST> = (
|
returnContext
|
myDepth >= targetDepth ifFalse: [deny].
returnContext:: UnresolvedSendAST new
receiver: hereNode;
message: (MessageAST new
selector: namer doitContextArgumentName;
arguments: {}).
(myDepth - targetDepth) timesRepeat: [
returnContext:: (UnresolvedSendAST new
receiver: returnContext;
message: (MessageAST new
selector: #outerContext;
arguments: {}))].
^returnContext
)
bci = (
isSqueak ifTrue: [^context pc - context method initialPC + 1].
^context bci
)
contextAtDepth: targetDepth <Integer> ^<MethodContext> = (
|
returnContext
|
myDepth >= targetDepth ifFalse: [deny].
returnContext: context.
(myDepth - targetDepth)
timesRepeat: [returnContext: returnContext outerContext].
^returnContext
)
depthOfContext: ctxt <MethodContext> ^<Integer> = (
^ctxt closure isNil
ifTrue: [0 (* method *)]
ifFalse: [1 + (depthOfContext: ctxt outerContext)]
)
getLeastLocalValueOf: varName <Symbol> ^<Object> = (
| info ctxt vector |
info:: leastLocalInfoFor: varName.
(* defensive programming, but should be unnecessary now that
DebugMapper>localNames filters out variables in deeper contexts. *)
myDepth >= info contextDepth ifFalse:
[^nil].
info remoteVector isNil ifTrue:
[ctxt:: contextAtDepth: info contextDepth.
ctxt size = 0 ifTrue: [^nil].
^ctxt tempAt: info zeroOriginOffset + 1].
vector:: getValueOf: info remoteVector.
vector isNil ifFalse:
[^vector at: info zeroOriginOffset + 1].
^nil
)
public getValueOf: varName <Symbol> ^<Object> = (
| info ctxt vector |
info:: mostLocalInfoFor: varName.
(* defensive programming, but should be unnecessary now that
DebugMapper>localNames filters out variables in deeper contexts. *)
myDepth >= info contextDepth ifFalse:
[^nil].
info remoteVector isNil ifTrue:
[ctxt:: contextAtDepth: info contextDepth.
ctxt size = 0 ifTrue: [^nil].
^ctxt tempAt: info zeroOriginOffset + 1].
vector:: (getLeastLocalValueOf: info remoteVector).
vector isNil ifFalse:
[^vector at: info zeroOriginOffset + 1].
^nil
)
public getterAstFor: varName <Symbol> ^<SendAST> = (
| info |
info:: mostLocalInfoFor: varName.
^info remoteVector isNil
ifTrue: [UnresolvedSendAST new
receiver: (astForContextAtDepth: info contextDepth);
message: (MessageAST new
selector: #tempAt:;
arguments: {NumberAST new value: info zeroOriginOffset + 1})]
ifFalse: [UnresolvedSendAST new
receiver: (leastLocalGetterAstFor: info remoteVector);
message: (MessageAST new
selector: #at:;
arguments: {NumberAST new value: info zeroOriginOffset + 1})]
)
leastLocalGetterAstFor: varName <Symbol> ^<SendAST> = (
| info |
info:: leastLocalInfoFor: varName.
assert: [info remoteVector isNil].
^UnresolvedSendAST new
receiver: (astForContextAtDepth: info contextDepth);
message: (MessageAST new
selector: #tempAt:;
arguments: {NumberAST new value: info zeroOriginOffset + 1})
)
leastLocalInfoFor: varName <Symbol> ^<LocalVarDebugInfo> = (
| bestInfo |
debugInfo localVariables do: [:info |
(info name = varName and: [info validBCIRange includes: bci]) ifTrue: [
(bestInfo isNil or: [info contextDepth < bestInfo contextDepth]) ifTrue: [
bestInfo:: info]]].
nil = bestInfo ifTrue: [deny].
^bestInfo
)
public localNames = (
| names |
#BOGUS. (* The pc needs to be adjusted the same way we do for the source range. *)
names: List new.
debugInfo localVariables do: [:info |
((info validBCIRange includes: bci)
and: [info isSynthetic not
and: [myDepth >= info contextDepth]]) ifTrue: [
names include: info name ]].
^names
)
mostLocalInfoFor: varName <Symbol> ^<LocalVarDebugInfo> = (
| bestInfo |
debugInfo localVariables do: [:info |
(info name = varName and: [info validBCIRange includes: bci]) ifTrue: [
(bestInfo isNil or: [info contextDepth > bestInfo contextDepth]) ifTrue: [
bestInfo:: info]]].
nil = bestInfo ifTrue: [deny].
^bestInfo
)
public setterAstFor: varName <Symbol> putting: newVal <AST> ^<SendAST> = (
| info |
info:: mostLocalInfoFor: varName.
^info remoteVector isNil
ifTrue: [UnresolvedSendAST new
receiver: (astForContextAtDepth: info contextDepth);
message: (MessageAST new
selector: #tempAt:put:;
arguments: {
NumberAST new value: info zeroOriginOffset + 1.
newVal})]
ifFalse: [UnresolvedSendAST new
receiver: (leastLocalGetterAstFor: info remoteVector);
message: (MessageAST new
selector: #at:put:;
arguments: {NumberAST new value: info zeroOriginOffset + 1.
newVal})]
)
) : (
)
class Interval from: start_ to: stop_ = (
|
public start = start_.
public stop = stop_.
|
) (
public first ^<E> = (
^self start
)
public includes: element <E> ^<Boolean> = (
^start <= element and: [element <= stop]
)
public last ^<E> = (
^self stop
)
) : (
)
class LocalVariableDebugInfo = (
(* Debugger information for a local. *)
|
public name <Symbol>
public validBCIRange <Interval>
public zeroOriginOffset <Integer> (* in remoteVector if not nil, otherwise in context *)
public remoteVector <Symbol | nil>
public contextDepth <Integer> (* 0 = method, 1 = closure, 2 = nested closure, ... *)
|) (
public isSynthetic ^<Boolean> = (
(* remoteVector or setter temp: debugger will not want to report these *)
^(name indexOf: "@") > 0
)
) : (
)
addDebugInfo: node <AST> = (
(* Byte to map is first byte of e.g. send, which is the next, i.e. pc.
Hence this must be sent immediately before generating the send or store bytecode.
Can't send after generating the bytecode because there may be multiple bytes and mapping the last byte is wrong. *)
debugInfo mapBCI: cgen bci to: (Interval from: node start to: node end).
)
addLocalVar: var <AllocatedLocal> = (
var remote ifFalse:
[cgen maxLocals: (cgen maxLocals max: var offset + 1)].
^debugInfo addInfoForLocal: var
)
applyForEffectTo: node <Node> = (
| original startDepth result |
original:: valueExpected.
startDepth:: cgen currentStackDepth.
valueExpected:: false.
result:: node apply: self.
valueExpected:: original.
cgen currentStackDepth = startDepth ifFalse: [unbalancedStack].
^result
)
applyForValueTo: node <Node> = (
| original startDepth result |
original:: valueExpected.
startDepth:: cgen currentStackDepth.
valueExpected:: true.
result:: node apply: self.
valueExpected:: original.
cgen currentStackDepth = (startDepth + 1) ifFalse: [unbalancedStack].
^result
)
public arrayNode: node <ArrayAST> = (
nil = node elements
ifTrue:
[addDebugInfo: node.
cgen createEmptyArray: node size]
ifFalse:
[node elements do: [:ea | self applyForValueTo: ea].
addDebugInfo: node.
cgen createFullArray: node size].
valueExpected ifFalse: [^cgen pop].
)
public blockLocalReturnNode: node <BlockLocalReturnAST> = (
valueExpected ifTrue: [returnUsedForValue].
self applyForValueTo: node expression.
addDebugInfo: node.
cgen returnFromBlock.
)
public boolNode: node <BoolAST> = (
valueExpected ifFalse: [^self].
addDebugInfo: node.
cgen pushLiteral: node value
)
cascadeNormal: node <SendAST> = (
node isKindOfCascadedSendNode
ifTrue: [cascadeNormal: node previousSend]
ifFalse: [self applyForValueTo: node receiver].
cgen dup.
node message arguments do: [:argument | self applyForValueTo: argument].
addDebugInfo: node message.
cgen normalSend: node message selector numArgs: node message arguments size.
cgen pop.
)
public cascadedSendNode: node <CascadedSendAST> = (
| baseSend ::= node. |
[baseSend isKindOfCascadedSendNode]
whileTrue: [baseSend: baseSend previousSend].
baseSend receiver isKindOfExplicitRcvrNode ifTrue:
[outerCascadeNotSupported.
^self].
baseSend isKindOfSuperSendNode ifTrue:
[superCascadeNotSupported.
^self].
assert: [baseSend isKindOfOrdinarySendNode or: [baseSend isKindOfSelfSendNode]]
message: 'Unknown type of cascade'.
cascadeNormal: node previousSend.
node message arguments do: [:argument | self applyForValueTo: argument].
addDebugInfo: node message.
cgen normalSend: node message selector numArgs: node message arguments size.
valueExpected ifFalse: [cgen pop].
)
public characterNode: node <CharacterAST> = (
valueExpected ifFalse: [^self].
addDebugInfo: node.
cgen pushLiteral: node value
)
cleanup = (
cgen: nil.
debugInfo: nil.
valueExpected: nil.
maxClosureContextSize: nil.
)
public closureNode: node <ClosureAST> = (
| blockPos <Integer> savedStackDepth <Integer> savedStackMax <Integer> |
valueExpected ifFalse: [^self].
node copiedOuter size = node copiedInner size ifFalse: [error].
node copiedOuter do:
[:each | cgen pushTemporary: each offset].
addDebugInfo: node.
cgen
createClosureOfArity: node parameters size
copying: node copiedOuter size
length: 0 (* needs patching *).
blockPos: cgen bci.
savedStackDepth:: cgen currentStackDepth.
savedStackMax:: cgen maxStackDepth.
cgen maxStackDepth: 0.
(* node copiedInner do: [:var | addLocalVar: var]. --- todo for debugger *)
(node pushNilCount) timesRepeat: [cgen pushNil].
(* These nils establish the temp frame for temps other than params and copied values. They contribute to the operand stack size, obviating the need to directly include the number of local variables in the context size *)
self applyForEffectTo: node body.
maxClosureContextSize::
maxClosureContextSize max: (node parameters size + node copiedOuter size + cgen maxStackDepth).
cgen currentStackDepth: savedStackDepth.
cgen maxStackDepth: savedStackMax.
cgen patchClosureJumpAt: blockPos with: cgen bci - blockPos. (* patch jump over block body *)
)
public codeBodyNode: node <CodeBodyAST> = (
(* This could be a method body, a closure body, part of an inlined block, or a setter send. Each method body and closure body needs to have separate indexing for temps because their activations will be separate MethodContexts. Inlined blocks and setter sends should continue to use the same indexing as their enclosing method or closure. To accomplish this, we use a stack of counters. *)
|
statements
localVariableDebugInfos
startBCI
validBCIRange
|
localVariableDebugInfos:: List new.
startBCI:: cgen bci.
localVariableDebugInfos addAll:
(node parameters collect: [:param | addLocalVar: param]).
(* Copied values are included at the beginning of temporaries thanks to the Rewriter. *)
node temporaries do:
[:temp |
temp remote
ifFalse: [localVariableDebugInfos add: (addLocalVar: temp)]
ifTrue:
[localVariableDebugInfos add:
(debugInfo addInfoForRemote: temp in: temp remoteVector)]].
statements:: node statements.
1 to: statements size do:
[:index | | statement = node statements at: index. |
index = statements size
ifTrue: [statement apply: self]