Eliot Miranda uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2028.mcz ==================== Summary ==================== Name: VMMaker.oscog-eem.2028 Author: eem Time: 6 December 2016, 6:08:48.407545 pm UUID: a66fd6b5-4adb-4770-83b4-a29622cc4314 Ancestors: VMMaker.oscog-eem.2027 RegisterAllocatingCogit: No longer flush on conditional branch; instead leave it up to the merge logic to harmonise the stacks on join. Consequently, fix the order of generating fixups and pushing the dummyValue when no deadCode in genSpecialSelectorEqalsEquals:. This didn't matter with the StackToRegisterMappingCogit but in the RegisterAllocatingCogit now that teh stack is not flushed on conditional branch, it is critical; the fixup must be established before the dummyValue is pushed. Also consequently make mergeCurrentSimStackWith:/reconcileForwardsWith: more intelligent, able to merge when the target has spills and the current does not. At least mark that mustBeBoolean: will switch to an interpreted frame (yet to be implementd). Fix a slip in genSubConstant:R: which always used TempReg and didn't use its argument. Fix a slip in temp name printing for sim stack printing. Use = to compare objectmemory falseObject and objectMemory trueObject swith SSConstant entry constants. These are integeger oops, not objects, and so integral comparison shoudl be used. =============== Diff against VMMaker.oscog-eem.2027 =============== Item was added: + ----- Method: CoInterpreter>>ceSendMustBeBooleanInterpreting: (in category 'trampolines') ----- + ceSendMustBeBooleanInterpreting: anObject + "For RegisterAllocatingCogit we want the address following a conditional branch not to be reachable, so we + don't have to generate code to reload registers. Instead simply convert to an interpreter frame." + <api> + self shouldBeImplemented. + instructionPointer := self popStack. + self push: anObject. + self push: instructionPointer. + ^self + ceSendAbort: (objectMemory splObj: SelectorMustBeBoolean) + to: anObject + numArgs: 0! Item was changed: ----- Method: CogRegisterAllocatingSimStackEntry>>reconcileForwardsWith: (in category 'compile abstract instructions') ----- reconcileForwardsWith: targetEntry "Make the state of the receiver, a stack entry at the end of a basic block, the same as the corresponding simStackEntry at the target of a preceding jump to the beginning of the next basic block. Make sure targetEntry reflects the state of the merged simStack; it will be installed as the current entry by restoreSimStackAtMergePoint: in mergeWithFixupIfRequired:. Answer if the liveRegister for the targetEntry (if any) should be deassigned; this is because if merging a non-temp with a temp that has a live register we can assign to the register, but must unassign the register from the temp, otherwise the temp will acquire the merged value without an assignment." <var: #targetEntry type: #'targetEntry *'> | targetReg | (targetReg := targetEntry registerOrNone) = NoReg ifTrue: + [| reg | + self assert: targetEntry spilled. + (self isSameEntryAs: targetEntry) ifTrue: + [self assert: spilled. + ^false]. + (reg := self registerOrNone) = NoReg ifTrue: [reg := TempReg]. + self storeToReg: reg. + spilled + ifTrue: [cogit MoveR: reg Mw: targetEntry offset r: targetEntry register] + ifFalse: [cogit PushR: reg]. - [self assert: (self isSameEntryAs: targetEntry). ^false]. liveRegister ~= NoReg ifTrue: [liveRegister ~= targetReg ifTrue: [cogit MoveR: liveRegister R: targetReg]. (spilled and: [targetEntry spilled not]) ifTrue: [cogit AddCq: objectRepresentation wordSize R: SPReg]. ^false]. spilled ifTrue: [targetEntry spilled ifFalse: [cogit PopR: targetReg. "KISS; generate the least number of instructions..." ^false]] ifFalse: [targetEntry spilled ifTrue: [cogit SubCq: objectRepresentation wordSize R: SPReg]]. type caseOf: { [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg]. [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg]. [SSConstant] -> [cogit genMoveConstant: constant R: targetReg]. [SSRegister] -> [register ~= targetReg ifTrue: [cogit MoveR: register R: targetReg]] }. (targetEntry type = SSConstant and: [type ~= SSConstant or: [constant ~= targetEntry constant]]) ifTrue: [targetEntry register: targetReg; type: SSRegister]. "If merging a non-temp with a temp that has a live register we can assign to the register, but must unassign the register from the temp, otherwise the temp will acquire the merged value without an assignment." ^targetEntry type = SSBaseOffset and: [targetEntry register = FPReg and: [(self isSameEntryAs: targetEntry) not]]! Item was added: + ----- Method: RegisterAllocatingCogit>>directionForMergeWith: (in category 'bytecode generator support') ----- + directionForMergeWith: mergeSimStack + "Answer > 0 if values must be pushed from simStack to merge with mergeSimStack, otherwise < 0 (the default)." + <var: #mergeSimStack type: #'SimStackEntry *'> + <inline: true> + simStackPtr to: methodOrBlockNumArgs by: -1 do: + [:i| + (self simStack: mergeSimStack at: i) spilled ifTrue: + [(self simStack: simStack at: i) spilled ifFalse: + [^1]]]. + ^-1! Item was changed: ----- Method: RegisterAllocatingCogit>>genForwardersInlinedIdenticalOrNotIf: (in category 'bytecode generators') ----- genForwardersInlinedIdenticalOrNotIf: orNot | nextPC branchDescriptor unforwardRcvr argReg targetBytecodePC unforwardArg rcvrReg postBranchPC label fixup | <var: #branchDescriptor type: #'BytecodeDescriptor *'> <var: #label type: #'AbstractInstruction *'> self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target | branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetBytecodePC := 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 := (objectRepresentation isUnannotatableConstant: (self ssValue: 1)) not. unforwardArg := (objectRepresentation isUnannotatableConstant: self ssTop) not. self allocateEqualsEqualsRegistersArgNeedsReg: unforwardArg rcvrNeedsReg: unforwardRcvr 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: unforwardArg not rcvrIsConstant: unforwardRcvr not argReg: argReg rcvrReg: rcvrReg orNotIf: orNot]. label := self Label. self genCmpArgIsConstant: unforwardArg not rcvrIsConstant: unforwardRcvr not argReg: argReg rcvrReg: rcvrReg. self ssPop: 2. "Further since there is a following conditional jump bytecode, define non-merge fixups and leave the cond bytecode to set the mergeness." (self fixupAt: nextPC - initialPC) notAFixup ifTrue: "The next instruction is dead. we can skip it." [deadCode := true. self ensureFixupAt: targetBytecodePC - initialPC. self ensureFixupAt: postBranchPC - initialPC] ifFalse: + [self deny: deadCode]. "push dummy value below" - [self ssPushConstant: objectMemory trueObject]. "dummy value" self assert: (unforwardArg or: [unforwardRcvr]). "We could use (branchDescriptor isBranchTrue xor: orNot) to simplify this." orNot ifFalse: [branchDescriptor isBranchTrue ifTrue: [ fixup := (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger ] ifFalse: "branchDescriptor is branchFalse" [ fixup := (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger ]] ifTrue: [branchDescriptor isBranchTrue ifFalse: "branchDescriptor is branchFalse" [ fixup := (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger ] ifTrue: [ fixup := (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger ]]. + + deadCode ifFalse: + [self ssPushConstant: objectMemory trueObject]. "dummy value" - "The forwarders checks need to jump back to the comparison (label) if a forwarder is found, else jump forward either to the next forwarder check or to the postBranch or branch target (fixup)." unforwardArg ifTrue: [ unforwardRcvr ifTrue: [ objectRepresentation genEnsureOopInRegNotForwarded: argReg scratchReg: TempReg jumpBackTo: label ] ifFalse: [ objectRepresentation genEnsureOopInRegNotForwarded: argReg scratchReg: TempReg ifForwarder: label ifNotForwarder: fixup ] ]. unforwardRcvr ifTrue: [ objectRepresentation genEnsureOopInRegNotForwarded: rcvrReg scratchReg: TempReg ifForwarder: label ifNotForwarder: fixup ]. "Not reached, execution flow have jumped to fixup" ^0! Item was added: + ----- Method: RegisterAllocatingCogit>>genJumpIf:to: (in category 'bytecode generator support') ----- + genJumpIf: boolean to: targetBytecodePC + <inline: false> + | desc reg fixup ok | + <var: #desc type: #'CogSimStackEntry *'> + <var: #fixup type: #'BytecodeFixup *'> + <var: #ok type: #'AbstractInstruction *'> + desc := self ssTop. + self ssPop: 1. + (desc type == SSConstant + and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) ifTrue: + ["Must arrange there's a fixup at the target whether it is jumped to or + not so that the simStackPtr can be kept correct." + fixup := self ensureFixupAt: targetBytecodePC - initialPC. + "Must annotate the bytecode for correct pc mapping." + self annotateBytecode: (desc constant = boolean + ifTrue: [self Jump: fixup] + ifFalse: [self prevInstIsPCAnnotated + ifTrue: [self Nop] + ifFalse: [self Label]]). + extA := 0. + ^0]. + "try and use the top entry's register if anty, but only if it can be destroyed." + reg := (desc type ~= SSRegister + or: [(self anyReferencesToRegister: desc register inAllButTopNItems: 0) + or: [(desc register = ReceiverResultReg and: [optStatus isReceiverResultRegLive])]]) + ifTrue: [TempReg] + ifFalse: [desc register]. + desc popToReg: reg. + "Cunning trick by LPD. If true and false are contiguous subtract the smaller. + Correct result is either 0 or the distance between them. If result is not 0 or + their distance send mustBeBoolean." + self assert: (objectMemory objectAfter: objectMemory falseObject) = objectMemory trueObject. + self genSubConstant: boolean R: reg. + self JumpZero: (self ensureFixupAt: targetBytecodePC - initialPC). + + self extASpecifiesNoMustBeBoolean ifTrue: + [extA := 0. + self annotateBytecode: self lastOpcode. + ^0]. + extA := 0. + + . self CmpCq: (boolean = objectMemory falseObject + ifTrue: [objectMemory trueObject - objectMemory falseObject] + ifFalse: [objectMemory falseObject - objectMemory trueObject]) + R: reg. + ok := self JumpZero: 0. + reg ~= TempReg ifTrue: + [self MoveR: reg R: TempReg]. + self copySimStackToScratch: simSpillBase. + self ssFlushTo: simStackPtr. + self CallRT: (boolean = objectMemory falseObject + ifTrue: [ceSendMustBeBooleanAddFalseTrampoline] + ifFalse: [ceSendMustBeBooleanAddTrueTrampoline]). + "NOTREACHED" + ok jmpTarget: (self annotateBytecode: self Label). + self restoreSimStackFromScratch. + ^0! Item was added: + ----- Method: RegisterAllocatingCogit>>genMustBeBooleanTrampolineFor:called: (in category 'initialization') ----- + genMustBeBooleanTrampolineFor: boolean called: trampolineName + <var: #trampolineName type: #'char *'> + "For RegisterAllocatingCogit we want the address following a conditional branch not to be reachable, so we + don't have to generate code to reload registers. Instead simply convert to an interpreter frame." + <inline: false> + self zeroOpcodeIndex. + "If the objectRepresentation does want true & false to be mobile then we need to record these addresses." + self assert: (objectRepresentation shouldAnnotateObjectReference: boolean) not. + self AddCq: boolean R: TempReg. + ^self genTrampolineFor: #ceSendMustBeBooleanInterpreting: + called: trampolineName + numArgs: 1 + arg: TempReg + arg: nil + arg: nil + arg: nil + regsToSave: self emptyRegisterMask + pushLinkReg: true + resultReg: NoReg + appendOpcodes: true! Item was added: + ----- Method: RegisterAllocatingCogit>>genVanillaInlinedIdenticalOrNotIf: (in category 'bytecode generators') ----- + genVanillaInlinedIdenticalOrNotIf: orNot + | nextPC postBranchPC targetBytecodePC branchDescriptor + rcvrReg argReg argIsConstant rcvrIsConstant | + <var: #branchDescriptor type: #'BytecodeDescriptor *'> + + self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target | + branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetBytecodePC := target ]. + + argIsConstant := self ssTop type = SSConstant. + "They can't be both constants to use correct machine opcodes. + However annotable constants can't be resolved statically, hence we need to careful." + rcvrIsConstant := argIsConstant not and: [(self ssValue: 1) type = SSConstant]. + + self + allocateEqualsEqualsRegistersArgNeedsReg: argIsConstant not + rcvrNeedsReg: rcvrIsConstant 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: argIsConstant + rcvrIsConstant: rcvrIsConstant + argReg: argReg + rcvrReg: rcvrReg + orNotIf: orNot]. + + self genCmpArgIsConstant: argIsConstant rcvrIsConstant: rcvrIsConstant argReg: argReg rcvrReg: rcvrReg. + self ssPop: 2. + + "Further since there is a following conditional jump bytecode, define + non-merge fixups and leave the cond bytecode to set the mergeness." + (self fixupAt: nextPC - initialPC) notAFixup + ifTrue: "The next instruction is dead. we can skip it." + [deadCode := true. + self ensureFixupAt: targetBytecodePC - initialPC. + self ensureFixupAt: postBranchPC - initialPC] + ifFalse: + [self deny: deadCode]. "push dummy value below" + + "We could simplify this with a xor:" + self genConditionalBranch: (orNot + ifFalse: [branchDescriptor isBranchTrue ifTrue: [JumpZero] ifFalse: [JumpNonZero]] + ifTrue: [branchDescriptor isBranchTrue ifTrue: [JumpNonZero] ifFalse: [JumpZero]]) + operand: (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger. + + "If the branch is dead, then we can just fall through postBranchPC (only a nop in-between), else + we need to jump over the code of the branch" + deadCode ifFalse: + [self Jump: (self ensureNonMergeFixupAt: postBranchPC - initialPC). + self ssPushConstant: objectMemory trueObject]. "dummy value" + ^0! Item was changed: ----- Method: RegisterAllocatingCogit>>mergeCurrentSimStackWith: (in category 'bytecode generator support') ----- mergeCurrentSimStackWith: fixup <var: #fixup type: #'BytecodeFixup *'> + | mergeSimStack direction currentEntry targetEntry | - | mergeSimStack currentEntry targetEntry | <var: #mergeSimStack type: #'SimStackEntry *'> "At a merge point the cogit expects the stack to be in the same state as mergeSimStack. mergeSimStack is the state as of some jump forward to this point. So make simStack agree with mergeSimStack (it is, um, problematic to plant code at the jump). Values may have to be assigned to registers. Registers may have to be swapped. The state of optStatus must agree." <var: #targetEntry type: #'SimStackEntry *'> <var: #currentEntry type: #'SimStackEntry *'> <var: #duplicateEntry type: #'SimStackEntry *'> (mergeSimStack := fixup mergeSimStack) ifNil: [^self]. "Assignments amongst the registers must be made in order to avoid overwriting. If necessary exchange registers amongst simStack's entries to resolve any conflicts." self resolveRegisterOrderConflictsBetweenCurrentSimStackAnd: mergeSimStack. self assert: (self conflcitsResolvedBetweenSimStackAnd: mergeSimStack). + "Must determine if we will push or pop values. + If pushing values must enumerate from bottom to top. + If popping, must enumerate from top to bottom." + direction := self directionForMergeWith: mergeSimStack. + direction > 0 + ifTrue: + [0 to: simStackPtr do: + [:i| + currentEntry := self simStack: simStack at: i. + targetEntry := self simStack: mergeSimStack at: i. + (currentEntry reconcileForwardsWith: targetEntry) ifTrue: + [self assert: i >= methodOrBlockNumArgs. + self deassignRegisterForTempVar: targetEntry in: mergeSimStack]. + "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint: + spilled ifFalse: + [simSpillBase := i - 1]. + simStack + at: i + put: (self + cCode: [mergeSimStack at: i] + inSmalltalk: [(mergeSimStack at: i) copy])"]] + ifFalse: + [simStackPtr to: 0 by: -1 do: + [:i| + currentEntry := self simStack: simStack at: i. + targetEntry := self simStack: mergeSimStack at: i. + (currentEntry reconcileForwardsWith: targetEntry) ifTrue: + [self assert: i >= methodOrBlockNumArgs. + self deassignRegisterForTempVar: targetEntry in: mergeSimStack]. + "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint: + spilled ifFalse: + [simSpillBase := i - 1]. + simStack + at: i + put: (self + cCode: [mergeSimStack at: i] + inSmalltalk: [(mergeSimStack at: i) copy])"]]. - simStackPtr to: 0 by: -1 do: - [:i| - currentEntry := self simStack: simStack at: i. - targetEntry := self simStack: mergeSimStack at: i. - (currentEntry reconcileForwardsWith: targetEntry) ifTrue: - [self assert: i >= methodOrBlockNumArgs. - self deassignRegisterForTempVar: targetEntry in: mergeSimStack]. - "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint: - spilled ifFalse: - [simSpillBase := i - 1]. - simStack - at: i - put: (self - cCode: [mergeSimStack at: i] - inSmalltalk: [(mergeSimStack at: i) copy])"]. "a.k.a. fixup isReceiverResultRegSelf: (fixup isReceiverResultRegSelf and: [optStatus isReceiverResultRegLive])" optStatus isReceiverResultRegLive ifFalse: [fixup isReceiverResultRegSelf: false]! Item was added: + ----- Method: RegisterAllocatingCogit>>restoreSimStackFromScratch (in category 'bytecode generator support') ----- + restoreSimStackFromScratch + <inline: true> + self cCode: [self mem: simStack cp: scratchSimStack y: self simStackSlots * (self sizeof: CogSimStackEntry)] + inSmalltalk: [0 to: simStackPtr do: + [:i| + simStack at: i put: (scratchSimStack at: i)]]. + simSpillBase := scratchSpillBase. + optStatus := scratchOptStatus! Item was changed: ----- Method: SimpleStackBasedCogit>>genJumpIf:to: (in category 'bytecode generator support') ----- genJumpIf: boolean to: targetBytecodePC <inline: false> "Cunning trick by LPD. If true and false are contiguous subtract the smaller. Correct result is either 0 or the distance between them. If result is not 0 or their distance send mustBeBoolean." | ok | <var: #ok type: #'AbstractInstruction *'> extA := 0. self assert: (objectMemory objectAfter: objectMemory falseObject) = objectMemory trueObject. self PopR: TempReg. self genSubConstant: boolean R: TempReg. self JumpZero: (self ensureFixupAt: targetBytecodePC - initialPC). self CmpCq: (boolean == objectMemory falseObject ifTrue: [objectMemory trueObject - objectMemory falseObject] ifFalse: [objectMemory falseObject - objectMemory trueObject]) R: TempReg. ok := self JumpZero: 0. + self CallRT: (boolean = objectMemory falseObject - self CallRT: (boolean == objectMemory falseObject ifTrue: [ceSendMustBeBooleanAddFalseTrampoline] ifFalse: [ceSendMustBeBooleanAddTrueTrampoline]). ok jmpTarget: (self annotateBytecode: self Label). ^0! Item was changed: ----- Method: SimpleStackBasedCogit>>genSubConstant:R: (in category 'constant support') ----- genSubConstant: constant R: reg "If the objectMemory allows it, generates a quick constant sub, else generates a word constant sub" <inline: true> ^ (objectRepresentation shouldAnnotateObjectReference: constant) + ifTrue: [ self annotate: (self SubCw: constant R: reg) objRef: reg. ] + ifFalse: [ self SubCq: constant R: reg ]! - ifTrue: [ self annotate: (self SubCw: constant R: TempReg) objRef: reg. ] - ifFalse: [ self SubCq: constant R: TempReg ]! Item was changed: ----- Method: StackToRegisterMappingCogit>>genForwardersInlinedIdenticalOrNotIf: (in category 'bytecode generators') ----- genForwardersInlinedIdenticalOrNotIf: orNot | nextPC branchDescriptor unforwardRcvr argReg targetBytecodePC unforwardArg rcvrReg postBranchPC label fixup | <var: #branchDescriptor type: #'BytecodeDescriptor *'> <var: #label type: #'AbstractInstruction *'> self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target | branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetBytecodePC := 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 := (objectRepresentation isUnannotatableConstant: (self ssValue: 1)) not. unforwardArg := (objectRepresentation isUnannotatableConstant: self ssTop) not. self allocateEqualsEqualsRegistersArgNeedsReg: unforwardArg rcvrNeedsReg: unforwardRcvr 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: unforwardArg not rcvrIsConstant: unforwardRcvr not argReg: argReg rcvrReg: rcvrReg orNotIf: orNot]. "If branching the stack must be flushed for the merge" self ssFlushTo: simStackPtr - 2. label := self Label. self genCmpArgIsConstant: unforwardArg not rcvrIsConstant: unforwardRcvr not argReg: argReg rcvrReg: rcvrReg. self ssPop: 2. "Further since there is a following conditional jump bytecode, define non-merge fixups and leave the cond bytecode to set the mergeness." (self fixupAt: nextPC - initialPC) notAFixup ifTrue: "The next instruction is dead. we can skip it." [deadCode := true. self ensureFixupAt: targetBytecodePC - initialPC. self ensureFixupAt: postBranchPC - initialPC] ifFalse: + [self deny: deadCode]. "push dummy value below" - [self ssPushConstant: objectMemory trueObject]. "dummy value" self assert: (unforwardArg or: [unforwardRcvr]). "We could use (branchDescriptor isBranchTrue xor: orNot) to simplify this." orNot ifFalse: [branchDescriptor isBranchTrue ifTrue: [ fixup := (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger ] ifFalse: "branchDescriptor is branchFalse" [ fixup := (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger ]] ifTrue: [branchDescriptor isBranchTrue ifFalse: "branchDescriptor is branchFalse" [ fixup := (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger ] ifTrue: [ fixup := (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger. self JumpZero: (self ensureNonMergeFixupAt: postBranchPC - initialPC) asUnsignedInteger ]]. + + deadCode ifFalse: + [self ssPushConstant: objectMemory trueObject]. "dummy value" "The forwarders checks need to jump back to the comparison (label) if a forwarder is found, else jump forward either to the next forwarder check or to the postBranch or branch target (fixup)." unforwardArg ifTrue: [ unforwardRcvr ifTrue: [ objectRepresentation genEnsureOopInRegNotForwarded: argReg scratchReg: TempReg jumpBackTo: label ] ifFalse: [ objectRepresentation genEnsureOopInRegNotForwarded: argReg scratchReg: TempReg ifForwarder: label ifNotForwarder: fixup ] ]. unforwardRcvr ifTrue: [ objectRepresentation genEnsureOopInRegNotForwarded: rcvrReg scratchReg: TempReg ifForwarder: label ifNotForwarder: fixup ]. "Not reached, execution flow have jumped to fixup" ^0! Item was changed: ----- Method: StackToRegisterMappingCogit>>genJumpIf:to: (in category 'bytecode generator support') ----- genJumpIf: boolean to: targetBytecodePC <inline: false> | desc fixup ok | <var: #desc type: #'CogSimStackEntry *'> <var: #fixup type: #'BytecodeFixup *'> <var: #ok type: #'AbstractInstruction *'> self ssFlushTo: simStackPtr - 1. desc := self ssTop. self ssPop: 1. (desc type == SSConstant and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) ifTrue: ["Must arrange there's a fixup at the target whether it is jumped to or not so that the simStackPtr can be kept correct." fixup := self ensureFixupAt: targetBytecodePC - initialPC. "Must annotate the bytecode for correct pc mapping." self annotateBytecode: (desc constant = boolean ifTrue: [self Jump: fixup] ifFalse: [self prevInstIsPCAnnotated ifTrue: [self Nop] ifFalse: [self Label]]). extA := 0. ^0]. desc popToReg: TempReg. "Cunning trick by LPD. If true and false are contiguous subtract the smaller. Correct result is either 0 or the distance between them. If result is not 0 or their distance send mustBeBoolean." self assert: (objectMemory objectAfter: objectMemory falseObject) = objectMemory trueObject. self genSubConstant: boolean R: TempReg. self JumpZero: (self ensureFixupAt: targetBytecodePC - initialPC). self extASpecifiesNoMustBeBoolean ifTrue: [ extA := 0. self annotateBytecode: self lastOpcode. ^ 0]. extA := 0. + . self CmpCq: (boolean = objectMemory falseObject - . self CmpCq: (boolean == objectMemory falseObject ifTrue: [objectMemory trueObject - objectMemory falseObject] ifFalse: [objectMemory falseObject - objectMemory trueObject]) R: TempReg. ok := self JumpZero: 0. + self CallRT: (boolean = objectMemory falseObject - self CallRT: (boolean == objectMemory falseObject ifTrue: [ceSendMustBeBooleanAddFalseTrampoline] ifFalse: [ceSendMustBeBooleanAddTrueTrampoline]). ok jmpTarget: (self annotateBytecode: self Label). ^0! Item was changed: ----- Method: StackToRegisterMappingCogit>>genVanillaInlinedIdenticalOrNotIf: (in category 'bytecode generators') ----- genVanillaInlinedIdenticalOrNotIf: orNot | nextPC postBranchPC targetBytecodePC branchDescriptor rcvrReg argReg argIsConstant rcvrIsConstant | <var: #branchDescriptor type: #'BytecodeDescriptor *'> self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target | branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetBytecodePC := target ]. argIsConstant := self ssTop type = SSConstant. "They can't be both constants to use correct machine opcodes. However annotable constants can't be resolved statically, hence we need to careful." rcvrIsConstant := argIsConstant not and: [(self ssValue: 1) type = SSConstant]. self allocateEqualsEqualsRegistersArgNeedsReg: argIsConstant not rcvrNeedsReg: rcvrIsConstant 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: argIsConstant rcvrIsConstant: rcvrIsConstant argReg: argReg rcvrReg: rcvrReg orNotIf: orNot]. "If branching the stack must be flushed for the merge" self ssFlushTo: simStackPtr - 2. self genCmpArgIsConstant: argIsConstant rcvrIsConstant: rcvrIsConstant argReg: argReg rcvrReg: rcvrReg. self ssPop: 2. "Further since there is a following conditional jump bytecode, define non-merge fixups and leave the cond bytecode to set the mergeness." (self fixupAt: nextPC - initialPC) notAFixup ifTrue: "The next instruction is dead. we can skip it." [deadCode := true. self ensureFixupAt: targetBytecodePC - initialPC. self ensureFixupAt: postBranchPC - initialPC] ifFalse: + [self deny: deadCode]. "push dummy value below" - [self ssPushConstant: objectMemory trueObject]. "dummy value" "We could simplify this with a xor:" self genConditionalBranch: (orNot ifFalse: [branchDescriptor isBranchTrue ifTrue: [JumpZero] ifFalse: [JumpNonZero]] ifTrue: [branchDescriptor isBranchTrue ifTrue: [JumpNonZero] ifFalse: [JumpZero]]) operand: (self ensureNonMergeFixupAt: targetBytecodePC - initialPC) asUnsignedInteger. "If the branch is dead, then we can just fall through postBranchPC (only a nop in-between), else we need to jump over the code of the branch" + deadCode ifFalse: + [self Jump: (self ensureNonMergeFixupAt: postBranchPC - initialPC). + self ssPushConstant: objectMemory trueObject]. "dummy value" - deadCode ifFalse: [self Jump: (self ensureNonMergeFixupAt: postBranchPC - initialPC)]. ^0! Item was changed: ----- Method: StackToRegisterMappingCogit>>printSimStack:toDepth:spillBase:on: (in category 'simulation only') ----- printSimStack: aSimStack toDepth: limit spillBase: spillBase on: aStream <doNotGenerate> | tempNamesOrNil width tabWidth | aStream ensureCr. limit < 0 ifTrue: [^aStream nextPutAll: 'simStackEmpty'; cr; flush]. inBlock ~~ true ifTrue: [(tempNamesOrNil := self class initializationOptions at: #tempNames ifAbsent: [#()]) isEmpty ifFalse: [| tab longest | longest := tempNamesOrNil inject: '' into: [:m :t| m size >= t size ifTrue: [m] ifFalse: [t]]. tabWidth := self widthInDefaultFontOf: (tab := String with: Character tab). width := self widthInDefaultFontOf: longest, tab. width <= ((self widthInDefaultFontOf: longest, (String with: Character space)) + 4) ifTrue: [width := width + tabWidth]]]. 0 to: limit do: [:i| + width ifNotNil: - tempNamesOrNil ifNotNil: [self put: (tempNamesOrNil at: i + 1 ifAbsent: ['']) paddedTo: width tabWidth: tabWidth on: aStream]. aStream print: i. i = spillBase ifTrue: [aStream nextPutAll: ' sb'; tab] ifFalse: [aStream tab; tab]. (aSimStack at: i) printStateOn: aStream. aStream cr; flush]! |
Free forum by Nabble | Edit this page |