Branch: refs/heads/Cog
Home: https://github.com/OpenSmalltalk/opensmalltalk-vm
Commit: 30d52787f7013aab83b6b811ee0ae94b004fa487
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/30d52787f7013aab83…
Author: Eliot Miranda <eliot.miranda(a)gmail.com>
Date: 2021-07-09 (Fri, 09 Jul 2021)
Changed paths:
M build.macos64ARMv8/HowToBuild
M build.macos64ARMv8/common/Makefile.rules
M build.macos64ARMv8/common/Makefile.vm
A spur64src/vm/_variable_order
M spur64src/vm/cogit.h
M spur64src/vm/cogitARMv8.c
M spur64src/vm/cogitX64SysV.c
M spur64src/vm/cogitX64WIN64.c
M spur64src/vm/cointerp.c
M spur64src/vm/cointerp.h
M spur64src/vm/cointerpmt.c
M spur64src/vm/cointerpmt.h
M spur64src/vm/gcc3x-cointerp.c
M spur64src/vm/gcc3x-cointerpmt.c
A spur64src/vm/variable_order
A spurlowcode64src/vm/_variable_order
M spurlowcode64src/vm/cogit.h
M spurlowcode64src/vm/cogitARMv8.c
M spurlowcode64src/vm/cogitX64SysV.c
M spurlowcode64src/vm/cogitX64WIN64.c
M spurlowcode64src/vm/cointerp.c
M spurlowcode64src/vm/cointerp.h
M spurlowcode64src/vm/gcc3x-cointerp.c
A spurlowcode64src/vm/variable_order
A spurlowcodesrc/vm/_variable_order
M spurlowcodesrc/vm/cogit.h
M spurlowcodesrc/vm/cogitARMv5.c
M spurlowcodesrc/vm/cogitIA32.c
M spurlowcodesrc/vm/cogitMIPSEL.c
M spurlowcodesrc/vm/cointerp.c
M spurlowcodesrc/vm/cointerp.h
M spurlowcodesrc/vm/gcc3x-cointerp.c
A spurlowcodesrc/vm/variable_order
M spurlowcodestack64src/vm/gcc3x-interp.c
M spurlowcodestack64src/vm/interp.c
M spurlowcodestacksrc/vm/gcc3x-interp.c
M spurlowcodestacksrc/vm/interp.c
A spursista64src/vm/_variable_order
M spursista64src/vm/cogit.h
M spursista64src/vm/cogitARMv8.c
M spursista64src/vm/cogitX64SysV.c
M spursista64src/vm/cogitX64WIN64.c
M spursista64src/vm/cointerp.c
M spursista64src/vm/cointerp.h
M spursista64src/vm/gcc3x-cointerp.c
A spursista64src/vm/variable_order
A spursistasrc/vm/_variable_order
M spursistasrc/vm/cogit.h
M spursistasrc/vm/cogitARMv5.c
M spursistasrc/vm/cogitIA32.c
M spursistasrc/vm/cogitMIPSEL.c
M spursistasrc/vm/cointerp.c
M spursistasrc/vm/cointerp.h
M spursistasrc/vm/gcc3x-cointerp.c
A spursistasrc/vm/variable_order
A spursrc/vm/_variable_order
M spursrc/vm/cogit.h
M spursrc/vm/cogitARMv5.c
M spursrc/vm/cogitIA32.c
M spursrc/vm/cogitMIPSEL.c
M spursrc/vm/cointerp.c
M spursrc/vm/cointerp.h
M spursrc/vm/cointerpmt.c
M spursrc/vm/cointerpmt.h
M spursrc/vm/gcc3x-cointerp.c
M spursrc/vm/gcc3x-cointerpmt.c
A spursrc/vm/variable_order
M spurstack64src/vm/gcc3x-interp.c
M spurstack64src/vm/interp.c
M spurstack64src/vm/validImage.c
M spurstacksrc/vm/gcc3x-interp.c
M spurstacksrc/vm/interp.c
M spurstacksrc/vm/validImage.c
A src/vm/_variable_order
M src/vm/cogit.h
M src/vm/cogitARMv5.c
M src/vm/cogitIA32.c
M src/vm/cogitMIPSEL.c
M src/vm/cointerp.c
M src/vm/cointerp.h
M src/vm/gcc3x-cointerp.c
A src/vm/variable_order
M stacksrc/vm/gcc3x-interp.c
M stacksrc/vm/interp.c
Log Message:
-----------
CogVM source as per VMMaker.oscog-eem.2984
Apple M1 ARMv8 good enough to recompile the Compiler package and update an 8
month old trunk image (October 29th 2020). The major weakness right now is
that this uses the Apple cache flush code, not the JITted code, which as yet
does not work. I'll investigate soon.
Abstract setting codeModified & conditionally sending makeCodeZoneWritable/
pthread_jit_write_protect_np into setCodeModified, invoked from the relevant
places. Not all places can rely on flushICacheFrom:to: to flip back into
executability. Do so explicitly in these places.
CogARMv8Compiler: provide two ways to detect features (determine cache
parameters and atomic instruction availability), one via direct access to
system registers, the other hard-wired, but to be done via sysctl(3).
Make provision for Apple M1 using pthread_jit_write_protect_np. Experiments
show that DUAL_MAPPED_CODE_ZONE will not work, but pthread_jit_write_protect_np
does. The zone is switched into writability explicitly in relevant entry points
(see senders of ensureWritableCodeZone). The zone is switched into
executability as part of the compiler class's flushICacheFrom:to:, and
explicitly when flushICacheFrom:to: is either conditional or unused.
Have CogARMv8Compiler>>detectFeatures introduce a noop before accessing a
system register so an illegal instruction can easily be distinguished from
lack of execute permission.
Nuke dead code (addAllToYoungReferrers sqMakeMemoryNotExecutableFrom:To: etc).
Make a few methods static that don't need to be exported but do need to be
retained (ceCPICMiss:receiver: et al).
Make kosherYoungReferrers more robust so it works mid method generation.
Slang:
Do a better job eliding dead code by including casts in sends that are effect
free and broadening the range of arithmetic and conditional sends that are
considered for being effect free, and in argument passing.
Slang fixes (noMask et al shouldn't use bit and directly).
Generate a variable order file of variables accessed by the JIT (potentially
via VarBaseReg) to guide linkers in clustering these variables (close to the
var base). Use the variable_order file in the Apple M1 build.
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2984.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2984
Author: eem
Time: 9 July 2021, 4:41:46.033726 pm
UUID: f8f3a253-0bf6-4dbd-b427-c815aefd1f89
Ancestors: VMMaker.oscog-eem.2983
Apple M1 ARMv8 good enough to recompile the Compiler package and update an 8 month old trunk image (October 29th 2020). The major weakness right now is that this uses the Apple cache flush code, not the JITted code, which as yet does nto work. I'll investigate soon.
Abstract setting codeModified & conditionally sending makeCodeZoneWritable/pthread_jit_write_protect_np into setCodeModified, invoked from the relevant places. Not all places can rely on flushICacheFrom:to: to lip back into executability. Do so explicitly in these places.
Simulation: simplify shortcutTrampoline:to: further, but hack in pushes & pops of link register using SPReg; pushWord:in: uses the NativeSPReg. Make linked send tracing work on ARMv8.
=============== Diff against VMMaker.oscog-eem.2983 ===============
Item was changed:
----- Method: CogARMv8Compiler class>>declareCVarsIn: (in category 'translation') -----
declareCVarsIn: aCCodeGenerator
#('hasAtomicInstructions' 'instructionCacheLineLength' 'instructionCacheFlushRequired' 'dataCacheLineLength' 'dataCacheFlushRequired') do:
[:varName|
aCCodeGenerator
declareVar: varName type: #'unsigned char';
+ removeConstant: varName capitalized].
+ aCCodeGenerator
+ var: #ceFlushDCache
+ declareC: '#if __APPLE__\static void (*ceFlushDCache)(usqIntptr_t from, usqIntptr_t to)\#endif'!
- removeConstant: varName capitalized]!
Item was added:
+ ----- Method: CogARMv8Compiler>>callFullInstructionByteSize (in category 'accessing') -----
+ callFullInstructionByteSize
+ ^8!
Item was changed:
----- Method: CogARMv8Compiler>>detectFeaturesOnLinux (in category 'feature detection') -----
detectFeaturesOnLinux
"Do throw-away compilations to read CTR_EL0 & ID_AA64ISAR0_EL1 and initialize ctrEl0 & idISAR0"
<notOption: #__APPLE__>
| startAddress getFeatureReg ctrEL0 idISAR0 |
<var: 'getFeatureReg' declareC: 'usqIntptr_t (*getFeatureReg)(void)'>
startAddress := cogit methodZoneBase.
cogit allocateOpcodes: 4 bytecodes: 0.
getFeatureReg := cogit cCoerceSimple: startAddress to: #'usqIntptr_t (*)(void)'.
"Return the value of CTR_EL0; that's the control register that defines the vital statistics of the processor's caches."
cogit
gen: Nop; "do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable."
gen: MRS_CTR_EL0 operand: ABIResultReg;
RetN: 0.
cogit outputInstructionsForGeneratedRuntimeAt: startAddress.
cogit resetMethodZoneBase: startAddress.
cogit ensureExecutableCodeZoneWithin:
[ctrEL0 := (self cCode: 'getFeatureReg()' inSmalltalk: [cogit simulateLeafCallOf: startAddress]).
+ "see e.g. CogARMv8Compiler class>>printCTR_EL0:, concretizeCacheControlOp1:CRm:Op2: &
+ http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_…
+ DminLine & IminLine are Log2 words; 16 words miniumum"
- "see e.g. CogARMv8Compiler class>>printCTR_EL0:"
self setDataCacheFlushRequired: (ctrEL0 noMask: 1 << 28).
self setDataCacheLineLength: 4 << (ctrEL0 >> 16 bitAnd: 15).
+ self dataCacheLineLength = 0 ifTrue:
+ [self setDataCacheLineLength: 4 << 4].
self setInstructionCacheFlushRequired: (ctrEL0 noMask: 1 << 29).
self setInstructionCacheLineLength: 4 << (ctrEL0 bitAnd: 15)].
+ self instructionCacheLineLength = 0 ifTrue:
+ [self setInstructionCacheLineLength: 4 << 4].
cogit zeroOpcodeIndexForNewOpcodes.
cogit
gen: Nop; "do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable."
gen: MRS_ID_AA64ISAR0_EL1 operand: ABIResultReg;
RetN: 0.
cogit outputInstructionsForGeneratedRuntimeAt: startAddress.
cogit resetMethodZoneBase: startAddress.
cogit ensureExecutableCodeZoneWithin:
[idISAR0 := (self cCode: 'getFeatureReg()' inSmalltalk: [cogit simulateLeafCallOf: startAddress]).
self setHasAtomicInstructions: (idISAR0 >> 20 bitAnd: 2r1111) = 2r10]!
Item was changed:
----- Method: CogARMv8Compiler>>flushICacheFrom:to: (in category 'inline cacheing') -----
flushICacheFrom: startAddress "<Integer>" to: endAddress "<Integer>"
"Flush the instruction cache from (startAddress to endAddress].
If there is a dual mapped code zone (the normal zone but marked with read/execute, and a
read/write zone codeToDataDelta bytes away) then also flush the data cache for the corresp-
onding range in the read/write zone and invalidate the data cache for the read/execute zone."
<inline: #always>
cogit ensureExecutableCodeZone.
+ self cCode:
+ [self cppIf: #__APPLE__
+ ifTrue: [self initialFlushICacheFrom: startAddress to: endAddress]
+ ifFalse:
+ [self cppIf: #__APPLE__
+ ifTrue: [cogit ceFlushDCache: startAddress _: endAddress].
+ cogit ceFlushICache: startAddress _: endAddress]]
- self cCode: [cogit ceFlushICache: startAddress _: endAddress]
inSmalltalk: [cogit simulateCeFlushICacheFrom: startAddress to: endAddress]!
Item was changed:
----- Method: CogARMv8Compiler>>generateDCacheFlush (in category 'inline cacheing') -----
generateDCacheFlush
"Use the DC instruction to implement ceFlushDCache(void *start, void *end); see flushDCacheFrom:to:.
If there is a dual mapped zone then clean data via DC_CVAU as address + codeToDataDelta,
then invalidate data at address via CIVAC."
"D4.4.7 About cache maintenance in AArch64 state D4-2478
Terminology for Clean, Invalidate, and Clean and Invalidate instructions D4-2479
...
- For instructions operating by VA, the following conceptual points are defined: D4-2480
Point of Unification (PoU)
The PoU for a PE is the point by which the instruction and data caches and the translation table walks of that
PE are guaranteed to see the same copy of a memory location. In many cases, the Point of Unification is the
point in a uniprocessor memory system by which the instruction and data caches and the translation table
walks have merged.
The PoU for an Inner Shareable shareability domain is the point by which the instruction and data caches
and the translation table walks of all the PEs in that Inner Shareable shareability domain are guaranteed to
see the same copy of a memory location. Defining this point permits self-modifying software to ensure future
instruction fetches are associated with the modified version of the software by using the standard correctness
policy of:
1. Clean data cache entry by address.
2. Invalidate instruction cache entry by address.
Example code for cache maintenance instructions D4-2490 - D4-2491"
+ | mask loop |
- | dataCacheMinLineLength mask loop |
self assert: cogit getCodeToDataDelta ~= 0.
- "See concretizeCacheControlOp1:CRm:Op2: &
- http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_…
- DminLine & IminLine are Log2 words; 16 words miniumum"
- (dataCacheMinLineLength := self dataCacheLineLength) = 0 ifTrue:
- [dataCacheMinLineLength := 4].
- dataCacheMinLineLength := 4 << dataCacheMinLineLength.
"Mask is large enough to encompass the method zone and has the correct minimum alignment."
+ mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - self dataCacheLineLength.
- mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - dataCacheMinLineLength.
"Since this is used from C code we must use only caller-saved registers.
C arg registers 2 & 3 are such a convenient pair of caller-saved registers."
cogit AndCq: mask R: CArg0Reg R: CArg2Reg. "CArg2Reg = aligned pointer to start of each data cache line"
cogit AddCq: cogit getCodeToDataDelta R: CArg2Reg R: CArg3Reg.
loop := cogit Label.
"see concretizeDataCacheControl"
cogit gen: DC operand: CArg3Reg operand: DC_CVAU. "clean (flush) address + codeToDataDelta"
cogit gen: DC operand: CArg2Reg operand: DC_CIVAC. "invalidate address"
cogit
+ AddCq: self dataCacheLineLength R: CArg2Reg;
+ AddCq: self dataCacheLineLength R: CArg3Reg;
- AddCq: dataCacheMinLineLength R: CArg2Reg;
- AddCq: dataCacheMinLineLength R: CArg3Reg;
CmpR: CArg1Reg R: CArg2Reg;
JumpBelowOrEqual: loop.
cogit RetN: 0!
Item was changed:
----- Method: CogARMv8Compiler>>generateICacheFlush (in category 'inline cacheing') -----
generateICacheFlush
"Use DC VAUC, DSB, IC IVAU, and ISB instructions to implement ceFlushICache(void *start, void *end); see flushICacheFrom:to:.
One might think that if there is a dual zone then data at address + codeToDataDelta must be cleaned,
but this isn't the case. All we need to do is clean data at address via DC VAUC and instructions via IC IVAU."
"B2.2.5 Concurrent modification and execution of instructions B2-112
...to avoid UNPREDICTABLE or CONSTRAINED UNPREDICTABLE behavior, instruction modifications must be explicitly synchronized before they are executed. The required synchronization is as follows:
1. No PE must be executing an instruction when another PE is modifying that instruction.
2. To ensure that the modified instructions are observable, a PE that is writing the instructions must issue the following sequence of instructions and operations:
; Coherency example for data and instruction accesses within the same Inner Shareable domain.
; enter this code with <Wt> containing a new 32-bit instruction, to be held in Cacheable space at a location pointed to by Xn.
STR Wt, [Xn]
DC CVAU, Xn ; Clean data cache by VA to point of unification (PoU)
DSB ISH ; Ensure visibility of the data cleaned from cache
IC IVAU, Xn ; Invalidate instruction cache by VA to PoU
DSB ISH
Note
- The DC CVAU operation is not required if the area of memory is either Non-cacheable or Write-Through Cacheable.
- If the contents of physical memory differ between the mappings, changing the mapping of VAs to PAs can cause
the instructions to be concurrently modified by one PE and executed by another PE. If the modifications affect
instructions other than those listed as being acceptable for modification, synchronization must be used to avoid
UNPREDICTABLE or CONSTRAINED UNPREDICTABLE behavior.
3. In a multiprocessor system, the IC IVAU is broadcast to all PEs within the Inner Shareable domain of the PE running this sequence.
However, when the modified instructions are observable, each PE that is executing the modified instructions must issue the following
instruction to ensure execution of the modified instructions:
ISB ; Synchronize fetched instruction stream"
"D4.4.7 About cache maintenance in AArch64 state D4-2478
Terminology for Clean, Invalidate, and Clean and Invalidate instructions D4-2479
...
- For instructions operating by VA, the following conceptual points are defined: D4-2480
Point of Unification (PoU)
The PoU for a PE is the point by which the instruction and data caches and the translation table walks of that
PE are guaranteed to see the same copy of a memory location. In many cases, the Point of Unification is the
point in a uniprocessor memory system by which the instruction and data caches and the translation table
walks have merged.
The PoU for an Inner Shareable shareability domain is the point by which the instruction and data caches
and the translation table walks of all the PEs in that Inner Shareable shareability domain are guaranteed to
see the same copy of a memory location. Defining this point permits self-modifying software to ensure future
instruction fetches are associated with the modified version of the software by using the standard correctness
policy of:
1. Clean data cache entry by address.
2. Invalidate instruction cache entry by address.
Example code for cache maintenance instructions D4-2490 - D4-2491"
+ | mask loop |
- | dataCacheMinLineLength instrCacheMinLineLength mask loop |
"See concretizeCacheControlOp1:CRm:Op2: &
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_…"
self dataCacheFlushRequired ifTrue: "CTR_EL0.IDC is zero; must clean data cache to point of unification."
["Since this is used from C code we must use only caller-saved registers.
C arg registers 2 & 3 are as such a convenient pair of caller-saved registers."
- dataCacheMinLineLength := self dataCacheLineLength.
"Mask is large enough to encompass the method zone and has the correct minimum alignment."
+ mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - self dataCacheLineLength.
- mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - dataCacheMinLineLength.
cogit AndCq: mask R: CArg0Reg R: CArg2Reg. "CArg2Reg = aligned pointer to start of each data cache line"
loop := cogit Label.
"see concretizeDataCacheControl"
cogit
gen: DC operand: CArg2Reg operand: DC_CVAU; "clean (flush) address"
+ AddCq: self dataCacheLineLength R: CArg2Reg;
- AddCq: dataCacheMinLineLength R: CArg2Reg;
CmpR: CArg1Reg R: CArg2Reg;
JumpBelowOrEqual: loop].
cogit gen: DSB operand: DSB_ISH operand: DSB_ALL. "Ensure visibility of the data cleaned from cache"
self instructionCacheFlushRequired ifTrue: "CTR_EL0.DIC is zero; must clean instruction cache to point of unification."
+ ["Mask is large enough to encompass the method zone and has the correct minimum alignment."
+ mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - self instructionCacheLineLength.
- [instrCacheMinLineLength := self instructionCacheLineLength.
- "Mask is large enough to encompass the method zone and has the correct minimum alignment."
- mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - instrCacheMinLineLength.
cogit AndCq: mask R: CArg0Reg R: CArg2Reg. "CArg2Reg = aligned pointer to start of each data cache line"
loop := cogit Label.
"see concretizeDataCacheControl"
cogit
gen: IC operand: CArg2Reg operand: IC_IVAU; "clean (flush) address"
+ AddCq: self instructionCacheLineLength R: CArg2Reg;
- AddCq: instrCacheMinLineLength R: CArg2Reg;
CmpR: CArg1Reg R: CArg2Reg;
JumpBelowOrEqual: loop.
cogit gen: DSB operand: DSB_ISH operand: DSB_ALL].
cogit
gen: ISB;
RetN: 0!
Item was changed:
----- Method: CogMethodZone>>ensureInYoungReferrers: (in category 'young referers') -----
ensureInYoungReferrers: cogMethod
<var: #cogMethod type: #'CogMethod *'>
cogit assertValidDualZoneReadAddress: cogMethod.
cogMethod cmRefersToYoung ifFalse:
[| writableMethod |
self assert: (self occurrencesInYoungReferrers: cogMethod) = 0.
+ self ensureWritableCodeZone.
(writableMethod := cogit writableMethodFor: cogMethod) cmRefersToYoung: true.
self addToYoungReferrers: writableMethod]!
Item was changed:
----- Method: CogMethodZone>>freeMethod: (in category 'compaction') -----
freeMethod: cogMethod
<var: #cogMethod type: #'CogMethod *'>
<inline: false>
| writableMethod |
self assert: cogMethod cmType ~= CMFree.
self assert: (cogit cogMethodDoesntLookKosher: cogMethod) = 0.
+ cogit ensureWritableCodeZone.
cogMethod cmType = CMMethod ifTrue:
["For non-Newspeak there should be a one-to-one mapping between bytecoded and
cog methods. For Newspeak not necessarily, but only for anonymous accessors."
"Only reset the original method's header if it is referring to this CogMethod."
(coInterpreter rawHeaderOf: cogMethod methodObject) asInteger = cogMethod asInteger
ifTrue:
[coInterpreter rawHeaderOf: cogMethod methodObject put: cogMethod methodHeader.
NewspeakVM ifTrue:
[(objectRepresentation canPinObjects and: [cogMethod nextMethodOrIRCs > self zoneEnd]) ifTrue:
[objectRepresentation freeIRCs: cogMethod nextMethodOrIRCs]]]
ifFalse:
[self cCode: [self assert: (cogit noAssertMethodClassAssociationOf: cogMethod methodObject) = objectMemory nilObject]
inSmalltalk: [self assert: ((cogit noAssertMethodClassAssociationOf: cogMethod methodObject) = objectMemory nilObject
or: [coInterpreter isKindOf: CurrentImageCoInterpreterFacade])].
NewspeakVM ifTrue:
[self removeFromUnpairedMethodList: cogMethod]].
cogit maybeFreeCountersOf: cogMethod].
cogMethod cmType = CMOpenPIC ifTrue:
[self removeFromOpenPICList: cogMethod].
writableMethod := cogit writableMethodFor: cogMethod.
writableMethod cmRefersToYoung: false.
writableMethod cmType: CMFree.
methodBytesFreedSinceLastCompaction := methodBytesFreedSinceLastCompaction
+ cogMethod blockSize!
Item was changed:
----- Method: CogMethodZone>>pruneYoungReferrers (in category 'young referers') -----
pruneYoungReferrers
| source dest next |
<api>
<var: #source type: #usqInt>
<var: #dest type: #usqInt>
<var: #next type: #usqInt>
<inline: false>
self assert: youngReferrers <= limitAddress.
youngReferrers = limitAddress ifTrue:
[^nil].
dest := limitAddress.
[next := dest - objectMemory wordSize.
next >= youngReferrers
and: [(coInterpreter cCoerceSimple: (objectMemory longAt: next) to: #'CogMethod *') cmRefersToYoung]] whileTrue:
[dest := next].
self assert: dest >= youngReferrers.
source := dest - objectMemory wordSize.
[source >= youngReferrers] whileTrue:
[(coInterpreter cCoerceSimple: (objectMemory longAt: source) to: #'CogMethod *') cmRefersToYoung ifTrue:
[self assert: source < (dest - objectMemory wordSize).
+ next ifNotNil: "convenient first-time flag"
+ [next := nil.
+ cogit ensureWritableCodeZone].
cogit codeLongAt: (dest := dest - objectMemory wordSize) put: (objectMemory longAt: source)].
source := source - objectMemory wordSize].
youngReferrers := dest.
self assert: self kosherYoungReferrers!
Item was changed:
----- Method: CogObjectRepresentationFor32BitSpur>>markAndTraceCacheTagLiteral:in:atpc: (in category 'garbage collection') -----
markAndTraceCacheTagLiteral: literal in: cogMethodOrNil atpc: address
"Mark and trace a literal in an inline cache preceding address in cogMethodOrNil.
Answer if code was modified."
<var: #cogMethodOrNil type: #'CogMethod *'>
<var: #address type: #usqInt>
| objOop |
(self couldBeObject: literal) ifFalse:
[^false].
self assert: (objectMemory addressCouldBeObj: literal).
(objectMemory isForwarded: literal) ifFalse:
[objectMemory markAndTrace: literal.
^false].
objOop := objectMemory followForwarded: literal.
+ self setCodeModified.
cogit backEnd rewriteInlineCacheTag: objOop at: address.
self markAndTraceUpdatedLiteral: objOop in: cogMethodOrNil.
^true!
Item was changed:
----- Method: CogObjectRepresentationForSpur>>markAndTraceLiteral:in:atpc: (in category 'garbage collection') -----
markAndTraceLiteral: literal in: cogMethodOrNil atpc: address
"Mark and trace a literal in a machine code instruction preceding address in cogMethodOrNil.
Answer if code was modified."
<var: #cogMethodOrNil type: #'CogMethod *'>
<var: #address type: #usqInt>
| objOop |
(self couldBeObject: literal) ifFalse:
[^false].
self assert: (objectMemory addressCouldBeObj: literal).
(objectMemory isForwarded: literal) ifFalse:
[objectMemory markAndTrace: literal.
^false].
+ cogit setCodeModified.
objOop := objectMemory followForwarded: literal.
cogit backEnd storeLiteral: objOop beforeFollowingAddress: address.
self markAndTraceUpdatedLiteral: objOop in: cogMethodOrNil.
^true!
Item was changed:
----- Method: CogVMSimulator>>ceTraceLinkedSend: (in category 'debug support') -----
ceTraceLinkedSend: theReceiver
(sendCount := sendCount + 1) \\ 500 = 0 ifTrue:
[self changed: #byteCountText].
cogit printOnTrace ifTrue:
[transcript print: byteCount; nextPut: $/; print: sendCount; space].
- cogit assertCStackWellAligned.
super ceTraceLinkedSend: theReceiver.
^#continue!
Item was changed:
----- Method: Cogit>>cogOpenPICSelector:numArgs: (in category 'in-line cacheing') -----
cogOpenPICSelector: selector numArgs: numArgs
"Create an Open PIC. Temporarily create a direct call of ceSendFromOpenPIC:.
Should become a probe of the first-level method lookup cache followed by a
call of ceSendFromOpenPIC: if the probe fails."
<returnTypeC: #'CogMethod *'>
| startAddress codeSize mapSize end |
coInterpreter compilationBreakpoint: selector isMNUCase: false.
startAddress := methodZone allocate: openPICSize.
startAddress = 0 ifTrue:
[^self cCoerceSimple: InsufficientCodeSpace to: #'CogMethod *'].
methodLabel
address: startAddress;
dependent: nil.
"stack allocate the various collections so that they
are effectively garbage collected on return."
self allocateOpcodes: 100 bytecodes: 0.
self compileOpenPIC: selector numArgs: numArgs.
self computeMaximumSizes.
methodLabel concretizeAt: startAddress.
codeSize := self generateInstructionsAt: startAddress + (self sizeof: CogMethod).
+ self ensureWritableCodeZone. "other clients first use generateMapAt: for sizing; this is an exception"
mapSize := self generateMapAt: startAddress + openPICSize - 1 start: startAddress + cmNoCheckEntryOffset.
self assert: entry address - startAddress = cmEntryOffset.
self assert: (methodZone roundUpLength: (self sizeof: CogMethod) + codeSize) + (methodZone roundUpLength: mapSize) <= openPICSize.
end := self outputInstructionsAt: startAddress + (self sizeof: CogMethod).
self
fillInOPICHeader: (self writableMethodFor: startAddress)
numArgs: numArgs
selector: selector.
^self cCoerceSimple: startAddress to: #'CogMethod *'!
Item was changed:
----- Method: Cogit>>compileSendTrace (in category 'debugging') -----
compileSendTrace
+ "2 is trace sends; 256 is traceLinkedSends"
+ <inline: #always>
+ ^traceFlags allMask: 256!
- "2 is trace sends; 256+2 is traceLinkedSends, so one can trace just unlinked sends using 2"
- <cmacro: '() ((traceFlags & 258) == 258)'>
- ^traceFlags allMask: 256 + 2!
Item was changed:
----- Method: Cogit>>followForwardedLiteralsIn: (in category 'garbage collection') -----
followForwardedLiteralsIn: cogMethod
<api>
<option: #SpurObjectMemory>
<var: #cogMethod type: #'CogMethod *'>
| writableCogMethod hasYoungObj hasYoungObjPtr |
self assert: (cogMethod cmType ~= CMMethod or: [(objectMemory isForwarded: cogMethod methodObject) not]).
writableCogMethod := self writableMethodFor: cogMethod.
+ self ensureWritableCodeZone.
hasYoungObj := objectMemory isYoung: cogMethod methodObject.
(objectMemory shouldRemapOop: cogMethod selector) ifTrue:
[writableCogMethod selector: (objectMemory remapObj: cogMethod selector).
(objectMemory isYoung: cogMethod selector) ifTrue:
[hasYoungObj := true]].
hasYoungObjPtr := (self addressOf: hasYoungObj put: [:val| hasYoungObj := val]) asInteger.
self mapFor: cogMethod
performUntil: #remapIfObjectRef:pc:hasYoung:
arg: hasYoungObjPtr.
hasYoungObj
ifTrue: [methodZone ensureInYoungReferrers: cogMethod]
ifFalse: [writableCogMethod cmRefersToYoung: false].
self ensureExecutableCodeZone!
Item was changed:
----- Method: Cogit>>followForwardedMethods (in category 'garbage collection') -----
followForwardedMethods
<api>
<option: #SpurObjectMemory>
- <var: #cogMethod type: #'CogMethod *'>
| cogMethod freedPIC |
+ self ensureWritableCodeZone.
- <var: #cogMethod type: #'CogMethod *'>
freedPIC := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod ifTrue:
[(objectMemory isForwarded: cogMethod methodObject) ifTrue:
[cogMethod methodObject: (objectMemory followForwarded: cogMethod methodObject).
(objectMemory isYoungObject: cogMethod methodObject) ifTrue:
[methodZone ensureInYoungReferrers: cogMethod]]].
cogMethod cmType = CMClosedPIC ifTrue:
[(self followMethodReferencesInClosedPIC: cogMethod) ifTrue:
[freedPIC := true.
methodZone freeMethod: cogMethod]].
cogMethod := methodZone methodAfter: cogMethod].
freedPIC ifTrue:
+ [self unlinkSendsToFree].
+ self ensureExecutableCodeZone!
- [self unlinkSendsToFree]!
Item was changed:
----- Method: Cogit>>followMaybeObjRefInClosedPICAt: (in category 'garbage collection') -----
followMaybeObjRefInClosedPICAt: mcpc
"Follow a potential object reference from a closed PIC.
This may be a method reference or null.
Answer if the followed literal is young.
'mcpc' refers to the jump/branch instruction at the end of
each cpic case"
| object subject |
object := backEnd literalBeforeFollowingAddress: mcpc.
(objectRepresentation couldBeObject: object) ifFalse:
[^false].
(objectMemory isForwarded: object) ifFalse:
[^objectMemory isYoungObject: object].
subject := objectMemory followForwarded: object.
+ self setCodeModified.
backEnd storeLiteral: subject beforeFollowingAddress: mcpc.
- codeModified := true.
^objectMemory isYoungObject: subject!
Item was changed:
----- Method: Cogit>>followMovableLiteralsAndUpdateYoungReferrers (in category 'garbage collection') -----
followMovableLiteralsAndUpdateYoungReferrers
"To avoid runtime checks on literal variable and literal accesses in == and ~~,
we follow literals in methods having movable literals in the postBecome action.
To avoid scanning every method, we annotate cogMethods with the
cmHasMovableLiteral flag."
<option: #SpurObjectMemory>
<api>
<returnTypeC: #void>
| cogMethod |
<var: #cogMethod type: #'CogMethod *'>
self assert: methodZone kosherYoungReferrers.
"methodZone firstBogusYoungReferrer"
"methodZone occurrencesInYoungReferrers: methodZone firstBogusYoungReferrer"
codeModified := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType ~= CMFree ifTrue:
[cogMethod cmHasMovableLiteral ifTrue:
[self followForwardedLiteralsIn: cogMethod]].
cogMethod := methodZone methodAfter: cogMethod]..
methodZone pruneYoungReferrers.
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone freeStart].
+ "And ensure code zone is executable. May have pruned young referrers..."
+ self ensureExecutableCodeZone!
- [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone freeStart]!
Item was changed:
----- Method: Cogit>>freeCogMethod: (in category 'jit - api') -----
freeCogMethod: cogMethod
<api>
<var: #cogMethod type: #'CogMethod *'>
- self ensureWritableCodeZone.
methodZone freeMethod: cogMethod.
self ensureExecutableCodeZone!
Item was changed:
----- Method: Cogit>>mapObjectReferencesInGeneratedRuntime (in category 'garbage collection') -----
mapObjectReferencesInGeneratedRuntime
"Update all references to objects in the generated runtime."
0 to: runtimeObjectRefIndex - 1 do:
[:i| | mcpc literal mappedLiteral |
mcpc := objectReferencesInRuntime at: i.
literal := literalsManager fetchLiteralAtAnnotatedAddress: mcpc using: backEnd.
mappedLiteral := objectRepresentation remapObject: literal.
mappedLiteral ~= literal ifTrue:
+ [self setCodeModified.
+ literalsManager storeLiteral: mappedLiteral atAnnotatedAddress: mcpc using: backEnd]]!
- [literalsManager storeLiteral: mappedLiteral atAnnotatedAddress: mcpc using: backEnd.
- codeModified := true]]!
Item was changed:
----- Method: Cogit>>markAndTraceOrFreeCogMethod:firstVisit: (in category 'garbage collection') -----
markAndTraceOrFreeCogMethod: cogMethod firstVisit: firstVisit
"Mark and trace objects in the argument and free if it is appropriate.
Answer if the method has been freed. firstVisit is a hint used to avoid
scanning methods we've already seen. False positives are fine.
For a CMMethod this
frees if the bytecode method isnt marked,
marks and traces object literals and selectors,
unlinks sends to targets that should be freed.
For a CMClosedPIC this
frees if it refers to anything that should be freed or isn't marked.
For a CMOpenPIC this
frees if the selector isn't marked."
<var: #cogMethod type: #'CogMethod *'>
<inline: false> "this recurses at most one level down"
cogMethod cmType = CMFree ifTrue:
[^true].
self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0.
cogMethod cmType = CMMethod ifTrue:
[(objectMemory isMarked: cogMethod methodObject) ifFalse:
+ [self ensureWritableCodeZone.
+ methodZone freeMethod: cogMethod.
- [methodZone freeMethod: cogMethod.
^true].
firstVisit ifTrue:
[self markLiteralsAndUnlinkUnmarkedSendsIn: cogMethod].
^false].
cogMethod cmType = CMClosedPIC ifTrue:
[(self closedPICRefersToUnmarkedObject: cogMethod) ifFalse:
[^false].
+ self ensureWritableCodeZone.
methodZone freeMethod: cogMethod.
^true].
cogMethod cmType = CMOpenPIC ifTrue:
[(objectMemory isMarked: cogMethod selector) ifTrue:
[^false].
+ self ensureWritableCodeZone.
methodZone freeMethod: cogMethod.
^true].
self assert: (cogMethod cmType = CMMethod
or: [cogMethod cmType = CMClosedPIC
or: [cogMethod cmType = CMOpenPIC]]).
^false!
Item was changed:
----- Method: Cogit>>remapIfObjectRef:pc:hasYoung: (in category 'garbage collection') -----
remapIfObjectRef: annotation pc: mcpc hasYoung: hasYoungPtr
<var: #mcpc type: #'char *'>
<var: #targetMethod type: #'CogMethod *'>
<var: #nsSendCache type: #'NSSendCache *'>
annotation = IsObjectReference ifTrue:
[| literal mappedLiteral |
literal := literalsManager fetchLiteralAtAnnotatedAddress: mcpc asUnsignedInteger using: backEnd.
(objectRepresentation couldBeObject: literal) ifTrue:
[mappedLiteral := objectRepresentation remapObject: literal.
literal ~= mappedLiteral ifTrue:
+ [self setCodeModified.
+ literalsManager storeLiteral: mappedLiteral atAnnotatedAddress: mcpc asUnsignedInteger using: backEnd].
- [literalsManager storeLiteral: mappedLiteral atAnnotatedAddress: mcpc asUnsignedInteger using: backEnd.
- codeModified := true].
(hasYoungPtr ~= 0
and: [objectMemory isYoung: mappedLiteral]) ifTrue:
[(self cCoerceSimple: hasYoungPtr to: #'sqInt *') at: 0 put: true]]].
NewspeakVM ifTrue: [annotation = IsNSSendCall ifTrue:
[| nsSendCache oop mappedOop |
nsSendCache := self nsSendCacheFromReturnAddress: mcpc.
oop := nsSendCache selector.
mappedOop := objectRepresentation remapObject: oop.
oop ~= mappedOop ifTrue:
[nsSendCache selector: mappedOop.
(hasYoungPtr ~= 0 and: [objectMemory isYoung: mappedOop]) ifTrue:
[(self cCoerceSimple: hasYoungPtr to: #'sqInt *') at: 0 put: true]].
oop := nsSendCache enclosingObject.
oop ~= 0 ifTrue: [
mappedOop := objectRepresentation remapObject: oop.
oop ~= mappedOop ifTrue:
[nsSendCache enclosingObject: mappedOop.
(hasYoungPtr ~= 0 and: [objectMemory isYoung: mappedOop]) ifTrue:
[(self cCoerceSimple: hasYoungPtr to: #'sqInt *') at: 0 put: true]]].
^0 "keep scanning"]].
(self isPureSendAnnotation: annotation) ifTrue:
[self entryCacheTagAndCouldBeObjectAt: mcpc annotation: annotation into:
[:entryPoint :cacheTag :tagCouldBeObj | | mappedCacheTag |
(tagCouldBeObj
and: [objectRepresentation couldBeObject: cacheTag]) ifTrue:
[mappedCacheTag := objectRepresentation remapObject: cacheTag.
cacheTag ~= mappedCacheTag ifTrue:
+ [self setCodeModified.
+ backEnd rewriteInlineCacheTag: mappedCacheTag at: mcpc asUnsignedInteger].
- [backEnd rewriteInlineCacheTag: mappedCacheTag at: mcpc asUnsignedInteger.
- codeModified := true].
(hasYoungPtr ~= 0
and: [objectMemory isYoung: mappedCacheTag]) ifTrue:
[(self cCoerceSimple: hasYoungPtr to: #'sqInt *') at: 0 put: true]].
hasYoungPtr ~= 0 ifTrue:
["Since the unlinking routines may rewrite the cacheTag to the send's selector, and
since they don't have the cogMethod to hand and can't add it to youngReferrers,
the method must remain in youngReferrers if the targetMethod's selector is young."
entryPoint > methodZoneBase ifTrue: "It's a linked send."
[self targetMethodAndSendTableFor: entryPoint annotation: annotation into:
[:targetMethod :ignored|
(objectMemory isYoung: targetMethod selector) ifTrue:
[(self cCoerceSimple: hasYoungPtr to: #'sqInt *') at: 0 put: true]]]]]].
^0 "keep scanning"!
Item was changed:
----- Method: Cogit>>remapMaybeObjRefInClosedPICAt: (in category 'garbage collection') -----
remapMaybeObjRefInClosedPICAt: mcpc
"Remap a potential object reference from a closed PIC.
This may be an object reference, an inline cache tag or null.
Answer if the updated literal is young.
mcpc is the address of the next instruction following either
the load of the method literal or the compare of the class tag."
| object subject |
object := backEnd literalBeforeFollowingAddress: mcpc.
(objectRepresentation couldBeObject: object) ifFalse:
[^false].
subject := objectRepresentation remapOop: object.
object ~= subject ifTrue:
+ [self setCodeModified.
+ backEnd storeLiteral: subject beforeFollowingAddress: mcpc].
- [backEnd storeLiteral: subject beforeFollowingAddress: mcpc.
- codeModified := true].
^objectMemory isYoungObject: subject!
Item was added:
+ ----- Method: Cogit>>setCodeModified (in category 'memory access') -----
+ setCodeModified
+ "Three cases to avoid reading codeModified unless absolutely necessary. On some platforms run-time calls
+ may be required to enable execution and disable write-protect of the code zone. See the comment in ensureExecutableCodeZone."
+ <inline: #always>
+ self cppIf: #DUAL_MAPPED_CODE_ZONE
+ ifTrue: [codeModified := true]
+ ifFalse:
+ [backEnd needsCodeZoneExecuteWriteSwitch
+ ifTrue:
+ [codeModified ifFalse:
+ [codeModified := true.
+ backEnd makeCodeZoneWritable]]
+ ifFalse: [codeModified := true]]!
Item was changed:
----- Method: Cogit>>setInterpreter: (in category 'initialization') -----
setInterpreter: aCoInterpreter
"Initialization of the code generator in the simulator.
These objects already exist in the generated C VM
or are used only in the simulation."
<doNotGenerate>
coInterpreter := aCoInterpreter.
objectMemory := aCoInterpreter objectMemory.
methodZone := self class methodZoneClass new.
objectRepresentation := objectMemory objectRepresentationClass
forCogit: self methodZone: methodZone.
methodZone setInterpreter: aCoInterpreter
objectRepresentation: objectRepresentation
cogit: self.
generatorTable := self class generatorTable.
processor := ProcessorClass new.
simulatedAddresses := Dictionary new.
coInterpreter class clusteredVariableNames do:
[:cvn| self simulatedAddressFor: (cvn first = $C ifTrue: ['get', cvn] ifFalse: [cvn]) asSymbol].
simulatedTrampolines := Dictionary new.
simulatedVariableGetters := Dictionary new.
simulatedVariableSetters := Dictionary new.
traceStores := 0.
traceFlags := (InitializationOptions at: #linkedSendTrace ifAbsent: [false])
+ ifTrue: [257 "compileSendTrace + print"]
- ifTrue: [257]
ifFalse:
[(InitializationOptions at: #recordPrimTrace ifAbsent: [true])
ifTrue: [8] "record prim trace on by default (see Cogit class>>decareCVarsIn:)"
ifFalse: [0]].
singleStep := printRegisters := printInstructions := clickConfirm := false.
backEnd := CogCompilerClass for: self.
methodLabel := CogCompilerClass for: self.
(literalsManager := backEnd class literalsManagerClass new) cogit: self.
ordinarySendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
superSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
BytecodeSetHasDirectedSuperSend ifTrue:
[directedSuperSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
directedSuperBindingSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
directedSendUsesBinding := false].
NewspeakVM ifTrue:
[selfSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
dynamicSuperSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
implicitReceiverSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
outerSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines)].
"debug metadata"
objectReferencesInRuntime := CArrayAccessor on: (Array new: NumObjRefsInRuntime).
runtimeObjectRefIndex := 0.
"debug metadata"
trampolineAddresses := CArrayAccessor on: (Array new: NumTrampolines * 2).
trampolineTableIndex := 0.
extA := numExtB := extB := 0.
compilationTrace ifNil: [compilationTrace := self class initializationOptions at: #compilationTrace ifAbsent: [0]].
debugOpcodeIndices := self class initializationOptions at: #debugOpcodeIndices ifAbsent: [Set new].
debugBytecodePointers := self class initializationOptions at: #debugBytecodePointers ifAbsent: [Set new].
self class initializationOptions at: #breakPC ifPresent: [:pc| breakPC := pc]!
Item was changed:
----- Method: Cogit>>shortcutTrampoline:to: (in category 'simulation only') -----
shortcutTrampoline: aProcessorSimulationTrap to: aBlock
<doNotGenerate>
"As a simulation performance hack for debugging trampolines such as ceTraceLinkedSend: allow the entire
trampoline to be implemented by this method instead of simulating all of the code for the trampoline."
+ | stsp |
+ stsp := processor registerAt: SPReg put: (processor registerAt: SPReg) - backEnd class wordSize.
+ "when the processor simulators are fixed to call the illegal address, and push the retpc on the stack, this will change to only push if there is a link reg."
+ objectMemory
+ longAt: stsp
+ put: (backEnd hasLinkRegister ifTrue: [processor lr] ifFalse: [aProcessorSimulationTrap nextpc]).
- backEnd hasLinkRegister ifTrue:
- [processor pushWord: processor lr in: coInterpreter memory].
- processor
- simulateLeafCallOf: aProcessorSimulationTrap address
- nextpc: aProcessorSimulationTrap nextpc
- memory: coInterpreter memory.
coInterpreter
+ stackPointer: (processor registerAt: SPReg);
- stackPointer: processor sp;
framePointer: processor fp.
- processor
- sp: coInterpreter getCStackPointer;
- fp: coInterpreter getCFramePointer.
aBlock value.
processor
+ registerAt: SPReg put: (processor registerAt: SPReg) + backEnd class wordSize;
+ pc: aProcessorSimulationTrap nextpc!
- sp: coInterpreter stackPointer;
- fp: coInterpreter framePointer;
- simulateLeafReturnIn: coInterpreter memory.
- backEnd hasLinkRegister ifTrue:
- [processor lr: (processor popWordIn: coInterpreter memory)]!
Item was changed:
----- Method: Cogit>>unlinkSendAt:targetMethod:sendTable: (in category 'in-line cacheing') -----
unlinkSendAt: mcpc targetMethod: targetMethod sendTable: sendTable
<inline: true>
| unlinkedRoutine |
unlinkedRoutine := sendTable at: (targetMethod cmNumArgs min: NumSendTrampolines - 1).
+ self setCodeModified.
backEnd
rewriteInlineCacheAt: mcpc asInteger
tag: (backEnd inlineCacheValueForSelector: targetMethod selector in: enumeratingCogMethod)
+ target: unlinkedRoutine!
- target: unlinkedRoutine.
- codeModified := true!
Item was changed:
----- Method: Cogit>>unlinkSendsOf:isMNUSelector: (in category 'jit - api') -----
unlinkSendsOf: selector isMNUSelector: isMNUSelector
<api>
"Unlink all sends in cog methods. Free all Closed PICs with the selector,
or with an MNU case if isMNUSelector. First check if any method actually
has the selector; if not there can't be any linked send to it. This routine
(including descendents) is performance critical. It contributes perhaps
30% of entire execution time in Compiler recompileAll."
| cogMethod mustScanAndUnlink |
<var: #cogMethod type: #'CogMethod *'>
methodZoneBase ifNil: [^self].
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
mustScanAndUnlink := false.
isMNUSelector
ifTrue:
[[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType ~= CMFree ifTrue:
[cogMethod cpicHasMNUCase
ifTrue:
[self assert: cogMethod cmType = CMClosedPIC.
methodZone freeMethod: cogMethod.
mustScanAndUnlink := true]
ifFalse:
[cogMethod selector = selector ifTrue:
[mustScanAndUnlink := true.
cogMethod cmType = CMClosedPIC ifTrue:
[methodZone freeMethod: cogMethod]]]].
cogMethod := methodZone methodAfter: cogMethod]]
ifFalse:
[[cogMethod < methodZone limitZony] whileTrue:
[(cogMethod cmType ~= CMFree
and: [cogMethod selector = selector]) ifTrue:
[mustScanAndUnlink := true.
cogMethod cmType = CMClosedPIC ifTrue:
[methodZone freeMethod: cogMethod]].
cogMethod := methodZone methodAfter: cogMethod]].
mustScanAndUnlink ifFalse:
[^self].
codeModified := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod ifTrue:
[self mapFor: cogMethod
performUntil: #unlinkIfFreeOrLinkedSend:pc:of:
arg: selector].
cogMethod := methodZone methodAfter: cogMethod].
codeModified ifTrue: "After possibly updating inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart].
+ "And ensure code zone is executable. May merely have freed methods..."
+ self ensureExecutableCodeZone!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
Item was changed:
----- Method: Cogit>>unlinkSendsTo:andFreeIf: (in category 'jit - api') -----
unlinkSendsTo: targetMethodObject andFreeIf: freeIfTrue
<api>
"Unlink all sends in cog methods to a particular target method.
If targetMethodObject isn't actually a method (perhaps being
used via invokeAsMethod) then there's nothing to do."
| cogMethod targetMethod freedPIC |
<var: #cogMethod type: #'CogMethod *'>
<var: #targetMethod type: #'CogMethod *'>
((objectMemory isOopCompiledMethod: targetMethodObject)
and: [coInterpreter methodHasCogMethod: targetMethodObject]) ifFalse:
[^self].
targetMethod := coInterpreter cogMethodOf: targetMethodObject.
methodZoneBase ifNil: [^self].
+ self ensureWritableCodeZone.
codeModified := freedPIC := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod
ifTrue:
[self mapFor: cogMethod
performUntil: #unlinkIfLinkedSend:pc:to:
arg: targetMethod asInteger]
ifFalse:
[(cogMethod cmType = CMClosedPIC
and: [self cPIC: cogMethod HasTarget: targetMethod]) ifTrue:
[methodZone freeMethod: cogMethod.
freedPIC := true]].
cogMethod := methodZone methodAfter: cogMethod].
freeIfTrue ifTrue: [self freeMethod: targetMethod].
freedPIC
ifTrue: [self unlinkSendsToFree]
ifFalse:
[codeModified ifTrue: "After possibly updating inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]].
+ self ensureExecutableCodeZone!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]]!
`build.win32x86/HowToBuild` describes how we can build the VM using cygwin. However, I have installed WSL instead. When running `mvm`, it gives me:
```
../../platforms/win32/vm/sqWin32Utils.c:11:10: fatal error: Windows.h: No such file or directory
```
What do I have to do to enable mingw to find these header files? Any help is appreciated.
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/OpenSmalltalk/opensmalltalk-vm/issues/510
Eliot Miranda uploaded a new version of VMMaker to project VM Maker Inbox:
http://source.squeak.org/VMMakerInbox/VMMaker.oscog-eem.2765.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2765
Author: eem
Time: 23 June 2020, 6:41:19.556484 pm
UUID: 91b9976e-60b9-4b5c-a2f8-b621dd454a07
Ancestors: VMMaker.oscog-eem.2764
Spur: Rewrite the revised followForwardedObjectFields:toDepth: soi it can be correctly inlined.
Slang: Change the order of application of ensureConditionalAssignmentsAreTransformedIn: so it is always the last thing done in tryToInlineMethodsIn:.
Straight-forward optimization of bindVariablesIn: in the common case of methods being inlined swith unchanged parameters.
=============== Diff against VMMaker.oscog-eem.2764 ===============
Item was changed:
----- Method: Spur32BitCoMemoryManager>>followForwardedObjectFields:toDepth: (in category 'as yet unclassified') -----
followForwardedObjectFields: objOop toDepth: depth
"Follow pointers in the object to depth.
Answer if any forwarders were found.
How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no need since depth is always finite."
<api>
<inline: false>
+ | found fmt limit |
- | oop found fmt |
found := false.
self assert: ((self isPointers: objOop) or: [self isOopCompiledMethod: objOop]).
fmt := self formatOf: objOop.
+ limit := (self numPointerSlotsOf: objOop format: fmt) - 1.
"It is essential to skip the first field of a method because it may be a
reference to a Cog method in the method zone, not a real object at all."
((self isCompiledMethodFormat: fmt)
ifTrue: [1]
ifFalse: [0])
+ to: limit
+ do: [:i| | oop |
- to: (self numPointerSlotsOf: objOop format: fmt) - 1
- do: [:i|
oop := self fetchPointer: i ofObject: objOop.
(self isNonImmediate: oop) ifTrue:
[(self isForwarded: oop) ifTrue:
[found := true.
oop := self followForwarded: oop.
self storePointer: i ofObject: objOop withValue: oop].
(depth > 0
and: [(self hasPointerFields: oop)
and: [self followForwardedObjectFields: oop toDepth: depth - 1]]) ifTrue:
[found := true]]].
^found!
Item was changed:
----- Method: Spur64BitCoMemoryManager>>followForwardedObjectFields:toDepth: (in category 'forwarding') -----
followForwardedObjectFields: objOop toDepth: depth
"Follow pointers in the object to depth.
Answer if any forwarders were found.
How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no need since depth is always finite."
<api>
<inline: false>
+ | found fmt limit |
- | oop found fmt |
found := false.
self assert: ((self isPointers: objOop) or: [self isOopCompiledMethod: objOop]).
fmt := self formatOf: objOop.
+ limit := (self numPointerSlotsOf: objOop format: fmt) - 1.
"It is essential to skip the first field of a method because it may be a
reference to a Cog method in the method zone, not a real object at all."
((self isCompiledMethodFormat: fmt)
ifTrue: [1]
ifFalse: [0])
+ to: limit
+ do: [:i| | oop |
- to: (self numPointerSlotsOf: objOop format: fmt) - 1
- do: [:i|
oop := self fetchPointer: i ofObject: objOop.
(self isNonImmediate: oop) ifTrue:
[(self isForwarded: oop) ifTrue:
[found := true.
oop := self followForwarded: oop.
self storePointer: i ofObject: objOop withValue: oop].
(depth > 0
and: [(self hasPointerFields: oop)
and: [self followForwardedObjectFields: oop toDepth: depth - 1]]) ifTrue:
[found := true]]].
^found!
Item was changed:
----- Method: TMethod>>exitVar:label:in: (in category 'inlining') -----
exitVar: exitVar label: exitLabel in: aCodeGen
"Replace each return statement in this method with an assignment to the
exit variable followed by either a return or a goto to the given label.
Answer if a goto was generated."
"Optimization: If exitVar is nil, the return value of the inlined method is not being used, so don't add the assignment statement."
| labelUsed map elisions eliminateReturnSelfs |
labelUsed := false.
map := Dictionary new.
elisions := Set new.
"Conceivably one might ^self from a struct class and mean it. In most cases though
^self means `get me outta here, fast'. So unless this method is from a VMStruct class,
elide any ^self's"
eliminateReturnSelfs := ((definingClass inheritsFrom: VMClass) and: [definingClass isStructClass]) not
and: [returnType = #void or: [returnType = #sqInt]].
parseTree nodesDo:
[:node | | replacement |
node isReturn ifTrue:
[self transformReturnSubExpression: node
toAssignmentOf: exitVar
andGoto: exitLabel
unless: eliminateReturnSelfs
into: [:rep :labelWasUsed|
replacement := rep.
labelWasUsed ifTrue: [labelUsed := true]]
in: aCodeGen.
"replaceNodesIn: is strictly top-down, so any replacement for ^expr ifTrue: [...^fu...] ifFalse: [...^bar...]
will prevent replacement of either ^fu or ^bar. The corollary is that ^expr ifTrue: [foo] ifFalse: [^bar]
must be transformed into expr ifTrue: [^foo] ifFalse: [^bar]"
(node expression isConditionalSend
and: [node expression hasExplicitReturn])
ifTrue:
[elisions add: node.
(node expression args reject: [:arg| arg endsWithReturn]) do:
[:nodeNeedingReturn|
self transformReturnSubExpression: nodeNeedingReturn statements last
toAssignmentOf: exitVar
andGoto: exitLabel
unless: eliminateReturnSelfs
into: [:rep :labelWasUsed|
replacement := rep.
+ labelWasUsed ifTrue: [labelUsed := true]]
+ in: aCodeGen.
- labelWasUsed ifTrue: [labelUsed := true]].
map
at: nodeNeedingReturn statements last
put: replacement]]
ifFalse:
[map
at: node
put: (replacement ifNil:
[TLabeledCommentNode new setComment: 'return ', node expression printString])]]].
map isEmpty ifTrue:
[self deny: labelUsed.
^false].
"Now do a top-down replacement for all returns that should be mapped to assignments and gotos"
parseTree replaceNodesIn: map.
"Now it is safe to eliminate the returning ifs..."
elisions isEmpty ifFalse:
[| elisionMap |
elisionMap := Dictionary new.
elisions do: [:returnNode| elisionMap at: returnNode put: returnNode expression].
parseTree replaceNodesIn: elisionMap].
"Now flatten any new statement lists..."
parseTree nodesDo:
[:node| | list |
(node isStmtList
and: [node statements notEmpty
and: [node statements last isStmtList]]) ifTrue:
[list := node statements last statements.
node statements removeLast; addAllLast: list]].
^labelUsed!
Item was changed:
----- Method: TMethod>>inlineFunctionCall:in: (in category 'inlining') -----
inlineFunctionCall: aSendNode in: aCodeGen
"Answer the body of the called function, substituting the actual
parameters for the formal argument variables in the method body.
Assume caller has established that:
1. the method arguments are all substitutable nodes, and
2. the method to be inlined contains no additional embedded returns."
| sel meth doNotRename argsForInlining substitutionDict |
+ aCodeGen maybeBreakForInlineOf: aSendNode in: self.
sel := aSendNode selector.
meth := (aCodeGen methodNamed: sel) copy.
meth ifNil:
[^self inlineBuiltin: aSendNode in: aCodeGen].
doNotRename := Set withAll: args.
argsForInlining := aSendNode argumentsForInliningCodeGenerator: aCodeGen.
meth args with: argsForInlining do:
[ :argName :exprNode |
exprNode isLeaf ifTrue:
[doNotRename add: argName]].
(meth statements size = 2
and: [meth statements first isSend
and: [meth statements first selector == #flag:]]) ifTrue:
[meth statements removeFirst].
meth renameVarsForInliningInto: self except: doNotRename in: aCodeGen.
meth renameLabelsForInliningInto: self.
self addVarsDeclarationsAndLabelsOf: meth except: doNotRename.
substitutionDict := Dictionary new: meth args size * 2.
meth args with: argsForInlining do:
[ :argName :exprNode |
+ (exprNode isVariable and: [exprNode name = argName]) ifFalse:
+ [substitutionDict at: argName put: exprNode].
- substitutionDict at: argName put: exprNode.
(doNotRename includes: argName) ifFalse:
[locals remove: argName]].
meth parseTree bindVariablesIn: substitutionDict.
^meth parseTree endsWithReturn
ifTrue: [meth parseTree copyWithoutReturn]
ifFalse: [meth parseTree]!
Item was changed:
----- Method: TMethod>>tryToInlineMethodsIn: (in category 'inlining') -----
tryToInlineMethodsIn: aCodeGen
"Expand any (complete) inline methods sent by this method.
Set the complete flag when all inlining has been done.
Answer if something was inlined."
| didSomething statementLists |
"complete ifTrue:
[^false]."
self definedAsMacro ifTrue:
[complete ifTrue:
[^false].
^complete := true].
- self ensureConditionalAssignmentsAreTransformedIn: aCodeGen.
didSomething := self tryToInlineMethodStatementsIn: aCodeGen statementListsInto: [:stmtLists| statementLists := stmtLists].
didSomething := (self tryToInlineMethodExpressionsIn: aCodeGen) or: [didSomething].
+ self ensureConditionalAssignmentsAreTransformedIn: aCodeGen.
didSomething ifTrue:
[writtenToGlobalVarsCache := nil].
complete ifFalse:
[self checkForCompletenessIn: aCodeGen.
complete ifTrue: [didSomething := true]]. "marking a method complete is progress"
^didSomething!
Item was changed:
----- Method: TStmtListNode>>bindVariablesIn: (in category 'transformations') -----
bindVariablesIn: aDictionary
+ aDictionary notEmpty ifTrue:
+ [statements := statements collect: [:s| s bindVariablesIn: aDictionary]]!
- statements := statements collect: [ :s | s bindVariablesIn: aDictionary ].!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2982.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2982
Author: eem
Time: 8 July 2021, 11:13:08.369309 am
UUID: 0b4d8d13-650d-4001-9ddc-d2232c9eb605
Ancestors: VMMaker.oscog-eem.2981
Generate a variable order file of variables accessed by the JIT (potentially via VarBaseReg) to guide linkers in clustering these variables (close to the var base).
=============== Diff against VMMaker.oscog-eem.2981 ===============
Item was changed:
----- Method: CoInterpreter class>>clusteredVariableNames (in category 'translation') -----
clusteredVariableNames
"Insist that these variables are present early in the list of variables, and in this order,
so that e.g. they are conveniently accessed via the VarBaseReg if it is available."
^#(stackLimitFromMachineCode "ensures zero offset in simulation" stackLimit "stackLimit is e.g. lowest using the clang toolchain on MacOS X"
stackPointer framePointer CStackPointer CFramePointer CReturnAddress
scavengeThreshold freeStart needGCFlag specialObjectsOop
+ primFailCode primTraceLogIndex primitiveFunctionPointer
+ newMethod instructionPointer argumentCount nextProfileTick
+ nativeSP nativeStackPointer shadowCallStackPointer
+ primTraceLog "should be last because it is large") "reject: [:n| self allInstVarNames includes: n]"!
- primFailCode newMethod instructionPointer argumentCount nextProfileTick
- nativeSP nativeStackPointer shadowCallStackPointer)!
Item was added:
+ ----- Method: CoInterpreterMT class>>clusteredVariableNames (in category 'translation') -----
+ clusteredVariableNames
+ "Insist that these variables are present early in the list of variables, and in this order,
+ so that e.g. they are conveniently accessed via the VarBaseReg if it is available."
+ ^#(vmOwner), super clusteredVariableNames "reject: [:n| self allInstVarNames includes: n]"!
Item was changed:
----- Method: VMMaker>>generateInterpreterFile (in category 'generate sources') -----
generateInterpreterFile
"Translate the Smalltalk description of the virtual machine into C. If 'self doInlining' is true, small method bodies are inlined to reduce procedure call overhead. On the PPC, this results in a factor of three speedup with only 30% increase in code size. Subclasses can use specialised versions of CCodeGenerator and interpreterClass."
| cg vmHeaderContents |
cg := [self buildCodeGeneratorForInterpreter]
on: Notification
do: [:ex|
ex tag == #getVMMaker
ifTrue: [ex resume: self]
ifFalse: [(ex respondsTo: #rearmHandlerDuring:)
ifTrue: [ex rearmHandlerDuring: [ex pass]]
ifFalse: [ex pass]]].
self reinitializeWordSizeFrom: cg.
self interpreterClass additionalHeadersDo:
[:headerName :headerContents| | filePath |
filePath := self coreVMDirectory fullNameFor: headerName.
(cg needToGenerateHeader: headerName file: filePath contents: headerContents) ifTrue:
[cg storeHeaderOnFile: filePath contents: headerContents]].
self needsToRegenerateInterpreterFile ifFalse: [^nil].
cg inferTypesForImplicitlyTypedVariablesAndMethods.
self interpreterClass preGenerationHook: cg.
vmHeaderContents := cg vmHeaderContentsWithBytesPerWord: self wordSize.
(cg needToGenerateHeader: self interpreterHeaderName file: self interpreterHeaderPath contents: vmHeaderContents) ifTrue:
[cg storeHeaderOnFile: self interpreterHeaderPath contents: vmHeaderContents].
cg storeCodeOnFile: (self sourceFilePathFor: self interpreterClass sourceFileName) doInlining: self doInlining.
self interpreterClass apiExportHeaderName ifNotNil:
[cg storeAPIExportHeader: self interpreterClass apiExportHeaderName
OnFile: (self sourceFilePathFor: self interpreterClass apiExportHeaderName)].
(cg methodNamed: #interpret) ifNotNil:
+ [self gnuifyInterpreterFile].
+ self maybeGenerateVariableOrderFiles: cg!
- [self gnuifyInterpreterFile]!
Item was added:
+ ----- Method: VMMaker>>maybeGenerateVariableOrderFiles: (in category 'generate sources') -----
+ maybeGenerateVariableOrderFiles: cg
+ | order |
+ (self interpreterClass respondsTo: #clusteredVariableNames) ifFalse: [^self].
+ "First find the longest order. These files can contain false positives.
+ So e.g. it's preferrable to generate the list for CoInterpreterMT
+ instead of two lists, one each for CoInterpreterMT and CoInterpreter."
+ order := self interpreterClass allSubclasses
+ inject: self interpreterClass clusteredVariableNames
+ into: [:o :sc| sc clusteredVariableNames size > o size ifTrue: [sc clusteredVariableNames] ifFalse: [o]].
+ "now generate versions for the different symbol formats in use."
+ #('variable_order' '_variable_order')
+ with: #(nil $_)
+ do: [:file :prefix| | contents filePath |
+ contents := String streamContents:
+ [:s|
+ order do:
+ [:name| prefix ifNotNil: [s nextPut: prefix]. s nextPutAll: name; cr]].
+ filePath := self coreVMDirectory fullNameFor: file.
+ (cg needToGenerateHeader: file file: filePath contents: contents) ifTrue:
+ [(self class forceNewFileNamed: filePath)
+ nextPutAll: contents;
+ close]]!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2981.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2981
Author: eem
Time: 7 July 2021, 11:04:39.038464 pm
UUID: 4705bb90-a5ad-4096-98ac-5893176f0014
Ancestors: VMMaker.oscog-eem.2980
CogARMv8Compiler: provide two ways to detect features (determine cache parameters and atomic instruction availability).
Slang fixes (noMask et al shouldn't use bit and directly). Simplifications. Add __APPLE__ & __MACH__ to names defines at compile time.
=============== Diff against VMMaker.oscog-eem.2980 ===============
Item was changed:
----- Method: CCodeGenerator>>emitBuiltinConstructAsArgumentFor:on:level: (in category 'utilities') -----
emitBuiltinConstructAsArgumentFor: msgNode on: aStream level: level
"If the given selector is in the translation dictionary, translate it into a target code construct and return true. Otherwise, do nothing and return false."
- | action |
(self shouldGenerateAsInterpreterProxySend: msgNode) ifTrue:
[^false].
+ ^(asArgumentTranslationDict
+ at: msgNode selector
+ ifAbsent: [translationDict at: msgNode selector ifAbsent: nil])
+ ifNil: [false]
+ ifNotNil: [:action|
+ self perform: action with: msgNode with: aStream with: level.
+ true]!
- action := asArgumentTranslationDict
- at: msgNode selector
- ifAbsent: [translationDict at: msgNode selector ifAbsent: [ ^false ]].
- self perform: action with: msgNode with: aStream with: level.
- ^true!
Item was changed:
----- Method: CCodeGenerator>>emitBuiltinConstructFor:on:level: (in category 'utilities') -----
emitBuiltinConstructFor: msgNode on: aStream level: level
"If the given selector is in the translation dictionary, translate it into a target code construct and return true. Otherwise, do nothing and return false."
- | action |
(self shouldGenerateAsInterpreterProxySend: msgNode) ifTrue:
[^false].
+ ^(translationDict at: msgNode selector ifAbsent: nil)
+ ifNil: [false]
+ ifNotNil: [:action|
+ self perform: action with: msgNode with: aStream with: level.
+ true]!
- action := translationDict at: msgNode selector ifAbsent: [ ^false ].
- self perform: action with: msgNode with: aStream with: level.
- ^true!
Item was added:
+ ----- Method: CCodeGenerator>>generateAnyMask:on:indent: (in category 'C translation') -----
+ generateAnyMask: msgNode on: aStream indent: level
+ "Generate the C code for this message onto the given stream."
+
+ aStream nextPutAll: '(('.
+ self generateBitAnd: msgNode on: aStream indent: level.
+ aStream nextPutAll: ') !!= 0)'!
Item was changed:
----- Method: CCodeGenerator>>generateNoMask:on:indent: (in category 'C translation') -----
generateNoMask: msgNode on: aStream indent: level
"Generate the C code for this message onto the given stream."
+ aStream nextPutAll: '(!!('.
- aStream nextPut: $(.
self generateBitAnd: msgNode on: aStream indent: level.
+ aStream nextPutAll: '))'!
- aStream nextPutAll: ') == 0'!
Item was changed:
----- Method: CCodeGenerator>>initializeCTranslationDictionary (in category 'C translation support') -----
initializeCTranslationDictionary
"Initialize the dictionary mapping message names to actions for C code generation."
| pairs |
translationDict := Dictionary new: 200.
pairs := #(
#& #generateAnd:on:indent:
#| #generateOr:on:indent:
#abs #generateAbs:on:indent:
#and: #generateSequentialAnd:on:indent:
#or: #generateSequentialOr:on:indent:
#not #generateNot:on:indent:
#+ #generatePlus:on:indent:
#- #generateMinus:on:indent:
#negated #generateNegated:on:indent:
#* #generateTimes:on:indent:
#/ #generateDivide:on:indent:
#// #generateDivide:on:indent:
#\\ #generateModulo:on:indent:
#<< #generateShiftLeft:on:indent:
#>> #generateShiftRight:on:indent:
#>>> #generateSignedShiftRight:on:indent:
#, #generateComma:on:indent:
#min: #generateMin:on:indent:
#max: #generateMax:on:indent:
#between:and: #generateBetweenAnd:on:indent:
#bitAnd: #generateBitAnd:on:indent:
#bitOr: #generateBitOr:on:indent:
#bitXor: #generateBitXor:on:indent:
#bitShift: #generateBitShift:on:indent:
#signedBitShift: #generateSignedBitShift:on:indent:
#bitInvert32 #generateBitInvert:on:indent:
#bitInvert64 #generateBitInvert:on:indent:
#bitClear: #generateBitClear:on:indent:
#truncateTo: #generateTruncateTo:on:indent:
#rounded #generateRounded:on:indent:
#even #generateEven:on:indent:
#odd #generateOdd:on:indent:
#byteSwap32 #generateByteSwap32:on:indent:
#byteSwap64 #generateByteSwap64:on:indent:
#byteSwapped32IfBigEndian: generateByteSwap32IfBigEndian:on:indent:
#byteSwapped64IfBigEndian: generateByteSwap64IfBigEndian:on:indent:
#< #generateLessThan:on:indent:
#<= #generateLessThanOrEqual:on:indent:
#= #generateEqual:on:indent:
#> #generateGreaterThan:on:indent:
#>= #generateGreaterThanOrEqual:on:indent:
#~= #generateNotEqual:on:indent:
#== #generateEqual:on:indent:
#~~ #generateNotEqual:on:indent:
#isNil #generateIsNil:on:indent:
#notNil #generateNotNil:on:indent:
#whileTrue: #generateWhileTrue:on:indent:
#whileFalse: #generateWhileFalse:on:indent:
#whileTrue #generateDoWhileTrue:on:indent:
#whileFalse #generateDoWhileFalse:on:indent:
#to:do: #generateToDo:on:indent:
#to:by:do: #generateToByDo:on:indent:
#repeat #generateRepeat:on:indent:
#timesRepeat: #generateTimesRepeat:on:indent:
#ifTrue: #generateIfTrue:on:indent:
#ifFalse: #generateIfFalse:on:indent:
#ifTrue:ifFalse: #generateIfTrueIfFalse:on:indent:
#ifFalse:ifTrue: #generateIfFalseIfTrue:on:indent:
#ifNotNil: #generateIfNotNil:on:indent:
#ifNil: #generateIfNil:on:indent:
#ifNotNil:ifNil: #generateIfNotNilIfNil:on:indent:
#ifNil:ifNotNil: #generateIfNilIfNotNil:on:indent:
#at: #generateAt:on:indent:
#at:put: #generateAtPut:on:indent:
#basicAt: #generateAt:on:indent:
#basicAt:put: #generateAtPut:on:indent:
#integerValueOf: #generateIntegerValueOf:on:indent:
#integerObjectOf: #generateIntegerObjectOf:on:indent:
#isIntegerObject: #generateIsIntegerObject:on:indent:
#cCode: #generateInlineCCode:on:indent:
#cCode:inSmalltalk: #generateInlineCCode:on:indent:
#cPreprocessorDirective: #generateInlineCPreprocessorDirective:on:indent:
#cppIf:ifTrue:ifFalse: #generateInlineCppIfElse:on:indent:
#cppIf:ifTrue:cppIf:ifTrue:ifFalse: #generateInlineCppIfElse:on:indent:
#cppIf:ifTrue: #generateInlineCppIfElse:on:indent:
#cppIf:ifFalse: #generateInlineCppIfElse:on:indent:
#cCoerce:to: #generateCCoercion:on:indent:
#cCoerceSimple:to: #generateCCoercion:on:indent:
#addressOf: #generateAddressOf:on:indent:
#addressOf:put: #generateAddressOf:on:indent:
#asAddress:put: #generateAsAddress:on:indent:
#signedIntFromLong64 #generateSignedIntFromLong64:on:indent:
#signedIntFromLong #generateSignedIntFromLong:on:indent:
#signedIntFromShort #generateSignedIntFromShort:on:indent:
#signedIntToLong64 #generateSignedIntToLong64:on:indent:
#signedIntToLong #generateSignedIntToLong:on:indent:
#signedIntToShort #generateSignedIntToShort:on:indent:
#preIncrement #generatePreIncrement:on:indent:
#preDecrement #generatePreDecrement:on:indent:
#inline: #generateInlineDirective:on:indent:
#asFloat #generateAsFloat:on:indent:
#asInteger #generateAsInteger:on:indent:
#asIntegerPtr #generateAsIntegerPtr:on:indent:
#asUnsignedInteger #generateAsUnsignedInteger:on:indent:
#asUnsignedIntegerPtr #generateAsUnsignedIntegerPtr:on:indent:
#asLong #generateAsLong:on:indent:
#asUnsignedLong #generateAsUnsignedLong:on:indent:
#asUnsignedLongLong #generateAsUnsignedLongLong:on:indent:
#asVoidPointer #generateAsVoidPointer:on:indent:
#asSymbol #generateAsSymbol:on:indent:
#flag: #generateFlag:on:indent:
+ #anyMask: #generateAnyMask:on:indent:
- #anyMask: #generateBitAnd:on:indent:
#allMask: #generateAllMask:on:indent:
#noMask: #generateNoMask:on:indent:
#raisedTo: #generateRaisedTo:on:indent:
#touch: #generateTouch:on:indent:
#bytesPerOop #generateBytesPerOop:on:indent:
#bytesPerWord #generateBytesPerWord:on:indent:
#wordSize #generateBytesPerWord:on:indent:
#baseHeaderSize #generateBaseHeaderSize:on:indent:
#minSmallInteger #generateSmallIntegerConstant:on:indent:
#maxSmallInteger #generateSmallIntegerConstant:on:indent:
#sharedCodeNamed:inCase: #generateSharedCodeDirective:on:indent:
#perform: #generatePerform:on:indent:
#perform:with: #generatePerform:on:indent:
#perform:with:with: #generatePerform:on:indent:
#perform:with:with:with: #generatePerform:on:indent:
#perform:with:with:with:with: #generatePerform:on:indent:
#perform:with:with:with:with:with: #generatePerform:on:indent:
#value #generateValue:on:indent:
#value: #generateValue:on:indent:
#value:value: #generateValue:on:indent:
#value:value:value: #generateValue:on:indent:
#value:value:value:value: #generateValue:on:indent:
#value:value:value:value:value: #generateValue:on:indent:
#value:value:value:value:value:value: #generateValue:on:indent:
#deny: #generateDeny:on:indent:
#shouldNotImplement #generateSmalltalkMetaError:on:indent:
#shouldBeImplemented #generateSmalltalkMetaError:on:indent:
#subclassResponsibility #generateSmalltalkMetaError:on:indent:
).
1 to: pairs size by: 2 do: [:i |
translationDict at: (pairs at: i) put: (pairs at: i + 1)].
pairs := #(
#ifTrue: #generateIfTrueAsArgument:on:indent:
#ifFalse: #generateIfFalseAsArgument:on:indent:
#ifTrue:ifFalse: #generateIfTrueIfFalseAsArgument:on:indent:
#ifFalse:ifTrue: #generateIfFalseIfTrueAsArgument:on:indent:
#ifNotNil: #generateIfNotNilAsArgument:on:indent:
#ifNil: #generateIfNilAsArgument:on:indent:
#ifNotNil:ifNil: #generateIfNotNilIfNilAsArgument:on:indent:
#ifNil:ifNotNil: #generateIfNilIfNotNilAsArgument:on:indent:
#cCode: #generateInlineCCodeAsArgument:on:indent:
#cCode:inSmalltalk: #generateInlineCCodeAsArgument:on:indent:
#cppIf:ifTrue:ifFalse: #generateInlineCppIfElseAsArgument:on:indent:
#cppIf:ifTrue: #generateInlineCppIfElseAsArgument:on:indent:
#value #generateValueAsArgument:on:indent:
#value: #generateValueAsArgument:on:indent:
#value:value: #generateValueAsArgument:on:indent:
).
asArgumentTranslationDict := Dictionary new: 8.
1 to: pairs size by: 2 do: [:i |
asArgumentTranslationDict at: (pairs at: i) put: (pairs at: i + 1)].
!
Item was changed:
----- Method: CCodeGenerator>>optionIsFalse:in: (in category 'utilities') -----
+ optionIsFalse: key in: aClass
+ "Answer whether a notOption: is false in the context of aClass. The key either a
+ Cogit class name or a class variable name or a variable name in VMBasicConstants."
- optionIsFalse: pragma in: aClass
- "Answer whether a notOption: pragma is false in the context of aClass.
- The argument to the option: pragma is interpreted as either a Cogit class name
- or a class variable name or a variable name in VMBasicConstants."
- | key |
- key := pragma argumentAt: 1.
- "If the option is one to be defined at compile time we'll generate a
- conditional around its declaration and definition."
- ((vmClass ifNil: [VMBasicConstants]) defineAtCompileTime: key) ifTrue:
- [^true].
-
"If the option is the name of a subclass of Cogit, include it if it dfoesn't inherit from the Cogit class."
(Smalltalk classNamed: key) ifNotNil:
[:optionClass|
aClass cogitClass ifNotNil:
[:cogitClass|
(optionClass includesBehavior: Cogit) ifTrue:
[^(cogitClass includesBehavior: optionClass) not]].
aClass objectMemoryClass ifNotNil:
[:objectMemoryClass|
((optionClass includesBehavior: ObjectMemory)
or: [optionClass includesBehavior: SpurMemoryManager]) ifTrue:
[^(objectMemoryClass includesBehavior: optionClass) not]]].
"Lookup options in options, class variables of the defining class, VMBasicConstants, the interpreterClass and the objectMemoryClass"
{aClass initializationOptions.
aClass.
VMBasicConstants.
aClass interpreterClass.
aClass objectMemoryClass} do:
[:scopeOrNil|
scopeOrNil ifNotNil:
[:scope|
(scope bindingOf: key) ifNotNil:
[:binding|
binding value ~~ true ifTrue: [^true]]]].
^false!
Item was changed:
----- Method: CCodeGenerator>>optionIsTrue:in: (in category 'utilities') -----
+ optionIsTrue: key in: aClass
+ "Answer whether an option: is true in the context of aClass. The key either a
+ Cogit class name or a class variable name or a variable name in VMBasicConstants."
- optionIsTrue: pragma in: aClass
- "Answer whether an option: or notOption: pragma is true in the context of aClass.
- The argument to the option: pragma is interpreted as either a Cogit class name
- or a class variable name or a variable name in VMBasicConstants."
- | key |
- key := pragma argumentAt: 1.
- "If the option is one to be defined at compile time we'll generate a
- conditional around its declaration and definition."
- ((vmClass ifNil: [VMBasicConstants]) defineAtCompileTime: key) ifTrue:
- [^true].
-
"If the option is the name of a subclass of Cogit, include it if it inherits from the Cogit class."
(Smalltalk classNamed: key) ifNotNil:
[:optionClass|
aClass cogitClass ifNotNil:
[:cogitClass|
(optionClass includesBehavior: Cogit) ifTrue:
[^cogitClass includesBehavior: optionClass]].
aClass objectMemoryClass ifNotNil:
[:objectMemoryClass|
((optionClass includesBehavior: ObjectMemory)
or: [optionClass includesBehavior: SpurMemoryManager]) ifTrue:
[^objectMemoryClass includesBehavior: optionClass]]].
"Lookup options in options, class variables of the defining class, VMBasicConstants, the interpreterClass and the objectMemoryClass"
{aClass initializationOptions.
aClass.
VMBasicConstants.
aClass interpreterClass.
aClass objectMemoryClass} do:
[:scopeOrNil|
scopeOrNil ifNotNil:
[:scope|
(scope bindingOf: key) ifNotNil:
[:binding|
binding value ~~ false ifTrue: [^true]]]].
^false!
Item was changed:
----- Method: CCodeGenerator>>shouldIncludeMethodFor:selector: (in category 'utilities') -----
shouldIncludeMethodFor: aClass selector: selector
+ "Answer whether a method should be translated. Process optional methods by
- "Answer whether a method shoud be translated. Process optional methods by
interpreting the argument to the option: pragma as either a Cogit class name
or a class variable name or a variable name in VMBasicConstants. Exclude
methods with the doNotGenerate pragma."
+ | options notOptions |
+
- | optionPragmas notOptionPragmas |
(aClass >> selector pragmaAt: #doNotGenerate) ifNotNil:
[^false].
"where is pragmasAt: ??"
+ options := (aClass >> selector) pragmas select: [:p| p keyword == #option:] thenCollect: [:p| p argumentAt: 1].
+ notOptions := (aClass >> selector) pragmas select: [:p| p keyword == #notOption:] thenCollect: [:p| p argumentAt: 1].
+ (options notEmpty or: [notOptions notEmpty]) ifTrue:
+ ["Anything defined at compile tiome must be included."
+ ((options anySatisfy: [:option| (vmClass ifNil: [VMBasicConstants]) defineAtCompileTime: option])
+ or: [notOptions anySatisfy: [:option| (vmClass ifNil: [VMBasicConstants]) defineAtCompileTime: option]]) ifTrue:
+ [^true].
+ "We have to include the method if either
- optionPragmas := (aClass >> selector) pragmas select: [:p| p keyword == #option:].
- notOptionPragmas := (aClass >> selector) pragmas select: [:p| p keyword == #notOption:].
- (optionPragmas notEmpty or: [notOptionPragmas notEmpty]) ifTrue:
- ["We have to include the method if either
- any one of the options is false (because we want #if option...)
- any one of the notOptions is true (because we want #if !!option...)
- all of the options is true and all of the notOptions are false (because they have all been satisfied)"
+ ^((options anySatisfy: [:option| (self optionIsTrue: option in: aClass) not])
+ and: [notOptions anySatisfy: [:option| (self optionIsFalse: option in: aClass) not]])
+ or: [(options allSatisfy: [:option| self optionIsTrue: option in: aClass])
+ and: [notOptions allSatisfy: [:option| self optionIsFalse: option in: aClass]]]].
- ^((optionPragmas anySatisfy: [:pragma| (self optionIsTrue: pragma in: aClass) not])
- and: [notOptionPragmas anySatisfy: [:pragma| (self optionIsFalse: pragma in: aClass) not]])
- or: [(optionPragmas allSatisfy: [:pragma| self optionIsTrue: pragma in: aClass])
- and: [notOptionPragmas allSatisfy: [:pragma| self optionIsFalse: pragma in: aClass]]]].
^true!
Item was changed:
CogAbstractInstruction subclass: #CogARMv8Compiler
instanceVariableNames: ''
+ classVariableNames: 'AL ArithmeticAdd ArithmeticAddS ArithmeticSub ArithmeticSubS CASAL CArg0Reg CArg1Reg CArg2Reg CArg3Reg CArg4Reg CArg5Reg CArg6Reg CBNZ CBZ CC CCMPNE CLREX CS CSET D0 D1 D10 D11 D12 D13 D14 D15 D16 D17 D18 D19 D2 D20 D21 D22 D23 D24 D25 D26 D27 D28 D29 D3 D30 D31 D4 D5 D6 D7 D8 D9 DC DC_CISW DC_CIVAC DC_CSW DC_CVAC DC_CVAU DC_ISW DC_IVAC DC_ZVA DMB DSB DSB_ALL DSB_ALLSY DSB_ISH DSB_NSH DSB_OSH DSB_READS DSB_SY DSB_WRITES DataCacheFlushRequired DataCacheLineLength DivRRR EQ FP GE GT HI HasAtomicInstructions IC IC_IALLU IC_IALLUIS IC_IVAU ISB InstructionCacheFlushRequired InstructionCacheLineLength LDAXR LE LR LS LT LogicalAnd LogicalAndS LogicalOr LogicalXor MI MRS_CTR_EL0 MRS_ID_AA64ISAR0_EL1 MSubRRR MoveAwRR MoveRRAw MulOverflowRRR MulRRR NE NativePopRR NativePushRR PL R0 R1 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R2 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 R3 R30 R31 R4 R5 R6 R7 R8 R9 SMULHRRR SP STLR STLXR SXTB SXTH SXTW SXTX UXTB UXTH UXTW UXTX VC VS XZR'
- classVariableNames: 'AL ArithmeticAdd ArithmeticAddS ArithmeticSub ArithmeticSubS CASAL CArg0Reg CArg1Reg CArg2Reg CArg3Reg CArg4Reg CArg5Reg CArg6Reg CBNZ CBZ CC CCMPNE CLREX CS CSET D0 D1 D10 D11 D12 D13 D14 D15 D16 D17 D18 D19 D2 D20 D21 D22 D23 D24 D25 D26 D27 D28 D29 D3 D30 D31 D4 D5 D6 D7 D8 D9 DC DC_CISW DC_CIVAC DC_CSW DC_CVAC DC_CVAU DC_ISW DC_IVAC DC_ZVA DMB DSB DSB_ALL DSB_ALLSY DSB_ISH DSB_NSH DSB_OSH DSB_READS DSB_SY DSB_WRITES DivRRR EQ FP GE GT HI IC IC_IALLU IC_IALLUIS IC_IVAU ISB LDAXR LE LR LS LT LogicalAnd LogicalAndS LogicalOr LogicalXor MI MRS_CTR_EL0 MRS_ID_AA64ISAR0_EL1 MSubRRR MoveAwRR MoveRRAw MulOverflowRRR MulRRR NE NativePopRR NativePushRR PL R0 R1 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R2 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 R3 R30 R31 R4 R5 R6 R7 R8 R9 SMULHRRR SP STLR STLXR SXTB SXTH SXTW SXTX UXTB UXTH UXTW UXTX VC VS XZR'
poolDictionaries: 'ARMv8A64Opcodes'
category: 'VMMaker-JIT'!
- CogARMv8Compiler class
- instanceVariableNames: 'ctrEl0 idISAR0'!
+ !CogARMv8Compiler commentStamp: 'eem 1/7/2021 23:01' prior: 0!
- !CogARMv8Compiler commentStamp: '' prior: 0!
I generate ARMv8 machine code instructions from CogAbstractInstructions with CogRTLOpcodes.
Here in "Arm ARM" refers to
Arm® Architecture Reference Manual
Armv8, for Armv8-A architecture profile
https://developer.arm.com/docs/ddi0487/latest/arm-architecture-reference-ma…
Some things to know about ARMv8 instructions:
Whether 31 in a register field implies the zero register or the SP register(s) depends on the specific instruction.
C3.2.1 Load/Store register
If a Load instruction specifies writeback and the register being loaded is also the base register,
then behavior is CONSTRAINED UNPREDICTABLE and one of the following behaviors must occur:
- The instruction is treated as UNDEFINED.
- The instruction is treated as a NOP.
- The instruction performs the load using the specified addressing mode and the base register
becomes UNKNOWN. In addition, if an exception occurs during the execution of such an
instruction, the base address might be corrupted so that the instruction cannot be repeated.
If a Store instruction performs a writeback and the register that is stored is also the base register,
then behavior is CONSTRAINED UNPREDICTABLE and one of the following behaviors must occur:
- The instruction is treated as UNDEFINED.
- The instruction is treated as a NOP.
- The instruction performs the store to the designated register using the specified addressing
mode, but the value stored is UNKNOWN.!
- CogARMv8Compiler class
- instanceVariableNames: 'ctrEl0 idISAR0'!
Item was removed:
- ----- Method: CogARMv8Compiler class>>ctrEl0 (in category 'accessing') -----
- ctrEl0
- ^ctrEl0!
Item was changed:
----- Method: CogARMv8Compiler class>>declareCVarsIn: (in category 'translation') -----
declareCVarsIn: aCCodeGenerator
+
+ #('hasAtomicInstructions' 'instructionCacheLineLength' 'instructionCacheFlushRequired' 'dataCacheLineLength' 'dataCacheFlushRequired') do:
+ [:varName|
+ aCCodeGenerator
+ declareVar: varName type: #'unsigned char';
+ removeConstant: varName capitalized]!
- aCCodeGenerator
- declareVar: 'ctrEl0' type: #usqIntptr_t;
- declareVar: 'idISAR0' type: #usqIntptr_t!
Item was added:
+ ----- Method: CogARMv8Compiler class>>extraClassVariableNames (in category 'class initialization') -----
+ extraClassVariableNames
+ "self extraClassVariableNames"
+ ^(organization listAtCategoryNamed: #'feature detection')
+ inject: Set new
+ into: [:them :selector|
+ (self >> selector) literalsDo:
+ [:l| (l isVariableBinding and: [classPool includesKey: l key]) ifTrue: [them add: l key]].
+ them]!
Item was removed:
- ----- Method: CogARMv8Compiler class>>idISAR0 (in category 'accessing') -----
- idISAR0
- ^idISAR0!
Item was added:
+ ----- Method: CogARMv8Compiler class>>printCTR_EL0: (in category 'debug printing') -----
+ printCTR_EL0: ctr_el0
+ <doNotGenerate>
+ ^String streamContents:
+ [:s| | fieldPrinter l1ip |
+ fieldPrinter := [:name :startBit| | field |
+ s nextPutAll: name; nextPutAll: ', ['; print: startBit + 3; nextPut: $,; print: startBit; nextPutAll: '] log2 words: '.
+ s print: (field := ctr_el0 >> startBit bitAnd: 15); nextPutAll: ' ('; print: 8 << field; nextPutAll: ' bytes)'; cr].
+ s
+ nextPutAll: 'DIC, bit [29] Instruction cache invalidation requirements for instruction to data coherence. The meaning of this bit is:';
+ crtab;
+ nextPutAll: ((ctr_el0 noMask: 1 << 29)
+ ifTrue: ['0b0 Instruction cache invalidation to the Point of Unification is required for instruction to data coherence.']
+ ifFalse: ['0b1 Instruction cache cleaning to the Point of Unification is not required for instruction to data coherence.']);
+ cr.
+ s
+ nextPutAll: 'IDC, bit [28] Data cache clean requirements for instruction to data coherence. The meaning of this bit is:';
+ crtab;
+ nextPutAll: ((ctr_el0 noMask: 1 << 28)
+ ifTrue: ['0b0 Data cache clean to the Point of Unification is required for instruction to data coherence, unless CLIDR.LoC == 0b000 or (CLIDR.LoUIS == 0b000 && CLIDR.LoUU == 0b000).']
+ ifFalse: ['0b1 Data cache clean to the Point of Unification is not required for instruction to data coherence.']);
+ cr.
+
+ fieldPrinter
+ value: 'Cache writeback granule' value: 24;
+ value: 'Exclusives reservation granule' value: 20;
+ value: 'DminLine' value: 16;
+ value: 'IminLine' value: 0.
+ s
+ nextPutAll: 'Level 1 instruction cache policy: '; print: (l1ip := ctr_el0 >> 14 bitAnd: 2); space;
+ nextPutAll: (#( 'VMID aware Physical Index, Physical tag (VPIPT)'
+ 'ASID-tagged Virtual Index, Virtual Tag (AIVIVT)'
+ 'Virtual Index, Physical Tag (VIPT)'
+ 'Physical Index, Physical Tag (PIPT)') at: l1ip + 1);
+ cr]!
Item was changed:
----- Method: CogARMv8Compiler class>>specificOpcodes (in category 'class initialization') -----
specificOpcodes
"Answer the processor-specific opcodes for this class.
They're all in an Array literal in the initialize method."
+ ^(self class >> #initialize) literals detect: [:l| l isArray and: [l includes: #MulRR]] ifNone: [#()]!
- ^(self class >> #initialize) literals detect: [:l| l isArray and: [l includes: #MulRR]]!
Item was removed:
- ----- Method: CogARMv8Compiler>>ctrEl0 (in category 'feature detection') -----
- ctrEl0
- <cmacro: '(ign) ctrEl0'>
- "For want of somewhere to put the variable..."
- ^self class ctrEl0!
Item was added:
+ ----- Method: CogARMv8Compiler>>dataCacheFlushRequired (in category 'feature detection') -----
+ dataCacheFlushRequired
+ <cmacro: '(ign) dataCacheFlushRequired'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ ^DataCacheFlushRequired!
Item was added:
+ ----- Method: CogARMv8Compiler>>dataCacheLineLength (in category 'feature detection') -----
+ dataCacheLineLength
+ <cmacro: '(ign) dataCacheLineLength'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ ^DataCacheLineLength!
Item was changed:
----- Method: CogARMv8Compiler>>detectFeatures (in category 'feature detection') -----
detectFeatures
+ <inline: #always>
+ self cppIf: #__APPLE__
+ ifTrue: [self detectFeaturesOnMacOS]
+ ifFalse: [self detectFeaturesOnLinux]!
- "Do throw-away compilations to read CTR_EL0 & ID_AA64ISAR0_EL1 and initialize ctrEl0 & idISAR0"
- | startAddress getFeatureReg |
- <var: 'getFeatureReg' declareC: 'usqIntptr_t (*getFeatureReg)(void)'>
- startAddress := cogit methodZoneBase.
- cogit allocateOpcodes: 4 bytecodes: 0.
- getFeatureReg := cogit cCoerceSimple: startAddress to: #'usqIntptr_t (*)(void)'.
- "Return the value of CTR_EL0; that's the control register that defines the vital statistics of the processor's caches."
- cogit
- gen: NOP; "do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable."
- gen: MRS_CTR_EL0 operand: ABIResultReg;
- RetN: 0.
- cogit outputInstructionsForGeneratedRuntimeAt: startAddress.
- cogit resetMethodZoneBase: startAddress.
- cogit ensureExecutableCodeZoneWithin:
- [self setCtrEl0: (self cCode: 'getFeatureReg()' inSmalltalk: [cogit simulateLeafCallOf: startAddress])].
- cogit zeroOpcodeIndexForNewOpcodes.
- cogit
- gen: NOP; "do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable."
- gen: MRS_ID_AA64ISAR0_EL1 operand: ABIResultReg;
- RetN: 0.
- cogit outputInstructionsForGeneratedRuntimeAt: startAddress.
- cogit resetMethodZoneBase: startAddress.
- cogit ensureExecutableCodeZoneWithin:
- [self setIdISAR0: (self cCode: 'getFeatureReg()' inSmalltalk: [cogit simulateLeafCallOf: startAddress])]!
Item was added:
+ ----- Method: CogARMv8Compiler>>detectFeaturesOnLinux (in category 'feature detection') -----
+ detectFeaturesOnLinux
+ "Do throw-away compilations to read CTR_EL0 & ID_AA64ISAR0_EL1 and initialize ctrEl0 & idISAR0"
+ <notOption: #__APPLE__>
+ | startAddress getFeatureReg ctrEL0 idISAR0 |
+ <var: 'getFeatureReg' declareC: 'usqIntptr_t (*getFeatureReg)(void)'>
+ startAddress := cogit methodZoneBase.
+ cogit allocateOpcodes: 4 bytecodes: 0.
+ getFeatureReg := cogit cCoerceSimple: startAddress to: #'usqIntptr_t (*)(void)'.
+ "Return the value of CTR_EL0; that's the control register that defines the vital statistics of the processor's caches."
+ cogit
+ gen: Nop; "do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable."
+ gen: MRS_CTR_EL0 operand: ABIResultReg;
+ RetN: 0.
+ cogit outputInstructionsForGeneratedRuntimeAt: startAddress.
+ cogit resetMethodZoneBase: startAddress.
+ cogit ensureExecutableCodeZoneWithin:
+ [ctrEL0 := (self cCode: 'getFeatureReg()' inSmalltalk: [cogit simulateLeafCallOf: startAddress]).
+ "see e.g. CogARMv8Compiler class>>printCTR_EL0:"
+ self setDataCacheFlushRequired: (ctrEL0 noMask: 1 << 28).
+ self setDataCacheLineLength: 4 << (ctrEL0 >> 16 bitAnd: 15).
+ self setInstructionCacheFlushRequired: (ctrEL0 noMask: 1 << 29).
+ self setInstructionCacheLineLength: 4 << (ctrEL0 bitAnd: 15)].
+ cogit zeroOpcodeIndexForNewOpcodes.
+ cogit
+ gen: Nop; "do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable."
+ gen: MRS_ID_AA64ISAR0_EL1 operand: ABIResultReg;
+ RetN: 0.
+ cogit outputInstructionsForGeneratedRuntimeAt: startAddress.
+ cogit resetMethodZoneBase: startAddress.
+ cogit ensureExecutableCodeZoneWithin:
+ [idISAR0 := (self cCode: 'getFeatureReg()' inSmalltalk: [cogit simulateLeafCallOf: startAddress]).
+ self setHasAtomicInstructions: (idISAR0 >> 20 bitAnd: 2r1111) = 2r10]!
Item was added:
+ ----- Method: CogARMv8Compiler>>detectFeaturesOnMacOS (in category 'memory access') -----
+ detectFeaturesOnMacOS
+ <option: #__APPLE__>
+ "MacOS does not allow access to ctl_el0, so derive cache information etc from sysctl"
+ "Here are values from sysctl(8), hardwired for now rather than derived through sysctl(3)
+ hw.cacheconfig: 8 1 1 0 0 0 0 0 0 0 (we speculate that the 1's indicate cache flush required)
+ hw.cachelinesize: 128
+ hw.l1icachesize: 131072
+ hw.l1dcachesize: 131072
+ hw.optional.neon: 1
+ hw.optional.neon_hpfp: 1
+ hw.optional.neon_fp16: 1
+ hw.optional.armv8_1_atomics: 1"
+
+ self setDataCacheLineLength: 128.
+ self setDataCacheFlushRequired: true.
+ self setInstructionCacheLineLength: 128.
+ self setInstructionCacheFlushRequired: true.
+ self setHasAtomicInstructions: true!
Item was changed:
----- Method: CogARMv8Compiler>>generateDCacheFlush (in category 'inline cacheing') -----
generateDCacheFlush
"Use the DC instruction to implement ceFlushDCache(void *start, void *end); see flushDCacheFrom:to:.
If there is a dual mapped zone then clean data via DC_CVAU as address + codeToDataDelta,
then invalidate data at address via CIVAC."
"D4.4.7 About cache maintenance in AArch64 state D4-2478
Terminology for Clean, Invalidate, and Clean and Invalidate instructions D4-2479
...
- For instructions operating by VA, the following conceptual points are defined: D4-2480
Point of Unification (PoU)
The PoU for a PE is the point by which the instruction and data caches and the translation table walks of that
PE are guaranteed to see the same copy of a memory location. In many cases, the Point of Unification is the
point in a uniprocessor memory system by which the instruction and data caches and the translation table
walks have merged.
The PoU for an Inner Shareable shareability domain is the point by which the instruction and data caches
and the translation table walks of all the PEs in that Inner Shareable shareability domain are guaranteed to
see the same copy of a memory location. Defining this point permits self-modifying software to ensure future
instruction fetches are associated with the modified version of the software by using the standard correctness
policy of:
1. Clean data cache entry by address.
2. Invalidate instruction cache entry by address.
Example code for cache maintenance instructions D4-2490 - D4-2491"
+ | dataCacheMinLineLength mask loop |
- | ctrEL0 dataCacheMinLineLength mask loop |
self assert: cogit getCodeToDataDelta ~= 0.
- ctrEL0 := self ctrEl0.
"See concretizeCacheControlOp1:CRm:Op2: &
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_…
DminLine & IminLine are Log2 words; 16 words miniumum"
+ (dataCacheMinLineLength := self dataCacheLineLength) = 0 ifTrue:
- (dataCacheMinLineLength := ctrEL0 >> 16 bitAnd: 15) = 0 ifTrue:
[dataCacheMinLineLength := 4].
dataCacheMinLineLength := 4 << dataCacheMinLineLength.
"Mask is large enough to encompass the method zone and has the correct minimum alignment."
mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - dataCacheMinLineLength.
"Since this is used from C code we must use only caller-saved registers.
C arg registers 2 & 3 are such a convenient pair of caller-saved registers."
cogit AndCq: mask R: CArg0Reg R: CArg2Reg. "CArg2Reg = aligned pointer to start of each data cache line"
cogit AddCq: cogit getCodeToDataDelta R: CArg2Reg R: CArg3Reg.
loop := cogit Label.
"see concretizeDataCacheControl"
cogit gen: DC operand: CArg3Reg operand: DC_CVAU. "clean (flush) address + codeToDataDelta"
cogit gen: DC operand: CArg2Reg operand: DC_CIVAC. "invalidate address"
cogit
AddCq: dataCacheMinLineLength R: CArg2Reg;
AddCq: dataCacheMinLineLength R: CArg3Reg;
CmpR: CArg1Reg R: CArg2Reg;
JumpBelowOrEqual: loop.
cogit RetN: 0!
Item was changed:
----- Method: CogARMv8Compiler>>generateICacheFlush (in category 'inline cacheing') -----
generateICacheFlush
"Use DC VAUC, DSB, IC IVAU, and ISB instructions to implement ceFlushICache(void *start, void *end); see flushICacheFrom:to:.
One might think that if there is a dual zone then data at address + codeToDataDelta must be cleaned,
but this isn't the case. All we need to do is clean data at address via DC VAUC and instructions via IC IVAU."
"B2.2.5 Concurrent modification and execution of instructions B2-112
...to avoid UNPREDICTABLE or CONSTRAINED UNPREDICTABLE behavior, instruction modifications must be explicitly synchronized before they are executed. The required synchronization is as follows:
1. No PE must be executing an instruction when another PE is modifying that instruction.
2. To ensure that the modified instructions are observable, a PE that is writing the instructions must issue the following sequence of instructions and operations:
; Coherency example for data and instruction accesses within the same Inner Shareable domain.
; enter this code with <Wt> containing a new 32-bit instruction, to be held in Cacheable space at a location pointed to by Xn.
STR Wt, [Xn]
DC CVAU, Xn ; Clean data cache by VA to point of unification (PoU)
DSB ISH ; Ensure visibility of the data cleaned from cache
IC IVAU, Xn ; Invalidate instruction cache by VA to PoU
DSB ISH
Note
- The DC CVAU operation is not required if the area of memory is either Non-cacheable or Write-Through Cacheable.
- If the contents of physical memory differ between the mappings, changing the mapping of VAs to PAs can cause
the instructions to be concurrently modified by one PE and executed by another PE. If the modifications affect
instructions other than those listed as being acceptable for modification, synchronization must be used to avoid
UNPREDICTABLE or CONSTRAINED UNPREDICTABLE behavior.
3. In a multiprocessor system, the IC IVAU is broadcast to all PEs within the Inner Shareable domain of the PE running this sequence.
However, when the modified instructions are observable, each PE that is executing the modified instructions must issue the following
instruction to ensure execution of the modified instructions:
ISB ; Synchronize fetched instruction stream"
"D4.4.7 About cache maintenance in AArch64 state D4-2478
Terminology for Clean, Invalidate, and Clean and Invalidate instructions D4-2479
...
- For instructions operating by VA, the following conceptual points are defined: D4-2480
Point of Unification (PoU)
The PoU for a PE is the point by which the instruction and data caches and the translation table walks of that
PE are guaranteed to see the same copy of a memory location. In many cases, the Point of Unification is the
point in a uniprocessor memory system by which the instruction and data caches and the translation table
walks have merged.
The PoU for an Inner Shareable shareability domain is the point by which the instruction and data caches
and the translation table walks of all the PEs in that Inner Shareable shareability domain are guaranteed to
see the same copy of a memory location. Defining this point permits self-modifying software to ensure future
instruction fetches are associated with the modified version of the software by using the standard correctness
policy of:
1. Clean data cache entry by address.
2. Invalidate instruction cache entry by address.
Example code for cache maintenance instructions D4-2490 - D4-2491"
+ | dataCacheMinLineLength instrCacheMinLineLength mask loop |
- | ctrEL0 dataCacheMinLineLength instrCacheMinLineLength mask loop |
- ctrEL0 := self ctrEl0.
"See concretizeCacheControlOp1:CRm:Op2: &
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_…"
+ self dataCacheFlushRequired ifTrue: "CTR_EL0.IDC is zero; must clean data cache to point of unification."
- (ctrEL0 noMask: 1 << 28) ifTrue: "CTR_EL0.IDC disabled; must clean data cache to point of unification."
["Since this is used from C code we must use only caller-saved registers.
C arg registers 2 & 3 are as such a convenient pair of caller-saved registers."
+ dataCacheMinLineLength := self dataCacheLineLength.
- dataCacheMinLineLength := 4 << (ctrEL0 >> 16 bitAnd: 15).
"Mask is large enough to encompass the method zone and has the correct minimum alignment."
mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - dataCacheMinLineLength.
cogit AndCq: mask R: CArg0Reg R: CArg2Reg. "CArg2Reg = aligned pointer to start of each data cache line"
loop := cogit Label.
"see concretizeDataCacheControl"
cogit
gen: DC operand: CArg2Reg operand: DC_CVAU; "clean (flush) address"
AddCq: dataCacheMinLineLength R: CArg2Reg;
CmpR: CArg1Reg R: CArg2Reg;
JumpBelowOrEqual: loop].
cogit gen: DSB operand: DSB_ISH operand: DSB_ALL. "Ensure visibility of the data cleaned from cache"
+ self instructionCacheFlushRequired ifTrue: "CTR_EL0.DIC is zero; must clean instruction cache to point of unification."
+ [instrCacheMinLineLength := self instructionCacheLineLength.
- (ctrEL0 noMask: 1 << 29) ifTrue: "CTR_EL0.DIC disabled; must clean instruction cache to point of unification."
- [instrCacheMinLineLength := 4 << (ctrEL0 bitAnd: 15).
"Mask is large enough to encompass the method zone and has the correct minimum alignment."
mask := 1 << (cogit coInterpreter highBit: cogit methodZone zoneEnd) - instrCacheMinLineLength.
cogit AndCq: mask R: CArg0Reg R: CArg2Reg. "CArg2Reg = aligned pointer to start of each data cache line"
loop := cogit Label.
"see concretizeDataCacheControl"
cogit
gen: IC operand: CArg2Reg operand: IC_IVAU; "clean (flush) address"
AddCq: instrCacheMinLineLength R: CArg2Reg;
CmpR: CArg1Reg R: CArg2Reg;
JumpBelowOrEqual: loop.
cogit gen: DSB operand: DSB_ISH operand: DSB_ALL].
cogit
gen: ISB;
RetN: 0!
Item was changed:
----- Method: CogARMv8Compiler>>hasAtomicInstructions (in category 'feature detection') -----
hasAtomicInstructions
+ <cmacro: '(ign) hasAtomicInstructions'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ ^HasAtomicInstructions!
- "D13.2.53 ID_AA64ISAR0_EL1, AArch64 Instruction Set Attribute Register 0 D13-3096
-
- The ID_AA64ISAR0_EL1 characteristics are:
- Purpose
- Provides information about the instructions implemented in AArch64 state.
- ...
- Atomic, bits [23:20]
- From ARMv8.1:
- Atomic instructions implemented in AArch64 state. Defined values are:
- 0b0000 No Atomic instructions implemented.
- 0b0010 LDADD, LDCLR, LDEOR, LDSET, LDSMAX, LDSMIN, LDUMAX, LDUMIN, CAS, CASP, and SWP instructions implemented.
- All other values are reserved.
- ARMv8.1-LSE implements the functionality identified by the value 0b0010.
- From Armv8.1, the only permitted value is 0b0010.
- Otherwise:
- Reserved, RES0."
-
- ^(self idISAR0 >> 20 bitAnd: 2r1111) = 2r10!
Item was removed:
- ----- Method: CogARMv8Compiler>>idISAR0 (in category 'feature detection') -----
- idISAR0
- <cmacro: '(ign) idISAR0'>
- "For want of somewhere to put the variable..."
- ^self class idISAR0!
Item was added:
+ ----- Method: CogARMv8Compiler>>instructionCacheFlushRequired (in category 'feature detection') -----
+ instructionCacheFlushRequired
+ <cmacro: '(ign) instructionCacheFlushRequired'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ ^InstructionCacheFlushRequired!
Item was added:
+ ----- Method: CogARMv8Compiler>>instructionCacheLineLength (in category 'feature detection') -----
+ instructionCacheLineLength
+ <cmacro: '(ign) instructionCacheLineLength'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ ^InstructionCacheLineLength!
Item was removed:
- ----- Method: CogARMv8Compiler>>printCTR_EL0 (in category 'feature detection') -----
- printCTR_EL0
- <doNotGenerate>
- ^String streamContents:
- [:s| | ctr_el0 fieldPrinter l1ip |
- ctr_el0 := self ctrEl0.
- fieldPrinter := [:name :startBit| | field |
- s nextPutAll: name; nextPutAll: ', ['; print: startBit + 3; nextPut: $,; print: startBit; nextPutAll: '] log2 words: '.
- s print: (field := ctr_el0 >> startBit bitAnd: 15); nextPutAll: ' ('; print: 8 << field; nextPutAll: ' bytes)'; cr].
- s
- nextPutAll: 'DIC, bit [29] Instruction cache invalidation requirements for instruction to data coherence. The meaning of this bit is:';
- crtab;
- nextPutAll: ((ctr_el0 noMask: 1 << 29)
- ifTrue: ['0b0 Instruction cache invalidation to the Point of Unification is required for instruction to data coherence.']
- ifFalse: ['0b1 Instruction cache cleaning to the Point of Unification is not required for instruction to data coherence.']);
- cr.
- s
- nextPutAll: 'IDC, bit [28] Data cache clean requirements for instruction to data coherence. The meaning of this bit is:';
- crtab;
- nextPutAll: ((ctr_el0 noMask: 1 << 28)
- ifTrue: ['0b0 Data cache clean to the Point of Unification is required for instruction to data coherence, unless CLIDR.LoC == 0b000 or (CLIDR.LoUIS == 0b000 && CLIDR.LoUU == 0b000).']
- ifFalse: ['0b1 Data cache clean to the Point of Unification is not required for instruction to data coherence.']);
- cr.
-
- fieldPrinter
- value: 'Cache writeback granule' value: 24;
- value: 'Exclusives reservation granule' value: 20;
- value: 'DminLine' value: 16;
- value: 'IminLine' value: 0.
- s
- nextPutAll: 'Level 1 instruction cache policy: '; print: (l1ip := ctr_el0 >> 14 bitAnd: 2); space;
- nextPutAll: (#( 'VMID aware Physical Index, Physical tag (VPIPT)'
- 'ASID-tagged Virtual Index, Virtual Tag (AIVIVT)'
- 'Virtual Index, Physical Tag (VIPT)'
- 'Physical Index, Physical Tag (PIPT)') at: l1ip + 1);
- cr]!
Item was removed:
- ----- Method: CogARMv8Compiler>>setCtrEl0: (in category 'feature detection') -----
- setCtrEl0: n
- <cmacro: '(ign,n) ctrEl0 = n'>
- "For want of somewhere to put the variable..."
- self class instVarNamed: 'ctrEl0' put: n!
Item was added:
+ ----- Method: CogARMv8Compiler>>setDataCacheFlushRequired: (in category 'feature detection') -----
+ setDataCacheFlushRequired: boolean
+ <cmacro: '(ign,b) dataCacheFlushRequired = b'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ DataCacheFlushRequired := boolean!
Item was added:
+ ----- Method: CogARMv8Compiler>>setDataCacheLineLength: (in category 'feature detection') -----
+ setDataCacheLineLength: n
+ <cmacro: '(ign,n) dataCacheLineLength = n'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ DataCacheLineLength := n!
Item was added:
+ ----- Method: CogARMv8Compiler>>setHasAtomicInstructions: (in category 'feature detection') -----
+ setHasAtomicInstructions: boolean
+ <cmacro: '(ign,b) hasAtomicInstructions = b'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ HasAtomicInstructions := boolean!
Item was removed:
- ----- Method: CogARMv8Compiler>>setIdISAR0: (in category 'feature detection') -----
- setIdISAR0: n
- <cmacro: '(ign,n) idISAR0 = n'>
- "For want of somewhere to put the variable..."
- self class instVarNamed: 'idISAR0' put: n!
Item was added:
+ ----- Method: CogARMv8Compiler>>setInstructionCacheFlushRequired: (in category 'feature detection') -----
+ setInstructionCacheFlushRequired: boolean
+ <cmacro: '(ign,b) instructionCacheFlushRequired = b'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ InstructionCacheFlushRequired := boolean!
Item was added:
+ ----- Method: CogARMv8Compiler>>setInstructionCacheLineLength: (in category 'feature detection') -----
+ setInstructionCacheLineLength: n
+ <cmacro: '(ign,n) instructionCacheLineLength = n'>
+ "For want of somewhere to put the variable that doesn't bloat an instance of the receiver..."
+ InstructionCacheLineLength := n!
Item was added:
+ ----- Method: CogAbstractInstruction class>>extraClassVariableNames (in category 'class initialization') -----
+ extraClassVariableNames
+ ^#()!
Item was changed:
----- Method: CogAbstractInstruction class>>initializeSpecificOpcodes:in: (in category 'class initialization') -----
initializeSpecificOpcodes: opcodeSymbolSequence in: initializeMethod
"Declare as class variables, the opcodes in opcodeSymbolSequence.
Assign values to them from LastRTLOpcode on. Undeclare any obsolete
class vars. The assumption is that initializeMethod defines all class vars
in the class. This method should be used by subclasses wishing to declare
their own specific opcodes."
+ ^self initializeSpecificOpcodes: opcodeSymbolSequence in: initializeMethod extraClassVarNames: self extraClassVariableNames!
- ^self initializeSpecificOpcodes: opcodeSymbolSequence in: initializeMethod extraClassVarNames: #()!
Item was changed:
CogClass subclass: #Cogit
(excessive size, no diff calculated)
Item was changed:
----- Method: Cogit class>>declareCVarsIn: (in category 'translation') -----
declareCVarsIn: aCCodeGenerator
| backEnd |
backEnd := CogCompilerClass basicNew.
#( 'coInterpreter' 'objectMemory' 'methodZone' 'objectRepresentation'
'cogBlockMethodSurrogateClass' 'cogMethodSurrogateClass' 'nsSendCacheSurrogateClass'
'threadManager' 'processor' 'lastNInstructions' 'simulatedAddresses'
'simulatedTrampolines' 'simulatedVariableGetters' 'simulatedVariableSetters'
'processorFrameValid' 'printRegisters' 'printInstructions' 'clickConfirm' 'singleStep') do:
[:simulationVariableNotNeededForRealVM|
aCCodeGenerator removeVariable: simulationVariableNotNeededForRealVM].
NewspeakVM ifFalse:
[#( 'selfSendTrampolines' 'dynamicSuperSendTrampolines'
'implicitReceiverSendTrampolines' 'outerSendTrampolines'
'ceEnclosingObjectTrampoline' 'numIRCs' 'indexOfIRC' 'theIRCs') do:
[:variableNotNeededInNormalVM|
aCCodeGenerator removeVariable: variableNotNeededInNormalVM]].
+ aCCodeGenerator
+ removeVariable: 'codeZoneIsExecutableNotWritable'; "these two are for simulation time assertion support"
+ removeVariable: 'debugAPISelector';
+ removeConstant: #COGMTVM. "this should be defined at compile time"
- aCCodeGenerator removeConstant: #COGMTVM. "this should be defined at compile time"
"N.B. We *do not* include sq.h; it pulls in conflicting definitions now that sqVirtualMachine.h
declares cointerp's functions, and declares some of them inaccurately for histrical reasons.
We pull in CoInterpreter's api via cointerp.h which is accurate."
aCCodeGenerator
addHeaderFile:'<stddef.h>'; "for e.g. offsetof"
addHeaderFile:'<stdio.h>';
addHeaderFile:'<stdlib.h>';
addHeaderFile:'<string.h>';
addHeaderFile:'"sqConfig.h"';
addHeaderFile:'"sqPlatformSpecific.h"'; "e.g. solaris overrides things for sqCogStackAlignment.h"
addHeaderFile:'"sqMemoryAccess.h"';
addHeaderFile:'"sqCogStackAlignment.h"';
addHeaderFile:'"dispdbg.h"'; "must precede cointerp.h & cogit.h otherwise NoDbgRegParms gets screwed up"
addHeaderFile:'"cogmethod.h"'.
NewspeakVM ifTrue:
[aCCodeGenerator addHeaderFile:'"nssendcache.h"'].
aCCodeGenerator
addHeaderFile:'#if COGMTVM';
addHeaderFile:'"cointerpmt.h"';
addHeaderFile:'#else';
addHeaderFile:'"cointerp.h"';
addHeaderFile:'#endif';
addHeaderFile:'"cogit.h"'.
aCCodeGenerator
var: #ceGetFP
declareC: 'usqIntptr_t (*ceGetFP)(void)';
var: #ceGetSP
declareC: 'usqIntptr_t (*ceGetSP)(void)';
var: #ceCaptureCStackPointers
declareC: 'void (*ceCaptureCStackPointers)(void)';
var: #ceInvokeInterpret
declareC: 'void (*ceInvokeInterpret)(void)';
var: #ceEnterCogCodePopReceiverReg
declareC: 'void (*ceEnterCogCodePopReceiverReg)(void)';
var: #realCEEnterCogCodePopReceiverReg
declareC: 'void (*realCEEnterCogCodePopReceiverReg)(void)';
var: #ceCallCogCodePopReceiverReg
declareC: 'void (*ceCallCogCodePopReceiverReg)(void)';
var: #realCECallCogCodePopReceiverReg
declareC: 'void (*realCECallCogCodePopReceiverReg)(void)';
var: #ceCallCogCodePopReceiverAndClassRegs
declareC: 'void (*ceCallCogCodePopReceiverAndClassRegs)(void)';
var: #realCECallCogCodePopReceiverAndClassRegs
declareC: 'void (*realCECallCogCodePopReceiverAndClassRegs)(void)';
var: #postCompileHook
declareC: 'void (*postCompileHook)(CogMethod *)';
var: #openPICList declareC: 'CogMethod *openPICList = 0';
var: #maxMethodBefore type: #'CogBlockMethod *';
var: 'enumeratingCogMethod' type: #'CogMethod *'.
aCCodeGenerator
var: #ceTryLockVMOwner
declareC: '#if COGMTVM\usqIntptr_t (*ceTryLockVMOwner)(usqIntptr_t)\#endif'.
backEnd numICacheFlushOpcodes > 0 ifTrue:
[aCCodeGenerator
var: #ceFlushICache
declareC: 'static void (*ceFlushICache)(usqIntptr_t from, usqIntptr_t to)'].
aCCodeGenerator
var: #ceFlushDCache
declareC: '#if DUAL_MAPPED_CODE_ZONE\static void (*ceFlushDCache)(usqIntptr_t from, usqIntptr_t to)\#endif';
var: #codeToDataDelta
declareC: '#if DUAL_MAPPED_CODE_ZONE\static sqInt codeToDataDelta\#else\# define codeToDataDelta 0\#endif'.
aCCodeGenerator
declareVar: 'aMethodLabel' type: #'AbstractInstruction'; "Has to come lexicographically before backEnd & methodLabel"
var: #backEnd declareC: 'AbstractInstruction * const backEnd = &aMethodLabel';
var: #methodLabel declareC: 'AbstractInstruction * const methodLabel = &aMethodLabel'.
self declareC: #(abstractOpcodes stackCheckLabel
blockEntryLabel blockEntryNoContextSwitch
stackOverflowCall sendMiss
entry noCheckEntry selfSendEntry dynSuperEntry
fullBlockNoContextSwitchEntry fullBlockEntry
picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 cPICEndOfCodeLabel)
as: #'AbstractInstruction *'
in: aCCodeGenerator.
aCCodeGenerator
declareVar: #cPICPrototype type: #'CogMethod *';
declareVar: #blockStarts type: #'BlockStart *';
declareVar: #fixups type: #'BytecodeFixup *';
declareVar: #methodZoneBase type: #usqInt.
aCCodeGenerator
var: #ordinarySendTrampolines
declareC: 'sqInt ordinarySendTrampolines[NumSendTrampolines]';
var: #superSendTrampolines
declareC: 'sqInt superSendTrampolines[NumSendTrampolines]'.
BytecodeSetHasDirectedSuperSend ifTrue:
[aCCodeGenerator
var: #directedSuperSendTrampolines
declareC: 'sqInt directedSuperSendTrampolines[NumSendTrampolines]';
var: #directedSuperBindingSendTrampolines
declareC: 'sqInt directedSuperBindingSendTrampolines[NumSendTrampolines]'].
NewspeakVM ifTrue:
[aCCodeGenerator
var: #selfSendTrampolines
declareC: 'sqInt selfSendTrampolines[NumSendTrampolines]';
var: #dynamicSuperSendTrampolines
declareC: 'sqInt dynamicSuperSendTrampolines[NumSendTrampolines]';
var: #implicitReceiverSendTrampolines
declareC: 'sqInt implicitReceiverSendTrampolines[NumSendTrampolines]';
var: #outerSendTrampolines
declareC: 'sqInt outerSendTrampolines[NumSendTrampolines]'].
aCCodeGenerator
addConstantForBinding: self bindingForNumTrampolines;
var: #trampolineAddresses
declareC: 'static char *trampolineAddresses[NumTrampolines*2]';
var: #objectReferencesInRuntime
declareC: 'static usqInt objectReferencesInRuntime[NumObjRefsInRuntime+1]';
var: #labelCounter
type: #int;
var: #traceFlags
declareC: 'int traceFlags = 8 /* prim trace log on by default */';
var: #cStackAlignment
declareC: 'const int cStackAlignment = STACK_ALIGN_BYTES'.
aCCodeGenerator
declareVar: #minValidCallAddress type: #'usqIntptr_t'.
aCCodeGenerator vmClass generatorTable ifNotNil:
[:bytecodeGenTable|
aCCodeGenerator
var: #generatorTable
declareC: 'static BytecodeDescriptor generatorTable[', bytecodeGenTable size printString, ']',
(self tableInitializerFor: bytecodeGenTable
in: aCCodeGenerator)].
"In C the abstract opcode names clash with the Smalltalk generator syntactic sugar.
Most of the syntactic sugar is inlined, but alas some remains. Rename the syntactic
sugar to avoid the clash."
(self organization listAtCategoryNamed: #'abstract instructions') do:
[:s|
aCCodeGenerator addSelectorTranslation: s to: 'g', (aCCodeGenerator cFunctionNameFor: s)].
aCCodeGenerator addSelectorTranslation: #halt: to: 'haltmsg'.
self declareFlagVarsAsByteIn: aCCodeGenerator!
Item was added:
+ ----- Method: Cogit>>debugAPISelector (in category 'debugging') -----
+ debugAPISelector
+ "Answer the selector theCoInterpereter called in on. Simulation only.
+ Used to help the codeZoneIsExecutableNotWritable assert work."
+ <doNotGenerate>
+ ^(thisContext findContextSuchThat: [:ctxt| ctxt sender notNil and: [ctxt sender receiver == coInterpreter]]) selector!
Item was changed:
+ ----- Method: Cogit>>ensureExecutableCodeZone (in category 'memory access') -----
- ----- Method: Cogit>>ensureExecutableCodeZone (in category 'support') -----
ensureExecutableCodeZone
"On some platforms run-time calls may be required to enable execution and disable
write-protect of the code zone. This is sequenced by ensuring that the code zone is
executable most of the time. Note that any code space modification requires an
icache flush (on processors with such an icache). Hence the least invasive time to
ensure code is executable is post icache flush. Making sure code is writable can be
done either before any bulk edit (e.g. code zone reclamation) or as part of any fine-
grained code modification (e.g. setting an anonymous method's selector)."
<inline: #always>
self cppIf: #DUAL_MAPPED_CODE_ZONE
ifFalse:
[backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
+ [self cCode: nil inSmalltalk: [self assert: (codeZoneIsExecutableNotWritable not
+ "this happens when the CoInterpreter sends
+ mapObjectReferencesInMachineCode: followed by cogitPostGCAction:"
+ or: [debugAPISelector == self debugAPISelector
+ or: [debugAPISelector == #mapObjectReferencesInMachineCode:]])].
+ backEnd makeCodeZoneExecutable.
+ self cCode: nil inSmalltalk: [codeZoneIsExecutableNotWritable := true. debugAPISelector := self debugAPISelector]]]!
- [codeZoneIsExecutableNotWritable ifFalse:
- [backEnd makeCodeZoneExecutable.
- codeZoneIsExecutableNotWritable := true]]]!
Item was changed:
+ ----- Method: Cogit>>ensureExecutableCodeZoneWithin: (in category 'memory access') -----
- ----- Method: Cogit>>ensureExecutableCodeZoneWithin: (in category 'support') -----
ensureExecutableCodeZoneWithin: aBlock
"On some platforms run-time calls may be required to enable execution and disable
write-protect of the code zone. See the comment in ensureExecutableCodeZone."
<inline: #always>
self ensureExecutableCodeZone.
aBlock value.
self ensureWritableCodeZone!
Item was changed:
+ ----- Method: Cogit>>ensureWritableCodeZone (in category 'memory access') -----
- ----- Method: Cogit>>ensureWritableCodeZone (in category 'support') -----
ensureWritableCodeZone
"On some platforms run-time calls may be required to enable execution and disable
write-protect of the code zone. See the comment in ensureExecutableCodeZone."
<inline: #always>
self cppIf: #DUAL_MAPPED_CODE_ZONE
ifFalse:
+ [backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
+ [self cCode: nil inSmalltalk: [debugAPISelector ifNil: [debugAPISelector := self debugAPISelector].
+ self assert: (codeZoneIsExecutableNotWritable or: [debugAPISelector == self debugAPISelector])].
+ backEnd makeCodeZoneWritable.
+ self cCode: nil inSmalltalk: [codeZoneIsExecutableNotWritable := false. debugAPISelector := self debugAPISelector]]]!
- [(backEnd needsCodeZoneExecuteWriteSwitch
- and: [codeZoneIsExecutableNotWritable]) ifTrue:
- [backEnd makeCodeZoneWritable.
- codeZoneIsExecutableNotWritable := false]]!
Item was changed:
----- Method: Cogit>>followMovableLiteralsAndUpdateYoungReferrers (in category 'garbage collection') -----
followMovableLiteralsAndUpdateYoungReferrers
"To avoid runtime checks on literal variable and literal accesses in == and ~~,
we follow literals in methods having movable literals in the postBecome action.
To avoid scanning every method, we annotate cogMethods with the
cmHasMovableLiteral flag."
<option: #SpurObjectMemory>
<api>
<returnTypeC: #void>
| cogMethod |
<var: #cogMethod type: #'CogMethod *'>
self assert: methodZone kosherYoungReferrers.
"methodZone firstBogusYoungReferrer"
"methodZone occurrencesInYoungReferrers: methodZone firstBogusYoungReferrer"
codeModified := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType ~= CMFree ifTrue:
[cogMethod cmHasMovableLiteral ifTrue:
[self followForwardedLiteralsIn: cogMethod]].
cogMethod := methodZone methodAfter: cogMethod]..
methodZone pruneYoungReferrers.
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>generateStackPointerCapture (in category 'initialization') -----
generateStackPointerCapture
"Generate a routine ceCaptureCStackPointers that will capture the C stack pointer,
and, if it is in use, the C frame pointer. These are used in trampolines to call
run-time routines in the interpreter from machine-code."
| oldMethodZoneBase oldTrampolineTableIndex |
cFramePointerInUse := false. "For the benefit of the following assert, assume the minimum at first."
self assertCStackWellAligned.
oldMethodZoneBase := methodZoneBase.
oldTrampolineTableIndex := trampolineTableIndex.
self generateCaptureCStackPointers: true.
self perform: #ceCaptureCStackPointers.
+ self ensureWritableCodeZone.
(cFramePointerInUse := coInterpreter checkIfCFramePointerInUse) ifFalse:
[methodZoneBase := oldMethodZoneBase.
trampolineTableIndex := oldTrampolineTableIndex.
+ self generateCaptureCStackPointers: false.
+ self ensureWritableCodeZone].
- self generateCaptureCStackPointers: false].
self assertCStackWellAligned!
Item was changed:
----- Method: Cogit>>lookupAddress: (in category 'disassembly') -----
lookupAddress: address
<doNotGenerate>
address < methodZone freeStart ifTrue:
[^address >= methodZoneBase
ifTrue:
[(methodZone methodFor: address) ifNotNil:
[:cogMethod|
((cogMethod selector ~= objectMemory nilObject
and: [objectRepresentation couldBeObject: cogMethod selector])
ifTrue: [coInterpreter stringOf: cogMethod selector]
ifFalse: [cogMethod asInteger hex]),
'@', ((address - cogMethod asInteger) hex allButFirst: 3)]]
ifFalse:
[(self trampolineRangeFor: address) ifNotNil:
[:range|
+ (self codeEntryNameFor: range first) ifNotNil:
+ [:name| name, (address = range first ifTrue: [''] ifFalse: [' + ', (address - range first) hex])]]]].
- (self codeEntryNameFor: range first), (address = range first ifTrue: [''] ifFalse: [' + ', (address - range first) hex])]]].
(simulatedTrampolines includesKey: address) ifTrue:
[^self labelForSimulationAccessor: (simulatedTrampolines at: address)].
(simulatedVariableGetters includesKey: address) ifTrue:
[^self labelForSimulationAccessor: (simulatedVariableGetters at: address)].
^(coInterpreter lookupAddress: address) ifNil:
[address = self cStackPointerAddress
ifTrue: [#CStackPointer]
ifFalse:
[address = self cFramePointerAddress ifTrue:
[#CFramePointer]]]!
Item was changed:
----- Method: Cogit>>mapObjectReferencesInMachineCodeForBecome (in category 'garbage collection') -----
mapObjectReferencesInMachineCodeForBecome
"Update all references to objects in machine code for a become.
Unlike incrementalGC or fullGC a method that does not refer to young may
refer to young as a result of the become operation. Unlike incrementalGC
or fullGC the reference from a Cog method to its methodObject *must not*
change since the two are two halves of the same object."
| cogMethod writableCogMethod hasYoungObj hasYoungObjPtr freedPIC |
hasYoungObj := false.
hasYoungObjPtr := (self addressOf: hasYoungObj put: [:val| hasYoungObj := val]) asInteger.
codeModified := freedPIC := false.
self mapObjectReferencesInGeneratedRuntime.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[self assert: hasYoungObj not.
cogMethod cmType ~= CMFree ifTrue:
[self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0.
writableCogMethod := self writableMethodFor: cogMethod.
writableCogMethod selector: (objectRepresentation remapOop: cogMethod selector).
cogMethod cmType = CMClosedPIC
ifTrue:
[((objectMemory isYoung: cogMethod selector)
or: [self mapObjectReferencesInClosedPIC: cogMethod]) ifTrue:
[freedPIC := true.
methodZone freeMethod: cogMethod]]
ifFalse:
[(objectMemory isYoung: cogMethod selector) ifTrue:
[hasYoungObj := true].
cogMethod cmType = CMMethod ifTrue:
[| remappedMethod |
self assert: cogMethod objectHeader = objectMemory nullHeaderForMachineCodeMethod.
remappedMethod := objectRepresentation remapOop: cogMethod methodObject.
remappedMethod ~= cogMethod methodObject ifTrue:
[(coInterpreter methodHasCogMethod: remappedMethod) ifTrue:
[self error: 'attempt to become two cogged methods'].
(objectMemory
withoutForwardingOn: cogMethod methodObject
and: remappedMethod
with: cogMethod cmUsesPenultimateLit
sendToCogit: #method:hasSameCodeAs:checkPenultimate:) ifFalse:
[self error: 'attempt to become cogged method into different method'].
"For non-Newspeak there should ne a one-to-one mapping between bytecoded and
cog methods. For Newspeak not necessarily, but only for anonymous accessors."
"Only reset the method object's header if it is referring to this CogMethod."
(coInterpreter rawHeaderOf: cogMethod methodObject) = cogMethod asInteger
ifTrue:
[coInterpreter
rawHeaderOf: cogMethod methodObject
put: cogMethod methodHeader.
writableCogMethod
methodHeader: (coInterpreter rawHeaderOf: remappedMethod);
methodObject: remappedMethod.
coInterpreter
rawHeaderOf: remappedMethod
put: cogMethod asInteger]
ifFalse:
[self assert: (self noAssertMethodClassAssociationOf: cogMethod methodObject)
= objectMemory nilObject.
writableCogMethod
methodHeader: (coInterpreter rawHeaderOf: remappedMethod);
methodObject: remappedMethod]].
(objectMemory isYoung: cogMethod methodObject) ifTrue:
[hasYoungObj := true]].
self mapFor: cogMethod
performUntil: #remapIfObjectRef:pc:hasYoung:
arg: hasYoungObjPtr.
hasYoungObj
ifTrue:
[methodZone ensureInYoungReferrers: cogMethod.
hasYoungObj := false]
ifFalse:
[cogMethod cmRefersToYoung: false]]].
cogMethod := methodZone methodAfter: cogMethod].
"we /must/ prune youngReferrers here because a) the [cogMethod cmRefersToYoung: false]
block could have removed a method and subsequently it could be added back, and b) we
can not tolerate duplicates in the youngReferrers list."
methodZone pruneYoungReferrers.
freedPIC ifTrue:
[self unlinkSendsToFree].
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>mapObjectReferencesInMachineCodeForFullGC (in category 'garbage collection') -----
mapObjectReferencesInMachineCodeForFullGC
"Update all references to objects in machine code for a full gc. Since
the current (New)ObjectMemory GC makes everything old in a full GC
a method not referring to young will not refer to young afterwards"
| cogMethod writableCogMethod |
codeModified := false.
self mapObjectReferencesInGeneratedRuntime.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType ~= CMFree ifTrue:
[self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0.
writableCogMethod := self writableMethodFor: cogMethod.
writableCogMethod selector: (objectRepresentation remapOop: cogMethod selector).
cogMethod cmType = CMClosedPIC
ifTrue:
[self assert: cogMethod cmRefersToYoung not.
self mapObjectReferencesInClosedPIC: cogMethod]
ifFalse:
[cogMethod cmType = CMMethod ifTrue:
[self assert: cogMethod objectHeader = objectMemory nullHeaderForMachineCodeMethod.
writableCogMethod methodObject: (objectRepresentation remapOop: cogMethod methodObject)].
self mapFor: cogMethod
performUntil: #remapIfObjectRef:pc:hasYoung:
arg: 0.
(cogMethod cmRefersToYoung
and: [objectRepresentation allYoungObjectsAgeInFullGC]) ifTrue:
[writableCogMethod cmRefersToYoung: false]]].
cogMethod := methodZone methodAfter: cogMethod].
methodZone pruneYoungReferrers.
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: codeBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>mapObjectReferencesInMachineCodeForYoungGC (in category 'garbage collection') -----
mapObjectReferencesInMachineCodeForYoungGC
"Update all references to objects in machine code for either a Spur scavenging gc
or a Squeak V3 incremental GC. Avoid scanning all code by using the youngReferrers
list. In a young gc a method referring to young may no longer refer to young, but a
method not referring to young cannot and will not refer to young afterwards."
| pointer cogMethod hasYoungObj hasYoungObjPtr |
hasYoungObj := false.
hasYoungObjPtr := (self addressOf: hasYoungObj put: [:val| hasYoungObj := val]) asInteger.
codeModified := false.
pointer := methodZone youngReferrers.
[pointer < methodZone zoneEnd] whileTrue:
[self assert: hasYoungObj not.
cogMethod := coInterpreter cCoerceSimple: (objectMemory longAt: pointer) to: #'CogMethod *'.
cogMethod cmType = CMFree
ifTrue: [self assert: cogMethod cmRefersToYoung not]
ifFalse:
[self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0.
cogMethod cmRefersToYoung ifTrue:
[| writableCogMethod |
self assert: (cogMethod cmType = CMMethod
or: [cogMethod cmType = CMOpenPIC]).
writableCogMethod := self writableMethodFor: cogMethod.
writableCogMethod selector: (objectRepresentation remapOop: cogMethod selector).
(objectMemory isYoung: cogMethod selector) ifTrue:
[hasYoungObj := true].
cogMethod cmType = CMMethod ifTrue:
[self assert: cogMethod objectHeader = objectMemory nullHeaderForMachineCodeMethod.
writableCogMethod methodObject: (objectRepresentation remapOop: cogMethod methodObject).
(objectMemory isYoung: cogMethod methodObject) ifTrue:
[hasYoungObj := true]].
self mapFor: cogMethod
performUntil: #remapIfObjectRef:pc:hasYoung:
arg: hasYoungObjPtr.
hasYoungObj
ifTrue: [hasYoungObj := false]
ifFalse: [writableCogMethod cmRefersToYoung: false]]].
pointer := pointer + objectMemory wordSize].
methodZone pruneYoungReferrers.
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>markAndTraceMachineCodeForNewSpaceGC (in category 'jit - api') -----
markAndTraceMachineCodeForNewSpaceGC
"Free any methods that refer to unmarked objects, unlinking sends to freed methods."
| pointer cogMethod |
<var: #cogMethod type: #'CogMethod *'>
objectMemory leakCheckNewSpaceGC ifTrue:
[self asserta: self allMachineCodeObjectReferencesValid].
codeModified := false.
pointer := methodZone youngReferrers.
[pointer < methodZone zoneEnd] whileTrue:
[cogMethod := coInterpreter cCoerceSimple: (objectMemory longAt: pointer) to: #'CogMethod *'.
cogMethod cmRefersToYoung ifTrue:
[self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0.
self assert: (cogMethod cmType = CMMethod
or: [cogMethod cmType = CMOpenPIC]).
(objectMemory isYoung: cogMethod selector) ifTrue:
[objectMemory markAndTrace: cogMethod selector].
cogMethod cmType = CMMethod ifTrue:
[(objectMemory isYoung: cogMethod methodObject) ifTrue:
[objectMemory markAndTrace: cogMethod methodObject].
self markYoungObjectsIn: cogMethod]].
pointer := pointer + objectMemory wordSize].
objectMemory leakCheckNewSpaceGC ifTrue:
[self asserta: self allMachineCodeObjectReferencesValid].
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>markAndTraceMachineCodeOfMarkedMethods (in category 'jit - api') -----
markAndTraceMachineCodeOfMarkedMethods
"Mark objects in machine-code of marked methods (or open PICs with marked selectors)."
<api>
<option: #SpurObjectMemory>
| cogMethod |
<var: #cogMethod type: #'CogMethod *'>
objectMemory leakCheckFullGC ifTrue:
[self asserta: self allMachineCodeObjectReferencesValid].
codeModified := false.
self markAndTraceObjectReferencesInGeneratedRuntime.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[(cogMethod cmType = CMMethod
and: [objectMemory isMarked: cogMethod methodObject]) ifTrue:
[self markAndTraceLiteralsIn: cogMethod].
(cogMethod cmType = CMOpenPIC
and: [(objectMemory isImmediate: cogMethod selector)
or: [objectMemory isMarked: cogMethod selector]]) ifTrue:
[self markAndTraceLiteralsIn: cogMethod].
cogMethod := methodZone methodAfter: cogMethod].
objectMemory leakCheckFullGC ifTrue:
[self asserta: self allMachineCodeObjectReferencesValid].
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>markAndTraceOrFreeMachineCodeForFullGC (in category 'jit - api') -----
markAndTraceOrFreeMachineCodeForFullGC
"Free any methods that refer to unmarked objects, unlinking sends to freed methods."
| cogMethod |
<var: #cogMethod type: #'CogMethod *'>
objectMemory leakCheckFullGC ifTrue:
[self asserta: self allMachineCodeObjectReferencesValid].
codeModified := false.
self markAndTraceObjectReferencesInGeneratedRuntime.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[self markAndTraceOrFreeCogMethod: cogMethod firstVisit: true.
cogMethod := methodZone methodAfter: cogMethod].
objectMemory leakCheckFullGC ifTrue:
[self asserta: self allMachineCodeObjectReferencesValid].
codeModified ifTrue: "After updating oops in inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>unlinkAllSends (in category 'jit - api') -----
unlinkAllSends
<api>
"Unlink all sends in cog methods."
| cogMethod |
<var: #cogMethod type: #'CogMethod *'>
methodZoneBase ifNil: [^self].
self ensureWritableCodeZone.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
methodZone voidOpenPICList.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod
ifTrue:
[self mapFor: cogMethod
performUntil: #unlinkIfLinkedSend:pc:ignored:
arg: 0]
ifFalse:
[cogMethod cmType ~= CMFree ifTrue:
[methodZone freeMethod: cogMethod]].
cogMethod := methodZone methodAfter: cogMethod].
"After updating inline caches we need to flush the icache."
+ backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart!
- backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger!
Item was changed:
----- Method: Cogit>>unlinkSendsLinkedForInvalidClasses (in category 'jit - api') -----
unlinkSendsLinkedForInvalidClasses
<api>
<option: #SpurObjectMemory>
"Unlink all sends in cog methods whose class tag is that of a forwarded class."
| cogMethod freedPIC |
<var: #cogMethod type: #'CogMethod *'>
methodZoneBase ifNil: [^self].
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
codeModified := freedPIC := false.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod
ifTrue:
[self mapFor: cogMethod
performUntil: #unlinkIfInvalidClassSend:pc:ignored:
arg: 0]
ifFalse:
[(cogMethod cmType = CMClosedPIC
and: [self cPICHasForwardedClass: cogMethod]) ifTrue:
[methodZone freeMethod: cogMethod.
freedPIC := true]].
cogMethod := methodZone methodAfter: cogMethod].
freedPIC
ifTrue: [self unlinkSendsToFree]
ifFalse:
[codeModified ifTrue: "After possibly updating inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]]!
Item was changed:
----- Method: Cogit>>unlinkSendsOf:isMNUSelector: (in category 'jit - api') -----
unlinkSendsOf: selector isMNUSelector: isMNUSelector
<api>
"Unlink all sends in cog methods. Free all Closed PICs with the selector,
or with an MNU case if isMNUSelector. First check if any method actually
has the selector; if not there can't be any linked send to it. This routine
(including descendents) is performance critical. It contributes perhaps
30% of entire execution time in Compiler recompileAll."
| cogMethod mustScanAndUnlink |
<var: #cogMethod type: #'CogMethod *'>
methodZoneBase ifNil: [^self].
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
mustScanAndUnlink := false.
isMNUSelector
ifTrue:
[[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType ~= CMFree ifTrue:
[cogMethod cpicHasMNUCase
ifTrue:
[self assert: cogMethod cmType = CMClosedPIC.
methodZone freeMethod: cogMethod.
mustScanAndUnlink := true]
ifFalse:
[cogMethod selector = selector ifTrue:
[mustScanAndUnlink := true.
cogMethod cmType = CMClosedPIC ifTrue:
[methodZone freeMethod: cogMethod]]]].
cogMethod := methodZone methodAfter: cogMethod]]
ifFalse:
[[cogMethod < methodZone limitZony] whileTrue:
[(cogMethod cmType ~= CMFree
and: [cogMethod selector = selector]) ifTrue:
[mustScanAndUnlink := true.
cogMethod cmType = CMClosedPIC ifTrue:
[methodZone freeMethod: cogMethod]].
cogMethod := methodZone methodAfter: cogMethod]].
mustScanAndUnlink ifFalse:
[^self].
codeModified := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod ifTrue:
[self mapFor: cogMethod
performUntil: #unlinkIfFreeOrLinkedSend:pc:of:
arg: selector].
cogMethod := methodZone methodAfter: cogMethod].
codeModified ifTrue: "After possibly updating inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>unlinkSendsTo:andFreeIf: (in category 'jit - api') -----
unlinkSendsTo: targetMethodObject andFreeIf: freeIfTrue
<api>
"Unlink all sends in cog methods to a particular target method.
If targetMethodObject isn't actually a method (perhaps being
used via invokeAsMethod) then there's nothing to do."
| cogMethod targetMethod freedPIC |
<var: #cogMethod type: #'CogMethod *'>
<var: #targetMethod type: #'CogMethod *'>
((objectMemory isOopCompiledMethod: targetMethodObject)
and: [coInterpreter methodHasCogMethod: targetMethodObject]) ifFalse:
[^self].
targetMethod := coInterpreter cogMethodOf: targetMethodObject.
methodZoneBase ifNil: [^self].
codeModified := freedPIC := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod
ifTrue:
[self mapFor: cogMethod
performUntil: #unlinkIfLinkedSend:pc:to:
arg: targetMethod asInteger]
ifFalse:
[(cogMethod cmType = CMClosedPIC
and: [self cPIC: cogMethod HasTarget: targetMethod]) ifTrue:
[methodZone freeMethod: cogMethod.
freedPIC := true]].
cogMethod := methodZone methodAfter: cogMethod].
freeIfTrue ifTrue: [self freeMethod: targetMethod].
freedPIC
ifTrue: [self unlinkSendsToFree]
ifFalse:
[codeModified ifTrue: "After possibly updating inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]]!
Item was changed:
----- Method: Cogit>>unlinkSendsToFree (in category 'garbage collection') -----
unlinkSendsToFree
<api>
"Unlink all sends in cog methods to free methods and/or pics."
| cogMethod |
<var: #cogMethod type: #'CogMethod *'>
methodZoneBase ifNil: [^self].
codeModified := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod
ifTrue:
[self mapFor: cogMethod
performUntil: #unlinkIfLinkedSendToFree:pc:ignored:
arg: 0]
ifFalse:
[cogMethod cmType = CMClosedPIC ifTrue:
[self assert: (self noTargetsFreeInClosedPIC: cogMethod)]].
cogMethod := methodZone methodAfter: cogMethod].
codeModified ifTrue: "After possibly updating inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed:
----- Method: Cogit>>unlinkSendsToMachineCodePrimitiveMethodsAndFreeIf: (in category 'jit - api') -----
unlinkSendsToMachineCodePrimitiveMethodsAndFreeIf: freeIfTrue
<api>
"Unlink all sends in cog methods to methods with a machine code
primitive, and free machine code primitive methods if freeIfTrue.
To avoid having to scan PICs, free any and all PICs"
| cogMethod freedSomething |
<var: #cogMethod type: #'CogMethod *'>
methodZoneBase ifNil: [^self].
codeModified := freedSomething := false.
cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'.
[cogMethod < methodZone limitZony] whileTrue:
[cogMethod cmType = CMMethod
ifTrue:
[(freeIfTrue
and: [self cogMethodHasMachineCodePrim: cogMethod])
ifTrue:
[methodZone freeMethod: cogMethod.
freedSomething := true]
ifFalse:
[self mapFor: cogMethod
performUntil: #unlinkIfLinkedSend:pc:toMachineCodePrim:
arg: 0]]
ifFalse:
[cogMethod cmType = CMClosedPIC ifTrue:
[methodZone freeMethod: cogMethod.
freedSomething := true]].
cogMethod := methodZone methodAfter: cogMethod].
freedSomething
ifTrue: [self unlinkSendsToFree]
ifFalse:
[codeModified ifTrue: "After possibly updating inline caches we need to flush the icache."
+ [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]]!
- [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]]!
Item was changed:
----- Method: TMethod>>hasUnrenamableCCode (in category 'utilities') -----
hasUnrenamableCCode
+ "Answer if the receiver uses inlined C strings which
- "Answer true if the receiver uses inlined C which
is not currently renamed properly by the the inliner."
+ ^parseTree anySatisfy: [:node| node isStringishCCode]!
- ^parseTree anySatisfy:
- [:node| node isNonNullCCode]!
Item was added:
+ ----- Method: TParseNode>>isNullCCode (in category 'testing') -----
+ isNullCCode
+ "overridden in TSendNode"
+ ^false!
Item was added:
+ ----- Method: TParseNode>>isStringishCCode (in category 'testing') -----
+ isStringishCCode
+ "overridden in TSendNode"
+ ^false!
Item was changed:
----- Method: TSendNode>>hasEffect (in category 'testing') -----
hasEffect
"Answer if this node has an effect on execution state (does something).
Statements that don't have any effect can be elided if their value is unused."
selector == #not ifTrue:
[^receiver hasEffect].
+ selector == #cCode:inSmalltalk: ifTrue:
+ [^(arguments first isConstant and: [arguments first value isString and: [arguments first value notEmpty]])
+ or: [arguments first hasEffect]].
self isBinaryArithmeticOrConditional ifTrue:
[^receiver hasEffect or: [arguments first hasEffect]].
self isUnaryCast ifTrue:
[^receiver hasEffect].
self isBinaryCast ifTrue:
[^arguments first hasEffect].
^true!
Item was changed:
----- Method: TSendNode>>isEffectFree (in category 'testing') -----
isEffectFree
+ ^self hasEffect not!
- ^(self isUnaryCast and: [receiver isEffectFree])
- or: [(self isBinaryCast and: [arguments first isEffectFree])
- or: [self isBinaryArithmeticOrConditional and: [receiver isEffectFree and: [arguments first isEffectFree]]]]!
Item was changed:
----- Method: TSendNode>>isNonNullCCode (in category 'testing') -----
isNonNullCCode
^(#(cCode: cCode:inSmalltalk:) includes: selector)
and: [arguments first isConstant
+ ifTrue: [arguments first value isString
+ and: [arguments first value notEmpty]]
+ ifFalse: [arguments first hasEffect]]!
- and: [arguments first value isString
- and: [arguments first value notEmpty]]]!
Item was added:
+ ----- Method: TSendNode>>isNullCCode (in category 'testing') -----
+ isNullCCode
+ | node |
+ (#(cCode: cCode:inSmalltalk:) includes: selector) ifFalse:
+ [^false].
+ "all of cCode: nil ..., cCode: []..., cCode: '' are null"
+ node := arguments first.
+ node isConstant ifTrue:
+ [^(node value isString
+ and: [node value notEmpty]) not].
+ ^node hasEffect not!
Item was added:
+ ----- Method: TSendNode>>isStringishCCode (in category 'testing') -----
+ isStringishCCode
+ ^(#(cCode: cCode:inSmalltalk:) includes: selector)
+ and: [arguments first isConstant
+ and: [arguments first value isString
+ and: [arguments first value notEmpty]]]!
Item was changed:
----- Method: TSwitchStmtNode>>hasEffect (in category 'testing') -----
hasEffect
"Answer if this node has an effect on execution state (does something).
Statements that don't have any effect can be elided if their value is unused."
^expression hasEffect
or: [(otherwiseOrNil notNil and: [otherwiseOrNil hasEffect])
+ or: [cases anySatisfy:
+ [:tuple|
+ (tuple first anySatisfy: [:array| array anySatisfy: [:node| node hasEffect]])
+ or: [tuple second hasEffect]]]]!
- or: [cases anySatisfy: [:node| node hasEffect]]]!
Item was changed:
----- Method: VMBasicConstants class>>namesDefinedAtCompileTime (in category 'C translation') -----
namesDefinedAtCompileTime
"Answer the set of names for variables that should be defined at compile time.
Some of these get default values during simulation, and hence get defaulted in
the various initializeMiscConstants methods. But that they have values should
/not/ cause the code generator to do dead code elimination based on their
default values. In particular, methods marked with <option: ANameDefinedAtCompileTime>
will be emitted within #if defined(ANameDefinedAtCompileTime)...#endif.
And of course this is backwards. We'd like to define names that are defined at translation time."
^#(VMBIGENDIAN
IMMUTABILITY
STACKVM COGVM COGMTVM SPURVM
PharoVM "Pharo vs Squeak"
TerfVM VM_TICKER "Terf vs Squeak & Qwaq/Teleplace/Terf high-priority thread support"
EnforceAccessControl "Newspeak"
CheckRememberedInTrampoline "IMMUTABILITY"
BIT_IDENTICAL_FLOATING_POINT PLATFORM_SPECIFIC_FLOATING_POINT "Alternatives for using fdlibm for floating-point"
TestingPrimitives
OBSOLETE_ALIEN_PRIMITIVES "Ancient crap in the IA32ABI plugin"
LLDB "As of lldb-370.0.42 Swift-3.1, passing function parameters to printOopsSuchThat fails with Internal error [IRForTarget]: Couldn't rewrite one of the arguments of a function call. Turning off link time optimization with -fno-lto has no effect. hence we define some debugging functions as being <option: LLDB>"
"processor related"
__ARM_ARCH__ __arm__ __arm32__ ARM32 __arm64__ ARM64
_M_I386 _X86_ i386 i486 i586 i686 __i386__ __386__ X86 I386
x86_64 __amd64 __x86_64 __amd64__ __x86_64__ _M_AMD64 _M_X64
__mips__ __mips
__powerpc __powerpc__ __powerpc64__ __POWERPC__
__ppc__ __ppc64__ __PPC__ __PPC64__
__sparc__ __sparc __sparc_v8__ __sparc_v9__ __sparcv8 __sparcv9
"Compiler brand related"
__ACK__
__CC_ARM
__clang__
__GNUC__
_MSC_VER
__ICC
__SUNPRO_C
"os related"
ACORN
_AIX
__ANDROID__
+ __APPLE__
__BEOS__
__linux__
+ __MACH__
__MINGW32__
__FreeBSD__ __NetBSD__ __OpenBSD__
__osf__
EPLAN9
__unix__ __unix UNIX
WIN32 _WIN32 _WIN32_WCE
WIN64 _WIN64 _WIN64_WCE)!
Item was changed:
----- Method: VMClass class>>shouldIncludeMethodForSelector: (in category 'translation') -----
shouldIncludeMethodForSelector: selector
"Answer whether a primitive method should be translated. Emit a warning to the transcript if the method doesn't exist."
+ selector == #detectFeaturesOnMacOS ifTrue: [self halt].
^(self whichClassIncludesSelector: selector)
ifNotNil:
[:c|
(c >> selector pragmaAt: #option:)
ifNotNil:
[:pragma|
(VMBasicConstants defineAtCompileTime: pragma arguments first)
or: [InitializationOptions
at: pragma arguments first
ifAbsent: [(self bindingOf: pragma arguments first)
ifNil: [false]
ifNotNil: [:binding| binding value ~~ #undefined]]]]
ifNil: [true]]
ifNil:
[Transcript nextPutAll: 'Cannot find implementation of '; nextPutAll: selector; nextPutAll: ' in hierarchy of '; print: self; cr; flush.
false]!
This function looks unfinished. "address" is not initialized but used as argument for sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(). After removing "roundUpToPage" from "address", I could compile the VM, but it would not start. Looking at its sibling"sqAllocateMemory(), I think that there is some magic missing to calculate the minAddress to be provided into sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(). Same for allocateJITMemory() in sqWin32Alloc.
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/1455c649198064291e…
Typo. Exported function. "static" should be removed again. See platforms/Cross/plugins/SoundPlugin/SoundPlugin.h.
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/fbc44f286244ab311c…