-
Notifications
You must be signed in to change notification settings - Fork 71
/
SimpleStackBasedCogit.class.st
3372 lines (3009 loc) · 127 KB
/
SimpleStackBasedCogit.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
"
I am the stage one JIT for Cog that does not attempt to eliminate the stack via deferred code generation.
"
Class {
#name : #SimpleStackBasedCogit,
#superclass : #Cogit,
#instVars : [
'primitiveGeneratorTable',
'primSetFunctionLabel',
'primInvokeInstruction',
'externalPrimCallOffsets',
'externalPrimJumpOffsets',
'externalSetPrimOffsets',
'introspectionDataIndex',
'introspectionData'
],
#pools : [
'VMClassIndices',
'VMMethodCacheConstants',
'VMObjectIndices',
'VMWellKnownPrimitivesConstants'
],
#category : #'VMMaker-JIT'
}
{ #category : #translation }
SimpleStackBasedCogit class >> ancilliaryClasses [
"Answer any extra classes to be included in the translation."
^ super ancilliaryClasses
, self objectRepresentationAncilliaryClasses
]
{ #category : #documentation }
SimpleStackBasedCogit class >> callingConvention [
"The Smalltalk-to-Smalltalk calling convention for SimpleStackBasedCogit is
designed to be congruent with the interpreter and convenient for inline cacheing.
For inline cacheing it is convenient if the receiver is in a register.
Hence the calling convention is:
On call ReceiverResultReg (edx on x86) contains the receiver, and the receiver
and arguments are all on the stack, receiver furthest from top-of-stack.
If the number of arguments is 3 or greater then the argument count is passed in
SendNumArgsReg (this is for the linking run-time routine; it is ignored in linked sends).
On return result is in ReceiverResultReg. The callee removes arguments from the stack.
The caller pushes the result if the result is used."
]
{ #category : #translation }
SimpleStackBasedCogit class >> declareCVarsIn: aCCodeGenerator [
aCCodeGenerator vmClass primitiveTable ifNotNil:
[:bytecodeGenTable|
aCCodeGenerator
var: #primitiveGeneratorTable
declareC: 'static PrimitiveDescriptor primitiveGeneratorTable[MaxCompiledPrimitiveIndex+1]',
(self tableInitializerFor: aCCodeGenerator vmClass primitiveTable
in: aCCodeGenerator)].
aCCodeGenerator
var: #externalPrimCallOffsets
declareC: 'sqInt externalPrimCallOffsets[MaxNumArgs + 1]';
var: #externalPrimJumpOffsets
declareC: 'sqInt externalPrimJumpOffsets[MaxNumArgs + 1]';
var: #externalSetPrimOffsets
declareC: 'sqInt externalSetPrimOffsets[MaxNumArgs + 1]';
var: #primSetFunctionLabel type: #'AbstractInstruction *';
var: #primInvokeInstruction type: #'AbstractInstruction *'
]
{ #category : #'class initialization' }
SimpleStackBasedCogit class >> initializeBytecodeTableForSistaV1 [
"SimpleStackBasedCogit initializeBytecodeTableForSistaV1"
BytecodeSetHasDirectedSuperSend := true.
BytecodeSetHasExtensions := true.
FirstSpecialSelector := 96.
NumSpecialSelectors := 32.
self flag:
'Special selector send class must be inlined to agree with the interpreter, which
inlines class. If class is sent to e.g. a general instance of ProtoObject then unless
class is inlined there will be an MNU. It must be that the Cointerpreter and Cogit
have identical semantics. We get away with not hardwiring the other special
selectors either because in the Cointerpreter they are not inlined or because they
are inlined only to instances of classes for which there will always be a method.'.
self generatorTableFrom: #(
"1 byte bytecodes"
"pushes"
(1 0 15 genPushReceiverVariableBytecode isInstVarRef)
(1 16 31 genPushLiteralVariable16CasesBytecode needsFrameNever: 1)
(1 32 63 genPushLiteralConstantBytecode needsFrameNever: 1)
(1 64 75 genPushTemporaryVariableBytecode)
(1 76 76 genPushReceiverBytecode)
(1 77 77 genPushConstantTrueBytecode needsFrameNever: 1)
(1 78 78 genPushConstantFalseBytecode needsFrameNever: 1)
(1 79 79 genPushConstantNilBytecode needsFrameNever: 1)
(1 80 80 genPushConstantZeroBytecode needsFrameNever: 1)
(1 81 81 genPushConstantOneBytecode needsFrameNever: 1)
(1 82 82 genExtPushPseudoVariable)
(1 83 83 duplicateTopBytecode needsFrameNever: 1)
(1 84 87 unknownBytecode)
"returns"
(1 88 88 genReturnReceiver return needsFrameIfInBlock: isMappedInBlock 0)
(1 89 89 genReturnTrue return needsFrameIfInBlock: isMappedInBlock 0)
(1 90 90 genReturnFalse return needsFrameIfInBlock: isMappedInBlock 0)
(1 91 91 genReturnNil return needsFrameIfInBlock: isMappedInBlock 0)
(1 92 92 genReturnTopFromMethod return needsFrameIfInBlock: isMappedInBlock -1)
(1 93 93 genReturnNilFromBlock return needsFrameNever: -1)
(1 94 94 genReturnTopFromBlock return needsFrameNever: -1)
(1 95 95 genExtNopBytecode needsFrameNever: 0)
"sends"
(1 96 117 genSpecialSelectorSend isMapped) "#+ #- #< #> #<= #>= #= #~= #* #/ #\\ #@ #bitShift: #// #bitAnd: #bitOr: #at: #at:put: #size #next #nextPut: #atEnd"
(1 118 118 genSpecialSelectorEqualsEquals needsFrameNever: notMapped -1) "not mapped because it is directly inlined (for now)"
(1 119 119 genSpecialSelectorClass needsFrameNever: notMapped 0) "not mapped because it is directly inlined (for now)"
(1 120 120 genSpecialSelectorNotEqualsEquals needsFrameNever: notMapped -1) "not mapped because it is directly inlined (for now)"
(1 121 127 genSpecialSelectorSend isMapped) "#value #value: #do: #new #new: #x #y"
(1 128 143 genSendLiteralSelector0ArgsBytecode isMapped)
(1 144 159 genSendLiteralSelector1ArgBytecode isMapped)
(1 160 175 genSendLiteralSelector2ArgsBytecode isMapped)
"jumps"
(1 176 183 genShortUnconditionalJump branch v3:ShortForward:Branch:Distance:)
(1 184 191 genShortJumpIfTrue branch isBranchTrue isMapped "because of mustBeBoolean"
v3:ShortForward:Branch:Distance:)
(1 192 199 genShortJumpIfFalse branch isBranchFalse isMapped "because of mustBeBoolean"
v3:ShortForward:Branch:Distance:)
"stores"
(1 200 207 genStoreAndPopReceiverVariableBytecode isInstVarRef isMappedIfImmutability needsFrameIfImmutability: -1)
(1 208 215 genStoreAndPopTemporaryVariableBytecode)
(1 216 216 genPopStackBytecode needsFrameNever: -1)
(1 217 217 genUnconditionalTrapBytecode isMapped)
(1 218 223 unknownBytecode)
"2 byte bytecodes"
(2 224 224 extABytecode extension)
(2 225 225 extBBytecode extension)
"pushes"
(2 226 226 genExtPushReceiverVariableBytecode isInstVarRef) "Needs a frame for context inst var access"
(2 227 227 genExtPushLiteralVariableBytecode needsFrameNever: 1)
(2 228 228 genExtPushLiteralBytecode needsFrameNever: 1)
(2 229 229 genLongPushTemporaryVariableBytecode)
(2 230 230 unknownBytecode)
(2 231 231 genPushNewArrayBytecode)
(2 232 232 genExtPushIntegerBytecode needsFrameNever: 1)
(2 233 233 genExtPushCharacterBytecode needsFrameNever: 1)
"returns"
"sends"
(2 234 234 genExtSendBytecode isMapped)
(2 235 235 genExtSendSuperBytecode isMapped)
"sista bytecodes"
(2 236 236 unknownBytecode)
"jumps"
(2 237 237 genExtUnconditionalJump branch isMapped "because of interrupt check" v4:Long:Branch:Distance:)
(2 238 238 genExtJumpIfTrue branch isBranchTrue isMapped "because of mustBeBoolean" v4:Long:Branch:Distance:)
(2 239 239 genExtJumpIfFalse branch isBranchFalse isMapped "because of mustBeBoolean" v4:Long:Branch:Distance:)
"stores"
(2 240 240 genExtStoreAndPopReceiverVariableBytecode isInstVarRef isMappedIfImmutability)
(2 241 241 genExtStoreAndPopLiteralVariableBytecode isMappedIfImmutability)
(2 242 242 genLongStoreAndPopTemporaryVariableBytecode)
(2 243 243 genExtStoreReceiverVariableBytecode isInstVarRef isMappedIfImmutability)
(2 244 244 genExtStoreLiteralVariableBytecode isMappedIfImmutability)
(2 245 245 genLongStoreTemporaryVariableBytecode)
(2 246 247 unknownBytecode)
"3 byte bytecodes"
(3 248 248 genCallPrimitiveBytecode)
(3 249 249 genExtPushFullClosureBytecode)
(3 250 250 unknownBytecode) "was genExtPushClosureBytecode"
(3 251 251 genPushRemoteTempLongBytecode)
(3 252 252 genStoreRemoteTempLongBytecode)
(3 253 253 genStoreAndPopRemoteTempLongBytecode)
(3 254 255 unknownBytecode))
]
{ #category : #translation }
SimpleStackBasedCogit class >> objectRepresentationAncilliaryClasses [
^ (self objectRepresentationClass withAllSuperclasses copyUpThrough:
CogObjectRepresentation) reverse
]
{ #category : #'class initialization' }
SimpleStackBasedCogit class >> table: primArray from: specArray [
"Fill in the specified entries in the primitive table."
specArray do:
[:spec |
(spec first <= primArray size
and: [spec second == #genFastPrimFail
or: [self objectRepresentationClass shouldIncludeMethodForSelector: spec second]]) ifTrue:
[(primArray at: spec first put: CogPrimitiveDescriptor new)
primitiveGenerator: spec second;
primNumArgs: (spec at: 3 ifAbsent: -1);
"Check that it is marked as maycallback"
primMayCallBack: (spec at: 4 ifAbsent: [ false ]) == #maycallback]].
primArray object withIndexDo:
[:generator :i|
generator ifNil:
[(primArray object at: i put: CogPrimitiveDescriptor new)
primNumArgs: -1;
primMayCallBack: false]]
]
{ #category : #'primitive generators' }
SimpleStackBasedCogit >> adjustArgumentsForPerform: numArgs [
"e.g. Receiver Receiver or Receiver Receiver (RISC)
Selector/Arg0 => Arg1 Selector/Arg0 => Arg1
Arg1 Arg2 Arg1 Arg2
Arg2 Arg3 Arg2 sp-> Arg3
Arg3 sp-> retpc sp-> Arg3
sp-> retpc"
backEnd hasLinkRegister
ifTrue:
[numArgs - 2 to: 0 by: -1 do:
[:index|
self MoveMw: index * objectMemory wordSize r: SPReg R: TempReg.
self MoveR: TempReg Mw: index + 1 * objectMemory wordSize r: SPReg].
self AddCq: objectMemory wordSize R: SPReg]
ifFalse:
[numArgs - 1 to: 1 by: -1 do:
[:index|
self MoveMw: index * objectMemory wordSize r: SPReg R: TempReg.
self MoveR: TempReg Mw: index + 1 * objectMemory wordSize r: SPReg].
self PopR: TempReg.
self MoveR: TempReg Mw: 0 r: SPReg]
]
{ #category : #'bytecode generator support' }
SimpleStackBasedCogit >> annotationForSendTable: sendTable [
"c.f. offsetAndSendTableFor:annotation:into:"
<inline: true>
<var: #sendTable type: #'sqInt *'>
sendTable == ordinarySendTrampolines ifTrue:
[^IsSendCall].
BytecodeSetHasDirectedSuperSend ifTrue:
[sendTable == directedSuperSendTrampolines ifTrue:
[^IsDirectedSuperSend].
sendTable == directedSuperBindingSendTrampolines ifTrue:
[^IsDirectedSuperBindingSend]].
self assert: sendTable == superSendTrampolines.
^IsSuperSend
]
{ #category : #'simulation only' }
SimpleStackBasedCogit >> bytecodeFixupClass [
<doNotGenerate>
^CogBytecodeFixup
]
{ #category : #accessing }
SimpleStackBasedCogit >> bytecodePC: anInteger [
<doNotGenerate>
bytecodePC := anInteger
]
{ #category : #trampolines }
SimpleStackBasedCogit >> cPICMissTrampolineFor: numArgs [
^ceCPICMissTrampoline
]
{ #category : #accessing }
SimpleStackBasedCogit >> ceCPICMissTrampoline: anAddress [
ceCPICMissTrampoline := anAddress
]
{ #category : #'simulation only' }
SimpleStackBasedCogit >> ceShortCutTraceBlockActivation: aProcessorSimulationTrap [
self shortcutTrampoline: aProcessorSimulationTrap
to: [coInterpreter ceTraceBlockActivation]
]
{ #category : #'simulation only' }
SimpleStackBasedCogit >> ceShortCutTraceLinkedSend: aProcessorSimulationTrap [
self shortcutTrampoline: aProcessorSimulationTrap
to: [coInterpreter ceTraceLinkedSend: (processor registerAt: ReceiverResultReg)]
]
{ #category : #'simulation only' }
SimpleStackBasedCogit >> ceShortCutTraceStore: aProcessorSimulationTrap [
<doNotGenerate>
self shortcutTrampoline: aProcessorSimulationTrap
to: [coInterpreter
ceTraceStoreOf: (processor registerAt: ClassReg)
into: (processor registerAt: ReceiverResultReg)]
]
{ #category : #'compile abstract instructions' }
SimpleStackBasedCogit >> compileFrameBuild [
"Build a frame for a CogMethod activation. See CoInterpreter class>>initializeFrameIndices.
receiver (in ReceiverResultReg)
arg0
...
argN
caller's saved ip/this stackPage (for a base frame)
fp-> saved fp
method
context (uninitialized?)
receiver
first temp
...
sp-> Nth temp
If there is a primitive and an error code the Nth temp is the error code.
Ensure SendNumArgsReg is set early on (incidentally to nilObj) because
it is the flag determining whether context switch is allowed on stack-overflow."
| jumpSkip |
<inline: false>
<var: #jumpSkip type: #'AbstractInstruction *'>
needsFrame ifFalse: [^self].
backEnd hasLinkRegister ifTrue: [self PushR: LinkReg].
self PushR: FPReg.
self MoveR: SPReg R: FPReg.
methodLabel addDependent: (self annotateAbsolutePCRef:
(self PushCw: methodLabel asInteger)). "method"
self genMoveNilR: SendNumArgsReg.
self PushR: SendNumArgsReg. "context"
self PushR: ReceiverResultReg.
methodOrBlockNumArgs + 1 to: (coInterpreter temporaryCountOfMethodHeader: methodHeader) do:
[:i|
self PushR: SendNumArgsReg].
(self methodUsesPrimitiveErrorCode: methodObj header: methodHeader) ifTrue:
[self compileGetErrorCode].
self MoveAw: coInterpreter stackLimitAddress R: TempReg.
self CmpR: TempReg R: SPReg. "N.B. FLAGS := SPReg - TempReg"
"If we can't context switch for this method, use a slightly
slower overflow check that clears SendNumArgsReg."
(coInterpreter canContextSwitchIfActivating: methodObj header: methodHeader)
ifTrue:
[self JumpBelow: stackOverflowCall.
stackCheckLabel := self Label]
ifFalse:
[jumpSkip := self JumpAboveOrEqual: 0.
self MoveCq: 0 R: SendNumArgsReg.
self Jump: stackOverflowCall.
jumpSkip jmpTarget: (stackCheckLabel := self Label)].
self annotateBytecode: stackCheckLabel
]
{ #category : #'compile abstract instructions' }
SimpleStackBasedCogit >> compileFullBlockFramelessEntry: numCopied [
"Make sure ReceiverResultReg holds the receiver, loaded from the closure,
which is what is initially in ReceiverResultReg. "
self flag: #TODO. "we could follow the receiver only if the block has inst var ref. Currently we use scanMethod for fullBlock
and that scanner does not provide this information. We could extend it based on the scanBlock: method"
"Use ReceiverResultReg for the closure to agree with store check trampoline"
objectRepresentation
genLoadSlot: FullClosureReceiverIndex
sourceReg: ReceiverResultReg
destReg: Arg0Reg.
objectRepresentation
genEnsureOopInRegNotForwarded: Arg0Reg scratchReg: TempReg updatingSlot: FullClosureReceiverIndex in: ReceiverResultReg.
self MoveR: Arg0Reg R: ReceiverResultReg.
]
{ #category : #'compile abstract instructions' }
SimpleStackBasedCogit >> compileFullBlockMethodFrameBuild: numCopied [
"Build a frame for a block activation. See CoInterpreter class>>initializeFrameIndices.
closure (in ReceiverResultReg)
arg0
...
argN
caller's saved ip/this stackPage (for a base frame)
fp-> saved fp
method
context (uninitialized?)
receiver
first temp
...
sp-> Nth temp
Avoid use of SendNumArgsReg which is the flag determining whether
context switch is allowed on stack-overflow."
<inline: false>
needsFrame ifFalse: [^self].
backEnd hasLinkRegister ifTrue: [self PushR: LinkReg].
self PushR: FPReg.
self MoveR: SPReg R: FPReg.
"Think of ClassReg as ClosureReg"
self MoveR: ReceiverResultReg R: ClassReg.
"The block method field must have its MFMethodFlagIsBlockFlag bit set.
We arrange this using a labelOffset. A hack, but it works."
methodLabel addDependent: (self annotateAbsolutePCRef:
(self PushCw: methodLabel asInteger));
setLabelOffset: MFMethodFlagIsBlockFlag.. "method"
self genMoveNilR: SendNumArgsReg.
self PushR: SendNumArgsReg. "context"
"Closure is on stack and initially in ReceiverResultReg.
It is safe to use Arg0Reg because reg args are pushed by the value primitives if there are any.".
self flag: #TODO. "we could follow the receiver only if the block has inst var ref. Currently we use scanMethod for fullBlock
and that scanner does not provide this information. We could extend it based on the scanBlock: method"
"Use ReceiverResultReg for the closure to agree with store check trampoline"
objectRepresentation
genLoadSlot: FullClosureReceiverIndex
sourceReg: ClassReg
destReg: Arg0Reg.
objectRepresentation
genEnsureOopInRegNotForwarded: Arg0Reg scratchReg: TempReg updatingSlot: FullClosureReceiverIndex in: ReceiverResultReg.
self MoveR: Arg0Reg R: ReceiverResultReg.
self PushR: ReceiverResultReg. "closure receiver"
"Push copied values"
0 to: numCopied - 1 do:
[:i|
objectRepresentation
genLoadSlot: i + FullClosureFirstCopiedValueIndex
sourceReg: ClassReg
destReg: TempReg.
self PushR: TempReg].
"Push slots for temps"
methodOrBlockNumArgs + numCopied + 1 to: (coInterpreter temporaryCountOfMethodHeader: methodHeader) do:
[:i|
self PushR: SendNumArgsReg].
self MoveAw: coInterpreter stackLimitAddress R: TempReg.
self CmpR: TempReg R: SPReg. "N.B. FLAGS := SPReg - TempReg"
self JumpBelow: stackOverflowCall.
stackCheckLabel := (self annotateBytecode: self Label)
]
{ #category : #'compile abstract instructions' }
SimpleStackBasedCogit >> compileGetErrorCode [
"After pushing the temporaries but before the stack limit check a primitive method
needs to fetch the error code, if any. If the primitive has failed, call the trampoline
that will assign it to the last temp."
<inline: false>
| jmpNoError |
<var: #jmpNoError type: #'AbstractInstruction *'>
self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
self flag: 'ask concrete code gen if move sets condition codes?'.
self CmpCq: 0 R: TempReg.
jmpNoError := self JumpZero: 0.
methodLabel addDependent:
(self annotateAbsolutePCRef:
(self MoveCw: methodLabel asInteger R: ClassReg)).
self CallRT: ceReapAndResetErrorCodeTrampoline.
jmpNoError jmpTarget: self Label
]
{ #category : #'primitive generators' }
SimpleStackBasedCogit >> compileInterpreterPrimitive [
<inline: true>
<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
| primitiveRoutine |
primitiveRoutine := coInterpreter
functionPointerForCompiledMethod: methodObj
primitiveIndex: primitiveIndex.
^ self
compileInterpreterPrimitive: primitiveRoutine
flags: (self
primitivePropertyFlags: primitiveIndex
primitiveDescriptor: self primitiveDescriptor)
]
{ #category : #'primitive generators - call' }
SimpleStackBasedCogit >> compileInterpreterPrimitive: primitiveRoutine flags: flags [
"Compile a call to an interpreter primitive. Call the C routine with the
usual stack-switching dance, test the primFailCode and then either
return on success or continue to the method body."
<var: #primitiveRoutine declareC: #'void (*primitiveRoutine)(void)'>
<var: #cogPrimitiveCallState type: #'CogPrimitiveCallState *'>
| cogPrimitiveCallState |
(flags anyMask: PrimCallMayCallBack)
ifTrue: [ ^ self compileInterpreterPrimitiveMayCallBack: primitiveRoutine flags: flags ].
self
cCode: [ cogPrimitiveCallState := self
cCoerce: (self alloca: (self sizeof: CogPrimitiveCallState))
to: #'CogPrimitiveCallState*' . ]
inSmalltalk: [cogPrimitiveCallState := CogPrimitiveCallState new].
"Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers"
self genExternalizePointersForPrimitiveCall.
"Switch to the C stack."
self genLoadCStackPointersForPrimCall.
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
["Test nextProfileTick for being non-zero and call checkProfileTick if so"
objectMemory wordSize = 4
ifTrue:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
self OrR: TempReg R: ClassReg]
ifFalse:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self CmpCq: 0 R: TempReg].
"If set, jump to record sample call."
cogPrimitiveCallState jmpSampleNonPrim: (self JumpNonZero: 0).
cogPrimitiveCallState continuePostSampleNonPrim: self Label].
"Old full prim trace is in VMMaker-eem.550 and prior"
self recordPrimTrace ifTrue:
[self genFastPrimTraceUsing: ClassReg and: SendNumArgsReg].
"Clear the primFailCode and set argumentCount"
self MoveCq: 0 R: TempReg.
self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
methodOrBlockNumArgs ~= 0 ifTrue:
[self MoveCq: methodOrBlockNumArgs R: TempReg].
self MoveR: TempReg Aw: coInterpreter argumentCountAddress.
"If required, set primitiveFunctionPointer and newMethod"
(flags anyMask: PrimCallNeedsPrimitiveFunction) ifTrue:
[self MoveCw: primitiveRoutine asInteger R: TempReg.
primSetFunctionLabel :=
self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress].
(flags anyMask: PrimCallNeedsNewMethod+PrimCallMayCallBack) ifTrue:
["The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness."
(flags anyMask: PrimCallMayCallBack) ifTrue:
[needsFrame := true].
methodLabel addDependent:
(self annotateAbsolutePCRef:
(self MoveCw: methodLabel asInteger R: ClassReg)).
self MoveMw: (self offset: CogMethod of: #methodObject) r: ClassReg R: TempReg.
self MoveR: TempReg Aw: coInterpreter newMethodAddress].
"Invoke the primitive"
self PrefetchAw: coInterpreter primFailCodeAddress.
"Call the C primitive routine."
backEnd genMarshallNArgs: 0 arg: 0 arg: 0 arg: 0 arg: 0.
primInvokeInstruction := self CallFullRT: primitiveRoutine asInteger.
backEnd genRemoveNArgsFromStack: 0.
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
[self assert: (flags anyMask: PrimCallNeedsNewMethod).
"Test nextProfileTick for being non-zero and call checkProfileTick if so"
objectMemory wordSize = 4
ifTrue:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
self OrR: TempReg R: ClassReg]
ifFalse:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self CmpCq: 0 R: TempReg].
"If set, jump to record sample call."
cogPrimitiveCallState jmpSamplePrim: (self JumpNonZero: 0).
cogPrimitiveCallState continuePostSamplePrim: self Label].
objectRepresentation maybeCompileRetryOnPrimitiveFail: primitiveIndex.
self maybeCompileAllocFillerCheck.
"Switch back to the Smalltalk stack. Stack better be in either of these two states:
success: stackPointer -> result (was receiver)
arg1
...
argN
return pc
failure: receiver
arg1
...
stackPointer -> argN
return pc
In either case we can push the instructionPointer or load it into the LinkRegister to reestablish the return pc"
self MoveAw: coInterpreter instructionPointerAddress
R: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [ClassReg]).
backEnd genLoadStackPointers.
"Test primitive failure"
self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
backEnd hasLinkRegister ifFalse: [self PushR: ClassReg]. "Restore return pc on CISCs"
self flag: 'ask concrete code gen if move sets condition codes?'.
self CmpCq: 0 R: TempReg.
cogPrimitiveCallState jmpToFallbackCode: (self JumpNonZero: 0).
"Fetch result from stack"
self MoveMw: (backEnd hasLinkRegister ifTrue: [0] ifFalse: [objectMemory wordSize])
r: SPReg
R: ReceiverResultReg.
self RetN: objectMemory wordSize. "return to caller, popping receiver"
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
["The sample is collected by cePrimReturnEnterCogCode for external calls"
"Call ceCheckProfileTick: to record sample and then continue."
self
generateCheckProfileTickFromJump: cogPrimitiveCallState jmpSamplePrim
returningTo: cogPrimitiveCallState continuePostSamplePrim
beforeCallDo: [].
"Call ceCheckProfileTick: to record sample and after the primitive and then continue."
self
generateCheckProfileTickFromJump: cogPrimitiveCallState jmpSampleNonPrim
returningTo: cogPrimitiveCallState continuePostSampleNonPrim
beforeCallDo: [
self MoveCq: 0 R: TempReg.
self MoveR: TempReg Aw: coInterpreter newMethodAddress]].
"Jump to restore of receiver reg and proceed to frame build for failure."
cogPrimitiveCallState jmpToFallbackCode jmpTarget: self Label.
"Restore receiver reg from stack. If on RISCs ret pc is in LinkReg, if on CISCs ret pc is on stack."
self MoveMw: objectMemory wordSize * (methodOrBlockNumArgs + (backEnd hasLinkRegister ifTrue: [0] ifFalse: [1]))
r: SPReg
R: ReceiverResultReg.
^0
]
{ #category : #'primitive generators - call' }
SimpleStackBasedCogit >> compileInterpreterPrimitiveMayCallBack: primitiveRoutine flags: flags [
"Compile a call to an interpreter primitive. Call the C routine with the
usual stack-switching dance, test the primFailCode and then either
return on success or continue to the method body."
<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
<var: #cogPrimitiveCallState type: #'CogPrimitiveCallState *'>
| cogPrimitiveCallState |
self
cCode: [ cogPrimitiveCallState := self
cCoerce: (self alloca: (self sizeof: CogPrimitiveCallState))
to: #'CogPrimitiveCallState*' . ]
inSmalltalk: [cogPrimitiveCallState := CogPrimitiveCallState new].
"Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers"
self genExternalizePointersForPrimitiveCall.
"Switch to the C stack."
self genLoadCStackPointersForPrimCall.
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
["Test nextProfileTick for being non-zero and call checkProfileTick if so"
objectMemory wordSize = 4
ifTrue:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
self OrR: TempReg R: ClassReg]
ifFalse:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self CmpCq: 0 R: TempReg].
"If set, jump to record sample call."
cogPrimitiveCallState jmpSampleNonPrim: (self JumpNonZero: 0).
cogPrimitiveCallState continuePostSampleNonPrim: self Label].
"Old full prim trace is in VMMaker-eem.550 and prior"
self recordPrimTrace ifTrue:
[self genFastPrimTraceUsing: ClassReg and: SendNumArgsReg].
"Clear the primFailCode and set argumentCount"
self MoveCq: 0 R: TempReg.
self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
methodOrBlockNumArgs ~= 0 ifTrue:
[self MoveCq: methodOrBlockNumArgs R: TempReg].
self MoveR: TempReg Aw: coInterpreter argumentCountAddress.
"If required, set primitiveFunctionPointer and newMethod"
(flags anyMask: PrimCallNeedsPrimitiveFunction) ifTrue:
[self MoveCw: primitiveRoutine asInteger R: TempReg.
primSetFunctionLabel :=
self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress].
(flags anyMask: PrimCallNeedsNewMethod+PrimCallMayCallBack) ifTrue:
["The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness."
(flags anyMask: PrimCallMayCallBack) ifTrue:
[needsFrame := true].
methodLabel addDependent:
(self annotateAbsolutePCRef:
(self MoveCw: methodLabel asInteger R: ClassReg)).
self MoveMw: (self offset: CogMethod of: #methodObject) r: ClassReg R: TempReg.
self MoveR: TempReg Aw: coInterpreter newMethodAddress].
"Invoke the primitive"
self PrefetchAw: coInterpreter primFailCodeAddress.
"Sideways call the C primitive routine so that we return through cePrimReturnEnterCogCode."
"On Spur ceActivateFailingPrimitiveMethod: would like to retry if forwarders
are found. So insist on PrimCallNeedsPrimitiveFunction being set too."
self assert: (flags anyMask: PrimCallNeedsPrimitiveFunction).
backEnd
genMarshallNArgs: 0 arg: 0 arg: 0 arg: 0 arg: 0;
genSubstituteReturnAddress:
((flags anyMask: PrimCallCollectsProfileSamples)
ifTrue: [cePrimReturnEnterCogCodeProfiling]
ifFalse: [cePrimReturnEnterCogCode]).
primInvokeInstruction := self JumpFullRT: primitiveRoutine asInteger.
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
["The sample is collected by cePrimReturnEnterCogCode for external calls"
"Call ceCheckProfileTick: to record sample and after the primitive and then continue."
self
generateCheckProfileTickFromJump: cogPrimitiveCallState jmpSampleNonPrim
returningTo: cogPrimitiveCallState continuePostSampleNonPrim
beforeCallDo: [
self MoveCq: 0 R: TempReg.
self MoveR: TempReg Aw: coInterpreter newMethodAddress]].
^0
]
{ #category : #'primitive generators' }
SimpleStackBasedCogit >> compileMachineCodeInterpreterPrimitive: primitiveRoutine [
"Compile a call to a machine-code convention interpreter primitive. Call the C routine
on the Smalltalk stack, assuming it consumes little or no stack space."
<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
| jmpFail liveRegsMask |
"for now handle functions with less than 4 arguments; our C call marshalling machinery
extends up to 4 arguments only, and the first argument of an mcprim is the receiver."
self assert: methodOrBlockNumArgs <= 3.
liveRegsMask := (methodOrBlockNumArgs > self numRegArgs
or: [methodOrBlockNumArgs = 0])
ifTrue:
[self registerMaskFor: ReceiverResultReg and: ClassReg]
ifFalse:
[(self numRegArgs > 1 and: [methodOrBlockNumArgs > 1])
ifFalse: [self registerMaskFor: ReceiverResultReg and: Arg0Reg and: ClassReg]
ifTrue: [self registerMaskFor: ReceiverResultReg and: Arg0Reg and: Arg1Reg and: ClassReg]].
"As this is using a call, I need to store the Link register."
backEnd hasLinkRegister
ifTrue: [ self MoveR: LinkReg R: Extra0Reg ].
backEnd genSaveRegs: (liveRegsMask bitAnd: CallerSavedRegisterMask).
methodOrBlockNumArgs > self numRegArgs ifTrue:
["Wrangle args into Arg0Reg, Arg1Reg, SendNumArgsReg & ClassReg"
"offset := self bitCountOf: (liveRegsMask bitAnd: CallerSavedRegisterMask)."
self shouldBeImplemented].
backEnd prepareStackToCallCFunctionInSmalltalkStack: methodOrBlockNumArgs + 1.
backEnd
genMarshallNArgs: methodOrBlockNumArgs + 1
arg: ReceiverResultReg
arg: Arg0Reg
arg: Arg1Reg
arg: SendNumArgsReg
"arg: ClassReg (when we extend C call marchalling to support 5 args for replaceFrom:to:with:startingAt:".
self CallFullRT: primitiveRoutine asInteger.
backEnd
genRemoveNArgsFromStack: methodOrBlockNumArgs + 1;
returnFromCallCFunctionInSmalltalkStack: methodOrBlockNumArgs + 1;
genRestoreRegs: (liveRegsMask bitAnd: CallerSavedRegisterMask).
backEnd hasLinkRegister
ifTrue: [ self MoveR: Extra0Reg R: LinkReg ].
self CmpCq: 0 R: backEnd cResultRegister.
jmpFail := self JumpZero: 0.
backEnd cResultRegister ~= ReceiverResultReg ifTrue:
[self MoveR: backEnd cResultRegister R: ReceiverResultReg].
self RetN: (methodOrBlockNumArgs > self numRegArgs
ifTrue: [methodOrBlockNumArgs + 1 * objectMemory wordSize]
ifFalse: [0]).
jmpFail jmpTarget: self Label.
^0
]
{ #category : #'in-line cacheing' }
SimpleStackBasedCogit >> compileOpenPIC: selector numArgs: numArgs [
"Compile the code for an open PIC. Perform a probe of the first-level method
lookup cache followed by a call of ceSendFromInLineCacheMiss: if the probe fails."
| cacheBaseReg jumpSelectorMiss jumpClassMiss itsAHit jumpBCMethod |
<var: #jumpSelectorMiss type: #'AbstractInstruction *'>
<var: #jumpClassMiss type: #'AbstractInstruction *'>
<var: #itsAHit type: #'AbstractInstruction *'>
<var: #jumpBCMethod type: #'AbstractInstruction *'>
self preenMethodLabel.
self compilePICAbort: numArgs.
entry := objectRepresentation genGetClassTagOf: ReceiverResultReg into: SendNumArgsReg scratchReg: TempReg.
self flag: #lookupInMethodCacheSel:classTag:. "so this method shows up as a sender of lookupInMethodCacheSel:class:"
cacheBaseReg := NoReg.
(backEnd isWithinMwOffsetRange: coInterpreter methodCacheAddress) ifFalse:
[self MoveCq: coInterpreter methodCacheAddress R: (cacheBaseReg := Extra0Reg)].
"Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag:"
jumpSelectorMiss := self compileOpenPICMethodCacheProbeFor: selector withShift: 0 baseRegOrNone: cacheBaseReg.
jumpClassMiss := self JumpNonZero: 0.
"Fetch the method. The interpret trampoline requires the bytecoded method in SendNumArgsReg"
itsAHit := self MoveMw: (cacheBaseReg = NoReg
ifTrue: [coInterpreter methodCacheAddress asUnsignedInteger + (MethodCacheMethod << objectMemory shiftForWord)]
ifFalse: [MethodCacheMethod << objectMemory shiftForWord])
r: ClassReg
R: SendNumArgsReg.
.
"If the method is compiled jump to its unchecked entry-point, otherwise interpret it."
objectRepresentation genLoadSlot: HeaderIndex sourceReg: SendNumArgsReg destReg: ClassReg.
jumpBCMethod := objectRepresentation genJumpImmediate: ClassReg.
jumpBCMethod jmpTarget: picInterpretAbort.
self AddCq: cmNoCheckEntryOffset R: ClassReg.
self JumpR: ClassReg.
"First probe missed. Do second of three probes. Shift hash right one and retry."
jumpSelectorMiss jmpTarget: (jumpClassMiss jmpTarget: self Label).
jumpSelectorMiss := self compileOpenPICMethodCacheProbeFor: selector withShift: 1 baseRegOrNone: cacheBaseReg.
self JumpZero: itsAHit.
"Second probe missed. Do last probe. Shift hash right two and retry."
jumpSelectorMiss jmpTarget: self Label.
jumpSelectorMiss := self compileOpenPICMethodCacheProbeFor: selector withShift: 2 baseRegOrNone: cacheBaseReg.
self JumpZero: itsAHit.
"Last probe missed. Call ceSendFromInLineCacheMiss: to do the full lookup."
jumpSelectorMiss jmpTarget: self Label.
self numRegArgs > 0 ifTrue:
[backEnd genPushRegisterArgsForNumArgs: numArgs scratchReg: SendNumArgsReg].
self genSmalltalkToCStackSwitch: true.
methodLabel addDependent: (self annotateAbsolutePCRef: (self MoveCw: methodLabel asInteger R: SendNumArgsReg)).
self
compileCallFor: #ceSendFromInLineCacheMiss:
numArgs: 1
arg: SendNumArgsReg
arg: nil
arg: nil
arg: nil
resultReg: NoReg
regsToSave: self emptyRegisterMask
"Note that this call does not return."
]
{ #category : #'in-line cacheing' }
SimpleStackBasedCogit >> compileOpenPICMethodCacheProbeFor: selector withShift: shift baseRegOrNone: baseRegOrNone [
"Compile one method cache probe in an OpenPIC's lookup of selector.
Answer the jump taken if the selector probe fails.
The class tag of the receiver must be in SendNumArgsReg. ClassReg and TempReg are used as scratch registers.
On a hit, the offset of the entry is in ClassReg."
<returnTypeC: #'AbstractInstruction *'>
<inline: false>
| jumpSelectorMiss |
<var: 'jumpSelectorMiss' type: #'AbstractInstruction *'>
self MoveR: SendNumArgsReg R: ClassReg.
objectRepresentation maybeShiftClassTagRegisterForMethodCacheProbe: ClassReg.
self annotate: (self XorCw: selector R: ClassReg) objRef: selector.
self assert: shift <= objectMemory shiftForWord.
"Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index.
So shift left by shiftForWord - shift and and with the shifted mask."
shift < objectMemory shiftForWord ifTrue:
[self LogicalShiftLeftCq: objectMemory shiftForWord - shift R: ClassReg].
self AndCq: MethodCacheMask << objectMemory shiftForWord R: ClassReg.
baseRegOrNone = NoReg
ifTrue:
[self MoveMw: coInterpreter methodCacheAddress asUnsignedInteger + (MethodCacheSelector << objectMemory shiftForWord)
r: ClassReg
R: TempReg]
ifFalse:
[self AddR: baseRegOrNone R: ClassReg;
MoveMw: MethodCacheSelector << objectMemory shiftForWord r: ClassReg R: TempReg].
self annotate: (self CmpCw: selector R: TempReg) objRef: selector.
jumpSelectorMiss := self JumpNonZero: 0.
baseRegOrNone = NoReg
ifTrue:
[self MoveMw: coInterpreter methodCacheAddress asUnsignedInteger + (MethodCacheClass << objectMemory shiftForWord)
r: ClassReg
R: TempReg]
ifFalse:
[self MoveMw: MethodCacheClass << objectMemory shiftForWord r: ClassReg R: TempReg].
self CmpR: SendNumArgsReg R: TempReg.
^jumpSelectorMiss
]
{ #category : #'in-line cacheing' }
SimpleStackBasedCogit >> compilePerformMethodCacheProbeFor: selectorReg withShift: shift baseRegOrNone: baseRegOrNone [
"Compile one method cache probe in a perform: primitive's lookup of selector.
Answer the jump taken if the selector probe fails."
<returnTypeC: #'AbstractInstruction *'>
<inline: false>
| jumpSelectorMiss |
<var: 'jumpSelectorMiss' type: #'AbstractInstruction *'>
self MoveR: SendNumArgsReg R: ClassReg.
objectRepresentation maybeShiftClassTagRegisterForMethodCacheProbe: ClassReg.
self XorR: selectorReg R: ClassReg.
self assert: shift <= objectMemory shiftForWord.
"Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index.
So shift left by shiftForWord - shift and and with the shifted mask."
shift < objectMemory shiftForWord ifTrue:
[self LogicalShiftLeftCq: objectMemory shiftForWord - shift R: ClassReg].
self AndCq: MethodCacheMask << objectMemory shiftForWord R: ClassReg.
baseRegOrNone = NoReg
ifTrue:
[self MoveMw: coInterpreter methodCacheAddress asUnsignedInteger + (MethodCacheSelector << objectMemory shiftForWord)
r: ClassReg
R: TempReg]
ifFalse:
[self AddR: baseRegOrNone R: ClassReg;
MoveMw: MethodCacheSelector << objectMemory shiftForWord r: ClassReg R: TempReg].
self CmpR: selectorReg R: TempReg.
jumpSelectorMiss := self JumpNonZero: 0.
baseRegOrNone = NoReg
ifTrue:
[self MoveMw: coInterpreter methodCacheAddress asUnsignedInteger + (MethodCacheClass << objectMemory shiftForWord)
r: ClassReg
R: TempReg]
ifFalse:
[self MoveMw: MethodCacheClass << objectMemory shiftForWord r: ClassReg R: TempReg].
self CmpR: SendNumArgsReg R: TempReg.
^jumpSelectorMiss
]
{ #category : #'primitive generators' }
SimpleStackBasedCogit >> compilePrimitive [
"Compile a primitive. If possible, performance-critical primtiives will
be generated by their own routines (primitiveGenerator). Otherwise,
if there is a primitive at all, we call the C routine with the usual
stack-switching dance, test the primFailCode and then either return
on success or continue to the method body."
<inline: false>
<var: #aPrimitiveDescriptor type: #'PrimitiveDescriptor *'>
<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
| code opcodeIndexAtPrimitive aPrimitiveDescriptor primitiveRoutine flags |
primitiveIndex = 0 ifTrue: [ ^ 0 ].
code := 0.
"Note opcodeIndex so that compileFallbackToInterpreterPrimitive:
can discard arg load instructions for unimplemented primitives."
opcodeIndexAtPrimitive := opcodeIndex.
"If a descriptor specifies an argument count (by numArgs >= 0) then it must match
for the generated code to be correct. For example for speed many primitives use
ResultReceiverReg instead of accessing the stack, so the receiver better be at
numArgs down the stack. Use the interpreter version if not."
aPrimitiveDescriptor := self primitiveDescriptor.
( aPrimitiveDescriptor primitiveGenerator notNil and: [
aPrimitiveDescriptor primNumArgs < 0 or: [
aPrimitiveDescriptor primNumArgs
= (coInterpreter argumentCountOf: methodObj) ] "means don't care" ] )
ifTrue: [
code := objectRepresentation perform:
aPrimitiveDescriptor primitiveGenerator ].
(code < 0 and: [ code ~= UnimplementedPrimitive ]) ifTrue: [ "Generator failed, so no point continuing..."
^ code ].
code = UnfailingPrimitive ifTrue: [ ^ 0 ].
"If the machine code verison handles all cases the only reason to call the interpreter
primitive is to reap the primitive error code. Don't bother if it isn't used."
(code = CompletePrimitive and: [
(self methodUsesPrimitiveErrorCode: methodObj header: methodHeader)
not ]) ifTrue: [ ^ 0 ].
"Discard any arg load code generated by the primitive generator."
code = UnimplementedPrimitive ifTrue: [
opcodeIndex := opcodeIndexAtPrimitive ].
flags := self
primitivePropertyFlags: primitiveIndex
primitiveDescriptor: aPrimitiveDescriptor.
(flags anyMask: PrimCallDoNotJIT) ifTrue: [ ^ ShouldNotJIT ].
(flags anyMask: PrimCallOnSmalltalkStack) ifTrue: [
self assert: flags = PrimCallOnSmalltalkStack.
^ self compileMachineCodeInterpreterPrimitive:
(coInterpreter mcprimFunctionForPrimitiveIndex: primitiveIndex) ].
((primitiveRoutine := coInterpreter
functionPointerForCompiledMethod: methodObj
primitiveIndex: primitiveIndex) = 0 or: [
primitiveRoutine = #primitiveFail ]) ifTrue: [
^ self genFastPrimFail ]. "no primitive"
minValidCallAddress := minValidCallAddress min:
primitiveRoutine asUnsignedInteger.
^ self compileInterpreterPrimitive: primitiveRoutine flags: flags
]
{ #category : #accessing }
SimpleStackBasedCogit >> directedBindingSuperSendTrampolineAt: anAddress put: aTrampolineAddress [
directedSuperBindingSendTrampolines at: anAddress put: aTrampolineAddress
]
{ #category : #accessing }
SimpleStackBasedCogit >> directedBindingSuperSendTrampolines [
<doNotGenerate>
^ directedSuperBindingSendTrampolines
]
{ #category : #'span functions' }
SimpleStackBasedCogit >> distanceForShortForwardBranch: pc inMethod: aMethodObj [
"N.B. This serves for both BlueBook/V3 and V4 short jumps."
<var: #descriptor type: #'BytecodeDescriptor *'>
<inline: true>
^((objectMemory fetchByte: pc ofObject: aMethodObj) bitAnd: 7) + 1
]
{ #category : #'bytecode generators' }
SimpleStackBasedCogit >> doubleExtendedDoAnythingBytecode [
"Replaces the Blue Book double-extended send [132], in which the first byte was wasted on 8 bits of argument count.
Here we use 3 bits for the operation sub-type (opType), and the remaining 5 bits for argument count where needed.
The last byte give access to 256 instVars or literals.
See also secondExtendedSendBytecode"
| opType |
opType := byte1 >> 5.