[Vm-dev] VM Maker: VMMaker.oscog-eem.3284.mcz
commits at source.squeak.org
commits at source.squeak.org
Sun Dec 25 20:56:48 UTC 2022
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3284.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3284
Author: eem
Time: 25 December 2022, 12:56:31.883528 pm
UUID: 99050939-5e02-4371-aa33-f716c24648f6
Ancestors: VMMaker.oscog-eem.3283
Slang: refactor macro generation to make it ewasier to check for validity in the slang test workspace.
Cog ARMv8 Simulator: make the DUAL_MAPPED_CODE_ZONE regime simulate again, fixing an assert fail due to overlap of the guard page and the end of the code zone when mapped. We need an additional guard poage's delta to make sure that the simulated hi-rez timer code is generated outside of the code zone. And the asserts for executability in simulateCogCodeAt:/simulateLeafCallOf: only apply to the non-dual-mapped regime.
Fix some excess var:type: in old code.
=============== Diff against VMMaker.oscog-eem.3283 ===============
Item was added:
+ ----- Method: CCodeGenerator>>emitAsCMacro:on: (in category 'C code generator') -----
+ emitAsCMacro: tMethod on: aStream
+ "Store the global variable declarations on the given stream. Answer any constants used in the macros."
+ aStream
+ nextPutAll: '#define ';
+ nextPutAll:(self cFunctionNameFor: tMethod selector);
+ nextPutAll: (macros at: tMethod selector); cr!
Item was changed:
----- Method: CCodeGenerator>>emitCMacros:on: (in category 'C code generator') -----
+ emitCMacros: methodList on: aStream
- emitCMacros: methodList on: aStream
"Store the global variable declarations on the given stream. Answer any constants used in the macros."
| usedConstants |
macros isEmpty ifTrue: [^#()].
aStream cr; nextPutAll: '/*** Macros ***/'; cr.
usedConstants := Set new.
(methodList reject: [:m| m isRealMethod]) do:
[:m |
m definedAsMacro ifTrue:
+ [self emitAsCMacro: m on: aStream.
- [aStream
- nextPutAll: '#define ';
- nextPutAll:(self cFunctionNameFor: m selector);
- nextPutAll: (macros at: m selector); cr.
m compiledMethod literalsDo:
[:lit|
(lit isVariableBinding and: [(macros at: m selector) includesSubstring: lit key]) ifTrue:
[usedConstants add: lit key]]]].
aStream cr.
^usedConstants!
Item was changed:
----- Method: CogRegisterAllocatingSimStackEntry>>isMergedWithTargetEntry: (in category 'comparing') -----
isMergedWithTargetEntry: targetEntry
"The receiver is a simStackEntry at a jump to the corresponding simStackEntry at the jump's target.
Answer if no merge is required for the jump."
+ <var: 'targetEntry' type: #'CogSimStackEntry *'>
- <var: 'ssEntry' type: #'CogSimStackEntry *'>
spilled ~= targetEntry spilled ifTrue: "push or pop required"
[^false].
(liveRegister = NoReg and: [targetEntry liveRegister ~= NoReg]) ifTrue: "register load required"
[^false].
(self isSameEntryAs: targetEntry) ifTrue:
[^liveRegister = targetEntry liveRegister].
(type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister = targetEntry register]]) ifTrue:
[^true].
"self: const =1 (16r1) (live: Extra4Reg) {172} vs reg ReceiverResultReg {127}"
"self: reg ReceiverResultReg {95} vs reg Extra5Reg {85}"
"self: (bo ReceiverResultReg+296 (live: Extra5Reg) {88} vs reg ReceiverResultReg {84}"
"self: const =1 (16r1) (spilled) {167} vs spill @ FPReg-48 {122}"
((type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister ~= targetEntry registerOrNone]])
or: [(type = SSRegister and: [targetEntry type = SSRegister and: [register ~= targetEntry registerOrNone]])
or: [(type = SSBaseOffset and: [register = ReceiverResultReg and: [targetEntry type = SSRegister]])
or: [(type = SSConstant and: [targetEntry type = SSSpill])]]]) ifFalse:
[self halt: 'comment the incompatible pair please'].
^false!
Item was changed:
----- Method: CogVMSimulator>>openOn:extraMemory: (in category 'initialize-release') -----
openOn: fileName extraMemory: extraBytes
"CogVMSimulator new openOn: 'clone.im' extraMemory: 100000"
| f version headerSize dataSize count oldBaseAddr bytesToShift swapBytes
headerFlags firstSegSize heapSize
hdrNumStackPages hdrEdenBytes hdrMaxExtSemTabSize hdrCogCodeSize
stackZoneSize methodCacheSize primTraceLogSize allocationReserve |
"open image file and read the header"
(f := self openImageFileNamed: fileName) ifNil: [^self].
"Set the image name and the first argument; there are
no arguments during simulation unless set explicitly."
systemAttributes at: 1 put: fileName.
["begin ensure block..."
imageName := f fullName.
f binary.
version := self getWord32FromFile: f swap: false. "current version: 16r1968 (=6504) vive la revolucion!!"
(self readableFormat: version)
ifTrue: [swapBytes := false]
ifFalse: [(version := version byteSwap32) = self imageFormatVersion
ifTrue: [swapBytes := true]
ifFalse: [self error: 'incomaptible image format']].
headerSize := self getWord32FromFile: f swap: swapBytes.
dataSize := self getLongFromFile: f swap: swapBytes. "length of heap in file"
oldBaseAddr := self getLongFromFile: f swap: swapBytes. "object memory base address of image"
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "Should be loaded from, and saved to the image header"
savedWindowSize := self getLongFromFile: f swap: swapBytes.
headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
extraVMMemory := self getWord32FromFile: f swap: swapBytes.
hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n"
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
stackZoneSize := self computeStackZoneSize.
"This slot holds the size of the native method zone in 1k units. (pad to word boundary)."
hdrCogCodeSize := (self getShortFromFile: f swap: swapBytes) * 1024.
cogCodeSize := desiredCogCodeSize ~= 0
ifTrue: [desiredCogCodeSize]
ifFalse:
[hdrCogCodeSize = 0
ifTrue: [cogit defaultCogCodeSize]
ifFalse: [hdrCogCodeSize]].
desiredCogCodeSize := hdrCogCodeSize.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [40] ifFalse: [64]).
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (desiredEdenBytes ~= 0
ifTrue: [desiredEdenBytes]
ifFalse:
[hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
Preserve it to be polite to other VMs."
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [48] ifFalse: [72]).
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"For Open PICs to be able to probe the method cache during
simulation the methodCache must be relocated to memory."
methodCacheSize := methodCache size * objectMemory wordSize.
primTraceLogSize := primTraceLog size * objectMemory wordSize.
"To cope with modern OSs that disallow executing code in writable memory we dual-map
the code zone, one mapping with read/write permissions and the other with read/execute
permissions. In simulation all we can do is use memory, so if we're simulating dual mapping
we use double the memory and simulate the memory sharing in the Cogit's backEnd."
effectiveCogCodeSize := (InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false])
+ ifTrue: [cogCodeSize * 2 + Cogit guardPageSize]
- ifTrue: [cogCodeSize * 2]
ifFalse: [cogCodeSize].
"allocate interpreter memory. This list is in address order, low to high.
In the actual VM the stack zone exists on the C stack."
heapBase := (Cogit guardPageSize
+ effectiveCogCodeSize
+ stackZoneSize
+ methodCacheSize
+ primTraceLogSize
+ self rumpCStackSize) roundUpTo: objectMemory allocationUnit.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := dataSize
+ extraBytes
+ objectMemory newSpaceBytes
+ (extraBytes > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])].
"allocate interpreter memory"
heapBase := objectMemory
setHeapBase: heapBase
memoryLimit: heapBase + heapSize
endOfMemory: heapBase + dataSize. "bogus for Spur"
self assert: cogCodeSize \\ 4 = 0.
self assert: objectMemory memoryLimit \\ 4 = 0.
self assert: self rumpCStackSize \\ 4 = 0.
objectMemory allocateMemoryOfSize: objectMemory memoryLimit.
"read in the image in bulk, then swap the bytes if necessary"
f position: headerSize.
count := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
count ~= dataSize ifTrue: [self halt]]
ensure: [f close].
self moveMethodCacheToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize.
self movePrimTraceLogToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize + methodCacheSize.
self ensureImageFormatIsUpToDate: swapBytes.
bytesToShift := objectMemory memoryBaseForImageRead - oldBaseAddr. "adjust pointers for zero base address"
UIManager default
informUser: 'Relocating object pointers...'
during: [self initializeInterpreter: bytesToShift].
cogit
initializeCodeZoneFrom: Cogit guardPageSize
upTo: Cogit guardPageSize + cogCodeSize!
Item was changed:
----- Method: Cogit>>assertValidDualZone (in category 'debugging') -----
assertValidDualZone
"{self firstInvalidDualZoneAddress. self firstInvalidDualZoneAddress + codeToDataDelta }"
"{self firstInvalidDualZoneAddress hex. (self firstInvalidDualZoneAddress + codeToDataDelta) hex }"
+ "[:fidza| {fidza. objectMemory longAt: fidza. objectMemory longAt: fidza + codeToDataDelta } collect: #hex] value: self firstInvalidDualZoneAddress"
- "{(objectMemory longAt: self firstInvalidDualZoneAddress) hex. (objectMemory longAt: self firstInvalidDualZoneAddress + codeToDataDelta) hex }"
"self armDisassembleDualZoneAnomalies"
"self armPrintDualZoneAnomalies"
+ <cmacro: '() true'>
+ self assert: self firstInvalidDualZoneAddress isNil!
- self cCode: ''
- inSmalltalk: [self assert: self firstInvalidDualZoneAddress isNil]!
Item was changed:
----- Method: Cogit>>ensureExecutableCodeZone (in category 'memory access') -----
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: [| currentAPISelector |
"What's all this crap? We're trying to catch cases where ensureExecutableCodeZone
is called without first calling ensureWritableCodeZone, and vice verse. But there are
lots of exceptions where code is not modified but executability is turned on unnecessarily.
The list of exceptions follows."
currentAPISelector := self debugAPISelector.
self assert: (codeZoneIsExecutableNotWritable not
or: [currentAPISelector == #mapObjectReferencesInMachineCode:
+ or: [currentAPISelector == #freeUnmarkedMachineCode
+ or: [currentAPISelector == #cogitPostGCAction:
+ or: [currentAPISelector == #ceSICMiss:
+ or: [currentAPISelector == #ceCPICMiss:receiver:
+ or: [currentAPISelector == #unlinkSendsOf:isMNUSelector:
+ or: [currentAPISelector == #unlinkSendsTo:andFreeIf:
+ or: [currentAPISelector == #unlinkSendsToMethodsSuchThat:AndFreeIf:
+ or: [currentAPISelector == #followMovableLiteralsAndUpdateYoungReferrers]]]]]]]]])].
- or: [currentAPISelector == #cogitPostGCAction:
- or: [currentAPISelector == #ceSICMiss:
- or: [currentAPISelector == #ceCPICMiss:receiver:
- or: [currentAPISelector == #unlinkSendsOf:isMNUSelector:
- or: [currentAPISelector == #unlinkSendsTo:andFreeIf:
- or: [currentAPISelector == #unlinkSendsToMethodsSuchThat:AndFreeIf:
- or: [currentAPISelector == #followMovableLiteralsAndUpdateYoungReferrers]]]]]]]])].
backEnd makeCodeZoneExecutable.
self cCode: nil inSmalltalk: [codeZoneIsExecutableNotWritable := true. debugAPISelector := self debugAPISelector]]]!
Item was changed:
----- Method: Cogit>>simulateCogCodeAt: (in category 'simulation only') -----
simulateCogCodeAt: address "<Integer>"
<doNotGenerate>
| stackZoneBase |
+ (InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false]) ifFalse:
+ [backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
+ [self assert: codeZoneIsExecutableNotWritable]].
- backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
- [self assert: codeZoneIsExecutableNotWritable].
stackZoneBase := coInterpreter stackZoneBase.
processor pc: address.
[[[singleStep
ifTrue:
[[processor sp < stackZoneBase ifTrue: [self halt].
self recordProcessing.
self maybeBreakAt: processor pc] value. "So that the Debugger's Over steps over all this"
processorLock critical:
[processor
singleStepIn: coInterpreter memory
minimumAddress: guardPageSize
readOnlyBelow: methodZone zoneEnd]]
ifFalse:
[processorLock critical:
[processor
runInMemory: coInterpreter memory
minimumAddress: guardPageSize
readOnlyBelow: methodZone zoneEnd]].
"((printRegisters or: [printInstructions]) and: [clickConfirm]) ifTrue:
[(self confirm: 'continue?') ifFalse:
[clickConfirm := false. self halt]]."
true] whileTrue]
on: ProcessorSimulationTrap
do: [:ex| ex applyTo: self].
true] whileTrue!
Item was changed:
----- Method: Cogit>>simulateLeafCallOf: (in category 'simulation only') -----
simulateLeafCallOf: someFunction
"Simulate execution of machine code that leaf-calls someFunction,
answering the result returned by someFunction."
"CogProcessorAlienInspector openFor: coInterpreter"
<doNotGenerate>
| priorSP priorPC priorLR spOnEntry bogusRetPC |
+ self cppIf: #DUAL_MAPPED_CODE_ZONE ifFalse:
+ [backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
+ [self assert: codeZoneIsExecutableNotWritable]].
- backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
- [self assert: codeZoneIsExecutableNotWritable].
self recordRegisters.
priorSP := processor sp.
priorPC := processor pc.
priorLR := backEnd hasLinkRegister ifTrue: [processor lr].
processor
simulateLeafCallOf: someFunction
nextpc: (bogusRetPC := 16rBADF00D5 roundTo: backEnd codeGranularity)
memory: coInterpreter memory.
spOnEntry := processor sp.
self recordInstruction: {'(simulated call of '. someFunction. ')'}.
^[[[processor pc between: self class guardPageSize and: methodZone zoneEnd] whileTrue:
[singleStep
ifTrue: [self recordProcessing.
self maybeBreakAt: processor pc.
processorLock critical:
[processor
singleStepIn: coInterpreter memory
minimumAddress: guardPageSize
readOnlyBelow: methodZone zoneEnd]]
ifFalse: [processorLock critical:
[processor
runInMemory: coInterpreter memory
minimumAddress: guardPageSize
readOnlyBelow: methodZone zoneEnd]]]]
on: ProcessorSimulationTrap, Error
do: [:ex|
"Again this is a hack for the processor simulators not properly simulating returns to bogus addresses.
In this case BochsX64Alien doesn't do the right thing."
processor pc = bogusRetPC ifTrue:
[self recordInstruction: {'(simulated (real) return to '. processor pc. ')'}.
^processor cResultRegister].
ex isProcessorSimulationTrap ifFalse:
[ex pass].
ex applyTo: self.
ex type == #return ifTrue:
[^processor cResultRegister]].
processor pc = bogusRetPC ifTrue:
[self recordInstruction: {'(simulated (real) return to '. processor pc. ')'}].
processor cResultRegister]
ensure:
[processor sp: priorSP.
processor pc: priorPC.
priorLR ifNotNil: [:lr| processor lr: lr]]!
Item was changed:
----- Method: Cogit>>sqMakeMemoryExecutableFrom:To:CodeToDataDelta: (in category 'initialization') -----
sqMakeMemoryExecutableFrom: startAddress To: endAddress CodeToDataDelta: codeToDataDeltaPtr
<doNotGenerate>
"Simulate setting executable permissions on the code zone. In production this will apply execute permission
to startAddress throguh endAddress - 1. If starting up in the DUAL_MAPPED_CODE_ZONE regime then it
will also create a writable mapping for the code zone and assign the distance from executable zone to the
+ writable zone through codeToDataDeltaPtr. If in this regime when simulating, the CogVMSimulator will
- writable zone throguh codeToDataDeltaPtr. If in this regime when simulating, the CogVMSimulator will
have allocated twice as much code memory as asked for (see CogVMSimulator openOn:extraMemory:) and
so simply set the delta to the code size."
(InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false]) ifTrue:
+ [codeToDataDeltaPtr at: 0 put: coInterpreter cogCodeSize + Cogit guardPageSize]!
- [codeToDataDeltaPtr at: 0 put: coInterpreter cogCodeSize]!
Item was changed:
----- Method: RegisterAllocatingCogit>>genForwardersInlinedIdenticalOrNotIf: (in category 'bytecode generators') -----
genForwardersInlinedIdenticalOrNotIf: orNot
| nextPC branchDescriptor unforwardRcvr argReg targetPC
unforwardArg rcvrReg postBranchPC retry fixup
comparison rcvrConstant argConstant
needMergeToTarget needMergeToContinue |
<var: #branchDescriptor type: #'BytecodeDescriptor *'>
- <var: #toContinueLabel type: #'AbstractInstruction *'>
- <var: #toTargetLabel type: #'AbstractInstruction *'>
<var: #comparison type: #'AbstractInstruction *'>
<var: #retry type: #'AbstractInstruction *'>
self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target |
branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetPC := target ].
"If an operand is an annotable constant, it may be forwarded, so we need to store it into a
register so the forwarder check can jump back to the comparison after unforwarding the constant.
However, if one of the operand is an unnanotable constant, does not allocate a register for it
(machine code will use operations on constants) and does not generate forwarder checks."
unforwardRcvr := (self ssValue: 1) mayBeAForwarder.
unforwardArg := self ssTop mayBeAForwarder.
(unforwardRcvr not and: [unforwardArg not])
ifTrue: [^self genVanillaInlinedIdenticalOrNotIf: orNot].
self assert: (unforwardArg or: [unforwardRcvr]).
"We use reg for non annotable constants to avoid duplicating objRef."
rcvrConstant := objectRepresentation isUnannotatableConstant: (self ssValue: 1).
argConstant := objectRepresentation isUnannotatableConstant: self ssTop.
self
allocateEqualsEqualsRegistersArgNeedsReg: argConstant not
rcvrNeedsReg: rcvrConstant not
into: [ :rcvr :arg | rcvrReg:= rcvr. argReg := arg ].
"If not followed by a branch, resolve to true or false."
(branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse]) ifFalse:
[^self
genIdenticalNoBranchArgIsConstant: argConstant
rcvrIsConstant: rcvrConstant
argReg: argReg
rcvrReg: rcvrReg
orNotIf: orNot].
self assert: (unforwardArg or: [unforwardRcvr]).
self ssPop: 2. "If we had moveAllButTop: 2 volatileSimStackEntriesToRegistersPreserving: we could avoid the extra ssPop:s"
self moveVolatileSimStackEntriesToRegistersPreserving:
(self allocatedRegisters bitOr: (argReg = NoReg
ifTrue: [self registerMaskFor: rcvrReg]
ifFalse:
[rcvrReg = NoReg
ifTrue: [self registerMaskFor: argReg]
ifFalse: [self registerMaskFor: rcvrReg and: argReg]])).
retry := self Label.
self ssPop: -2.
self genCmpArgIsConstant: argConstant rcvrIsConstant: rcvrConstant argReg: argReg rcvrReg: rcvrReg.
self ssPop: 2.
(self fixupAt: nextPC) notAFixup "The next instruction is dead. we can skip it."
ifTrue: [deadCode := true]
ifFalse: [self deny: deadCode]. "push dummy value below"
"self printSimStack; printSimStack: (self fixupAt: postBranchPC) mergeSimStack"
"If there are merges to be performed on the forward branches we have to execute
the merge code only along the path requiring that merge, and exactly once."
needMergeToTarget := self mergeRequiredForJumpTo: targetPC.
needMergeToContinue := self mergeRequiredForJumpTo: postBranchPC.
orNot == branchDescriptor isBranchTrue
ifFalse: "a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc"
[fixup := needMergeToContinue
ifTrue: [0] "jumps will fall-through to to-continue merge code"
ifFalse: [self ensureFixupAt: postBranchPC].
comparison := self JumpZero: (needMergeToTarget
ifTrue: [0] "comparison will be fixed up to to-target merge code"
ifFalse: [self ensureFixupAt: targetPC])]
ifTrue: "a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc"
[fixup := needMergeToTarget
ifTrue: [0] "jumps will fall-through to to-target merge code"
ifFalse: [self ensureFixupAt: targetPC].
comparison := self JumpZero: (needMergeToContinue
ifTrue: [0] "comparison will be fixed up to to-continue merge code"
ifFalse: [self ensureFixupAt: postBranchPC])].
"The forwarders check(s) need(s) to jump back to the comparison (retry) if a forwarder is found,
else jump forward either to the next forwarder check or to the postBranch or branch target (fixup).
But if there is merge code along a path, the jump must be to the merge code."
(unforwardArg and: [unforwardRcvr]) ifTrue:
[objectRepresentation genEnsureOopInRegNotForwarded: argReg scratchReg: TempReg jumpBackTo: retry].
objectRepresentation
genEnsureOopInRegNotForwarded: (unforwardRcvr ifTrue: [rcvrReg] ifFalse: [argReg])
scratchReg: TempReg
ifForwarder: retry
ifNotForwarder: fixup.
"If fixup is zero then the ifNotForwarder path falls through to a Label which is interpreted
as either to-continue or to-target, depending on orNot == branchDescriptor isBranchTrue."
orNot == branchDescriptor isBranchTrue
ifFalse: "a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc"
[needMergeToContinue ifTrue: "fall-through to to-continue merge code"
[self Jump: (self ensureFixupAt: postBranchPC)].
needMergeToTarget ifTrue: "fixup comparison to to-target merge code"
[comparison jmpTarget: self Label.
self Jump: (self ensureFixupAt: targetPC)]]
ifTrue: "a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc"
[needMergeToTarget ifTrue: "fall-through to to-target merge code"
[self Jump: (self ensureFixupAt: targetPC)].
needMergeToContinue ifTrue: "fixup comparison to to-continue merge code"
[comparison jmpTarget: self Label.
self Jump: (self ensureFixupAt: postBranchPC)]].
deadCode ifFalse: "duplicate the merge fixup's top of stack so as to avoid a false confict."
[self ssPushDesc: ((self fixupAt: nextPC) mergeSimStack at: simStackPtr + 1)].
^0!
Item was changed:
----- Method: SistaCogitClone>>genByteEqualsInlinePrimitiveResult:returnReg: (in category 'inline primitive generators') -----
genByteEqualsInlinePrimitiveResult: jmp returnReg: reg
"Byte equal is falling through if the result is true, or jumping using jmp if the result is false.
The method is required to set the jump target of jmp.
We look ahead for a branch and pipeline the jumps if possible..
ReturnReg is used only if not followed immediately by a branch."
| branchDescriptor nextPC postBranchPC targetBytecodePC localJump canElide |
+ <var: #jmp type: #'AbstractInstruction *'>
<var: #localJump type: #'AbstractInstruction *'>
<var: #branchDescriptor type: #'BytecodeDescriptor *'>
self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target |
branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetBytecodePC := target ].
"Case 1 - not followed by a branch"
(branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse])
ifFalse:
[self genMoveTrueR: reg.
localJump := self Jump: 0.
jmp jmpTarget: (self genMoveFalseR: reg).
localJump jmpTarget: self Label.
self ssPushRegister: reg.
^ 0].
"Case 2 - followed by a branch"
(self fixupAt: nextPC) notAFixup
ifTrue: "The next instruction is dead. we can skip it."
[deadCode := true.
self ensureFixupAt: targetBytecodePC.
self ensureFixupAt: postBranchPC ]
ifFalse:
[self ssPushConstant: objectMemory trueObject]. "dummy value"
"We can only elide the jump if the pc after nextPC is the same as postBranchPC.
Branch following means it may not be."
self nextDescriptorExtensionsAndNextPCInto:
[:iguana1 :iguana2 :iguana3 :followingPC| nextPC := followingPC].
canElide := deadCode and: [nextPC = postBranchPC].
branchDescriptor isBranchTrue
ifTrue:
[ self Jump: (self ensureNonMergeFixupAt: targetBytecodePC).
canElide
ifFalse: [ jmp jmpTarget: (self ensureNonMergeFixupAt: postBranchPC) ]
ifTrue: [ jmp jmpTarget: self Label ] ]
ifFalse: [ canElide ifFalse: [ self Jump: (self ensureNonMergeFixupAt: postBranchPC).
jmp jmpTarget: (self ensureNonMergeFixupAt: targetBytecodePC) ] ].
^0!
More information about the Vm-dev
mailing list