-
Notifications
You must be signed in to change notification settings - Fork 67
/
CoInterpreter.class.st
6016 lines (5452 loc) · 233 KB
/
CoInterpreter.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 a variant of the StackInterpreter that can co-exist with the Cog JIT. I interpret unjitted methods, either because they have been found for the first time or because they are judged to be too big to JIT. See CogMethod class's comment for method interoperability.
cogCodeSize
- the current size of the machine code zone
cogCompiledCodeCompactionCalledFor
- a variable set when the machine code zone runs out of space, causing a machine code zone compaction at the next available opportunity
cogMethodZone
- the manager for the machine code zone (instance of CogMethodZone)
cogit
- the JIT (co-jit) (instance of SimpleStackBasedCogit, StackToRegisterMappoingCogit, etc)
deferSmash
- a flag causing deferral of smashes of the stackLimit around the call of functionSymbol (for assert checks)
deferredSmash
- a flag noting deferral of smashes of the stackLimit around the call of functionSymbol (for assert checks)
desiredCogCodeSize
- the desred size of the machine code zone, set at startup or via primitiveVMParameter to be written at snapshot time
flagInterpretedMethods
- true if methods that are interpreted shoudl have their flag bit set (used to identity methods that are interpreted because they're unjittable for some reason)
gcMode
- the variable holding the gcMode, used to inform the cogit of how to scan the machine code zone for oops on GC
heapBase
- the address in memory of the base of the objectMemory's heap, which is immediately above the machine code zone
lastCoggableInterpretedBlockMethod
- a variable used to invoke the cogit for a block mehtod being invoked repeatedly in the interpreter
lastUncoggableInterpretedBlockMethod
- a variable used to avoid invoking the cogit for an unjittable method encountered on block evaluation
maxLiteralCountForCompile
- the variable controlling which methods to jit. methods with a literal count above this value will not be jitted (on the grounds that large methods are typically used for initialization, and take up a lot of space in the code zone)
minBackwardJumpCountForCompile
- the variable controlling when to attempt to jit a method being interpreted. If as many backward jumps as this occur, the current method will be jitted
primTraceLog
- a small array implementing a crcular buffer logging the last N primitive invocations, GCs, code compactions, etc used for crash reporting
primTraceLogIndex
- the index into primTraceLog of the next entry
reenterInterpreter
- the jmpbuf used to jmp back into the interpreter when transitioning from machine code to the interpreter
statCodeCompactionCount
- the count of machine code zone compactions
statCodeCompactionUsecs
- the total microseconds spent in machine code zone compactions
traceLog
- a log of various events, used in debugging
traceLogIndex
- the index into traceLog of the next entry
traceSources
- the names associated with the codes of events in traceLog
"
Class {
#name : #CoInterpreter,
#superclass : #StackInterpreterPrimitives,
#instVars : [
'cogit',
'cogMethodZone',
'gcMode',
'cogCodeSize',
'desiredCogCodeSize',
'lastCoggableInterpretedBlockMethod',
'deferSmash',
'deferredSmash',
'primTraceLog',
'primTraceLogIndex',
'traceLog',
'traceLogIndex',
'traceSources',
'cogCompiledCodeCompactionCalledFor',
'statCodeCompactionCount',
'statCodeCompactionUsecs',
'lastUncoggableInterpretedBlockMethod',
'flagInterpretedMethods',
'maxLiteralCountForCompile',
'minBackwardJumpCountForCompile',
'initialMemoryAddress',
'rumpCStackBase'
],
#classVars : [
'HasBeenReturnedFromMCPC',
'HasBeenReturnedFromMCPCOop',
'MFMethodFlagFrameIsMarkedFlag',
'MinBackwardJumpCountForCompile',
'PrimTraceLogSize',
'RumpCStackSize',
'TraceBlockActivation',
'TraceBlockCreation',
'TraceBufferSize',
'TraceCodeCompaction',
'TraceContextSwitch',
'TraceFullGC',
'TraceIncrementalGC',
'TraceIsFromInterpreter',
'TraceIsFromMachineCode',
'TracePrimitiveFailure',
'TracePrimitiveRetry',
'TraceSources',
'TraceStackOverflow',
'TraceVMCallback',
'TraceVMCallbackReturn'
],
#pools : [
'CogMethodConstants',
'VMStackFrameOffsets'
],
#category : #'VMMaker-JIT'
}
{ #category : #translation }
CoInterpreter class >> ancilliaryClasses [
"Answer any extra classes to be included in the translation."
^ super ancilliaryClasses , {
CogMethod.
CogPrimitiveDescriptor }
, (Cogit ancilliaryClasses select: [ :class |
class inheritsFrom: CogMethod ])
]
{ #category : #translation }
CoInterpreter class >> apiExportHeaderName [
^'cointerp.h'
]
{ #category : #translation }
CoInterpreter class >> declareCVarsIn: aCCodeGenerator [
"Override to avoid repeating StackInterpreter's declarations and add our own extensions"
self class == thisContext methodClass ifFalse: [^self]. "Don't duplicate decls in subclasses"
aCCodeGenerator
addHeaderFile:'"sqCogStackAlignment.h"';
addHeaderFile:'"cogmethod.h"'.
aCCodeGenerator
addHeaderFile: '"cointerp.h"';
addHeaderFile:'"cogit.h"'.
aCCodeGenerator vmClass
declareInterpreterVersionIn: aCCodeGenerator
defaultName: aCCodeGenerator interpreterVersion.
aCCodeGenerator
var: #statCodeCompactionUsecs type: #usqLong;
var: #maxLiteralCountForCompile
declareC: 'sqInt maxLiteralCountForCompile = MaxLiteralCountForCompile /* ', MaxLiteralCountForCompile printString, ' */';
var: #minBackwardJumpCountForCompile
declareC: 'sqInt minBackwardJumpCountForCompile = MinBackwardJumpCountForCompile /* ', MinBackwardJumpCountForCompile printString, ' */'.
aCCodeGenerator removeVariable: 'atCache'. "Way too much trouble than it's worth in the Cog VM"
aCCodeGenerator
var: #primTraceLogIndex type: #'unsigned char';
var: #primTraceLog declareC: 'sqInt primTraceLog[256]';
var: #traceLog
declareC: 'sqInt traceLog[TraceBufferSize /* ', TraceBufferSize printString, ' */]';
var: #traceSources type: #'char *' array: TraceSources
]
{ #category : #'accessing class hierarchy' }
CoInterpreter class >> hasCogit [
^true
]
{ #category : #initialization }
CoInterpreter class >> initializeCaches [
"Eliminate the AtCache"
super initializeCaches.
AtCacheTotalSize := AtCacheSize := AtCacheMask := AtCacheFixedFields := AtCacheFmt := AtCacheOop := #undefined
]
{ #category : #initialization }
CoInterpreter class >> initializeContextIndices [
super initializeContextIndices.
HasBeenReturnedFromMCPC := -1.
HasBeenReturnedFromMCPCOop := self objectMemoryClass basicNew integerObjectOf: HasBeenReturnedFromMCPC
]
{ #category : #initialization }
CoInterpreter class >> initializeFrameIndices [
"Format of a stack frame. Word-sized indices relative to the frame pointer.
Terminology
Frames are either single (have no context) or married (have a context).
Contexts are either single (exist on the heap), married (have a context) or widowed (had a frame that has exited).
Stacks grow down:
receiver for method activations/closure for block activations
arg0
...
argN
caller's saved ip/this stackPage (for a base frame)
fp-> saved fp
method
context (initialized to nil)
frame flags (interpreter only)
saved method ip (initialized to 0; interpreter only)
receiver
first temp
...
sp-> Nth temp
In an interpreter frame
frame flags holds
the backward jump count (see ifBackwardsCheckForEvents)
the number of arguments (since argument temporaries are above the frame)
the flag for a block activation
and the flag indicating if the context field is valid (whether the frame is married).
saved method ip holds the saved method ip when the callee frame is a machine code frame.
This is because the saved method ip is actually the ceReturnToInterpreterTrampoline address.
In a machine code frame
the flag indicating if the context is valid is the least significant bit of the method pointer
the flag for a block activation is the next most significant bit of the method pointer
Interpreter frames are distinguished from method frames by the method field which will
be a pointer into the heap for an interpreter frame and a pointer into the method zone for
a machine code frame.
The first frame in a stack page is the baseFrame and is marked as such by a saved fp being its stackPage,
in which case the first word on the stack is the caller context (possibly hybrid) beneath the base frame."
| fxCallerSavedIP fxSavedFP fxMethod fxIFrameFlags fxThisContext fxIFReceiver fxMFReceiver fxIFSavedIP |
fxCallerSavedIP := 1.
fxSavedFP := 0.
fxMethod := -1.
fxThisContext := -2.
fxIFrameFlags := -3. "Can find numArgs, needed for fast temp access. args are above fxCallerSavedIP.
Can find ``is block'' bit
Can find ``has context'' bit"
fxIFSavedIP := -4.
fxIFReceiver := -5.
fxMFReceiver := -3.
"For debugging undefine values that differ in the StackInterpreter."
FrameSlots := #undefined.
IFrameSlots := fxCallerSavedIP - fxIFReceiver + 1.
MFrameSlots := fxCallerSavedIP - fxMFReceiver + 1.
FoxCallerSavedIP := fxCallerSavedIP * BytesPerWord.
"In Cog a base frame's caller context is stored on the first word of the stack page."
FoxCallerContext := #undefined.
FoxSavedFP := fxSavedFP * BytesPerWord.
FoxMethod := fxMethod * BytesPerWord.
FoxThisContext := fxThisContext * BytesPerWord.
FoxFrameFlags := #undefined.
FoxIFrameFlags := fxIFrameFlags * BytesPerWord.
FoxIFSavedIP := fxIFSavedIP * BytesPerWord.
FoxReceiver := #undefined.
FoxIFReceiver := fxIFReceiver * BytesPerWord.
FoxMFReceiver := fxMFReceiver * BytesPerWord.
"N.B. There is room for one more flag given the current 8 byte alignment of methods (which
is at least needed to distinguish the checked and uncecked entry points by their alignment."
MFMethodFlagHasContextFlag := 1.
MFMethodFlagIsBlockFlag := 2.
MFMethodFlagFrameIsMarkedFlag := 4. "for pathTo:using:followWeak:"
MFMethodFlagsMask := MFMethodFlagHasContextFlag + MFMethodFlagIsBlockFlag + MFMethodFlagFrameIsMarkedFlag.
MFMethodMask := (MFMethodFlagsMask + 1) negated
]
{ #category : #initialization }
CoInterpreter class >> initializeMiscConstants [
super initializeMiscConstants.
COGVM := true.
MinBackwardJumpCountForCompile := 40.
MaxNumArgs := 15.
PrimCallNeedsNewMethod := 1.
PrimCallNeedsPrimitiveFunction := 2.
PrimCallMayCallBack := 4.
PrimCallOnSmalltalkStack := 8.
PrimCallCollectsProfileSamples := 16.
CheckAllocationFillerAfterPrimCall := 32.
PrimCallDoNotJIT := 64.
PrimTraceLogSize := 256. "Room for 256 selectors. Must be 256 because we use a byte to hold the index"
TraceBufferSize := 256 * 3. "Room for 256 events"
TraceContextSwitch := self objectMemoryClass basicNew integerObjectOf: 1.
TraceBlockActivation := self objectMemoryClass basicNew integerObjectOf: 2.
TraceBlockCreation := self objectMemoryClass basicNew integerObjectOf: 3.
TraceIncrementalGC := self objectMemoryClass basicNew integerObjectOf: 4.
TraceFullGC := self objectMemoryClass basicNew integerObjectOf: 5.
TraceCodeCompaction := self objectMemoryClass basicNew integerObjectOf: 6.
TraceVMCallback := self objectMemoryClass basicNew integerObjectOf: 11.
TraceVMCallbackReturn := self objectMemoryClass basicNew integerObjectOf: 12.
TraceStackOverflow := self objectMemoryClass basicNew integerObjectOf: 13.
TracePrimitiveFailure := self objectMemoryClass basicNew integerObjectOf: 14.
TracePrimitiveRetry := self objectMemoryClass basicNew integerObjectOf: 15.
TraceIsFromMachineCode := 1.
TraceIsFromInterpreter := 2.
TraceSources := CArrayAccessor on: #('?' 'm' 'i' 'callbackEnter' 'callbackLeave' 'enterCritical' 'exitCritical' 'resume' 'signal' 'suspend' 'wait' 'yield' 'eventcheck' ).
"this is simulation only"
RumpCStackSize := 4096
]
{ #category : #initialization }
CoInterpreter class >> initializePrimitiveTable [
super initializePrimitiveTable.
PrimNumberHashMultiply := 159.
self assert: (PrimitiveTable at: PrimNumberHashMultiply + 1) = #primitiveHashMultiply.
#(216 253) do:
[:pidx| self assert: (PrimitiveTable at: pidx + 1) = #primitiveFail].
self assert: (PrimitiveTable at: 215 + 1) = #primitiveFlushCacheByMethod.
PrimitiveTable
at: 253 + 1 put: #primitiveCollectCogCodeConstituents;
at: 215 + 1 put: #primitiveVoidVMStateForMethod;
at: 216 + 1 put: #primitiveMethodXray;
at: 217 + 1 put: #primitiveMethodProfilingData
]
{ #category : #'class initialization' }
CoInterpreter class >> initializeWithOptions: optionsDictionaryOrArray objectMemoryClass: objectMemoryClassOrNil [
super initializeWithOptions: optionsDictionaryOrArray objectMemoryClass: objectMemoryClassOrNil.
(self cogitClass withAllSuperclasses copyUpTo: Cogit) reverseDo:
[:c| c initializeWithOptions: InitializationOptions]
]
{ #category : #documentation }
CoInterpreter class >> interpreterMachineCodeTransitions [
"The CoInterpreter only asks the Cog compiler to generate machine-code methods
when a bytecoded method has been found in the cache, or block value has tried to
invoke a block in the method two times consecutively. This prevents the compiler
being asked to compile an infrequenttly used method.
I would like the following to be true, but it isn't. The interpreter *does* invoke
machine-code primitives that may context switch.
The CoInterpreter will only activate a Cog method that doesn't have a primitive
(this does not mean it won't invoke a Cog block method; it just does so through the
interpreted block value primitives). This is to avoid serious complications with the
process switch primitives. The CoInterpreter needs to know if it should push the
instructionPointer or save it in frameSavedIP and substitute ceReturtnToInterpreterPC
as the pushed instruction pointer. The process switch primitives need to know if
they were called from the interpreter or from machine-code to know how to continue.
If a process switch primitive has been invoked from the interpreter and switches to
a process suspended in an interpreted method it can return to the interpreter. In both
cases switching to a process in machine-code the primtiive can continue via the
ceEnterCogCodeXXX enilopmart(s). But if in machine-code and switching to a process
in the interpreter it must longjmp to the interpreter. So the process-switch primtiives
need to know whether they werer invoked from the interpreter or not.
If the process-switch primitives are allowed to be invoked from the interpreter via a
machine-code method then, in the immortal words of Robert Fripp, ``affairs stand a
good chance of getting severely out of hand...'' (The Guitar Handbook, Ralph Denyer,
p 114, Pan Books). The VM now has to longjmp not only if invoked from machine code
and switching to the interpreter but if invoked from the interpreter via machine code
and switching to the interpreter. The issue is that it is difficult to discover from within
a primitive whether the primitive call is from machine code, as it should be; it isn't a
concern of the primitive. Hence KISS says ``no machine-code invocation of primitives
from the interpreter''."
]
{ #category : #accessing }
CoInterpreter class >> interpreterVersion [
^ 'Cog'
]
{ #category : #translation }
CoInterpreter class >> isNonArgumentImplicitReceiverVariableName: aString [
^ (#( 'cogit' 'cogMethodZone' ) includes: aString) or: [
super isNonArgumentImplicitReceiverVariableName: aString ]
]
{ #category : #translation }
CoInterpreter class >> mustBeGlobal: var [
"Answer if a variable must be global and exported. Used for inst vars that are accessed from VM support code."
^(super mustBeGlobal: var)
or: [#('desiredCogCodeSize' 'heapBase'
'maxLiteralCountForCompile' 'minBackwardJumpCountForCompile') includes: var]
]
{ #category : #translation }
CoInterpreter class >> needsCogit [
^true
]
{ #category : #translation }
CoInterpreter class >> preGenerationHook: aCCodeGenerator [
"Override to undo the hiding of primitiveClosureValueNoContextSwitch"
super preGenerationHook: aCCodeGenerator.
"horrible hack to declare primErrorCode and argumentCount as bytes in the same word, and
hence save an instruction by initializing two birds^H^H^H^H^Hbytes with one word write.
Stalled awaiting MoveAbR and MoveRAb support in the Cogit"
false ifTrue:
[aCCodeGenerator
var: #argumentCount declareC: '#define argumentCount acpfc.ac\#define primFailCode acpfc.pfc' withCRs;
var: #primFailCode declareC: '#if VMBIGENDIAN\struct { short pad; unsigned char pfc; unsigned char ac; } acpfc;\#else /* argumentCount & primFailCode */\struct { unsigned char ac; unsigned char pfc; } acpfc;\#endif' withCRs]
]
{ #category : #'accessing class hierarchy' }
CoInterpreter class >> primitivesClass [
^CoInterpreterPrimitives
]
{ #category : #translation }
CoInterpreter class >> shouldGenerateTypedefFor: aStructClass [
"Hack to work-around multiple definitions. Sometimes a type has been defined in an include."
^(super shouldGenerateTypedefFor: aStructClass)
and: [Cogit shouldGenerateTypedefFor: aStructClass]
]
{ #category : #translation }
CoInterpreter class >> sourceFileName [
"Answer the filename for the core interpreter"
^'cointerp.c'
]
{ #category : #translation }
CoInterpreter class >> specialValueForConstant: constantName default: defaultValue [
constantName = 'DoAssertionChecks' ifTrue:
[^'(!PRODUCTION)'].
constantName = 'AllocationCheckFiller' ifTrue:
[^('#if !defined(AllocationCheckFiller)\# define AllocationCheckFiller ', defaultValue, '\#endif') withCRs].
^super specialValueForConstant: constantName default: defaultValue
]
{ #category : #translation }
CoInterpreter class >> writeVMHeaderTo: aStream bytesPerWord: bytesPerWord generator: aCCodeGenerator [
super writeVMHeaderTo: aStream bytesPerWord: bytesPerWord generator: aCCodeGenerator.
aCCodeGenerator
putDefineOf: #COGVM as: 1 on: aStream.
aStream cr
]
{ #category : #simulation }
CoInterpreter >> ISA [
<doNotGenerate>
^cogit backEnd class ISA
]
{ #category : #'cog jit support' }
CoInterpreter >> accessorDepthForPrimitiveIndex: primIndex [
<api>
<option: #SpurObjectMemory>
^primitiveAccessorDepthTable at: primIndex
]
{ #category : #'message sending' }
CoInterpreter >> activateCoggedNewMethod: inInterpreter [
"Activate newMethod when newMethod has been cogged, i.e. create a machine-code frame and (re)enter machine-code."
| methodHeader cogMethod rcvr numTemps switched |
<var: #cogMethod type: #'CogMethod *'>
methodHeader := self rawHeaderOf: newMethod.
self assert: (self isCogMethodReference: methodHeader).
cogMethod := self cCoerceSimple: methodHeader to: #'CogMethod *'.
methodHeader := cogMethod methodHeader.
rcvr := self stackValue: cogMethod cmNumArgs. "could new rcvr be set at point of send?"
self push: instructionPointer.
cogMethod stackCheckOffset = 0 ifTrue:
["frameless method; nothing to activate..."
cogit numRegArgs > 0 ifTrue: "dont use and: so as to get Slang to inline cogit numRegArgs > 0"
[cogMethod cmNumArgs <= cogit numRegArgs ifTrue:
[self callRegisterArgCogMethod: cogMethod at: cogit noCheckEntryOffset receiver: rcvr]].
self push: cogMethod asInteger + cogit noCheckEntryOffset.
self push: rcvr.
self callEnilopmart: #ceCallCogCodePopReceiverReg.
self unreachable].
self push: framePointer.
framePointer := stackPointer.
self push: cogMethod asInteger.
self push: objectMemory nilObject. "FxThisContext field"
self push: rcvr.
"clear remaining temps to nil"
numTemps := self temporaryCountOfMethodHeader: methodHeader.
cogMethod cmNumArgs + 1 to: numTemps do:
[:i | self push: objectMemory nilObject].
((self methodHeaderHasPrimitive: methodHeader)
and: [primFailCode ~= 0]) ifTrue:
[self reapAndResetErrorCodeTo: framePointer header: methodHeader].
"Now check for stack overflow or an event (interrupt, must scavenge, etc)."
stackPointer >= stackLimit ifTrue:
[self assert: cogMethod stackCheckOffset > cogit noCheckEntryOffset.
self push: cogMethod asInteger + cogMethod stackCheckOffset.
self push: rcvr.
self callEnilopmart: #ceEnterCogCodePopReceiverReg.
self unreachable].
instructionPointer := cogMethod asInteger + cogMethod stackCheckOffset.
switched := self handleStackOverflowOrEventAllowContextSwitch: (self canContextSwitchIfActivating: newMethod header: methodHeader).
self returnToExecutive: inInterpreter postContextSwitch: switched
]
{ #category : #'control primitives' }
CoInterpreter >> activateNewFullClosure: blockClosure method: theMethod numArgs: numArgs mayContextSwitch: mayContextSwitch [
"Similar to activateNewMethod but for Closure and newMethod."
<inline: true>
| methodHeader |
self assert: theMethod = (objectMemory
fetchPointer: FullClosureCompiledBlockIndex
ofObject: blockClosure).
methodHeader := self rawHeaderOf: theMethod.
(self isCogMethodReference: methodHeader) ifTrue: [
self interpreterIgnore: [ self forcePushRegisterCallingConvention ].
^ self
executeFullCogBlock: (self cogMethodOf: theMethod)
closure: blockClosure
mayContextSwitch: mayContextSwitch ].
self
activateNewFullClosure: blockClosure
method: theMethod
numArgs: numArgs
mayContextSwitch: mayContextSwitch
methodHeader: methodHeader
]
{ #category : #'control primitives' }
CoInterpreter >> activateNewFullClosure: blockClosure method: theMethod numArgs: numArgs mayContextSwitch: mayContextSwitch methodHeader: aMethodHeader [
<druidExitPoint>
| numCopied inInterpreter methodHeader numTemps switched |
methodHeader := aMethodHeader.
numCopied := self copiedValueCountOfFullClosure: blockClosure.
"How do we know when to compile a block method?
One simple criterion is to check if the block is running within its inner context,
i.e. if the outerContext is married.
Even simpler is to remember the previous block entered via the interpreter and
compile if this is the same one. But we can thrash trying to compile an uncoggable
method unless we try and remember which ones can't be cogged. So also record
the last block method we failed to compile and avoid recompiling it."
(self methodShouldBeCogged: theMethod)
ifTrue:
[((self isInstructionPointerInInterpreter: instructionPointer) not "If from machine code (via value primitive) attempt jitting"
or: [theMethod = lastCoggableInterpretedBlockMethod]) "If from interpreter and repeat block, attempt jitting"
ifTrue:
[theMethod ~= lastUncoggableInterpretedBlockMethod ifTrue: [
numCopied := self copiedValueCountOfFullClosure: blockClosure.
cogit cogFullBlockMethod: theMethod numCopied: numCopied.
(self methodHasCogMethod: theMethod) ifTrue:
[^self executeFullCogBlock: (self cogMethodOf: theMethod)
closure: blockClosure
mayContextSwitch: mayContextSwitch].
cogCompiledCodeCompactionCalledFor ifFalse:
[lastUncoggableInterpretedBlockMethod := theMethod]]]
ifFalse:
[lastCoggableInterpretedBlockMethod := theMethod]]
ifFalse:
[self maybeFlagMethodAsInterpreted: theMethod].
self assert: (self methodHasCogMethod: theMethod) not.
"Because this is an uncogged method we need to continue via the interpreter.
We could have been reached either from the interpreter, in which case we
should simply return, or from a machine code frame or from a compiled
primitive. In these latter two cases we must longjmp back to the interpreter.
The instructionPointer tells us which path we took.
If the sender was an interpreter frame but called through a (failing) primitive
then make sure we restore the saved instruction pointer and avoid pushing
ceReturnToInterpreterPC which is only valid between an interpreter caller
frame and a machine code callee frame."
(inInterpreter := self isInstructionPointerInInterpreter: instructionPointer) ifFalse:
[instructionPointer = cogit ceReturnToInterpreterPC ifTrue:
[instructionPointer := self iframeSavedIP: framePointer]].
^ super activateNewFullClosure: blockClosure method: theMethod numArgs: numArgs mayContextSwitch: mayContextSwitch
]
{ #category : #'message sending' }
CoInterpreter >> activateNewMethod [
| methodHeader inInterpreter switched |
"Eagerly compile it if appropriate so that doits are fast."
methodHeader := self rawHeaderOf: newMethod.
inInterpreter := self isInstructionPointerInInterpreter:
instructionPointer.
(self isCogMethodReference: methodHeader)
ifTrue:
[inInterpreter ifTrue:
[self iframeSavedIP: framePointer put: instructionPointer asInteger.
instructionPointer := cogit ceReturnToInterpreterPC].
^ self activateCoggedNewMethod: inInterpreter].
"We are in the interpreter"
methodHeader := self justActivateNewMethod: true.
"Now check for stack overflow or an event (interrupt, must scavenge, etc)."
switched := true.
stackPointer < stackLimit ifTrue: [
switched := self handleStackOverflowOrEventAllowContextSwitch:
(self
canContextSwitchIfActivating: newMethod
header: methodHeader) ].
self returnToExecutive: inInterpreter postContextSwitch: switched
]
{ #category : #'method lookup cache' }
CoInterpreter >> addNewMethodToCache: classObj [
"Override to refuse to cache other than compiled methods.
This protects open PICs against having to test for compiled methods."
(objectMemory isOopCompiledMethod: newMethod) ifFalse:
[primitiveFunctionPointer := #primitiveInvokeObjectAsMethod.
^self].
super addNewMethodToCache: classObj
]
{ #category : #'image save/restore' }
CoInterpreter >> allocateMemoryForImage: f withHeader: header [
<var: #f type: #sqImageFile>
<var: #header type: #SpurImageHeaderStruct>
cogCodeSize := desiredCogCodeSize ~= 0
ifTrue: [ desiredCogCodeSize ]
ifFalse: [
header hdrCogCodeSize = 0
ifTrue: [ cogit defaultCogCodeSize ]
ifFalse: [ header hdrCogCodeSize ] ].
cogCodeSize := cogCodeSize min: cogit maxCogCodeSize.
objectMemory getMemoryMap initialCodeZoneSize: cogCodeSize.
super allocateMemoryForImage: f withHeader: header.
self beforeCodeZoneInitialization.
cogit
initializeCodeZoneFrom: objectMemory getMemoryMap codeZoneStart
upTo: objectMemory getMemoryMap codeZoneEnd
]
{ #category : #'trampoline support' }
CoInterpreter >> argumentCountAddress [
<api>
<returnTypeC: #usqInt>
^self cCode: [(self addressOf: argumentCount) asUnsignedInteger]
inSmalltalk: [cogit simulatedReadWriteVariableAddress: #argumentCount in: self]
]
{ #category : #'debug support' }
CoInterpreter >> assertValidExecutionPointe: lip r: lifp s: lisp imbar: inInterpreter line: ln [
<var: #lip type: #usqInt>
<var: #lifp type: #'char *'>
<var: #lisp type: #'char *'>
| methodField cogMethod theIP |
<var: #cogMethod type: #'CogMethod *'>
self assert: stackPage = stackPages mostRecentlyUsedPage l: ln.
self assert: (stackPage addressIsInPage: lifp) l: ln.
self assert: (self deferStackLimitSmashAround: #assertValidStackLimits: asSymbol with: ln).
self assert: lisp < lifp l: ln.
self assert: lifp > lisp l: ln.
self assert: lisp >= (stackPage realStackLimit - self stackLimitOffset) l: ln.
self assert: (lifp - lisp) / objectMemory bytesPerOop < LargeContextSlots l: ln.
methodField := self frameMethodField: lifp.
inInterpreter
ifTrue:
[self assert: (self isMachineCodeFrame: lifp) not l: ln.
self assert: method = methodField l: ln.
(self asserta: (objectMemory cheapAddressCouldBeInHeap: methodField) l: ln) ifTrue:
[theIP := lip = cogit ceReturnToInterpreterPC
ifTrue: [self iframeSavedIP: lifp]
ifFalse: [lip].
self assert: (theIP >= (methodField + (objectMemory lastPointerOf: methodField))
and: [theIP <= (methodField + (objectMemory numBytesOfBytes: methodField) + objectMemory baseHeaderSize - 1)])
l: ln].
self assert: ((self iframeIsBlockActivation: lifp)
or: [(self pushedReceiverOrClosureOfFrame: lifp) = (self iframeReceiver: lifp)])
l: ln]
ifFalse:
[self assert: (self isMachineCodeFrame: lifp) l: ln.
((self asserta: methodField asUnsignedInteger >= cogit minCogMethodAddress l: ln)
and: [self asserta: methodField asUnsignedInteger < cogit maxCogMethodAddress l: ln]) ifTrue:
[cogMethod := self mframeHomeMethod: lifp.
self assert: (lip > (methodField + (self sizeof: CogMethod))
and: [lip < (methodField + cogMethod blockSize)])
l: ln].
self assert: ((self mframeIsBlockActivation: lifp)
or: [(self pushedReceiverOrClosureOfFrame: lifp) = (self mframeReceiver: lifp)])
l: ln].
(self isBaseFrame: lifp) ifTrue:
[self assert: (self frameHasContext: lifp) l: ln.
self assert: (self frameContext: lifp) = (stackPages unsignedLongAt: stackPage baseAddress - objectMemory wordSize) l: ln]
]
{ #category : #'debug support' }
CoInterpreter >> assertValidExternalStackPointers [
self assert: framePointer < stackPage baseAddress.
self assert: stackPointer < framePointer.
self assert: framePointer > stackPointer.
self assert: stackPointer >= (stackPage realStackLimit - self stackLimitOffset)
]
{ #category : #'cog jit support' }
CoInterpreter >> assertValidMachineCodeFrame: instrPtr [
<api>
<var: #cogMethod type: #'CogMethod *'>
| cogMethod |
self assert: (self isMachineCodeFrame: framePointer).
cogMethod := self mframeCogMethod: framePointer.
self assert: (cogMethodZone methodFor: cogMethod) = cogMethod.
self assert: (instrPtr > cogMethod asInteger and: [
instrPtr < (cogMethod asInteger + cogMethod blockSize) ])
]
{ #category : #'debug support' }
CoInterpreter >> assertValidStackPageHeadPointers [
self assert: stackPage headFP < stackPage baseAddress.
self assert: stackPage headSP < stackPage headFP.
self assert: stackPage headFP > stackPage headSP.
self assert: stackPage headSP >= (stackPage realStackLimit - self stackLimitOffset)
]
{ #category : #'debug support' }
CoInterpreter >> assertValidStackedInstructionPointers: ln [
"Check that the stacked instruction pointers in all pages are correct.
Checks the interpreter sender/machine code callee contract.
Written so it will be optimized away if not in an assert VM."
| thePage |
<inline: false>
<var: #thePage type: #'StackPage *'>
0 to: numStackPages - 1 do:
[:i|
thePage := stackPages stackPageAt: i.
(stackPages isFree: thePage) ifFalse:
[self assert: (self assertValidStackedInstructionPointersIn: thePage line: ln) l: ln]]
]
{ #category : #'debug support' }
CoInterpreter >> assertValidStackedInstructionPointersIn: aStackPage line: ln [
"Check that the stacked instruction pointers in the given page are correct.
Checks the interpreter sender/machine code callee contract."
<var: #aStackPage type: #'StackPage *'>
<var: #theFP type: #'char *'>
<var: #callerFP type: #'char *'>
<var: #theIP type: #usqInt>
<var: #theMethod type: #'CogMethod *'>
<inline: false>
| prevFrameWasCogged theFP callerFP theMethod theIP methodObj |
(self asserta: (stackPages isFree: aStackPage) not l: ln) ifFalse:
[^false].
prevFrameWasCogged := false.
"The top of stack of an inactive page is always the instructionPointer.
The top of stack of the active page may be the instructionPointer if it has been pushed,
which is indicated by a 0 instructionPointer."
(stackPage = aStackPage and: [instructionPointer ~= 0])
ifTrue:
[theIP := instructionPointer.
theFP := framePointer]
ifFalse:
[theIP := (stackPages unsignedLongAt: aStackPage headSP) asUnsignedInteger.
theFP := aStackPage headFP.
stackPage = aStackPage ifTrue:
[self assert: framePointer = theFP l: ln]].
[(self isMachineCodeFrame: theFP)
ifTrue:
[theMethod := self mframeHomeMethod: theFP.
self assert: (theIP = cogit ceCannotResumePC
or: [theIP >= theMethod asUnsignedInteger
and: [theIP < (theMethod asUnsignedInteger + theMethod blockSize)]])
l: ln.
prevFrameWasCogged := true]
ifFalse: "assert-check the interpreter frame."
[methodObj := self iframeMethod: theFP.
prevFrameWasCogged ifTrue:
[self assert: theIP = cogit ceReturnToInterpreterPC l: ln].
theIP = cogit ceReturnToInterpreterPC ifTrue:
[theIP := self iframeSavedIP: theFP].
self assert: (theIP >= (methodObj + (objectMemory lastPointerOf: methodObj))
and: [theIP <= (methodObj + (objectMemory numBytesOfBytes: methodObj) + objectMemory baseHeaderSize - 1)])
l: ln.
prevFrameWasCogged := false].
theIP := (stackPages unsignedLongAt: theFP + FoxCallerSavedIP) asUnsignedInteger.
(callerFP := self frameCallerFP: theFP) ~= 0] whileTrue:
[theFP := callerFP].
self assert: theIP = cogit ceBaseFrameReturnPC l: ln.
^true
]
{ #category : #'jump bytecodes' }
CoInterpreter >> attemptToSwitchToMachineCode: bcpc [
"Attempt to convert the current interpreted activation into a machine code
activation, and if this is popssible, jump into machine code. bcpc is the
0-relative pc of the backward branch bytecode (not any preceding extension)."
<inline: false>
<var: #cogMethod type: #'CogMethod *'>
| cogMethod pc cls |
(self methodHasCogMethod: method) ifFalse: [
(self iframeIsBlockActivation: framePointer)
ifTrue: [ "Compiled block / full closure"
cls := self
frameStackedReceiver: framePointer
numArgs: (self frameNumArgs: framePointer).
cogit
cogFullBlockMethod: method
numCopied: (objectMemory numPointerSlotsOf: cls)
- FullClosureFirstCopiedValueIndex ]
ifFalse: [ "Compiled method"
cogit cog: method selector: objectMemory nilObject ] ].
(self methodHasCogMethod: method) ifTrue: [
cogMethod := self cogMethodOf: method.
pc := self convertToMachineCodeFrame: cogMethod bcpc: bcpc.
"If conversion failed, continue interpreted"
pc = 0 ifTrue: [ ^ self ].
self assertValidMachineCodeFrame: pc.
self push: pc.
self push: objectMemory nilObject.
self callEnilopmart: #ceEnterCogCodePopReceiverReg ]
]
{ #category : #'return bytecodes' }
CoInterpreter >> baseFrameCannotReturnTo: contextToReturnTo [
| contextToReturnFrom |
contextToReturnFrom := stackPages longAt:
stackPage baseAddress - objectMemory wordSize.
self
tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom:
contextToReturnFrom
to: contextToReturnTo
returnValue: localReturnValue.
^ self
externalCannotReturn: localReturnValue
from: contextToReturnFrom
]
{ #category : #initialization }
CoInterpreter >> basicInitialize [
"Initialize the CogVMSimulator when running the interpreter inside Smalltalk. The
primary responsibility of this method is to allocate Smalltalk Arrays for variables
that will be declared as statically-allocated global arrays in the translated code."
cogit ifNil:
[cogit := self class cogitClass new setInterpreter: self].
objectMemory coInterpreter: self cogit: cogit.
super basicInitialize.
cogMethodZone := cogit methodZone. "Because Slang can't remove intermediate implicit receivers (cogit methodZone foo doesn't reduce to foo())"
self flushMethodCache.
cogCompiledCodeCompactionCalledFor := false.
desiredNumStackPages := desiredEdenBytes := desiredCogCodeSize := 0.
maxLiteralCountForCompile := MaxLiteralCountForCompile.
minBackwardJumpCountForCompile := MinBackwardJumpCountForCompile.
flagInterpretedMethods := false.
suppressHeartbeatFlag := deferSmash := deferredSmash := false.
primTraceLog := CArrayAccessor on: (Array new: 256 withAll: 0).
primTraceLogIndex := 0.
traceLog := CArrayAccessor on: (Array new: TraceBufferSize withAll: 0).
traceLogIndex := 0.
traceSources := TraceSources.
statCodeCompactionCount := 0.
statCodeCompactionUsecs := 0.
]
{ #category : #hooks }
CoInterpreter >> beforeCodeZoneInitialization [
"Hook point for the simulator"
]
{ #category : #'frame access' }
CoInterpreter >> bytecodePCFor: theIP cogMethod: cogMethod startBcpc: startBcpc [
"Answer the mapping of the native pc theIP to a zero-relative bytecode pc.
See contextInstructionPointer:frame: for the explanation."
<var: #cogMethod type: #'CogMethod *'>
<inline: true>
| mcpc |
self assert: theIP < 0.
self assert: cogMethod cmType = CMMethod.
mcpc := cogMethod asInteger - theIP.
"map any pcs in primitive code (i.e. return addresses for interpreter primitive calls) to the initial pc"
mcpc asUnsignedInteger < cogMethod stackCheckOffset ifTrue: [
^ startBcpc ].
self assert: (mcpc
between: cogMethod asInteger
and: cogMethod asInteger + cogMethod blockSize).
^ cogit bytecodePCFor: mcpc startBcpc: startBcpc in: cogMethod
]
{ #category : #'common selector sends' }
CoInterpreter >> bytecodePrimAt [
"Override to eliminate the atCache, something of little benefit to the JIT."
<compilationInfo: #isMapped>
<druidInfo: #hasSend>
self normalSendSpecialSelector: 16 argumentCount: 1
]
{ #category : #'common selector sends' }
CoInterpreter >> bytecodePrimAtPut [
"Override to eliminate the atCache, something of little benefit to the JIT."
<compilationInfo: #isMapped>
<druidInfo: #hasSend>
self normalSendSpecialSelector: 17 argumentCount: 2
]
{ #category : #enilopmarts }
CoInterpreter >> callEnilopmart: anElinopmart [
<inline: true>
"Since the called method lives outside of the interpreter, use a perform to convey it.
This is a limitation of Slang that does not distinguish between:
- methods outside the interpreter (but within the VM)
- built in methods with special translations"
^ cogit perform: anElinopmart
]
{ #category : #'cog jit support' }
CoInterpreter >> callForCogCompiledCodeCompaction [
<api>
cogCompiledCodeCompactionCalledFor := true.
self forceInterruptCheck
]
{ #category : #enilopmarts }
CoInterpreter >> callRegisterArgCogMethod: cogMethod at: entryOffset receiver: rcvr [
"convert
rcvr base
arg(s)
retpc <- sp
to
retpc base
entrypc
rcvr
arg(s) <- sp
and then enter at either the checked or the unchecked entry-point."
<option: #StackToRegisterMappingCogit>
<var: #cogMethod type: #'CogMethod *'>
self assert: (cogit numRegArgs > 0 and: [cogit numRegArgs <= 2 and: [cogMethod cmNumArgs <= cogit numRegArgs]]).
cogMethod cmNumArgs = 2 ifTrue:
[self stackValue: 3 put: self stackTop. "retpc"
self push: (self stackValue: 1). "last arg"
self stackValue: 1 put: (self stackValue: 3). "first arg"
self stackValue: 2 put: rcvr.
self stackValue: 3 put: cogMethod asInteger + entryOffset.
self callEnilopmart: #ceCallCogCodePopReceiverArg1Arg0Regs.
self unreachable].
cogMethod cmNumArgs = 1 ifTrue:
[self stackValue: 2 put: self stackTop. "retpc"
self push: (self stackValue: 1). "arg"
self stackValue: 1 put: rcvr.
self stackValue: 2 put: cogMethod asInteger + entryOffset.
self callEnilopmart: #ceCallCogCodePopReceiverArg0Regs.
self unreachable].
self assert: cogMethod cmNumArgs = 0.
self stackValue: 1 put: self stackTop. "retpc"
self stackValue: 0 put: cogMethod asInteger + entryOffset.
self push: rcvr.
self callEnilopmart: #ceCallCogCodePopReceiverReg.
self unreachable
]
{ #category : #enilopmarts }
CoInterpreter >> ceActivateFailingPrimitiveMethod: aPrimitiveMethod [
"An external call or FFI primitive has failed. Build the frame and
activate as appropriate. Enter either the interpreter or machine
code depending on whether aPrimitiveMethod has been or is still
cogged. Note that we could always interpret but want the efficiency
of executing machine code if it is available."
<api>
| methodHeader result |
self assert: primFailCode ~= 0.
self assert: newMethod = aPrimitiveMethod.
"If we're on Spur, retry the primitive, if appropriate,
returning if successful after retry."
self retryPrimitiveOnFailure.
self successful ifTrue: [
result := self stackTop.
self stackTopPut: instructionPointer.
self push: result.