[Vm-dev] VM Maker: VMMaker.oscog-eem.1351.mcz
commits at source.squeak.org
commits at source.squeak.org
Tue Jun 16 00:29:35 UTC 2015
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.1351.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.1351
Author: eem
Time: 15 June 2015, 5:27:35.339 pm
UUID: 7152bdb6-6f96-4cbc-97ae-2095989ab1e2
Ancestors: VMMaker.oscog-eem.1350
Cogit:
Fix bug in rewriting compiler primitives on module
unload, etc. The old code didn't change the
assignment to the primitiveFunctionPointer, which is
needed for correct management of failing primitive
calls on Spur. Simplify the post compile hook to
eliminate the label parameter; this is local to the
Cogit. Move all the relevant variables down to
SimpleStackBasedCogit.
ARM Cogit:
Get out-of-line literals fully working, at least in the
simulator. Implement the necessary rewriting code
and implement saving and restoring around block
compiles for the cases where they're redone.
Fix some bugs with PushCw, and AndCqR.
=============== Diff against VMMaker.oscog-eem.1350 ===============
Item was changed:
----- Method: CoInterpreter>>functionForPrimitiveExternalCall: (in category 'plugin primitives') -----
functionForPrimitiveExternalCall: methodObj
"Arrange to call the external primitive directly. The complication is arranging
that the call can be flushed, given that it is embedded in machine code."
<returnTypeC: 'void (*functionForPrimitiveExternalCall(sqInt methodObj))(void)'>
| lit index functionPointer |
<var: #functionPointer declareC: #'void (*functionPointer)(void)'>
+ cogit setPostCompileHook: #recordCallOffsetIn:.
- cogit setPostCompileHook: #recordCallOffsetIn:of:.
(objectMemory literalCountOf: methodObj) > 0 ifFalse:
[^#primitiveExternalCall].
lit := self literal: 0 ofMethod: methodObj.
"Check if it's an array of length 4"
((objectMemory isArray: lit) and: [(objectMemory lengthOf: lit) = 4]) ifFalse:
[^#primitiveExternalCall].
index := objectMemory fetchPointer: 3 ofObject: lit.
((objectMemory isIntegerObject: index)
and: [(index := objectMemory integerValueOf: index) > 0
and: [index <= MaxExternalPrimitiveTableSize]]) ifFalse:
[^#primitiveExternalCall].
functionPointer := externalPrimitiveTable at: index - 1.
functionPointer = 0 ifTrue:
[^#primitiveExternalCall].
^functionPointer!
Item was changed:
----- Method: CogARMCompiler>>computeMaximumSize (in category 'generate machine code') -----
computeMaximumSize
"Because we don't use Thumb, each ARM instruction has 4 bytes. Many
abstract opcodes need more than one instruction. Instructions that refer
to constants and/or literals depend on literals being stored in-line or out-of-line.
N.B. The ^N forms are to get around the bytecode compiler's long branch
limits which are exceeded when each case jumps around the otherwise."
opcode
caseOf: {
"Noops & Pseudo Ops"
[Label] -> [^0].
[Literal] -> [^4].
[AlignmentNops] -> [^(operands at: 0) - 4].
[Fill16] -> [^4].
[Fill32] -> [^4].
[FillFromWord] -> [^4].
[Nop] -> [^4].
"Control"
[Call] -> [^4].
[CallFull] -> [^self literalLoadInstructionBytes + 4].
[JumpR] -> [^4].
[Jump] -> [^4].
[JumpFull] -> [^self literalLoadInstructionBytes + 4].
[JumpLong] -> [^4].
[JumpZero] -> [^4].
[JumpNonZero] -> [^4].
[JumpNegative] -> [^4].
[JumpNonNegative] -> [^4].
[JumpOverflow] -> [^4].
[JumpNoOverflow] -> [^4].
[JumpCarry] -> [^4].
[JumpNoCarry] -> [^4].
[JumpLess] -> [^4].
[JumpGreaterOrEqual] -> [^4].
[JumpGreater] -> [^4].
[JumpLessOrEqual] -> [^4].
[JumpBelow] -> [^4].
[JumpAboveOrEqual] -> [^4].
[JumpAbove] -> [^4].
[JumpBelowOrEqual] -> [^4].
[JumpLongZero] -> [^4].
[JumpLongNonZero] -> [^4].
[JumpFPEqual] -> [^8].
[JumpFPNotEqual] -> [^8].
[JumpFPLess] -> [^8].
[JumpFPGreaterOrEqual]-> [^8].
[JumpFPGreater] -> [^8].
[JumpFPLessOrEqual] -> [^8].
[JumpFPOrdered] -> [^8].
[JumpFPUnordered] -> [^8].
[RetN] -> [^(operands at: 0) = 0 ifTrue: [4] ifFalse: [8]].
[Stop] -> [^4].
"Arithmetic"
[AddCqR] -> [^self rotateable8bitSignedImmediate: (operands at: 0)
ifTrue: [:r :i :n| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[AndCqR] -> [^self rotateable8bitBitwiseImmediate: (operands at: 0)
ifTrue: [:r :i :n| 4]
ifFalse:
[self literalLoadInstructionBytes = 4
ifTrue: [8]
ifFalse:
[1 << (operands at: 0) highBit = ((operands at: 0) + 1)
ifTrue: [8]
ifFalse: [self literalLoadInstructionBytes + 4]]]].
[AndCqRR] -> [^self rotateable8bitBitwiseImmediate: (operands at: 0)
ifTrue: [:r :i :n| 4]
ifFalse:
[self literalLoadInstructionBytes = 4
ifTrue: [8]
ifFalse:
[1 << (operands at: 0) highBit = ((operands at: 0) + 1)
ifTrue: [8]
ifFalse: [self literalLoadInstructionBytes + 4]]]].
[CmpCqR] -> [^self rotateable8bitSignedImmediate: (operands at: 0)
ifTrue: [:r :i :n| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[OrCqR] -> [^self rotateable8bitImmediate: (operands at: 0)
ifTrue: [:r :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[SubCqR] -> [^self rotateable8bitSignedImmediate: (operands at: 0)
ifTrue: [:r :i :n| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[TstCqR] -> [^self rotateable8bitImmediate: (operands at: 0)
ifTrue: [:r :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[XorCqR] -> [^self rotateable8bitBitwiseImmediate: (operands at: 0)
ifTrue: [:r :i :n| 4]
ifFalse:
[self literalLoadInstructionBytes = 4
ifTrue: [8]
ifFalse:
[1 << (operands at: 0) highBit = ((operands at: 0) + 1)
ifTrue: [8]
ifFalse: [self literalLoadInstructionBytes + 4]]]].
[AddCwR] -> [^self literalLoadInstructionBytes + 4].
[AndCwR] -> [^self literalLoadInstructionBytes + 4].
[CmpCwR] -> [^self literalLoadInstructionBytes + 4].
[OrCwR] -> [^self literalLoadInstructionBytes + 4].
[SubCwR] -> [^self literalLoadInstructionBytes + 4].
[XorCwR] -> [^self literalLoadInstructionBytes + 4].
[AddRR] -> [^4].
[AndRR] -> [^4].
[CmpRR] -> [^4].
[OrRR] -> [^4].
[XorRR] -> [^4].
[SubRR] -> [^4].
[NegateR] -> [^4].
[LoadEffectiveAddressMwrR]
-> [^self rotateable8bitImmediate: (operands at: 0)
ifTrue: [:r :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[LogicalShiftLeftCqR] -> [^4].
[LogicalShiftRightCqR] -> [^4].
[ArithmeticShiftRightCqR] -> [^4].
[LogicalShiftLeftRR] -> [^4].
[LogicalShiftRightRR] -> [^4].
[ArithmeticShiftRightRR] -> [^4].
[AddRdRd] -> [^4].
[CmpRdRd] -> [^4].
[SubRdRd] -> [^4].
[MulRdRd] -> [^4].
[DivRdRd] -> [^4].
[SqrtRd] -> [^4].
"ARM Specific Arithmetic"
[SMULL] -> [^4].
[MSR] -> [^4].
[CMPSMULL] -> [^4]. "special compare for genMulR:R: usage"
"Data Movement"
[MoveCqR] -> [^self literalLoadInstructionBytes = 4
ifTrue: [self literalLoadInstructionBytes]
ifFalse:
[self rotateable8bitBitwiseImmediate: (operands at: 0)
ifTrue: [:r :i :n| 4]
ifFalse: [self literalLoadInstructionBytes]]].
[MoveCwR] -> [^self literalLoadInstructionBytes = 4
ifTrue: [self literalLoadInstructionBytes]
ifFalse:
[((self isAnInstruction: (operands at: 0)) "i.e. a label, which by definition will be in the current compilation."
or: [cogit addressIsInCurrentCompilation: (operands at: 0)])
ifTrue: [4]
ifFalse: [self literalLoadInstructionBytes]]].
[MoveRR] -> [^4].
[MoveRdRd] -> [^4].
[MoveAwR] -> [^(self isAddressRelativeToVarBase: (operands at: 0))
ifTrue: [4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[MoveRAw] -> [^(self isAddressRelativeToVarBase: (operands at: 1))
ifTrue: [4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[MoveRMwr] -> [^self is12BitValue: (operands at: 1)
ifTrue: [:u :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[MoveRdM64r] -> [^self literalLoadInstructionBytes + 4].
[MoveMbrR] -> [^self is12BitValue: (operands at: 0)
ifTrue: [:u :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[MoveRMbr] -> [^self is12BitValue: (operands at: 1)
ifTrue: [:u :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[MoveM16rR] -> [^self rotateable8bitImmediate: (operands at: 0)
ifTrue: [:r :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[MoveM64rRd] -> [^self literalLoadInstructionBytes + 4].
[MoveMwrR] -> [^self is12BitValue: (operands at: 0)
ifTrue: [:u :i| 4]
ifFalse: [self literalLoadInstructionBytes + 4]].
[MoveXbrRR] -> [^4].
[MoveRXbrR] -> [^4].
[MoveXwrRR] -> [^4].
[MoveRXwrR] -> [^4].
[PopR] -> [^4].
[PushR] -> [^4].
[PushCw] -> [^self literalLoadInstructionBytes = 4
ifTrue: [self literalLoadInstructionBytes + 4]
ifFalse:
[((self isAnInstruction: (operands at: 0)) "i.e. a label, which by definition will be in the current compilation."
or: [cogit addressIsInCurrentCompilation: (operands at: 0)])
ifTrue: [8]
+ ifFalse:
+ [self rotateable8bitBitwiseImmediate: (operands at: 0)
+ ifTrue: [:r :i :n| 8]
+ ifFalse: [self literalLoadInstructionBytes + 4]]]].
- ifFalse: [self literalLoadInstructionBytes + 4]]].
[PushCq] -> [^self literalLoadInstructionBytes = 4
ifTrue: [self literalLoadInstructionBytes + 4]
ifFalse:
[self rotateable8bitBitwiseImmediate: (operands at: 0)
ifTrue: [:r :i :n| 8]
ifFalse: [self literalLoadInstructionBytes + 4]]].
[PrefetchAw] -> [^(self isAddressRelativeToVarBase: (operands at: 0))
ifTrue: [4]
ifFalse: [self literalLoadInstructionBytes + 4]].
"Conversion"
[ConvertRRd] -> [^4].
}.
^0 "to keep C compiler quiet"
!
Item was changed:
----- Method: CogARMCompiler>>concretizeInvertibleDataOperationCqR: (in category 'generate machine code - concretize') -----
concretizeInvertibleDataOperationCqR: armOpcode
"Will get inlined into concretizeAt: switch."
"Xor == 1, And == 0, Or == 12, Bic == 14"
<inline: true>
| val rn |
val := operands at: 0.
rn := self concreteRegister: (operands at: 1).
self deny: opcode = CmpOpcode.
self rotateable8bitBitwiseImmediate: val
ifTrue:
[:rot :immediate : invert|
self machineCodeAt: 0
put: (self
type: 1
op: (invert ifTrue: [self inverseOpcodeFor: armOpcode] ifFalse: [armOpcode])
set: 1
rn: rn
rd: rn
shifterOperand: (rot >> 1"in this usage we have to halve the rot value" << 8 bitOr: immediate)).
^machineCodeSize := 4]
ifFalse: "let's try to see if the constant can be made from a simple shift of 0xFFFFFFFF"
[val > 0 ifTrue:
[| hb |
hb := val highBit.
1 << hb = (val +1) ifTrue: "MVN temp, #0, making 0xffffffff"
[self machineCodeAt: 0 put: (self mvn: ConcreteIPReg imm: 0 ror: 0).
"Then armOpcode reg, temp reg, lsr #(32-hb)"
self machineCodeAt: 4 put: (self dataOpType: armOpcode rd: rn rn: rn rm: ConcreteIPReg lsr: 32 - hb).
^machineCodeSize := 8]].
+ ^self concretizeDataOperationCqR: armOpcode].
- ^self concretizeDataOperationCwR: armOpcode].
^0 "to keep Slang happy"!
Item was changed:
----- Method: CogARMCompiler>>concretizePushCw (in category 'generate machine code - concretize') -----
concretizePushCw
"Will get inlined into concretizeAt: switch."
<inline: true>
+ | word instrOffset |
+ word := operands at: 0.
+ ((self isAnInstruction: word)
+ or: [cogit addressIsInCurrentCompilation: word])
+ ifTrue:
+ [instrOffset := self loadCwInto: ConcreteIPReg]
+ ifFalse:
+ [self
+ rotateable8bitBitwiseImmediate: word
+ ifTrue:
+ [:rot :immediate :invert|
+ self machineCodeAt: 0
+ put: (invert
+ ifTrue: [self mvn: ConcreteIPReg imm: immediate ror: rot]
+ ifFalse: [self mov: ConcreteIPReg imm: immediate ror: rot]).
+ instrOffset := 4]
+ ifFalse:
+ [instrOffset := self loadCwInto: ConcreteIPReg]].
- | instrOffset |
- instrOffset := self loadCwInto: ConcreteIPReg.
self machineCodeAt: instrOffset put: (self pushR: ConcreteIPReg).
^machineCodeSize := instrOffset + 4!
Item was added:
+ ----- Method: CogOutOfLineLiteralsARMCompiler>>rewriteFullTransferAt:target:expectedInstruction: (in category 'inline cacheing') -----
+ rewriteFullTransferAt: callSiteReturnAddress target: callTargetAddress expectedInstruction: expectedInstruction
+ "Rewrite a CallFull or JumpFull instruction to transfer to a different target.
+ This variant is used to rewrite cached primitive calls. Answer the extent
+ of the code change which is used to compute the range of the icache to flush."
+ <var: #callSiteReturnAddress type: #usqInt>
+ <var: #callTargetAddress type: #usqInt>
+ self assert: (self instructionBeforeAddress: callSiteReturnAddress) = expectedInstruction.
+ objectMemory longAt: (self pcRelativeAddressAt: callSiteReturnAddress - 8) put: callTargetAddress.
+ "self cCode: ''
+ inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 8 to: (self pcRelativeAddressAt: callSiteReturnAddress - 8)]."
+ ^0!
Item was changed:
----- Method: CogOutOfLineLiteralsARMCompiler>>rewriteInlineCacheAt:tag:target: (in category 'inline cacheing') -----
rewriteInlineCacheAt: callSiteReturnAddress tag: cacheTag target: callTargetAddress
"Rewrite an inline cache to call a different target for a new tag. This variant is used
to link unlinked sends in ceSend:to:numArgs: et al. Answer the extent of the code
change which is used to compute the range of the icache to flush."
<var: #callSiteReturnAddress type: #usqInt>
<var: #callTargetAddress type: #usqInt>
| call callDistance |
callTargetAddress >= cogit minCallAddress ifFalse:
[self error: 'linking callsite to invalid address'].
callDistance := (callTargetAddress - (callSiteReturnAddress + 8 "pc offset"- 4 "return offset")) signedIntToLong.
self assert: (self isInImmediateJumpRange: callDistance). "we don't support long call updates here"
call := self bl: callDistance.
objectMemory
longAt: callSiteReturnAddress - 4 put: call;
longAt: (self pcRelativeAddressAt: callSiteReturnAddress - 8) put: cacheTag.
self assert: (self inlineCacheTagAt: callSiteReturnAddress) = cacheTag.
"self cCode: ''
+ inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 8 to: (self pcRelativeAddressAt: callSiteReturnAddress - 8)]."
- inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 20 to: callSiteReturnAddress - 1]."
^4!
Item was added:
+ ----- Method: CogOutOfLineLiteralsARMCompiler>>storeLiteral:beforeFollowingAddress: (in category 'inline cacheing') -----
+ storeLiteral: literal beforeFollowingAddress: followingAddress
+ "Rewrite the literal in the instruction immediately preceeding followingAddress."
+ objectMemory longAt: (self pcRelativeAddressAt: followingAddress - 4) put: literal!
Item was changed:
----- Method: CogSSOptStatus>>printStateOn: (in category 'printing') -----
printStateOn: aStream
<doNotGenerate>
(isReceiverResultRegLive notNil
or: [ssEntry notNil]) ifTrue:
[aStream
nextPut: $(;
print: isReceiverResultRegLive;
space.
+ ssEntry
+ ifNil: [aStream nextPutAll: 'ssEntry is nil']
+ ifNotNil: [ssEntry printStateOn: aStream].
- ssEntry printStateOn: aStream.
aStream nextPut: $)]!
Item was changed:
CogClass subclass: #Cogit
+ instanceVariableNames: 'coInterpreter objectMemory objectRepresentation processor threadManager methodZone methodZoneBase codeBase minValidCallAddress lastNInstructions simulatedAddresses simulatedTrampolines simulatedVariableGetters simulatedVariableSetters printRegisters printInstructions compilationTrace clickConfirm breakPC breakBlock singleStep guardPageSize traceFlags traceStores breakMethod methodObj initialPC endPC methodOrBlockNumArgs inBlock needsFrame hasYoungReferent primitiveIndex backEnd literalsManager callerSavedRegMask postCompileHook methodLabel stackCheckLabel blockEntryLabel blockEntryNoContextSwitch blockNoContextSwitchOffset stackOverflowCall sendMiss missOffset entryPointMask checkedEntryAlignment uncheckedEntryAlignment cmEntryOffset entry cmNoCheckEntryOffset noCheckEntry picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 numPICCases firstCPICCaseOffset cPICCaseSize cPICEndSize closedPICSize openPICSize fixups abstractOpcodes generatorTable primitiveGeneratorTable byte0 byte1 byte2 byte3 bytecodePC bytecodeSetOffset opcodeIndex numAbstractOpcodes blockStarts blockCount labelCounter cStackAlignment expectedSPAlignment expectedFPAlignment codeModified maxLitIndex ceMethodAbortTrampoline cePICAbortTrampoline ceCheckForInterruptTrampoline ceCPICMissTrampoline ceReturnToInterpreterTrampoline ceBaseFrameReturnTrampoline ceSendMustBeBooleanAddTrueTrampoline ceSendMustBeBooleanAddFalseTrampoline ceCannotResumeTrampoline ceEnterCogCodePopReceiverReg ceCallCogCodePopReceiverReg ceCallCogCodePopReceiverAndClassRegs cePrimReturnEnterCogCode cePrimReturnEnterCogCodeProfiling ceNonLocalReturnTrampoline ceFetchContextInstVarTrampoline ceStoreContextInstVarTrampoline ceEnclosingObjectTrampoline ceCaptureCStackPointers ceFlushICache ceCheckFeaturesFunction ceTraceLinkedSendTrampoline ceTraceBlockActivationTrampoline ceTraceStoreTrampoline ceGetSP ordinarySendTrampolines superSendTrampolines directedSuperSendTrampolines dynamicSuperSendTrampolines outerSendTrampolines selfSendTrampolines firstSend lastSend realCEEnterCogCodePopReceiverReg realCECallCogCodePopReceiverReg realCECallCogCodePopReceiverAndClassRegs trampolineTableIndex trampolineAddresses objectReferencesInRuntime runtimeObjectRefIndex cFramePointerInUse debugPrimCallStackOffset ceTryLockVMOwner ceUnlockVMOwner cogMethodSurrogateClass cogBlockMethodSurrogateClass extA extB numIRCs indexOfIRC theIRCs implicitReceiverSendTrampolines nsSendCacheSurrogateClass tempOop'
- instanceVariableNames: 'coInterpreter objectMemory objectRepresentation processor threadManager methodZone methodZoneBase codeBase minValidCallAddress lastNInstructions simulatedAddresses simulatedTrampolines simulatedVariableGetters simulatedVariableSetters printRegisters printInstructions compilationTrace clickConfirm breakPC breakBlock singleStep guardPageSize traceFlags traceStores breakMethod methodObj initialPC endPC methodOrBlockNumArgs inBlock needsFrame hasYoungReferent primitiveIndex backEnd literalsManager callerSavedRegMask postCompileHook primInvokeLabel methodLabel stackCheckLabel blockEntryLabel blockEntryNoContextSwitch blockNoContextSwitchOffset stackOverflowCall sendMiss missOffset entryPointMask checkedEntryAlignment uncheckedEntryAlignment cmEntryOffset entry cmNoCheckEntryOffset noCheckEntry picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 numPICCases firstCPICCaseOffset cPICCaseSize cPICEndSize closedPICSize openPICSize fixups abstractOpcodes generatorTable primitiveGeneratorTable byte0 byte1 byte2 byte3 bytecodePC bytecodeSetOffset opcodeIndex numAbstractOpcodes blockStarts blockCount labelCounter cStackAlignment expectedSPAlignment expectedFPAlignment codeModified maxLitIndex ceMethodAbortTrampoline cePICAbortTrampoline ceCheckForInterruptTrampoline ceCPICMissTrampoline ceReturnToInterpreterTrampoline ceBaseFrameReturnTrampoline ceSendMustBeBooleanAddTrueTrampoline ceSendMustBeBooleanAddFalseTrampoline ceCannotResumeTrampoline ceEnterCogCodePopReceiverReg ceCallCogCodePopReceiverReg ceCallCogCodePopReceiverAndClassRegs cePrimReturnEnterCogCode cePrimReturnEnterCogCodeProfiling ceNonLocalReturnTrampoline ceFetchContextInstVarTrampoline ceStoreContextInstVarTrampoline ceEnclosingObjectTrampoline ceCaptureCStackPointers ceFlushICache ceCheckFeaturesFunction ceTraceLinkedSendTrampoline ceTraceBlockActivationTrampoline ceTraceStoreTrampoline ceGetSP ordinarySendTrampolines superSendTrampolines directedSuperSendTrampolines dynamicSuperSendTrampolines outerSendTrampolines selfSendTrampolines firstSend lastSend realCEEnterCogCodePopReceiverReg realCECallCogCodePopReceiverReg realCECallCogCodePopReceiverAndClassRegs trampolineTableIndex trampolineAddresses objectReferencesInRuntime runtimeObjectRefIndex cFramePointerInUse debugPrimCallStackOffset ceTryLockVMOwner ceUnlockVMOwner cogMethodSurrogateClass cogBlockMethodSurrogateClass extA extB numIRCs indexOfIRC theIRCs implicitReceiverSendTrampolines nsSendCacheSurrogateClass tempOop'
classVariableNames: 'AltBlockCreationBytecodeSize AltFirstSpecialSelector AltNSSendIsPCAnnotated AnnotationConstantNames AnnotationShift AnnotationsWithBytecodePCs BlockCreationBytecodeSize Debug DisplacementMask DisplacementX2N EagerInstructionDecoration FirstAnnotation FirstSpecialSelector HasBytecodePC IsAbsPCReference IsAnnotationExtension IsDirectedSuperSend IsDisplacementX2N IsNSDynamicSuperSend IsNSImplicitReceiverSend IsNSSelfSend IsNSSendCall IsObjectReference IsRelativeCall IsSendCall IsSuperSend MapEnd MaxCompiledPrimitiveIndex MaxStackAllocSize MaxX2NDisplacement NSCClassTagIndex NSCEnclosingObjectIndex NSCNumArgsIndex NSCSelectorIndex NSCTargetIndex NSSendIsPCAnnotated NumObjRefsInRuntime NumOopsPerNSC NumTrampolines ProcessorClass'
poolDictionaries: 'CogCompilationConstants CogMethodConstants CogRTLOpcodes VMBasicConstants VMBytecodeConstants VMObjectIndices VMStackFrameOffsets'
category: 'VMMaker-JIT'!
Cogit class
instanceVariableNames: 'generatorTable primitiveTable'!
!Cogit commentStamp: 'eem 4/6/2015 15:56' prior: 0!
I am the code generator for the Cog VM. My job is to produce machine code versions of methods for faster execution and to manage inline caches for faster send performance. I can be tested in the current image using my class-side in-image compilation facilities. e.g. try
StackToRegisterMappingCogit genAndDis: (Integer >> #benchFib)
I have concrete subclasses that implement different levels of optimization:
SimpleStackBasedCogit is the simplest code generator.
StackToRegisterMappingCogit is the current production code generator It defers pushing operands
to the stack until necessary and implements a register-based calling convention for low-arity sends.
StackToRegisterMappingCogit is an experimental code generator with support for counting
conditional branches, intended to support adaptive optimization.
coInterpreter <CoInterpreterSimulator>
the VM's interpreter with which I cooperate
methodZoneManager <CogMethodZoneManager>
the manager of the machine code zone
objectRepresentation <CogObjectRepresentation>
the object used to generate object accesses
processor <BochsIA32Alien|?>
the simulator that executes the IA32/x86 machine code I generate when simulating execution in Smalltalk
simulatedTrampolines <Dictionary of Integer -> MessageSend>
the dictionary mapping trap jump addresses to run-time routines used to warp from simulated machine code in to the Smalltalk run-time.
simulatedVariableGetters <Dictionary of Integer -> MessageSend>
the dictionary mapping trap read addresses to variables in run-time objects used to allow simulated machine code to read variables in the Smalltalk run-time.
simulatedVariableSetters <Dictionary of Integer -> MessageSend>
the dictionary mapping trap write addresses to variables in run-time objects used to allow simulated machine code to write variables in the Smalltalk run-time.
printRegisters printInstructions clickConfirm <Boolean>
flags controlling debug printing and code simulation
breakPC <Integer>
machine code pc breakpoint
cFramePointer cStackPointer <Integer>
the variables representing the C stack & frame pointers, which must change on FFI callback and return
selectorOop <sqInt>
the oop of the methodObj being compiled
methodObj <sqInt>
the bytecode method being compiled
initialPC endPC <Integer>
the start and end pcs of the methodObj being compiled
methodOrBlockNumArgs <Integer>
argument count of current method or block being compiled
needsFrame <Boolean>
whether methodObj or block needs a frame to execute
primitiveIndex <Integer>
primitive index of current method being compiled
methodLabel <CogAbstractOpcode>
label for the method header
blockEntryLabel <CogAbstractOpcode>
label for the start of the block dispatch code
stackOverflowCall <CogAbstractOpcode>
label for the call of ceStackOverflow in the method prolog
sendMissCall <CogAbstractOpcode>
label for the call of ceSICMiss in the method prolog
entryOffset <Integer>
offset of method entry code from start (header) of method
entry <CogAbstractOpcode>
label for the first instruction of the method entry code
noCheckEntryOffset <Integer>
offset of the start of a method proper (after the method entry code) from start (header) of method
noCheckEntry <CogAbstractOpcode>
label for the first instruction of start of a method proper
fixups <Array of <AbstractOpcode Label | nil>>
the labels for forward jumps that will be fixed up when reaching the relevant bytecode. fixup shas one element per byte in methodObj's bytecode
abstractOpcodes <Array of <AbstractOpcode>>
the code generated when compiling methodObj
byte0 byte1 byte2 byte3 <Integer>
individual bytes of current bytecode being compiled in methodObj
bytecodePointer <Integer>
bytecode pc (same as Smalltalk) of the current bytecode being compiled
opcodeIndex <Integer>
the index of the next free entry in abstractOpcodes (this code is translated into C where OrderedCollection et al do not exist)
numAbstractOpcodes <Integer>
the number of elements in abstractOpcocdes
blockStarts <Array of <BlockStart>>
the starts of blocks in the current method
blockCount
the index into blockStarts as they are being noted, and hence eventually the total number of blocks in the current method
labelCounter <Integer>
a nicety for numbering labels not needed in the production system but probably not expensive enough to worry about
ceStackOverflowTrampoline <Integer>
ceSend0ArgsTrampoline <Integer>
ceSend1ArgsTrampoline <Integer>
ceSend2ArgsTrampoline <Integer>
ceSendNArgsTrampoline <Integer>
ceSendSuper0ArgsTrampoline <Integer>
ceSendSuper1ArgsTrampoline <Integer>
ceSendSuper2ArgsTrampoline <Integer>
ceSendSuperNArgsTrampoline <Integer>
ceSICMissTrampoline <Integer>
ceCPICMissTrampoline <Integer>
ceStoreCheckTrampoline <Integer>
ceReturnToInterpreterTrampoline <Integer>
ceBaseFrameReturnTrampoline <Integer>
ceSendMustBeBooleanTrampoline <Integer>
ceClosureCopyTrampoline <Integer>
the various trampolines (system-call-like jumps from machine code to the run-time).
See Cogit>>generateTrampolines for the mapping from trampoline to run-time
routine and then read the run-time routine for a funcitonal description.
ceEnterCogCodePopReceiverReg <Integer>
the enilopmart (jump from run-time to machine-code)
methodZoneBase <Integer>
!
Cogit class
instanceVariableNames: 'generatorTable primitiveTable'!
Item was changed:
----- Method: Cogit class>>declareCVarsIn: (in category 'translation') -----
declareCVarsIn: aCCodeGenerator
#( 'coInterpreter' 'objectMemory' 'methodZone' 'objectRepresentation'
'cogBlockMethodSurrogateClass' 'cogMethodSurrogateClass' 'nsSendCacheSurrogateClass'
'threadManager' 'processor' 'lastNInstructions' 'simulatedAddresses'
'simulatedTrampolines' 'simulatedVariableGetters' 'simulatedVariableSetters'
'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 removeConstant: #COGMTVM. "this should be defined at compile time"
aCCodeGenerator
addHeaderFile:'<stddef.h>'; "for e.g. offsetof"
addHeaderFile:'"sqCogStackAlignment.h"';
addHeaderFile:'"dispdbg.h"'; "must preceed 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: #ceGetSP
declareC: 'unsigned long (*ceGetSP)(void)';
var: #ceCaptureCStackPointers
declareC: 'void (*ceCaptureCStackPointers)(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: #ceFlushICache
declareC: 'static void (*ceFlushICache)(unsigned long from, unsigned long to)';
var: #ceCheckFeaturesFunction
declareC: 'static unsigned long (*ceCheckFeaturesFunction)(void)';
var: #ceTryLockVMOwner
declareC: 'unsigned long (*ceTryLockVMOwner)(void)';
var: #ceUnlockVMOwner
declareC: 'void (*ceUnlockVMOwner)(void)';
var: #postCompileHook
+ declareC: 'void (*postCompileHook)(CogMethod *)';
- declareC: 'void (*postCompileHook)(CogMethod *, void *)';
var: #openPICList declareC: 'CogMethod *openPICList = 0';
var: #maxMethodBefore type: #'CogBlockMethod *'.
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'.
- var: #methodLabel declareC: 'AbstractInstruction * const methodLabel = &aMethodLabel';
- var: #primInvokeLabel type: #'AbstractInstruction *'.
self declareC: #(abstractOpcodes stackCheckLabel
blockEntryLabel blockEntryNoContextSwitch
stackOverflowCall sendMiss
entry noCheckEntry selfSendEntry dynSuperEntry
picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1)
as: #'AbstractInstruction *'
in: aCCodeGenerator.
aCCodeGenerator
declareVar: #blockStarts type: #'BlockStart *';
declareVar: #fixups type: #'BytecodeFixup *'.
aCCodeGenerator
var: #ordinarySendTrampolines
declareC: 'sqInt ordinarySendTrampolines[NumSendTrampolines]';
var: #superSendTrampolines
declareC: 'sqInt superSendTrampolines[NumSendTrampolines]';
var: #directedSuperSendTrampolines
declareC: 'sqInt directedSuperSendTrampolines[NumSendTrampolines]';
var: #selfSendTrampolines
declareC: 'sqInt selfSendTrampolines[NumSendTrampolines]';
var: #dynamicSuperSendTrampolines
declareC: 'sqInt dynamicSuperSendTrampolines[NumSendTrampolines]';
var: #implicitReceiverSendTrampolines
declareC: 'sqInt implicitReceiverSendTrampolines[NumSendTrampolines]';
var: #outerSendTrampolines
declareC: 'sqInt outerSendTrampolines[NumSendTrampolines]';
var: #trampolineAddresses
declareC: 'static char *trampolineAddresses[NumTrampolines*2]';
var: #objectReferencesInRuntime
declareC: 'static sqInt objectReferencesInRuntime[NumObjRefsInRuntime]';
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: #CFramePointer type: #'void *';
declareVar: #CStackPointer type: #'void *';
declareVar: #minValidCallAddress type: #'unsigned long';
declareVar: #debugPrimCallStackOffset type: #'unsigned long'.
aCCodeGenerator vmClass generatorTable ifNotNil:
[:bytecodeGenTable|
aCCodeGenerator
var: #generatorTable
declareC: 'static BytecodeDescriptor generatorTable[', bytecodeGenTable size, ']',
(self tableInitializerFor: bytecodeGenTable
in: aCCodeGenerator);
var: #primitiveGeneratorTable
declareC: 'static PrimitiveDescriptor primitiveGeneratorTable[MaxCompiledPrimitiveIndex+1]',
(self tableInitializerFor: aCCodeGenerator vmClass primitiveTable
in: aCCodeGenerator)].
"In C the abstract opcode names clash with the Smalltak 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'!
Item was changed:
----- Method: Cogit>>compileCogMethod: (in category 'compile abstract instructions') -----
compileCogMethod: selector
<returnTypeC: #'CogMethod *'>
| numBytecodes numBlocks numCleanBlocks result extra |
hasYoungReferent := (objectMemory isYoungObject: methodObj)
or: [objectMemory isYoung: selector].
methodOrBlockNumArgs := coInterpreter argumentCountOf: methodObj.
inBlock := false.
- primInvokeLabel := nil.
postCompileHook := nil.
maxLitIndex := -1.
extra := ((primitiveIndex := coInterpreter primitiveIndexOf: methodObj) > 0
and: [(coInterpreter isQuickPrimitiveIndex: primitiveIndex) not])
ifTrue: [30]
ifFalse: [10].
initialPC := coInterpreter startPCOfMethod: methodObj.
"initial estimate. Actual endPC is determined in scanMethod."
endPC := (coInterpreter isQuickPrimitiveIndex: primitiveIndex)
ifTrue: [initialPC - 1]
ifFalse: [objectMemory numBytesOf: methodObj].
numBytecodes := endPC - initialPC + 1.
self allocateOpcodes: (numBytecodes + extra) * self estimateOfAbstractOpcodesPerBytecodes
bytecodes: numBytecodes
ifFail: [^coInterpreter cCoerceSimple: MethodTooBig to: #'CogMethod *'].
literalsManager resetLiterals: (objectMemory literalCountOf: methodObj).
(numBlocks := self scanMethod) < 0 ifTrue:
[^coInterpreter cCoerceSimple: numBlocks to: #'CogMethod *'].
numCleanBlocks := self scanForCleanBlocks.
self allocateBlockStarts: numBlocks + numCleanBlocks.
blockCount := 0.
numCleanBlocks > 0 ifTrue:
[self addCleanBlockStarts].
(self maybeAllocAndInitCounters
and: [self maybeAllocAndInitIRCs]) ifFalse: "Inaccurate error code, but it'll do. This will likely never fail."
[^coInterpreter cCoerceSimple: InsufficientCodeSpace to: #'CogMethod *'].
blockEntryLabel := nil.
methodLabel dependent: nil.
(result := self compileEntireMethod) < 0 ifTrue:
[^coInterpreter cCoerceSimple: result to: #'CogMethod *'].
^self generateCogMethod: selector!
Item was changed:
----- Method: Cogit>>generateCogMethod: (in category 'generate machine code') -----
generateCogMethod: selector
"We handle jump sizing simply. First we make a pass that asks each
instruction to compute its maximum size. Then we make a pass that
sizes jumps based on the maxmimum sizes. Then we make a pass
that fixes up jumps. When fixing up a jump the jump is not allowed to
choose a smaller offset but must stick to the size set in the second pass."
<returnTypeC: #'CogMethod *'>
| codeSize headerSize mapSize totalSize startAddress result method |
<var: #method type: #'CogMethod *'>
headerSize := self sizeof: CogMethod.
methodLabel address: methodZone freeStart.
self computeMaximumSizes.
methodLabel concretizeAt: methodZone freeStart.
codeSize := self generateInstructionsAt: methodLabel address + headerSize.
mapSize := self generateMapAt: nil start: methodLabel address + cmNoCheckEntryOffset.
totalSize := methodZone roundUpLength: headerSize + codeSize + mapSize.
totalSize > MaxMethodSize ifTrue:
[^self cCoerceSimple: MethodTooBig to: #'CogMethod *'].
startAddress := methodZone allocate: totalSize.
startAddress = 0 ifTrue:
[^self cCoerceSimple: InsufficientCodeSpace to: #'CogMethod *'].
self assert: startAddress + cmEntryOffset = entry address.
self assert: startAddress + cmNoCheckEntryOffset = noCheckEntry address.
result := self outputInstructionsAt: startAddress + headerSize.
self assert: startAddress + headerSize + codeSize = result.
backEnd padIfPossibleWithNopsFrom: result to: startAddress + totalSize - mapSize.
self generateMapAt: startAddress + totalSize - 1 start: startAddress + cmNoCheckEntryOffset.
self fillInBlockHeadersAt: startAddress.
method := self fillInMethodHeader: (self cCoerceSimple: startAddress to: #'CogMethod *')
size: totalSize
selector: selector.
+ postCompileHook ifNotNil:
+ [self perform: postCompileHook with: method.
- postCompileHook notNil ifTrue:
- [self perform: postCompileHook with: method with: primInvokeLabel.
postCompileHook := nil].
^method!
Item was changed:
----- Method: Cogit>>setPostCompileHook: (in category 'jit - api') -----
setPostCompileHook: aFunction
<api>
+ <var: #aFunction declareC: #'void (*aFunction)(CogMethod *)'>
- <var: #aFunction declareC: #'void (*aFunction)(CogMethod *, void *)'>
postCompileHook := aFunction!
Item was added:
+ ----- Method: InLineLiteralsManager>>resetForBlockCompile (in category 'compile abstract instructions') -----
+ resetForBlockCompile
+ <inline: true>!
Item was added:
+ ----- Method: InLineLiteralsManager>>saveForBlockCompile (in category 'compile abstract instructions') -----
+ saveForBlockCompile
+ <inline: true>!
Item was changed:
VMClass subclass: #OutOfLineLiteralsManager
+ instanceVariableNames: 'cogit objectMemory firstOpcodeIndex nextLiteralIndex lastDumpedLiteralIndex literals literalsSize savedFirstOpcodeIndex savedNextLiteralIndex savedLastDumpedLiteralIndex'
- instanceVariableNames: 'cogit objectMemory firstOpcodeIndex nextLiteralIndex lastDumpedLiteralIndex literals literalsSize'
classVariableNames: ''
poolDictionaries: 'CogCompilationConstants CogRTLOpcodes'
category: 'VMMaker-JIT'!
!OutOfLineLiteralsManager commentStamp: 'eem 6/7/2015 12:10' prior: 0!
An OutOfLineLiteralsManager manages the dumping of literals for backends that wat to keep literals out-of-line, accessed by pc-relative addressing.
Instance Variables
cogit: <Cogit>!
Item was added:
+ ----- Method: OutOfLineLiteralsManager>>resetForBlockCompile (in category 'compile abstract instructions') -----
+ resetForBlockCompile
+ firstOpcodeIndex := savedFirstOpcodeIndex.
+ nextLiteralIndex := savedNextLiteralIndex.
+ lastDumpedLiteralIndex := savedLastDumpedLiteralIndex!
Item was added:
+ ----- Method: OutOfLineLiteralsManager>>saveForBlockCompile (in category 'compile abstract instructions') -----
+ saveForBlockCompile
+ savedFirstOpcodeIndex := firstOpcodeIndex.
+ savedNextLiteralIndex := nextLiteralIndex.
+ savedLastDumpedLiteralIndex := lastDumpedLiteralIndex!
Item was changed:
Cogit subclass: #SimpleStackBasedCogit
+ instanceVariableNames: 'primSetFunctionLabel primInvokeLabel externalPrimCallOffsets externalPrimJumpOffsets externalSetPrimOffsets'
- instanceVariableNames: 'externalPrimCallOffsets externalPrimJumpOffsets'
classVariableNames: ''
poolDictionaries: 'VMMethodCacheConstants VMObjectIndices'
category: 'VMMaker-JIT'!
!SimpleStackBasedCogit commentStamp: '<historical>' prior: 0!
I am the stage one JIT for Cog that does not attempt to eliminate the stack via deferred code generation.!
Item was changed:
----- Method: SimpleStackBasedCogit class>>declareCVarsIn: (in category 'translation') -----
declareCVarsIn: aCCodeGenerator
aCCodeGenerator
var: #externalPrimCallOffsets
declareC: 'sqInt externalPrimCallOffsets[MaxNumArgs + 1]';
var: #externalPrimJumpOffsets
+ declareC: 'sqInt externalPrimJumpOffsets[MaxNumArgs + 1]';
+ var: #externalSetPrimOffsets
+ declareC: 'sqInt externalSetPrimOffsets[MaxNumArgs + 1]';
+ var: #primSetFunctionLabel type: #'AbstractInstruction *';
+ var: #primInvokeLabel type: #'AbstractInstruction *'!
- declareC: 'sqInt externalPrimJumpOffsets[MaxNumArgs + 1]'!
Item was changed:
----- Method: SimpleStackBasedCogit>>compileInterpreterPrimitive: (in category 'primitive generators') -----
compileInterpreterPrimitive: primitiveRoutine
"Compile a call to an interpreter primitive. Call the C routine with the
usual stack-switching dance, test the primFailCode and then either
return on success or continue to the method body."
<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
| flags jmp jmpSamplePrim retry continuePostSamplePrim jmpSampleNonPrim continuePostSampleNonPrim |
<var: #jmp type: #'AbstractInstruction *'>
<var: #retry type: #'AbstractInstruction *'>
<var: #jmpSamplePrim type: #'AbstractInstruction *'>
<var: #continuePostSamplePrim type: #'AbstractInstruction *'>
<var: #jmpSampleNonPrim type: #'AbstractInstruction *'>
<var: #continuePostSampleNonPrim type: #'AbstractInstruction *'>
"Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers"
self genExternalizePointersForPrimitiveCall.
"Switch to the C stack."
self genLoadCStackPointersForPrimCall.
flags := coInterpreter primitivePropertyFlags: primitiveIndex.
(flags anyMask: PrimCallDoNotJIT) ifTrue:
[^ShouldNotJIT].
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
["Test nextProfileTick for being non-zero and call checkProfileTick if so"
objectMemory wordSize = 4
ifTrue:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
self OrR: TempReg R: ClassReg]
ifFalse:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self CmpCq: 0 R: TempReg].
"If set, jump to record sample call."
jmpSampleNonPrim := self JumpNonZero: 0.
continuePostSampleNonPrim := self Label].
"Old full prim trace is in VMMaker-eem.550 and prior"
self recordPrimTrace ifTrue:
[self genFastPrimTraceUsing: ClassReg and: SendNumArgsReg].
"Clear the primFailCode and set argumentCount"
retry := self MoveCq: 0 R: TempReg.
self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
methodOrBlockNumArgs ~= 0 ifTrue:
[self MoveCq: methodOrBlockNumArgs R: TempReg].
self MoveR: TempReg Aw: coInterpreter argumentCountAddress.
"If required, set primitiveFunctionPointer and newMethod"
(flags anyMask: PrimCallNeedsPrimitiveFunction) ifTrue:
[self MoveCw: primitiveRoutine asInteger R: TempReg.
+ primSetFunctionLabel :=
self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress].
(flags anyMask: PrimCallNeedsNewMethod+PrimCallMayCallBack) ifTrue:
["The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness."
(flags anyMask: PrimCallMayCallBack) ifTrue:
[needsFrame := true].
methodLabel addDependent:
(self annotateAbsolutePCRef:
(self MoveCw: methodLabel asInteger R: ClassReg)).
self MoveMw: (self offset: CogMethod of: #methodObject) r: ClassReg R: TempReg.
self MoveR: TempReg Aw: coInterpreter newMethodAddress].
"Invoke the primitive"
self PrefetchAw: coInterpreter primFailCodeAddress.
(flags anyMask: PrimCallMayCallBack)
ifTrue: "Sideways call the C primitive routine so that we return through cePrimReturnEnterCogCode."
["On Spur ceActivateFailingPrimitiveMethod: would like to retry if forwarders
are found. So insist on PrimCallNeedsPrimitiveFunction being set too."
self assert: (flags anyMask: PrimCallNeedsPrimitiveFunction).
backEnd genSubstituteReturnAddress:
((flags anyMask: PrimCallCollectsProfileSamples)
ifTrue: [cePrimReturnEnterCogCodeProfiling]
ifFalse: [cePrimReturnEnterCogCode]).
self JumpFullRT: primitiveRoutine asInteger.
primInvokeLabel := self Label.
jmp := jmpSamplePrim := continuePostSamplePrim := nil]
ifFalse:
["Call the C primitive routine."
self CallFullRT: primitiveRoutine asInteger.
primInvokeLabel := self Label.
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
[self assert: (flags anyMask: PrimCallNeedsNewMethod).
"Test nextProfileTick for being non-zero and call checkProfileTick if so"
objectMemory wordSize = 4
ifTrue:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
self OrR: TempReg R: ClassReg]
ifFalse:
[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
self CmpCq: 0 R: TempReg].
"If set, jump to record sample call."
jmpSamplePrim := self JumpNonZero: 0.
continuePostSamplePrim := self Label].
objectRepresentation maybeCompileRetry: retry onPrimitiveFail: primitiveIndex.
self maybeCompileAllocFillerCheck.
"Switch back to the Smalltalk stack. Stack better be in either of these two states:
success: stackPointer -> result (was receiver)
arg1
...
argN
return pc
failure: receiver
arg1
...
stackPointer -> argN
return pc
In either case we can push the instructionPointer or load it into the LinkRegister to reestablish the return pc"
self MoveAw: coInterpreter instructionPointerAddress
R: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [ClassReg]).
backEnd genLoadStackPointers.
"Test primitive failure"
self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
backEnd hasLinkRegister ifFalse: [self PushR: ClassReg]. "Restore return pc on CISCs"
self flag: 'ask concrete code gen if move sets condition codes?'.
self CmpCq: 0 R: TempReg.
jmp := self JumpNonZero: 0.
"Fetch result from stack"
self MoveMw: (backEnd hasLinkRegister ifTrue: [0] ifFalse: [objectMemory wordSize])
r: SPReg
R: ReceiverResultReg.
self RetN: objectMemory wordSize]. "return to caller, popping receiver"
(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
["The sample is collected by cePrimReturnEnterCogCode for external calls"
jmpSamplePrim notNil ifTrue:
["Call ceCheckProfileTick: to record sample and then continue."
jmpSamplePrim jmpTarget: self Label.
self assert: (flags anyMask: PrimCallNeedsNewMethod).
self CallFullRT: (self cCode: [#ceCheckProfileTick asUnsignedLong]
inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick]).
"reenter the post-primitive call flow"
self Jump: continuePostSamplePrim].
"Null newMethod and call ceCheckProfileTick: to record sample and then continue.
ceCheckProfileTick will map null/0 to coInterpreter nilObject"
jmpSampleNonPrim jmpTarget: self Label.
self MoveCq: 0 R: TempReg.
self MoveR: TempReg Aw: coInterpreter newMethodAddress.
self CallFullRT: (self cCode: [#ceCheckProfileTick asUnsignedLong]
inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick]).
"reenter the post-primitive call flow"
self Jump: continuePostSampleNonPrim].
jmp notNil ifTrue:
["Jump to restore of receiver reg and proceed to frame build for failure."
jmp jmpTarget: self Label.
"Restore receiver reg from stack. If on RISCs ret pc is in LinkReg, if on CISCs ret pc is on stack."
self MoveMw: objectMemory wordSize * (methodOrBlockNumArgs + (backEnd hasLinkRegister ifTrue: [0] ifFalse: [1]))
r: SPReg
R: ReceiverResultReg].
^0!
Item was added:
+ ----- Method: SimpleStackBasedCogit>>recordCallOffsetIn: (in category 'external primitive support') -----
+ recordCallOffsetIn: cogMethod
+ <api>
+ <var: #cogMethod type: #'CogMethod *'>
+ | offset offsetTable |
+ <var: #offsetTable type: #'sqInt *'>
+ offset := primSetFunctionLabel address - cogMethod asInteger.
+ (externalSetPrimOffsets at: cogMethod cmNumArgs) isNil
+ ifTrue: [externalSetPrimOffsets at: cogMethod cmNumArgs put: offset]
+ ifFalse: [self assert: (externalSetPrimOffsets at: cogMethod cmNumArgs) = offset].
+ offsetTable := (backEnd isCallPreceedingReturnPC: primInvokeLabel address asUnsignedInteger)
+ ifTrue: [externalPrimCallOffsets]
+ ifFalse: [externalPrimJumpOffsets].
+ offset := primInvokeLabel address - cogMethod asInteger.
+ (offsetTable at: cogMethod cmNumArgs) isNil
+ ifTrue: [offsetTable at: cogMethod cmNumArgs put: offset]
+ ifFalse: [self assert: (offsetTable at: cogMethod cmNumArgs) = offset]!
Item was removed:
- ----- Method: SimpleStackBasedCogit>>recordCallOffsetIn:of: (in category 'external primitive support') -----
- recordCallOffsetIn: cogMethod of: callLabelArg
- <api>
- <var: #cogMethod type: #'CogMethod *'>
- <var: #callLabelArg type: #'void *'>
- | callLabel offset offsetTable |
- <var: #callLabel type: #'AbstractInstruction *'>
- <var: #offsetTable type: #'sqInt *'>
- callLabel := callLabelArg. "this function is exported but AbstractInstruction isn't; hence pun through void *"
- offsetTable := (backEnd isCallPreceedingReturnPC: callLabel address asUnsignedInteger)
- ifTrue: [externalPrimCallOffsets]
- ifFalse: [externalPrimJumpOffsets].
- offset := callLabel address - cogMethod asInteger.
- (offsetTable at: cogMethod cmNumArgs) isNil
- ifTrue: [offsetTable at: cogMethod cmNumArgs put: offset]
- ifFalse: [self assert: (offsetTable at: cogMethod cmNumArgs) = offset]!
Item was changed:
----- Method: SimpleStackBasedCogit>>rewritePrimInvocationIn:to: (in category 'external primitive support') -----
rewritePrimInvocationIn: cogMethod to: primFunctionPointer
<api>
<var: #cogMethod type: #'CogMethod *'>
<var: #primFunctionPointer declareC: #'void (*primFunctionPointer)(void)'>
| primIndex flags address extent |
self assert: cogMethod cmType = CMMethod.
primIndex := coInterpreter
primitiveIndexOfMethod: cogMethod methodObject
header: cogMethod methodHeader.
flags := coInterpreter primitivePropertyFlags: primIndex.
+ (flags anyMask: PrimCallNeedsPrimitiveFunction) ifTrue:
+ [backEnd
+ storeLiteral: primFunctionPointer asUnsignedInteger
+ beforeFollowingAddress: cogMethod asUnsignedInteger
+ + (externalSetPrimOffsets at: cogMethod cmNumArgs)].
"See compileInterpreterPrimitive:"
+ (flags anyMask: PrimCallMayCallBack)
- (flags bitAnd: PrimCallMayCallBack) ~= 0
ifTrue:
[address := cogMethod asUnsignedInteger
+ (externalPrimJumpOffsets at: cogMethod cmNumArgs).
extent := backEnd
rewriteJumpFullAt: address
target: (self cCode: [primFunctionPointer asUnsignedInteger]
inSmalltalk: [self simulatedTrampolineFor: primFunctionPointer])]
ifFalse:
[address := cogMethod asUnsignedInteger
+ (externalPrimCallOffsets at: cogMethod cmNumArgs).
extent := backEnd
rewriteCallFullAt: address
target: (self cCode: [primFunctionPointer asUnsignedInteger]
inSmalltalk: [self simulatedTrampolineFor: primFunctionPointer])].
+ processor
+ flushICacheFrom: cogMethod asUnsignedInteger + cmNoCheckEntryOffset
+ to: address asUnsignedInteger + extent!
- processor flushICacheFrom: address asUnsignedInteger to: address asUnsignedInteger + extent!
Item was changed:
----- Method: SimpleStackBasedCogit>>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>
super setInterpreter: aCoInterpreter.
externalPrimJumpOffsets := CArrayAccessor on: (Array new: MaxNumArgs + 1).
+ externalPrimCallOffsets := CArrayAccessor on: (Array new: MaxNumArgs + 1).
+ externalSetPrimOffsets := CArrayAccessor on: (Array new: MaxNumArgs + 1)!
- externalPrimCallOffsets := CArrayAccessor on: (Array new: MaxNumArgs + 1)!
Item was changed:
----- Method: StackToRegisterMappingCogit>>compileBlockBodies (in category 'compile abstract instructions') -----
compileBlockBodies
<inline: false>
| result compiledBlocksCount blockStart savedNeedsFrame savedNumArgs savedNumTemps
initialStackPtr initialOpcodeIndex initialIndexOfIRC |
<var: #blockStart type: #'BlockStart *'>
self assert: blockCount > 0.
"scanBlock: in compileBlockEntry: sets both of these appropriately for each block."
savedNeedsFrame := needsFrame.
savedNumArgs := methodOrBlockNumArgs.
savedNumTemps := methodOrBlockNumTemps.
inBlock := true.
compiledBlocksCount := 0.
[compiledBlocksCount < blockCount] whileTrue:
[blockStart := self blockStartAt: compiledBlocksCount.
self scanBlock: blockStart.
initialOpcodeIndex := opcodeIndex.
+ literalsManager saveForBlockCompile.
self cppIf: #NewspeakVM ifTrue:
[initialIndexOfIRC := indexOfIRC].
[self compileBlockEntry: blockStart.
initialStackPtr := simStackPtr.
(result := self compileAbstractInstructionsFrom: blockStart startpc + (self pushNilSize: methodObj numInitialNils: blockStart numInitialNils)
through: blockStart startpc + blockStart span - 1) < 0 ifTrue:
[^result].
"If the final simStackPtr is less than the initial simStackPtr then scanBlock: over-
estimated the number of initial nils (because it assumed one or more pushNils to
produce an operand were pushNils to initialize temps. This is very rare, so
compensate by checking, adjusting numInitialNils and recompiling the block body.
N.B. No need to reinitialize the literalsManager because it answers existing literals."
initialStackPtr = simStackPtr]
whileFalse:
[self assert: initialStackPtr > simStackPtr.
blockStart numInitialNils: blockStart numInitialNils + simStackPtr - initialStackPtr.
blockStart fakeHeader dependent: nil.
self reinitializeFixupsFrom: blockStart startpc + blockStart numInitialNils
through: blockStart startpc + blockStart span - 1.
self cCode: 'bzero(abstractOpcodes + initialOpcodeIndex,
(opcodeIndex - initialOpcodeIndex) * sizeof(AbstractInstruction))'
inSmalltalk: [initialOpcodeIndex to: opcodeIndex - 1 do:
[:i| abstractOpcodes at: i put: (CogCompilerClass for: self)]].
opcodeIndex := initialOpcodeIndex.
+ literalsManager resetForBlockCompile.
self cppIf: #NewspeakVM ifTrue:
[indexOfIRC := initialIndexOfIRC]].
compiledBlocksCount := compiledBlocksCount + 1].
needsFrame := savedNeedsFrame.
methodOrBlockNumArgs := savedNumArgs.
methodOrBlockNumTemps := savedNumTemps.
^0!
More information about the Vm-dev
mailing list