-
Notifications
You must be signed in to change notification settings - Fork 68
/
SistaCogitClone.class.st
2378 lines (2203 loc) · 101 KB
/
SistaCogitClone.class.st
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
Class {
#name : #SistaCogitClone,
#superclass : #SistaRegisterAllocatingCogit,
#category : #'VMMaker-JIT'
}
{ #category : #translation }
SistaCogitClone class >> additionalHeadersDo: aBinaryBlock [
"Evaluate aBinaryBlock with the names and contents of
any additional header files that need to be generated."
aBinaryBlock
value: 'cogmethod.h'
value: SistaCogMethod cogMethodHeader
]
{ #category : #translation }
SistaCogitClone class >> ancilliaryClasses [
"Answer any extra classes to be included in the translation."
^super ancilliaryClasses copyWith: SistaCogMethod
]
{ #category : #translation }
SistaCogitClone class >> declareCVarsIn: aCodeGen [
aCodeGen var: 'counters' type: #usqInt
]
{ #category : #'class initialization' }
SistaCogitClone class >> initializeWithOptions: optionsDictionary [
super initializeWithOptions: optionsDictionary.
CounterBytes := 4.
MaxCounterValue := (1 << 16) - 1
]
{ #category : #'accessing class hierarchy' }
SistaCogitClone class >> methodZoneClass [
^SistaMethodZone
]
{ #category : #accessing }
SistaCogitClone class >> numTrampolines [
^super numTrampolines + 1
"Cogit withAllSubclasses collect: [:c| {c. (c instVarNames select: [:ea| ea beginsWith: 'ce']) size}]"
"self instVarNames select: [:ea| ea beginsWith: 'ce']"
]
{ #category : #'in-line cacheing' }
SistaCogitClone >> allowEarlyOpenPICPromotion [
<inline: true>
^ false
]
{ #category : #'compile abstract instructions' }
SistaCogitClone >> compileCogFullBlockMethod: numCopied [
<option: #SistaV1BytecodeSet>
counters := 0.
^super compileCogFullBlockMethod: numCopied
]
{ #category : #'compile abstract instructions' }
SistaCogitClone >> compileCogMethod: selector [
counters := 0.
^super compileCogMethod: selector
]
{ #category : #'compile abstract instructions' }
SistaCogitClone >> compileFrameBuild [
"Override to prefetch counters, if any."
super compileFrameBuild.
counters ~= 0 ifTrue:
[self PrefetchAw: counters]
]
{ #category : #'compile abstract instructions' }
SistaCogitClone >> compileFullBlockMethodFrameBuild: numCopied [
"Override to prefetch counters if any"
super compileFullBlockMethodFrameBuild: numCopied.
counters ~= 0 ifTrue:
[self PrefetchAw: counters]
]
{ #category : #'compile abstract instructions' }
SistaCogitClone >> decodePushIntegerLongBefore: bcpc in: aMethodObj [
<inline: true>
| distance upperByte |
distance := objectMemory fetchByte: bcpc - 1 ofObject: aMethodObj.
upperByte := objectMemory fetchByte: bcpc - 3 ofObject: aMethodObj.
upperByte > 127 ifTrue: [upperByte := upperByte - 256].
^ (upperByte << 8) + distance.
]
{ #category : #accessing }
SistaCogitClone >> defaultCogCodeSize [
"Return the default number of bytes to allocate for native code at startup.
The actual value can be set via vmParameterAt: and/or a preference in the ini file."
<api>
^2 * backEnd getDefaultCogCodeSize
]
{ #category : #disassembly }
SistaCogitClone >> disassembleMethod: surrogateOrAddress on: aStream [
<doNotGenerate>
| cogMethod |
cogMethod := super disassembleMethod: surrogateOrAddress on: aStream.
(cogMethod cmType = CMMethod
and: [cogMethod counters ~= 0]) ifTrue:
[aStream nextPutAll: 'counters:'; cr.
0 to: (objectRepresentation numCountersFor: cogMethod counters) - 1 do:
[:i| | addr |
addr := i * CounterBytes + counters.
addr printOn: aStream base: 16.
aStream nextPut: $:; space.
(objectMemory long32At: addr) printOn: aStream base: 16.
aStream cr].
aStream flush]
]
{ #category : #accessing }
SistaCogitClone >> estimateOfAbstractOpcodesPerBytecodes [
"Due to the counter logic, the estimation is higher"
<inline: true>
^ 11
]
{ #category : #'generate machine code' }
SistaCogitClone >> fillInCPICHeader: pic numArgs: numArgs numCases: numCases hasMNUCase: hasMNUCase selector: selector [
pic counters: 0.
^super fillInCPICHeader: pic numArgs: numArgs numCases: numCases hasMNUCase: hasMNUCase selector: selector
]
{ #category : #'generate machine code' }
SistaCogitClone >> fillInCounters: nCounters atEndAddress: endAddress [
endAddress - (nCounters * CounterBytes)
to: endAddress - CounterBytes
by: CounterBytes
do: [:address|
objectMemory
long32At: address
put: (initialCounterValue << 16 + initialCounterValue)]
]
{ #category : #'generate machine code' }
SistaCogitClone >> fillInCounters: nCounters atStartAddress: startAddress [
startAddress
to: startAddress + (nCounters - 1 * CounterBytes)
by: CounterBytes
do: [:address|
objectMemory
long32At: address
put: (initialCounterValue << 16 + initialCounterValue)]
]
{ #category : #'generate machine code' }
SistaCogitClone >> fillInMethodHeader: method size: size selector: selector [
super fillInMethodHeader: method size: size selector: selector.
self fillInCounters: numCounters atStartAddress: counters.
method counters: counters.
^method
]
{ #category : #'generate machine code' }
SistaCogitClone >> fillInOPICHeader: pic numArgs: numArgs selector: selector [
pic counters: 0.
^super fillInOPICHeader: pic numArgs: numArgs selector: selector
]
{ #category : #'inline primitive generators' }
SistaCogitClone >> genAtPutInlinePrimitive: prim [
"The store check requires rr to be ReceiverResultReg"
prim caseOf: {
"3000 pointerAt:put:
Mutable pointer object (Fixed sized or not) and not a context, Smi, Anything => arg2 (1-based, optimised if arg1 is a constant)"
[0] -> [self genPointerAtPutMaybeContext: false storeCheck: false immutabilityCheck: false].
"3001 storeCheckPointerAt:put:
Mutable pointer object (Fixed sized or not) and not a context, Smi, Anything => arg2 (1-based, optimised if arg1 is a constant)"
[1] -> [self genPointerAtPutMaybeContext: false storeCheck: true immutabilityCheck: false].
"3002 maybeContextPointerAt:put:
Mutable pointer object (Fixed sized or not), Smi, Anything => arg2 (1-based, optimised if arg1 is a constant)"
[2] -> [self genPointerAtPutMaybeContext: true storeCheck: false immutabilityCheck: false].
"3003 maybeContextStoreCheckPointerAt:put:
Mutable pointer object (Fixed sized or not), Smi, Anything => arg2 (1-based, optimised if arg1 is a constant)"
[3] -> [self genPointerAtPutMaybeContext: true storeCheck: true immutabilityCheck: false].
"3004 byteAt:put:
Mutable byte object, Smi, 8 bits unsigned Smi => arg2 (1-based, optimised if arg1 is a constant)"
[4] -> [self genByteAtPut].
}
otherwise: [^EncounteredUnknownBytecode].
^0
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryAtConstInlinePrimitive: primIndex [
"2064 pointerAt:
Pointer object (Fixed sized or not) and not a context, Smi => (1-based, optimised if arg1 is a constant)
2065 maybeContextPointerAt:
Pointer object (Fixed sized or not), Smi => (1-based, optimised if arg1 is a constant)
2066 byteAt:
byte object, Smi => 8 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2067 shortAt:
short object, Smi => 16 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2068 wordAt:
word object, Smi => 32 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2069 doubleWordAt:
double word object, Smi => 64 bits unsigned Smi or LargePositiveInteger (1-based, optimised if arg1 is a constant)"
| rr val zeroBasedIndex |
val := self ssTop constant.
rr := primIndex = 65
ifFalse: [self allocateRegForStackEntryAt: 1]
ifTrue: [self ssAllocateRequiredReg: ReceiverResultReg.
self voidReceiverResultRegContainsSelf.
ReceiverResultReg].
(self ssValue: 1) popToReg: rr.
self ssPop: 2.
zeroBasedIndex := (objectMemory integerValueOf: val) - 1.
primIndex caseOf: {
[64] -> [objectRepresentation genLoadSlot: zeroBasedIndex sourceReg: rr destReg: rr].
[65] -> [self ssAllocateRequiredReg: SendNumArgsReg.
^self genPushMaybeContextSlotIndex: zeroBasedIndex].
[66] -> [self MoveMb: zeroBasedIndex + objectMemory baseHeaderSize r: rr R: rr.
objectRepresentation genConvertIntegerToSmallIntegerInReg: rr]
}
otherwise: [^EncounteredUnknownBytecode].
^self ssPushRegister: rr
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryAtInlinePrimitive: primIndex [
"2064 pointerAt:
Pointer object (Fixed sized or not) and not a context, Smi => (1-based, optimised if arg1 is a constant)
2065 maybeContextPointerAt:
Pointer object (Fixed sized or not), Smi => (1-based, optimised if arg1 is a constant)
2066 byteAt:
byte object, Smi => 8 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2067 shortAt:
short object, Smi => 16 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2068 wordAt:
word object, Smi => 32 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2069 doubleWordAt:
double word object, Smi => 64 bits unsigned Smi or LargePositiveInteger (1-based, optimised if arg1 is a constant)"
| ra rr adjust |
self allocateRegForStackTopTwoEntriesInto: [:rTop :rNext | ra := rTop. rr := rNext ].
self ssTop popToReg: ra.
self ssPop: 1.
self ssTop popToReg: rr.
self ssPop: 1.
primIndex caseOf: {
[64] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
adjust := (objectMemory baseHeaderSize >> objectMemory shiftForWord) - 1.
adjust ~= 0 ifTrue: [ self AddCq: adjust R: ra. ].
self MoveXwr: ra R: rr R: rr ].
[66] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
adjust := objectMemory baseHeaderSize - 1.
self AddCq: adjust R: ra.
self MoveXbr: ra R: rr R: rr.
objectRepresentation genConvertIntegerToSmallIntegerInReg: rr].
}
otherwise: [^EncounteredUnknownBytecode].
^self ssPushRegister: rr
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryCompInlinePrimitive: primIndex [
"2032 >
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2033 <
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2034 >=
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2035 <=
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2036 =
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2037 ~=
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2038 rawEqualsEquals:
not a forwarder, not a forwarder => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2039 rawNotEqualsEquals:
not a forwarder, not a forwarder => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)"
|resultReg otherReg opTrue opFalse invertedOpTrue invertedOpFalse|
self assert: (primIndex between: 32 and: 39).
primIndex caseOf: {
[32] -> [opTrue := JumpGreater. opFalse := JumpLessOrEqual. invertedOpTrue := JumpLess. invertedOpFalse := JumpGreaterOrEqual].
[33] -> [opTrue := JumpLess. opFalse := JumpGreaterOrEqual. invertedOpTrue := JumpGreater. invertedOpFalse := JumpLessOrEqual].
[34] -> [opTrue := JumpGreaterOrEqual. opFalse := JumpLess. invertedOpTrue := JumpLessOrEqual. invertedOpFalse := JumpGreater].
[35] -> [opTrue := JumpLessOrEqual. opFalse := JumpGreater. invertedOpTrue := JumpGreaterOrEqual. invertedOpFalse := JumpLess].
[36] -> [opTrue := JumpZero. opFalse := JumpNonZero. invertedOpTrue := JumpZero. invertedOpFalse := JumpNonZero].
[37] -> [opTrue := JumpNonZero. opFalse := JumpZero. invertedOpTrue := JumpNonZero. invertedOpFalse := JumpZero].
[38] -> [opTrue := JumpZero. opFalse := JumpNonZero. invertedOpTrue := JumpZero. invertedOpFalse := JumpNonZero].
[39] -> [opTrue := JumpNonZero. opFalse := JumpZero. invertedOpTrue := JumpNonZero. invertedOpFalse := JumpZero].
}.
"Gen comparison"
self ssTop type = SSConstant
ifTrue: [resultReg := self allocateRegForStackEntryAt: 1.
(self ssValue: 1) popToReg: resultReg.
self genCmpConstant: self ssTop constant R: resultReg.
self ssPop: 2.
self genBinaryInlineComparison: opTrue opFalse: opFalse destReg: resultReg.
^self ssPushRegister: resultReg].
(self ssValue: 1) type = SSConstant
ifTrue: [resultReg := self allocateRegForStackEntryAt: 0.
self ssTop popToReg: resultReg.
self genCmpConstant: (self ssValue: 1) constant R: resultReg.
self ssPop: 2.
self genBinaryInlineComparison: invertedOpTrue opFalse: invertedOpFalse destReg: resultReg.
^self ssPushRegister: resultReg].
otherReg := self allocateRegForStackEntryAt: 0.
self ssTop popToReg: otherReg.
resultReg := self allocateRegForStackEntryAt: 1 notConflictingWith: (self registerMaskFor: otherReg).
(self ssValue: 1) popToReg: resultReg.
self CmpR: otherReg R: resultReg.
self ssPop: 2.
self genBinaryInlineComparison: opTrue opFalse: opFalse destReg: resultReg.
^self ssPushRegister: resultReg
]
{ #category : #'inline primitive generators' }
SistaCogitClone >> genBinaryConstOpVarInlinePrimitive: prim [
"Const op var version of binary inline primitives."
"SistaV1: 248 11111000 iiiiiiii mjjjjjjj Call Primitive #iiiiiiii + (jjjjjjj * 256) m=1 means inlined primitive, no hard return after execution.
See EncoderForSistaV1's class comment and StackInterpreter>>#binaryInlinePrimitive:"
<option: #SistaVM>
| ra val untaggedVal adjust |
ra := self allocateRegForStackEntryAt: 0.
self ssTop popToReg: ra.
self ssPop: 1.
val := self ssTop constant.
self ssPop: 1.
untaggedVal := val - objectMemory smallIntegerTag.
prim caseOf: {
"0 through 6, +, -, *, /, //, \\, quo:, SmallInteger op SmallInteger => SmallInteger, no overflow"
[0] -> [self AddCq: untaggedVal R: ra].
[1] -> [self MoveCq: val R: TempReg.
self SubR: ra R: TempReg.
objectRepresentation genAddSmallIntegerTagsTo: TempReg.
self MoveR: TempReg R: ra].
[2] -> [objectRepresentation genShiftAwaySmallIntegerTagsInScratchReg: ra.
self MoveCq: untaggedVal R: TempReg.
self MulR: TempReg R: ra.
objectRepresentation genSetSmallIntegerTagsIn: ra].
"2011 through 2015 Variable-sized pointers new, byte new, 16-bit new, 32-bit new, 64-bit new"
"2016 through 2020, bitAnd:, bitOr:, bitXor, bitShiftLeft:, bitShiftRight:, SmallInteger op SmallInteger => SmallInteger, no overflow"
[16] -> [ self AndCq: val R: ra ].
[17] -> [ self OrCq: val R: ra ].
[18] -> [ self XorCw: untaggedVal R: ra. ].
[19] -> [ objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
self MoveCq: untaggedVal R: TempReg.
self LogicalShiftLeftR: ra R: TempReg.
objectRepresentation genAddSmallIntegerTagsTo: TempReg.
self MoveR: TempReg R: ra].
[20] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
self MoveCq: untaggedVal R: TempReg.
self ArithmeticShiftRightR: ra R: TempReg.
objectRepresentation genClearAndSetSmallIntegerTagsIn: TempReg.
self MoveR: TempReg R: ra].
"2032 through 2037, >, <, >=, <=. =, ~=, SmallInteger op SmallInteger => Boolean (flags?? then in jump bytecodes if ssTop is a flags value, just generate the instruction!!)"
"CmpCqR is SubRCq so everything is reversed, but because no CmpRCq things are reversed again and we invert the sense of the jumps."
[32] -> [ self CmpCq: val R: ra.
self genBinaryInlineComparison: JumpLess opFalse: JumpGreaterOrEqual destReg: ra ].
[33] -> [ self CmpCq: val R: ra.
self genBinaryInlineComparison: JumpGreater opFalse: JumpLessOrEqual destReg: ra ].
[34] -> [ self CmpCq: val R: ra.
self genBinaryInlineComparison: JumpLessOrEqual opFalse: JumpGreater destReg: ra ].
[35] -> [ self CmpCq: val R: ra.
self genBinaryInlineComparison: JumpGreaterOrEqual opFalse: JumpLess destReg: ra ].
[36] -> [ self CmpCq: val R: ra.
self genBinaryInlineComparison: JumpZero opFalse: JumpNonZero destReg: ra ].
[37] -> [ self CmpCq: val R: ra.
self genBinaryInlineComparison: JumpNonZero opFalse: JumpZero destReg: ra ].
"2064 through 2068, Pointer Object>>at:, Byte Object>>at:, Short16 Word Object>>at: LongWord32 Object>>at: Quad64Word Object>>at:. obj op 0-rel SmallInteger => oop"
[64] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
adjust := (objectMemory baseHeaderSize >> objectMemory shiftForWord) - 1. "shift by baseHeaderSize and then move from 1 relative to zero relative"
adjust ~= 0 ifTrue: [ self AddCq: adjust R: ra. ].
self genMoveConstant: val R: TempReg.
self MoveXwr: ra R: TempReg R: ra].
[65] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
adjust := objectMemory baseHeaderSize - 1. "shift by baseHeaderSize and then move from 1 relative to zero relative"
self AddCq: adjust R: ra.
self genMoveConstant: val R: TempReg.
self MoveXbr: ra R: TempReg R: ra.
objectRepresentation genConvertIntegerToSmallIntegerInReg: ra]
}
otherwise: [^EncounteredUnknownBytecode].
self ssPushRegister: ra.
^0
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryConstOpVarSmiInlinePrimitive: primIndex [
| ra val untaggedVal |
ra := self allocateRegForStackEntryAt: 0.
self ssTop popToReg: ra.
self ssPop: 1.
val := self ssTop constant.
self ssPop: 1.
untaggedVal := val - objectMemory smallIntegerTag.
primIndex caseOf: {
"2000 +
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[0] -> [self AddCq: untaggedVal R: ra].
"2001 -
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[1] -> [self MoveCq: val R: TempReg.
self SubR: ra R: TempReg.
objectRepresentation genAddSmallIntegerTagsTo: TempReg.
self MoveR: TempReg R: ra].
"2002 *
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[2] -> [objectRepresentation genShiftAwaySmallIntegerTagsInScratchReg: ra.
self MoveCq: untaggedVal R: TempReg.
self MulR: TempReg R: ra.
objectRepresentation genSetSmallIntegerTagsIn: ra].
"2016 bitAnd:
Smi, Smi => Smi (optimised if one operand is a constant)"
[16] -> [ self AndCq: val R: ra ].
"2017 bitOr:
Smi, Smi => Smi (optimised if one operand is a constant)"
[17] -> [ self OrCq: val R: ra ].
"2018 bitXor:
Smi, Smi => Smi (optimised if one operand is a constant)"
[18] -> [ self XorCw: untaggedVal R: ra. ].
}
otherwise: [^EncounteredUnknownBytecode].
self ssPushRegister: ra.
^0
]
{ #category : #'inline primitive generators' }
SistaCogitClone >> genBinaryInlineComparison: opTrue opFalse: opFalse destReg: destReg [
"Inlined comparison. opTrue = jump for true and opFalse = jump for false"
<var: #branchDescriptor type: #'BytecodeDescriptor *'>
| nextPC branchDescriptor targetBytecodePC postBranchPC |
self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target |
branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetBytecodePC := target ].
(branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse])
ifTrue: "This is the path where the inlined comparison is followed immediately by a branch"
[ (self fixupAt: nextPC) notAFixup
ifTrue: "The next instruction is dead. we can skip it."
[deadCode := true.
self ensureFixupAt: targetBytecodePC.
self ensureFixupAt: postBranchPC ]
ifFalse:
[self ssPushConstant: objectMemory trueObject]. "dummy value"
self genConditionalBranch: (branchDescriptor isBranchTrue ifTrue: [opTrue] ifFalse: [opFalse])
operand: (self ensureNonMergeFixupAt: targetBytecodePC) asUnsignedInteger.
"We can only elide the jump if the pc after nextPC is the same as postBranchPC.
Branch following means it may not be."
self nextDescriptorExtensionsAndNextPCInto:
[:iguana1 :iguana2 :iguana3 :followingPC| nextPC := followingPC].
(deadCode and: [nextPC = postBranchPC]) ifFalse:
[ self Jump: (self ensureNonMergeFixupAt: postBranchPC) ] ]
ifFalse: "This is the path where the inlined comparison is *not* followed immediately by a branch"
[| condJump jump |
condJump := self genConditionalBranch: opTrue operand: 0.
self genMoveFalseR: destReg.
jump := self Jump: 0.
condJump jmpTarget: (self genMoveTrueR: destReg).
jump jmpTarget: self Label].
^ 0
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryInlinePrimitive: primIndex [
"Bulk comments: each sub-method has its own comment with the specific case.
2000 +
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)
2001 -
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)
2002 *
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)
2003 /
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)
2004 //
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)
2005 \
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)
2006 quo:
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)
2016 bitAnd:
Smi, Smi => Smi (optimised if one operand is a constant)
2017 bitOr:
Smi, Smi => Smi (optimised if one operand is a constant)
2018 bitXor:
Smi, Smi => Smi (optimised if one operand is a constant)
2019 bitShiftLeft:
Smi greater or equal to 0, Smi greater or equal to 0 => Smi (no overflow, optimised if arg1 is a constant)
2020 bitShiftRight:
Smi, Smi greater or equal to 0 => Smi (optimised if arg1 is a constant)
2032 >
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2033 <
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2034 >=
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2035 <=
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2036 =
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2037 ~=
Smi, Smi => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2038 rawEqualsEquals:
not a forwarder, not a forwarder => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2039 rawNotEqualsEquals:
not a forwarder, not a forwarder => Boolean (optimised if one operand is a constant, Pipelined with ifTrue:ifFalse:)
2048 rawNew:
literal which is a fixed-sized behavior, Smi => instance of receiver, fields nilled out (optimised if arg1 is a constant)
2049 rawNewNoInit:
literal which is a fixed-sized behavior, Smi => instance of receiver (Fields of returned value contain undefined data, optimised if arg1 is a constant)
2064 pointerAt:
Pointer object (Fixed sized or not) and not a context, Smi => (1-based, optimised if arg1 is a constant)
2065 maybeContextPointerAt:
Pointer object (Fixed sized or not), Smi => (1-based, optimised if arg1 is a constant)
2066 byteAt:
byte object, Smi => 8 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2067 shortAt:
short object, Smi => 16 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2068 wordAt:
word object, Smi => 32 bits unsigned Smi (1-based, optimised if arg1 is a constant)
2069 doubleWordAt:
double word object, Smi => 64 bits unsigned Smi or LargePositiveInteger (1-based, optimised if arg1 is a constant)"
(primIndex <= 18 and: [primIndex <= 2 or: [primIndex > 6]]) ifTrue:
[self ssTop type = SSConstant
ifTrue: [^self genBinaryVarOpConstSmiInlinePrimitive: primIndex].
(self ssValue: 1) type = SSConstant
ifTrue: [^self genBinaryConstOpVarSmiInlinePrimitive: primIndex].
^self genBinaryVarOpVarSmiInlinePrimitive: primIndex].
primIndex <= 6 ifTrue: [^self genDivInlinePrimitive: primIndex].
primIndex = 19 ifTrue: [^self genBinarySmiBitShiftLeftInlinePrimitive].
primIndex = 20 ifTrue: [^self genBinarySmiBitShiftRightInlinePrimitive].
primIndex < 32 ifTrue: [^EncounteredUnknownBytecode].
primIndex <= 39 ifTrue: [^self genBinaryCompInlinePrimitive: primIndex].
primIndex < 48 ifTrue: [^EncounteredUnknownBytecode].
primIndex <= 49 ifTrue: [^self genBinaryNewInlinePrimitive: primIndex].
primIndex < 64 ifTrue: [^EncounteredUnknownBytecode].
primIndex <= 69 ifTrue:
[self ssTop type = SSConstant
ifTrue: [^self genBinaryAtConstInlinePrimitive: primIndex].
^self genBinaryAtInlinePrimitive: primIndex].
^EncounteredUnknownBytecode
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryNewInlinePrimitive: primIndex [
"2048 rawNew:
literal which is a variable-sized behavior, Smi => instance of receiver, fields nilled/zeroed out (optimised if arg1 is a constant)
2049 rawNewNoInit:
literal which is a variable-sized behavior, Smi => instance of receiver (Fields of returned value contain undefined data, optimised if arg1 is a constant)"
| resultReg classObj classFormat argInt |
"Assertion"
(self ssValue: 1) type = SSConstant ifFalse: [^UnimplementedOperation].
"It would be nice to deal with variable-sized allocation on non-constant.
We need to see, however, than inlined allocation cannot deal with large allocations (i.e.
allocation directly in old space, > 65k currently. So Scorch still needs to perform range analysis
or generate 2 paths at the bytecode level in this case."
self ssTop type = SSConstant ifFalse: [^UnimplementedOperation].
classObj := (self ssValue: 1) constant.
self assert: (objectMemory isNonImmediate: classObj).
self assert: (coInterpreter objCouldBeClassObj: classObj).
self deny: (objectMemory isFixedSizePointerFormat: (objectMemory instSpecOfClassFormat: (objectMemory formatOfClass: classObj))).
objectMemory classTagForClass: classObj. "Ensure Behavior hash"
"Actual code"
resultReg := self allocateRegNotConflictingWith: 0.
classFormat := objectMemory instSpecOfClassFormat: (objectMemory formatOfClass: classObj).
argInt := objectMemory integerValueOf: self ssTop constant.
self ssPop: 2.
self ssPushRegister: resultReg.
(classFormat = objectMemory arrayFormat or: [classFormat = objectMemory indexablePointersFormat]) ifTrue:
[^objectRepresentation genGetInstanceOfPointerClass: classObj into: resultReg initializingIf: primIndex = 48 numVariableSlots: argInt].
(classFormat = objectMemory firstByteFormat) ifTrue:
[^objectRepresentation genGetInstanceOfByteClass: classObj into: resultReg initializingIf: primIndex = 48 numBytes: argInt].
^UnimplementedOperation
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinarySmiBitShiftLeftInlinePrimitive [
"2019 bitShiftLeft:
Smi greater or equal to 0, Smi greater or equal to 0 => Smi (no overflow, optimised if arg1 is a constant)"
| rr ra |
rr := self allocateRegForStackEntryAt: 1.
(self ssValue: 1) popToReg: rr.
objectRepresentation genRemoveSmallIntegerTagsInScratchReg: rr.
self ssTop type = SSConstant
ifTrue: [self LogicalShiftLeftCq: (objectMemory integerValueOf: self ssTop constant) R: rr]
ifFalse: [ra := self allocateRegForStackEntryAt: 0 notConflictingWith: (self registerMaskFor: rr).
self ssTop popToReg: ra.
objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
self LogicalShiftLeftR: ra R: rr.].
objectRepresentation genAddSmallIntegerTagsTo: rr.
self ssPop: 2.
^self ssPushRegister: rr
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinarySmiBitShiftRightInlinePrimitive [
"2019 bitShiftLeft:
Smi greater or equal to 0, Smi greater or equal to 0 => Smi (no overflow, optimised if arg1 is a constant)"
| rr ra |
rr := self allocateRegForStackEntryAt: 1.
(self ssValue: 1) popToReg: rr.
self ssTop type = SSConstant
ifTrue: [self ArithmeticShiftRightCq: (objectMemory integerValueOf: self ssTop constant) R: rr]
ifFalse: [ra := self allocateRegForStackEntryAt: 0 notConflictingWith: (self registerMaskFor: rr).
self ssTop popToReg: ra.
objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
self ArithmeticShiftRightR: ra R: rr].
objectRepresentation genClearAndSetSmallIntegerTagsIn: rr.
self ssPop: 2.
^self ssPushRegister: rr
]
{ #category : #'inline primitive generators' }
SistaCogitClone >> genBinaryVarOpConstInlinePrimitive: prim [
"Var op const version of inline binary inline primitives."
"SistaV1: 248 11111000 iiiiiiii mjjjjjjj Call Primitive #iiiiiiii + (jjjjjjj * 256) m=1 means inlined primitive, no hard return after execution.
See EncoderForSistaV1's class comment and StackInterpreter>>#binaryInlinePrimitive:"
<option: #SistaVM>
| rr val untaggedVal |
val := self ssTop constant.
self ssPop: 1.
rr := self allocateRegForStackEntryAt: 0.
self ssTop popToReg: rr.
self ssPop: 1.
untaggedVal := val - objectMemory smallIntegerTag.
prim caseOf: {
"0 through 6, +, -, *, /, //, \\, quo:, SmallInteger op SmallInteger => SmallInteger, no overflow"
[0] -> [self AddCq: untaggedVal R: rr].
[1] -> [self SubCq: untaggedVal R: rr ].
[2] -> [self flag: 'could use MulCq:R'.
objectRepresentation genShiftAwaySmallIntegerTagsInScratchReg: rr.
self MoveCq: untaggedVal R: TempReg.
self MulR: TempReg R: rr.
objectRepresentation genSetSmallIntegerTagsIn: rr].
"2016 through 2020, bitAnd:, bitOr:, bitXor, bitShiftLeft:, bitShiftRight:, SmallInteger op SmallInteger => SmallInteger, no overflow"
[16] -> [ self AndCq: val R: rr ].
[17] -> [ self OrCq: val R: rr ].
[18] -> [ self flag: 'could use XorCq:'.
self XorCw: untaggedVal R: rr. ].
[19] -> [ objectRepresentation genRemoveSmallIntegerTagsInScratchReg: rr.
self LogicalShiftLeftCq: (objectMemory integerValueOf: val) R: rr.
objectRepresentation genAddSmallIntegerTagsTo: rr ].
[20] -> [self ArithmeticShiftRightCq: (objectMemory integerValueOf: val) R: rr.
objectRepresentation genClearAndSetSmallIntegerTagsIn: rr].
"2032 through 2037, >, <, >=, <=. =, ~=, SmallInteger op SmallInteger => Boolean (flags?? then in jump bytecodes if ssTop is a flags value, just generate the instruction!!)"
"CmpCqR is SubRCq so everything is reversed."
[32] -> [ self CmpCq: val R: rr.
self genBinaryInlineComparison: JumpGreater opFalse: JumpLessOrEqual destReg: rr ].
[33] -> [ self CmpCq: val R: rr.
self genBinaryInlineComparison: JumpLess opFalse: JumpGreaterOrEqual destReg: rr ].
[34] -> [ self CmpCq: val R: rr.
self genBinaryInlineComparison: JumpGreaterOrEqual opFalse: JumpLess destReg: rr ].
[35] -> [ self CmpCq: val R: rr.
self genBinaryInlineComparison: JumpLessOrEqual opFalse: JumpGreater destReg: rr ].
[36] -> [ self CmpCq: val R: rr.
self genBinaryInlineComparison: JumpZero opFalse: JumpNonZero destReg: rr ].
[37] -> [ self CmpCq: val R: rr.
self genBinaryInlineComparison: JumpNonZero opFalse: JumpZero destReg: rr ].
"2064 through 2068, Pointer Object>>at:, Byte Object>>at:, Short16 Word Object>>at: LongWord32 Object>>at: Quad64Word Object>>at:. obj op 0-rel SmallInteger => oop"
[64] -> [objectRepresentation genLoadSlot: (objectMemory integerValueOf: val) - 1 sourceReg: rr destReg: rr].
[65] -> [self MoveCq: (objectMemory integerValueOf: val) + objectMemory baseHeaderSize - 1 R: TempReg.
self MoveXbr: TempReg R: rr R: rr.
objectRepresentation genConvertIntegerToSmallIntegerInReg: rr]
}
otherwise: [^EncounteredUnknownBytecode].
self ssPushRegister: rr.
^0
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryVarOpConstSmiInlinePrimitive: primIndex [
| rr val untaggedVal |
self assert: primIndex <= 18.
val := self ssTop constant.
self ssPop: 1.
rr := self allocateRegForStackEntryAt: 0.
self ssTop popToReg: rr.
self ssPop: 1.
untaggedVal := val - objectMemory smallIntegerTag.
primIndex caseOf: {
"2000 +
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[0] -> [self AddCq: untaggedVal R: rr].
"2001 -
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[1] -> [self SubCq: untaggedVal R: rr ].
"2002 *
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[2] -> [self flag: 'could use MulCq:R'.
objectRepresentation genShiftAwaySmallIntegerTagsInScratchReg: rr.
self MoveCq: untaggedVal R: TempReg.
self MulR: TempReg R: rr.
objectRepresentation genSetSmallIntegerTagsIn: rr].
"2016 bitAnd:
Smi, Smi => Smi (optimised if one operand is a constant)"
[16] -> [ self AndCq: val R: rr ].
"2017 bitOr:
Smi, Smi => Smi (optimised if one operand is a constant)"
[17] -> [ self OrCq: val R: rr ].
"2018 bitXor:
Smi, Smi => Smi (optimised if one operand is a constant)"
[18] -> [ self flag: 'could use XorCq:'.
self XorCw: untaggedVal R: rr. ]
}
otherwise: [^EncounteredUnknownBytecode].
self ssPushRegister: rr.
^0
]
{ #category : #'inline primitive generators' }
SistaCogitClone >> genBinaryVarOpVarInlinePrimitive: prim [
"Var op var version of binary inline primitives."
"SistaV1: 248 11111000 iiiiiiii mjjjjjjj Call Primitive #iiiiiiii + (jjjjjjj * 256) m=1 means inlined primitive, no hard return after execution.
See EncoderForSistaV1's class comment and StackInterpreter>>#binaryInlinePrimitive:"
<option: #SistaVM>
| ra rr adjust |
self allocateRegForStackTopTwoEntriesInto: [:rTop :rNext | ra := rTop. rr := rNext ].
self ssTop popToReg: ra.
self ssPop: 1.
self ssTop popToReg: rr.
self ssPop: 1.
prim caseOf: {
"0 through 6, +, -, *, /, //, \\, quo:, SmallInteger op SmallInteger => SmallInteger, no overflow"
[0] -> [objectRepresentation genRemoveSmallIntegerTagsInScratchReg: ra.
self AddR: ra R: rr].
[1] -> [self SubR: ra R: rr.
objectRepresentation genAddSmallIntegerTagsTo: rr].
[2] -> [self genShiftAwaySmallIntegerTagsInScratchReg: rr.
self genRemoveSmallIntegerTagsInScratchReg: ra.
self MulR: ra R: rr.
self genSetSmallIntegerTagsIn: rr].
"[4] -> [].
[5] -> [].
[6] -> []."
"2016 through 2020, bitAnd:, bitOr:, bitXor, bitShiftLeft:, bitShiftRight:, SmallInteger op SmallInteger => SmallInteger, no overflow"
[16] -> [ self AndR: ra R: rr ].
[17] -> [ self OrR: ra R: rr ].
[18] -> [objectRepresentation genRemoveSmallIntegerTagsInScratchReg: ra.
self XorR: ra R: rr. ].
[19] -> [ objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
objectRepresentation genRemoveSmallIntegerTagsInScratchReg: rr.
self LogicalShiftLeftR: ra R: rr.
objectRepresentation genAddSmallIntegerTagsTo: rr].
[20] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
self ArithmeticShiftRightR: ra R: rr.
objectRepresentation genClearAndSetSmallIntegerTagsIn: rr.].
"2032 through 2037, >, <, >=, <=. =, ~=, SmallInteger op SmallInteger => Boolean (flags?? then in jump bytecodes if ssTop is a flags value, just generate the instruction!!)"
"CmpCqR is SubRCq so everything is reversed."
[32] -> [ self CmpR: ra R: rr.
self genBinaryInlineComparison: JumpGreater opFalse: JumpLessOrEqual destReg: rr ].
[33] -> [ self CmpR: ra R: rr.
self genBinaryInlineComparison: JumpLess opFalse: JumpGreaterOrEqual destReg: rr ].
[34] -> [ self CmpR: ra R: rr.
self genBinaryInlineComparison: JumpGreaterOrEqual opFalse: JumpLess destReg: rr ].
[35] -> [ self CmpR: ra R: rr.
self genBinaryInlineComparison: JumpLessOrEqual opFalse: JumpGreater destReg: rr ].
[36] -> [ self CmpR: ra R: rr.
self genBinaryInlineComparison: JumpZero opFalse: JumpNonZero destReg: rr ].
[37] -> [ self CmpR: ra R: rr.
self genBinaryInlineComparison: JumpNonZero opFalse: JumpZero destReg: rr ].
"2064 through 2068, Pointer Object>>at:, Byte Object>>at:, Short16 Word Object>>at: LongWord32 Object>>at: Quad64Word Object>>at:. obj op 0-rel SmallInteger => oop"
[64] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
adjust := (objectMemory baseHeaderSize >> objectMemory shiftForWord) - 1. "shift by baseHeaderSize and then move from 1 relative to zero relative"
adjust ~= 0 ifTrue: [ self AddCq: adjust R: ra. ].
self MoveXwr: ra R: rr R: rr ].
[65] -> [objectRepresentation genConvertSmallIntegerToIntegerInReg: ra.
adjust := objectMemory baseHeaderSize - 1. "shift by baseHeaderSize and then move from 1 relative to zero relative"
self AddCq: adjust R: ra.
self MoveXbr: ra R: rr R: rr.
objectRepresentation genConvertIntegerToSmallIntegerInReg: rr]
}
otherwise: [^EncounteredUnknownBytecode].
self ssPushRegister: rr.
^0
]
{ #category : #'inline primitive binary generators' }
SistaCogitClone >> genBinaryVarOpVarSmiInlinePrimitive: primIndex [
| ra rr |
self allocateRegForStackTopTwoEntriesInto: [:rTop :rNext | ra := rTop. rr := rNext ].
self ssTop popToReg: ra.
self ssPop: 1.
self ssTop popToReg: rr.
self ssPop: 1.
primIndex caseOf: {
"2000 +
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[0] -> [objectRepresentation genRemoveSmallIntegerTagsInScratchReg: ra.
self AddR: ra R: rr].
"2001 -
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[1] -> [self SubR: ra R: rr.
objectRepresentation genAddSmallIntegerTagsTo: rr].
"2002 *
Smi, Smi => Smi (no overflow, optimised if one operand is a constant)"
[2] -> [self genShiftAwaySmallIntegerTagsInScratchReg: rr.
self genRemoveSmallIntegerTagsInScratchReg: ra.
self MulR: ra R: rr.
self genSetSmallIntegerTagsIn: rr].
"2016 bitAnd:
Smi, Smi => Smi (optimised if one operand is a constant)"
[16] -> [ self AndR: ra R: rr ].
"2017 bitOr:
Smi, Smi => Smi (optimised if one operand is a constant)"
[17] -> [ self OrR: ra R: rr ].
"2018 bitXor:
Smi, Smi => Smi (optimised if one operand is a constant)"
[18] -> [objectRepresentation genRemoveSmallIntegerTagsInScratchReg: ra.
self XorR: ra R: rr. ]
}
otherwise: [^EncounteredUnknownBytecode].
self ssPushRegister: rr.
^0
]
{ #category : #'inline primitive generators' }
SistaCogitClone >> genByteAtPut [
| ra1 ra2 rr adjust |
self
allocateRegForStackTopThreeEntriesInto: [:rTop :rNext :rThird | ra2 := rTop. ra1 := rNext. rr := rThird ]
thirdIsReceiver: false.
self assert: (rr ~= ra1 and: [rr ~= ra2 and: [ra1 ~= ra2]]).
self ssTop popToReg: ra2.
self ssPop: 1.
self ssTop popToReg: ra1.
self ssPop: 1.
self ssTop popToReg: rr.
self ssPop: 1.
adjust := objectMemory baseHeaderSize - 1. "shift by baseHeaderSize and then move from 1 relative to zero relative"
objectRepresentation genConvertSmallIntegerToIntegerInReg: ra1.
objectRepresentation genConvertSmallIntegerToIntegerInReg: ra2.
self AddCq: adjust R: ra1.
self MoveR: ra2 Xbr: ra1 R: rr.
objectRepresentation genConvertIntegerToSmallIntegerInReg: ra2.
^self ssPushRegister: ra2
]
{ #category : #'mapped inline primitive generators' }
SistaCogitClone >> genByteAtPutImmutabilityCheck [
| ra1 ra2 rr adjust mutableJump immutabilityFailure indexIsCst |
<var: #mutableJump type: #'AbstractInstruction *'>
<var: #immutabilityFailure type: #'AbstractInstruction *'>
"Assumes rr is not a context and no store check is needed"
indexIsCst := (self ssValue: 1) type = SSConstant.
rr := ReceiverResultReg.
ra1 := TempReg.
ra2 := ClassReg.
self voidReceiverResultRegContainsSelf.
self ssFlushTo: simStackPtr - 1. "we use top value immediately after, so it does not require flush"
self ssTop popToReg: ra2.
adjust := objectMemory baseHeaderSize - 1. "shift by baseHeaderSize and then move from 1 relative to zero relative"
indexIsCst
ifFalse:
[(self ssValue: 1) popToReg: ra1.
objectRepresentation genConvertSmallIntegerToIntegerInReg: ra1.
self AddCq: adjust R: ra1]
ifTrue: [self MoveCq: (objectMemory integerValueOf: (self ssValue: 1) constant) + adjust R: ra1].
(self ssValue: 2) popToReg: rr.
self ssPop: 3.
self ssPushRegister: ra2.
"Everything is flushed except returned value. All regs are unused except rr ra1 ra2"
mutableJump := self genJumpMutable: rr scratchReg: Arg0Reg.
self PushR: ra2. "flush sim stack top"
self SubCq: 1 + adjust R: ra1. "index 0-relative for trampoline, ra1 unused afterwards"
objectRepresentation genVarIndexCallStoreTrampoline.
self PopR: ra2. "restore sim stack top"
immutabilityFailure := self Jump: 0.
mutableJump jmpTarget: (self MoveR: ra2 R: Arg0Reg).
objectRepresentation genConvertSmallIntegerToIntegerInReg: Arg0Reg.
self MoveR: Arg0Reg Xbr: ra1 R: rr.
immutabilityFailure jmpTarget: self Label.
^0
]
{ #category : #'inline primitive generators' }
SistaCogitClone >> genByteEqualsInlinePrimitive: prim [
"3021 Byte Object >> equals:length:
The receiver and the arguments are both byte objects and have both the same size (length in bytes).
The length argument is a smallinteger.
Answers true if all fields are equal, false if not.
Comparison is bulked to word comparison."
"Overview:
1. The primitive is called like that: [byteObj1 equals: byteObj2 length: length].
In the worst case we use 5 registers including TempReg
and we produce a loop bulk comparing words.
2. The common case is a comparison against a cst: [byteString = 'foo'].
which produces in Scorch [byteString equals: 'foo' length: 3].
We try to generate fast code for this case with 3 heuristics:
- specific fast code if len is a constant
- unroll the loop if len < 2 * wordSize
- compile-time reads if str1 or str2 is a constant and loop is unrolled.
We use 3 registers including TempReg in the common case.
We could use 1 less reg if the loop is unrolled, the instr is followed by a branch
AND one operand is a constant, but this is complicated enough.
3. We ignore the case where all operands are constants
(We assume Scorch simplifies it, it works but it is not optimised)"
| str1Reg str2Reg lenReg extraReg jmp jmp2 needjmpZeroSize needLoop unroll jmpZeroSize instr lenCst mask |
<var: #jmp type: #'AbstractInstruction *'>
<var: #instr type: #'AbstractInstruction *'>
<var: #jmp2 type: #'AbstractInstruction *'>
<var: #jmpZeroSize type: #'AbstractInstruction *'>
"--- quick path for empty string---"
"This path does not allocate registers and right shift on negative int later in the code.
Normally this is resolved by Scorch but we keep it for correctness and consistency"
self ssTop type = SSConstant ifTrue:
[ lenCst := objectMemory integerValueOf: self ssTop constant.
lenCst = 0 ifTrue: [ self ssPop: 3. self ssPushConstant: objectMemory trueObject. ^ 0 ] ].
"--- Allocating & loading registers --- "
needLoop := (self ssTop type = SSConstant and: [ lenCst <= (objectMemory wordSize * 2) ]) not.
unroll := needLoop not and: [lenCst > objectMemory wordSize ].
needLoop
ifTrue:
[ self assert: self ssTop spilled not.
str1Reg := self allocateRegForStackEntryAt: 1 notConflictingWith: self emptyRegisterMask.
str2Reg := self allocateRegForStackEntryAt: 2 notConflictingWith: (self registerMaskFor: str1Reg).
lenReg := self allocateRegForStackEntryAt: 0 notConflictingWith: (self registerMaskFor: str1Reg and: str2Reg).
(self ssValue: 1) popToReg: str1Reg.
(self ssValue: 2) popToReg: str2Reg.
extraReg := self allocateRegNotConflictingWith: (self registerMaskFor: str1Reg and: str2Reg and: lenReg)]
ifFalse:
[ mask := self emptyRegisterMask.
(self ssValue: 1) type = SSConstant ifFalse:
[ str1Reg := self allocateRegForStackEntryAt: 1 notConflictingWith: mask.
(self ssValue: 1) popToReg: str1Reg.
mask := mask bitOr: (self registerMaskFor: str1Reg) ].
(self ssValue: 2) type = SSConstant ifFalse:
[ str2Reg := self allocateRegForStackEntryAt: 2 notConflictingWith: mask.
(self ssValue: 2) popToReg: str2Reg.
mask := mask bitOr: (self registerMaskFor: str2Reg) ].
extraReg := self allocateRegNotConflictingWith: mask].
"--- Loading LenReg (or statically resolving it) --- "
"LenReg is loaded with (lenInBytes + objectMemory baseHeaderSize - 1 >> shiftForWord)
LenReg is the index for the last word to compare with MoveXwr:r:R:.
The loop iterates from LenReg to first word of ByteObj"
self ssTop type = SSConstant
ifTrue: "common case, str = 'foo'. We can precompute lenReg."
[ lenCst := lenCst + objectMemory baseHeaderSize - 1 >> objectMemory shiftForWord.
needLoop ifTrue: [self MoveCq: lenCst R: lenReg ].
needjmpZeroSize := false]
ifFalse: "uncommon case, str = str2. lenReg in word computed at runtime."
[ self ssTop popToReg: lenReg.
objectRepresentation genConvertSmallIntegerToIntegerInReg: lenReg.
self CmpCq: 0 R: lenReg.
jmpZeroSize := self JumpZero: 0.
needjmpZeroSize := true.
self AddCq: objectMemory baseHeaderSize - 1 R: lenReg.
self ArithmeticShiftRightCq: objectMemory shiftForWord R: lenReg ].
"--- Comparing the strings --- "
"LenReg has the index of the last word to read (unless no loop).
We decrement it to adjust -1 (0 in 64 bits) while comparing"
needLoop
ifTrue:
[instr := self MoveXwr: lenReg R: str1Reg R: extraReg.
self MoveXwr: lenReg R: str2Reg R: TempReg.
self CmpR: extraReg R: TempReg.
jmp := self JumpNonZero: 0. "then string are not equal (jmp target)"
self AddCq: -1 R: lenReg.
self CmpCq: (objectMemory baseHeaderSize >> objectMemory shiftForWord) - 1 R: lenReg. "first word of ByteObj, stop looping."
self JumpNonZero: instr]
ifFalse: "Common case, only 1 or 2 word to check: no lenReg allocation, cst micro optimisations"
[self genByteEqualsInlinePrimitiveCmp: str1Reg with: str2Reg scratch1: extraReg scratch2: TempReg field: 0.
jmp := self JumpNonZero: 0. "then string are not equal (jmp target)"
unroll ifTrue: "unrolling more than twice generate more instructions than the loop so we don't do it"
[self genByteEqualsInlinePrimitiveCmp: str1Reg with: str2Reg scratch1: extraReg scratch2: TempReg field: 1.
jmp2 := self JumpNonZero: 0. "then string are not equal (jmp target)"]].