[Vm-dev] VM Maker: VMMaker.oscog-tpr.803.mcz
commits at source.squeak.org
commits at source.squeak.org
Sat Jul 5 00:17:21 UTC 2014
tim Rowledge uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-tpr.803.mcz
==================== Summary ====================
Name: VMMaker.oscog-tpr.803
Author: tpr
Time: 3 July 2014, 11:13:54.112 pm
UUID: c45ecb48-3e6b-469d-9cb6-463e70ef2cce
Ancestors: VMMaker.oscog-eem.802
Changes that get us to compiling primitive 3 and needing all the floating point ARM stuff worked on.
Correct enilopmart used in activateCoggedNewMethod: avoids stack imbalance failure.
Fix genPushRegisterArgsForNumArgs: to push the receiverresultreg even when there are no args, just like the ia32 does.
Change to pushingthe substitue return address rather than putting it in the LR and leaving it to get smashed later.
Correct genEnilopmartFor: [and: regArg2][ and: regArg3] called: to pop into the PC when appropriate. LR just doesn't do the job in this case.
=============== Diff against VMMaker.oscog-eem.802 ===============
Item was changed:
----- Method: CoInterpreter>>activateCoggedNewMethod: (in category 'message sending') -----
activateCoggedNewMethod: inInterpreter
"Activate newMethod when newMethod has been cogged, i.e. create a machine-code frame and (re)enter machine-code."
| methodHeader cogMethod rcvr numTemps errorCode switched |
<var: #cogMethod type: #'CogMethod *'>
methodHeader := self rawHeaderOf: newMethod.
self assert: (self isCogMethodReference: methodHeader).
cogMethod := self cCoerceSimple: methodHeader to: #'CogMethod *'.
methodHeader := cogMethod methodHeader.
rcvr := self stackValue: cogMethod cmNumArgs. "could new rcvr be set at point of send?"
self push: instructionPointer.
cogMethod stackCheckOffset = 0 ifTrue:
["frameless method; nothing to activate..."
cogit numRegArgs > 0 ifTrue: "dont use and: so as to get Slang to inline cogit numRegArgs > 0"
[cogMethod cmNumArgs <= cogit numRegArgs ifTrue:
[self callRegisterArgCogMethod: cogMethod at: cogit noCheckEntryOffset receiver: rcvr]].
self push: cogMethod asInteger + cogit noCheckEntryOffset.
self push: rcvr.
cogit ceCallCogCodePopReceiverReg.
self error: 'should not be reached'].
self push: framePointer.
framePointer := stackPointer.
self push: cogMethod asInteger.
self push: objectMemory nilObject. "FxThisContext field"
self push: rcvr.
"clear remaining temps to nil"
numTemps := self temporaryCountOfMethodHeader: methodHeader.
cogMethod cmNumArgs + 1 to: numTemps do:
[:i | self push: objectMemory nilObject].
(self methodHeaderHasPrimitive: methodHeader) ifTrue:
[| initialPC |
"Store the error code if the method starts with a long store temp. No instructionPointer skip because we're heading for machine code."
initialPC := (self initialPCForHeader: methodHeader method: newMethod) + (self sizeOfCallPrimitiveBytecode: methodHeader).
primFailCode ~= 0 ifTrue:
[(objectMemory byteAt: initialPC) = (self longStoreBytecodeForHeader: methodHeader) ifTrue:
[errorCode := self getErrorObjectFromPrimFailCode.
self stackTopPut: errorCode "nil if primFailCode == 1, or primFailCode"].
primFailCode := 0]].
"Now check for stack overflow or an event (interrupt, must scavenge, etc)."
stackPointer >= stackLimit ifTrue:
[self assert: cogMethod stackCheckOffset > cogit noCheckEntryOffset.
self push: cogMethod asInteger + cogMethod stackCheckOffset.
self push: rcvr.
+ cogit ceEnterCogCodePopReceiverReg.
- cogit ceCallCogCodePopReceiverReg.
self error: 'should not be reached'].
instructionPointer := cogMethod asInteger + cogMethod stackCheckOffset.
switched := self handleStackOverflowOrEventAllowContextSwitch: (self canContextSwitchIfActivating: newMethod header: methodHeader).
self returnToExecutive: inInterpreter postContextSwitch: switched!
Item was changed:
----- Method: CogARMCompiler>>genAlignCStackSavingRegisters:numArgs:wordAlignment: (in category 'abi') -----
genAlignCStackSavingRegisters: saveRegs numArgs: numArgs wordAlignment: alignment
"ARM needs 8 byte stack alignment but it's hard to be sure where the stack is at this
point due to the complexities of whether we push the return address or not. So do
a simple bitAnd to effectively round-down the SP - except the vagaries of the ARM
instruction set means we actually need a BIC sp, sp, $7"
- cogit gen: BICCqR operand: 2r111 operand: SPReg.
^0!
Item was changed:
----- Method: CogARMCompiler>>genPushRegisterArgsForNumArgs: (in category 'smalltalk calling convention') -----
genPushRegisterArgsForNumArgs: numArgs
"Ensure that the register args are pushed before the retpc for arity <= self numRegArgs."
"This is easy on a RISC like ARM because the return address is in the link register. Putting
the receiver and args above the return address means the CoInterpreter has a single
+ machine-code frame format which saves us a lot of work
+ NOTA BENE: we do NOT push the return address here, which means it must be dealt with later."
- machine-code frame format which saves us a lot of work."
numArgs <= cogit numRegArgs ifTrue:
[self assert: cogit numRegArgs <= 2.
+ cogit PushR: ReceiverResultReg.
+ numArgs > 0 ifTrue:
+ [cogit PushR: Arg0Reg.
- numArgs > 0 ifTrue:
- [cogit PushR: ReceiverResultReg.
- cogit PushR: Arg0Reg.
numArgs > 1 ifTrue:
[cogit PushR: Arg1Reg]]]!
Item was changed:
----- Method: CogARMCompiler>>genSubstituteReturnAddress: (in category 'abstract instructions') -----
genSubstituteReturnAddress: retpc
<inline: true>
<returnTypeC: #'AbstractInstruction *'>
+ "This code needs to be tightly correlated across:-
+ GdbARMAlien>simulateJumpCallOf:memory:
+ GdbARMAlien>simulateCallOf:nextpc:memory:
+ GdbARMAlien>simulateReturnIn:
+ GdbARMAlien>retpcIn:
+ CogARMCompiler>genSubstituteReturnAddress:
+ Getting any one of them doing something different will cause much pain"
+ ^cogit PushCw: retpc!
- ^cogit MoveCw: retpc R: LR!
Item was changed:
----- Method: CogARMCompilerTests>>testAdd (in category 'tests') -----
testAdd
"self new testAdd"
"the forms are valid, "
"test AddCqR"
self concreteCompilerClass registersWithNamesDo: [ :reg :regName |
#(0 16rF 16rFF) do:
[:n| | inst len |
inst := self gen: AddCqR operand: n operand: reg.
len := inst concretizeAt: 0.
self processor
disassembleInstructionAt: 0
In: inst machineCode object
into: [:str :sz| | plainJane herIntended |
plainJane := self strip: str.
herIntended := 'adds ', regName, ', ', regName, ', #', n asString.
self assert: (plainJane match: herIntended)]]].
"test AddCwR"
self concreteCompilerClass registersWithNamesDo: [ :reg :regName |
#(16rFFFFFFFF 16r88888888 0) do:
[:n| | inst len |
inst := self gen: AddCwR operand: n operand: reg.
len := inst concretizeAt: 0.
self processor
disassembleInstructionAt: 0
In: inst machineCode object
into: [:str :sz| | plainJane herIntended |
plainJane := self strip: str.
+ herIntended := 'mov sl, #', (n bitAnd: 16rFF << 24) asString.
- herIntended := 'mov sl, #', (n bitAnd: 16rFF << 8) asString.
self assert: (plainJane match: herIntended)].
self processor
disassembleInstructionAt: 4
In: inst machineCode object
into: [:str :sz| | plainJane herIntended |
plainJane := self strip: str.
herIntended := 'orr sl, sl, #', (n bitAnd: 16rFF << 16) asString.
self assert: (plainJane match: herIntended)].
self processor
disassembleInstructionAt: 8
In: inst machineCode object
into: [:str :sz| | plainJane herIntended |
plainJane := self strip: str.
+ herIntended := 'orr sl, sl, #', (n bitAnd: 16rFF << 8) signedIntFromLong asString.
- herIntended := 'orr sl, sl, #', (n bitAnd: 16rFF << 24) signedIntFromLong asString.
self assert: (plainJane match: herIntended)].
self processor
disassembleInstructionAt: 12
In: inst machineCode object
into: [:str :sz| | plainJane herIntended |
plainJane := self strip: str.
herIntended := 'orr sl, sl, #', (n bitAnd: 16rFF) asString.
self assert: (plainJane match: herIntended)].
self processor
disassembleInstructionAt: 16
In: inst machineCode object
into: [:str :sz| | plainJane herIntended |
plainJane := self strip: str.
herIntended := 'adds ', regName, ', ', regName, ', sl'.
self assert: (plainJane match: herIntended)]]]
!
Item was changed:
----- Method: Cogit>>compileAbort (in category 'compile abstract instructions') -----
compileAbort
"The start of a CogMethod has a call to a run-time abort routine that either
handles an in-line cache failure or a stack overflow. The routine selects the
path depending on ReceiverResultReg; if zero it takes the stack overflow
path; if nonzero the in-line cache miss path. Neither of these paths returns.
The abort routine must be called; In the callee the method is located by
adding the relevant offset to the return address of the call."
stackOverflowCall := self MoveCq: 0 R: ReceiverResultReg.
backEnd hasLinkRegister
ifTrue:
+ ["if we have a link register we will assume that it does not get automatically pushed onto the stack
+ and thus needs to be manually handled here"
+ sendMiss := self PushR: LinkReg.
- [sendMiss := self PushR: LinkReg.
sendMissCall := self Call: (self methodAbortTrampolineFor: methodOrBlockNumArgs)]
ifFalse:
[sendMiss :=
sendMissCall := self Call: (self methodAbortTrampolineFor: methodOrBlockNumArgs)]!
Item was changed:
----- Method: Cogit>>compilePICProlog: (in category 'in-line cacheing') -----
compilePICProlog: numArgs
"The start of a PIC has a call to a run-time abort routine that either handles
a dispatch to an interpreted method or a dispatch of an MNU case. The
routine selects the path depending on ClassReg; if zero it takes the MNU
path; if nonzero the dispatch to interpreter path. Neither of these paths
returns. The abort routine must be called; In the callee the PIC is located
by adding the relevant offset to the return address of the call."
mnuCall := self MoveCq: 0 R: ClassReg.
backEnd hasLinkRegister
ifTrue:
+ ["if we have a link register we will assume that it does not get automatically pushed onto the stack
+ and thus needs to be manually handled here"
+ interpretLabel := self PushR: LinkReg.
- [interpretLabel := self PushR: LinkReg.
interpretCall := self Call: (self picAbortTrampolineFor: numArgs)]
ifFalse:
[interpretLabel :=
interpretCall := self Call: (self picAbortTrampolineFor: numArgs)].
^0!
Item was changed:
----- Method: Cogit>>genCheckForInterruptsTrampoline (in category 'initialization') -----
genCheckForInterruptsTrampoline
opcodeIndex := 0.
+ "if we have a link register we will assume that it does not get automatically pushed onto the stack
+ and thus there is no need to pop it before saving to instructionPointerAddress"
backEnd hasLinkRegister
ifTrue:
[self MoveR: LinkReg Aw: coInterpreter instructionPointerAddress]
ifFalse:
[self PopR: TempReg. "instruction pointer"
self MoveR: TempReg Aw: coInterpreter instructionPointerAddress].
^self genTrampolineFor: #ceCheckForInterrupts
called: 'ceCheckForInterruptsTrampoline'
numArgs: 0
arg: nil
arg: nil
arg: nil
arg: nil
saveRegs: false
pushLinkReg: false
resultReg: nil
appendOpcodes: true!
Item was changed:
----- Method: Cogit>>genEnilopmartFor:and:and:called: (in category 'initialization') -----
genEnilopmartFor: regArg1 and: regArg2 and: regArg3 called: trampolineName
"An enilopmart (the reverse of a trampoline) is a piece of code that makes
the system-call-like transition from the C runtime into generated machine
code. The desired arguments and entry-point are pushed on a stackPage's
stack. The enilopmart pops off the values to be loaded into registers and
then executes a return instruction to pop off the entry-point and jump to it.
BEFORE AFTER (stacks grow down)
whatever stackPointer -> whatever
target address => reg1 = reg1val, etc
reg1val pc = target address
reg2val
stackPointer -> reg3val
C.F. genCallEnilopmartFor:and:and:called:"
<returnTypeC: 'void (*genEnilopmartForandandcalled(sqInt regArg1, sqInt regArg2, sqInt regArg3, char *trampolineName))(void)'>
| size endAddress enilopmart |
opcodeIndex := 0.
backEnd genLoadStackPointers.
self PopR: regArg3.
self PopR: regArg2.
self PopR: regArg1.
backEnd hasLinkRegister
ifTrue: [backEnd hasPCRegister
+ ifTrue: [self PopR: PCReg]
- ifTrue: [self PopR: LinkReg]
ifFalse: [self PopR: LinkReg; RetN: 0]]
ifFalse: [self RetN: 0].
self computeMaximumSizes.
size := self generateInstructionsAt: methodZoneBase.
endAddress := self outputInstructionsAt: methodZoneBase.
self assert: methodZoneBase + size = endAddress.
enilopmart := methodZoneBase.
methodZoneBase := self alignUptoRoutineBoundary: endAddress.
backEnd nopsFrom: endAddress to: methodZoneBase - 1.
self recordGeneratedRunTime: trampolineName address: enilopmart.
^self cCoerceSimple: enilopmart to: #'void (*)(void)'!
Item was changed:
----- Method: Cogit>>genEnilopmartFor:and:called: (in category 'initialization') -----
genEnilopmartFor: regArg1 and: regArg2 called: trampolineName
"An enilopmart (the reverse of a trampoline) is a piece of code that makes
the system-call-like transition from the C runtime into generated machine
code. The desired arguments and entry-point are pushed on a stackPage's
stack. The enilopmart pops off the values to be loaded into registers and
then executes a return instruction to pop off the entry-point and jump to it.
BEFORE AFTER (stacks grow down)
whatever stackPointer -> whatever
target address => reg1 = reg1val, etc
reg1val pc = target address
stackPointer -> reg2val
C.F. genCallEnilopmartFor:and:and:called:"
<returnTypeC: 'void (*genEnilopmartForandandcalled(sqInt regArg1, sqInt regArg2, char *trampolineName))(void)'>
| size endAddress enilopmart |
opcodeIndex := 0.
backEnd genLoadStackPointers.
self PopR: regArg2.
self PopR: regArg1.
backEnd hasLinkRegister
ifTrue: [backEnd hasPCRegister
+ ifTrue: [self PopR: PCReg]
- ifTrue: [self PopR: LinkReg]
ifFalse: [self PopR: LinkReg; RetN: 0]]
ifFalse: [self RetN: 0].
self computeMaximumSizes.
size := self generateInstructionsAt: methodZoneBase.
endAddress := self outputInstructionsAt: methodZoneBase.
self assert: methodZoneBase + size = endAddress.
enilopmart := methodZoneBase.
methodZoneBase := self alignUptoRoutineBoundary: endAddress.
backEnd nopsFrom: endAddress to: methodZoneBase - 1.
self recordGeneratedRunTime: trampolineName address: enilopmart.
^self cCoerceSimple: enilopmart to: #'void (*)(void)'!
Item was changed:
----- Method: Cogit>>genEnilopmartFor:called: (in category 'initialization') -----
genEnilopmartFor: regArg1 called: trampolineName
"An enilopmart (the reverse of a trampoline) is a piece of code that makes
the system-call-like transition from the C runtime into generated machine
code. The desired arguments and entry-point are pushed on a stackPage's
stack. The enilopmart pops off the values to be loaded into registers and
then executes a return instruction to pop off the entry-point and jump to it.
BEFORE AFTER (stacks grow down)
whatever stackPointer -> whatever
target address => reg1 = reg1val
stackPointer -> reg1val pc = target address
C.F. genCallEnilopmartFor:and:and:called:"
<returnTypeC: 'void (*genEnilopmartForandandcalled(sqInt regArg1, char *trampolineName))(void)'>
| size endAddress enilopmart |
opcodeIndex := 0.
backEnd genLoadStackPointers.
self PopR: regArg1.
backEnd hasLinkRegister
ifTrue: [backEnd hasPCRegister
+ ifTrue: [self PopR: PCReg]
- ifTrue: [self PopR: LinkReg]
ifFalse: [self PopR: LinkReg; RetN: 0]]
ifFalse: [self RetN: 0].
self computeMaximumSizes.
size := self generateInstructionsAt: methodZoneBase.
endAddress := self outputInstructionsAt: methodZoneBase.
self assert: methodZoneBase + size = endAddress.
enilopmart := methodZoneBase.
methodZoneBase := self alignUptoRoutineBoundary: endAddress.
backEnd nopsFrom: endAddress to: methodZoneBase - 1.
self recordGeneratedRunTime: trampolineName address: enilopmart.
^self cCoerceSimple: enilopmart to: #'void (*)(void)'!
More information about the Vm-dev
mailing list