Eliot Miranda uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2094.mcz ==================== Summary ==================== Name: VMMaker.oscog-eem.2094 Author: eem Time: 16 January 2017, 8:20:54.697006 pm UUID: 69b76ffe-68d9-423c-a3a7-546a6bbb3110 Ancestors: VMMaker.oscog-rsf.2093 StackToRegisterMappingCogit: Follow jumps to jumps and push: aBoolean; jump:if:s, eliminating dead code. Elimnate jumps to the immediately following instruction in StackToRegisterMappingCogit>>generateInstructionsAt:. Neaten the simulation-only breakpointing for bytecode and machine code pcs. Add suport for breakPC during in-image compilation. =============== Diff against VMMaker.oscog-rsf.2093 =============== Item was changed: ----- Method: Cogit class>>testPCMappingSelect:options: (in category 'tests') ----- testPCMappingSelect: aBlock options: optionsDictionaryOrArray "Test pc mapping both ways using a selection of the methods in the current image." + | n cogit coInterpreter | - | cogit coInterpreter | cogit := self instanceForTests: optionsDictionaryOrArray. coInterpreter := CurrentImageCoInterpreterFacade forCogit: cogit. [cogit setInterpreter: coInterpreter; singleStep: true; initializeCodeZoneFrom: 1024 upTo: coInterpreter memory size] on: Notification do: [:ex| (ex messageText beginsWith: 'cannot find receiver for') ifTrue: [ex resume: coInterpreter]]. + n := -1. SystemNavigation new allSelect: [:m| | cm | (m isQuick not and: [aBlock value: m]) ifTrue: + [(n := n + 1) \\ 10 = 0 ifTrue: [Transcript nextPut: $.; flush]. - [Transcript nextPut: $.; flush. cm := cogit cog: (coInterpreter oopForObject: m) selector: (coInterpreter oopForObject: m selector). cm ifNil: [cogit methodZone clearCogCompiledCode. coInterpreter initializeObjectMap. cm := cogit cog: (coInterpreter oopForObject: m) selector: (coInterpreter oopForObject: m selector). cm ifNil: [Transcript show: 'After 1 Cog compiled code compaction, still not able to generate the cog method...' ] ]. cm ifNotNil: [cogit testPCMappingForCompiledMethod: m cogMethod: cm]]. false] ! Item was changed: ----- Method: Cogit>>compileAbstractInstructionsFrom:through: (in category 'compile abstract instructions') ----- compileAbstractInstructionsFrom: start through: end "Loop over bytecodes, dispatching to the generator for each bytecode, handling fixups in due course." | nextOpcodeIndex descriptor fixup result nExts | <var: #descriptor type: #'BytecodeDescriptor *'> <var: #fixup type: #'BytecodeFixup *'> bytecodePC := start. nExts := result := 0. descriptor := nil. + [self maybeHaltIfDebugPC. - [self cCode: '' inSmalltalk: [self isDebugPC ifTrue: [self halt]]. descriptor := self loadBytesAndGetDescriptor. nextOpcodeIndex := opcodeIndex. result := self perform: descriptor generator. self assertExtsAreConsumed: descriptor. fixup := self fixupAt: bytecodePC - initialPC. self patchFixupTargetIfNeeded: fixup nextOpcodeIndex: nextOpcodeIndex. self maybeDumpLiterals: descriptor. bytecodePC := self nextBytecodePCFor: descriptor exts: nExts. result = 0 and: [bytecodePC <= end]] whileTrue: [nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]]. self checkEnoughOpcodes. ^result! Item was changed: ----- Method: Cogit>>generateInstructionsAt: (in category 'generate machine code') ----- generateInstructionsAt: eventualAbsoluteAddress "Size pc-dependent instructions and assign eventual addresses to all instructions. Answer the size of the code. Compute forward branches based on virtual address (abstract code starts at 0), assuming that any branches branched over are long. Compute backward branches based on actual address. Reuse the fixups array to record the pc-dependent instructions that need to have their code generation postponed until after the others." | absoluteAddress pcDependentIndex abstractInstruction fixup | <var: #abstractInstruction type: #'AbstractInstruction *'> <var: #fixup type: #'BytecodeFixup *'> absoluteAddress := eventualAbsoluteAddress. pcDependentIndex := 0. 0 to: opcodeIndex - 1 do: [:i| + self maybeBreakGeneratingAt: absoluteAddress. - self cCode: [] inSmalltalk: [self maybeBreakGeneratingAt: absoluteAddress]. abstractInstruction := self abstractInstructionAt: i. abstractInstruction isPCDependent ifTrue: [abstractInstruction sizePCDependentInstructionAt: absoluteAddress. fixup := self fixupAt: pcDependentIndex. pcDependentIndex := pcDependentIndex + 1. fixup instructionIndex: i. absoluteAddress := absoluteAddress + abstractInstruction machineCodeSize] ifFalse: [absoluteAddress := abstractInstruction concretizeAt: absoluteAddress]]. 0 to: pcDependentIndex - 1 do: [:j| fixup := self fixupAt: j. abstractInstruction := self abstractInstructionAt: fixup instructionIndex. + self maybeBreakGeneratingAt: abstractInstruction address. - self cCode: [] inSmalltalk: [self maybeBreakGeneratingAt: abstractInstruction address]. abstractInstruction concretizeAt: abstractInstruction address]. ^absoluteAddress - eventualAbsoluteAddress! Item was removed: - ----- Method: Cogit>>isDebugPC (in category 'compile abstract instructions') ----- - isDebugPC - <doNotGenerate> - ^ debugBytecodePointers includes: bytecodePC! Item was changed: ----- Method: Cogit>>maybeBreakGeneratingAt: (in category 'simulation only') ----- maybeBreakGeneratingAt: address + "Variation on maybeBreakAt: that only works for integer breakPCs, - "Variation on maybeBreakAt: that inly forks for integer breakPCs, so we can have break blocks that stop at any pc, except when generating." + <cmacro: '(address) 0'> "Simulation only; void in C" - <doNotGenerate> (breakPC = address and: [breakBlock shouldStopIfAtPC: address]) ifTrue: [coInterpreter changed: #byteCountText. self halt: 'machine code generation at ', address hex, ' in ', thisContext sender selector]! Item was added: + ----- Method: Cogit>>maybeHaltIfDebugPC (in category 'compile abstract instructions') ----- + maybeHaltIfDebugPC + <cmacro: '0'> "Simulation only; void in C" + (debugBytecodePointers includes: bytecodePC) ifTrue: + [self halt]! Item was changed: ----- Method: Cogit>>setInterpreter: (in category 'initialization') ----- setInterpreter: aCoInterpreter "Initialization of the code generator in the simulator. These objects already exist in the generated C VM or are used only in the simulation." <doNotGenerate> coInterpreter := aCoInterpreter. objectMemory := aCoInterpreter objectMemory. threadManager := aCoInterpreter threadManager. "N.B. may be nil" methodZone := CogMethodZone new. objectRepresentation := objectMemory objectRepresentationClass forCogit: self methodZone: methodZone. methodZone setInterpreter: aCoInterpreter objectRepresentation: objectRepresentation cogit: self. generatorTable := self class generatorTable. processor := ProcessorClass new. simulatedAddresses := Dictionary new. simulatedTrampolines := Dictionary new. simulatedVariableGetters := Dictionary new. simulatedVariableSetters := Dictionary new. traceStores := 0. traceFlags := (self class initializationOptions at: #recordPrimTrace ifAbsent: [true]) ifTrue: [8] "record prim trace on by default (see Cogit class>>decareCVarsIn:)" ifFalse: [0]. debugPrimCallStackOffset := 0. singleStep := printRegisters := printInstructions := clickConfirm := false. backEnd := CogCompilerClass for: self. methodLabel := CogCompilerClass for: self. (literalsManager := backEnd class literalsManagerClass new) cogit: self. ordinarySendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines). superSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines). BytecodeSetHasDirectedSuperSend ifTrue: [directedSuperSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines)]. NewspeakVM ifTrue: [selfSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines). dynamicSuperSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines). implicitReceiverSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines). outerSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines)]. "debug metadata" objectReferencesInRuntime := CArrayAccessor on: (Array new: NumObjRefsInRuntime). runtimeObjectRefIndex := 0. "debug metadata" trampolineAddresses := CArrayAccessor on: (Array new: NumTrampolines * 2). trampolineTableIndex := 0. extA := numExtB := extB := 0. compilationTrace ifNil: [compilationTrace := self class initializationOptions at: #compilationTrace ifAbsent: [0]]. debugOpcodeIndices := self class initializationOptions at: #debugOpcodeIndices ifAbsent: [Set new]. + debugBytecodePointers := self class initializationOptions at: #debugBytecodePointers ifAbsent: [Set new]. + self class initializationOptions at: #breakPC ifPresent: [:pc| breakPC := pc]! - debugBytecodePointers := self class initializationOptions at: #debugBytecodePointers ifAbsent: [Set new]! Item was changed: ----- Method: RegisterAllocatingCogit>>genJumpIf:to: (in category 'bytecode generator support') ----- genJumpIf: boolean to: targetBytecodePC <inline: false> + | eventualTarget desc reg fixup ok | - | desc reg fixup ok | <var: #desc type: #'CogSimStackEntry *'> <var: #fixup type: #'BytecodeFixup *'> <var: #ok type: #'AbstractInstruction *'> + eventualTarget := self eventualTargetOf: targetBytecodePC. 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: eventualTarget - initialPC. - 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: eventualTarget - initialPC). - 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 genCallMustBeBooleanFor: boolean. "NOTREACHED" ok jmpTarget: (self annotateBytecode: self Label). self restoreSimStackFromScratch. ^0! Item was changed: ----- Method: RegisterAllocatingCogit>>genJumpTo: (in category 'bytecode generator support') ----- genJumpTo: targetBytecodePC "Overriden to avoid the flush because in this cogit stack state is merged at merge point." deadCode := true. "can't fall through" + self Jump: (self ensureFixupAt: (self eventualTargetOf: targetBytecodePC) - initialPC). - self Jump: (self ensureFixupAt: targetBytecodePC - initialPC). ^ 0! Item was changed: ----- Method: SistaCogit>>genJumpIf:to: (in category 'bytecode generator support') ----- genJumpIf: boolean to: targetBytecodePC "The heart of performance counting in Sista. Conditional branches are 6 times less frequent than sends and can provide basic block frequencies (send counters can't). Each conditional has a 32-bit counter split into an upper 16 bits counting executions and a lower half counting untaken executions of the branch. Executing the branch decrements the upper half, tripping if the count goes negative. Not taking the branch decrements the lower half. N.B. We *do not* eliminate dead branches (true ifTrue:/true ifFalse:) so that scanning for send and branch data is simplified and that branch data is correct." <inline: false> + | ok counterAddress countTripped retry nextPC nextDescriptor desc eventualTarget | - | ok counterAddress countTripped retry nextPC nextDescriptor desc | <var: #ok type: #'AbstractInstruction *'> <var: #desc type: #'CogSimStackEntry *'> <var: #retry type: #'AbstractInstruction *'> <var: #countTripped type: #'AbstractInstruction *'> <var: #nextDescriptor type: #'BytecodeDescriptor *'> "In optimized code we don't generate counters to improve performance" (coInterpreter isOptimizedMethod: methodObj) ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. "If the branch is reached only for the counter trip trampoline (typically, var1 == var2 ifTrue: falls through to the branch only for the trampoline) we generate a specific path to drastically reduce the number of machine instructions" branchReachedOnlyForCounterTrip ifTrue: [ branchReachedOnlyForCounterTrip := false. ^ self genCounterTripOnlyJumpIf: boolean to: targetBytecodePC ]. "We detect and: / or:, if found, we don't generate the counters to avoid pathological counter slow down" boolean = objectMemory falseObject ifTrue: [ nextPC := bytecodePC + (self generatorAt: byte0) numBytes. nextDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset. nextDescriptor generator == #genPushConstantTrueBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. nextDescriptor := self generatorAt: (objectMemory fetchByte: targetBytecodePC ofObject: methodObj) + bytecodeSetOffset. nextDescriptor generator == #genPushConstantFalseBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. ]. extA := 0. "We ignore the noMustBeBoolean flag. It should not be present in methods with counters, and if it is we don't care." "We don't generate counters on branches on true/false, the basicblock usage can be inferred" desc := self ssTop. (desc type == SSConstant and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. + + eventualTarget := self eventualTargetOf: targetBytecodePC. + - self ssFlushTo: simStackPtr - 1. desc popToReg: TempReg. self ssPop: 1. "We need SendNumArgsReg because of the mustBeBooleanTrampoline" self ssAllocateRequiredReg: SendNumArgsReg. retry := self Label. self genExecutionCountLogicInto: [ :cAddress :countTripBranch | counterAddress := cAddress. countTripped := countTripBranch ] counterReg: SendNumArgsReg. counterIndex := counterIndex + 1. "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: eventualTarget - initialPC). - self JumpZero: (self ensureFixupAt: targetBytecodePC - initialPC). self genFallsThroughCountLogicCounterReg: SendNumArgsReg counterAddress: counterAddress. self CmpCq: (boolean = objectMemory falseObject ifTrue: [objectMemory trueObject - objectMemory falseObject] ifFalse: [objectMemory falseObject - objectMemory trueObject]) R: TempReg. ok := self JumpZero: 0. self MoveCq: 0 R: SendNumArgsReg. "if counterReg is 0 this is a mustBeBoolean, not a counter trip." countTripped jmpTarget: (self genCallMustBeBooleanFor: boolean). "If we're in an image which hasn't got the Sista code loaded then the ceCounterTripped: trampoline will return directly to machine code, returning the boolean. So the code should jump back to the retry point. The trampoline makes sure that TempReg has been reloaded." "Clément: For some reason if I write self annotateBytecode: (self Jump: retry) the annotation is not at the correct place." "Eliot: Annotations apply the the address following an instruction, and the annotation must be for the return address of the call (since this is the address the run-time sees), so it must be on a label before the jump, not after the jump." self annotateBytecode: self Label. self Jump: retry. ok jmpTarget: self Label. ^0! Item was changed: ----- Method: SistaRegisterAllocatingCogit>>genJumpIf:to: (in category 'bytecode generator support') ----- genJumpIf: boolean to: targetBytecodePC "The heart of performance counting in Sista. Conditional branches are 6 times less frequent than sends and can provide basic block frequencies (send counters can't). Each conditional has a 32-bit counter split into an upper 16 bits counting executions and a lower half counting untaken executions of the branch. Executing the branch decrements the upper half, tripping if the count goes negative. Not taking the branch decrements the lower half. N.B. We *do not* eliminate dead branches (true ifTrue:/true ifFalse:) so that scanning for send and branch data is simplified and that branch data is correct." <inline: false> + | ok counterAddress countTripped retry nextPC nextDescriptor desc eventualTarget reg | - | ok counterAddress countTripped retry nextPC nextDescriptor desc reg | <var: #ok type: #'AbstractInstruction *'> <var: #desc type: #'CogSimStackEntry *'> <var: #retry type: #'AbstractInstruction *'> <var: #countTripped type: #'AbstractInstruction *'> <var: #nextDescriptor type: #'BytecodeDescriptor *'> "In optimized code we don't generate counters to improve performance" (coInterpreter isOptimizedMethod: methodObj) ifTrue: [^super genJumpIf: boolean to: targetBytecodePC]. "If the branch is reached only for the counter trip trampoline (typically, var1 == var2 ifTrue: falls through to the branch only for the trampoline) we generate a specific path to drastically reduce the number of machine instructions" branchReachedOnlyForCounterTrip ifTrue: [branchReachedOnlyForCounterTrip := false. ^self genCounterTripOnlyJumpIf: boolean to: targetBytecodePC]. "We detect and: / or:, if found, we don't generate the counters to avoid pathological counter slow down" boolean = objectMemory falseObject ifTrue: [ nextPC := bytecodePC + (self generatorAt: byte0) numBytes. nextDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset. nextDescriptor generator == #genPushConstantTrueBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. nextDescriptor := self generatorAt: (objectMemory fetchByte: targetBytecodePC ofObject: methodObj) + bytecodeSetOffset. nextDescriptor generator == #genPushConstantFalseBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. ]. extA := 0. "We ignore the noMustBeBoolean flag. It should not be present in methods with counters, and if it is we don't care." "We don't generate counters on branches on true/false, the basicblock usage can be inferred" desc := self ssTop. (desc type == SSConstant and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. + eventualTarget := self eventualTargetOf: targetBytecodePC. + self flag: 'Because of the restriction on x64 that absolute loads must target %rax, it would perhaps be a better choice to use TempReg (%rax) for the counter reg and SendNumArgsReg for the boolean.'. "try and use the top entry's register if ant, 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. self ssPop: 1. "We need SendNumArgsReg because of the mustBeBooleanTrampoline" self ssAllocateRequiredReg: SendNumArgsReg. retry := self Label. self genExecutionCountLogicInto: [ :cAddress :countTripBranch | counterAddress := cAddress. countTripped := countTripBranch ] counterReg: SendNumArgsReg. counterIndex := counterIndex + 1. "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: eventualTarget - initialPC). - self JumpZero: (self ensureFixupAt: targetBytecodePC - initialPC). self genFallsThroughCountLogicCounterReg: SendNumArgsReg counterAddress: counterAddress. self CmpCq: (boolean = objectMemory falseObject ifTrue: [objectMemory trueObject - objectMemory falseObject] ifFalse: [objectMemory falseObject - objectMemory trueObject]) R: reg. ok := self JumpZero: 0. self MoveCq: 0 R: SendNumArgsReg. "if counterReg is 0 this is a mustBeBoolean, not a counter trip." reg ~= TempReg ifTrue: [self MoveR: reg R: TempReg]. countTripped jmpTarget: self Label. self copySimStackToScratch: simSpillBase. self ssFlushTo: simStackPtr. self genCallMustBeBooleanFor: boolean. "If we're in an image which hasn't got the Sista code loaded then the ceCounterTripped: trampoline will return directly to machine code, returning the boolean. So the code should jump back to the retry point. The trampoline preserves register state when taking the ceCounterTripped: path." "Clément: For some reason if I write self annotateBytecode: (self Jump: retry) the annotation is not at the correct place." "Eliot: Annotations apply the the address following an instruction, and the annotation must be for the return address of the call (since this is the address the run-time sees), so it must be on a label before the jump, not after the jump." self annotateBytecode: self Label. simSpillBase ~= scratchSpillBase ifTrue: [self assert: simSpillBase > scratchSpillBase. self AddCq: simSpillBase - scratchSpillBase * objectMemory wordSize R: SPReg]. self Jump: retry. ok jmpTarget: self Label. self restoreSimStackFromScratch. ^0! Item was changed: ----- Method: StackToRegisterMappingCogit>>compileAbstractInstructionsFrom:through: (in category 'compile abstract instructions') ----- compileAbstractInstructionsFrom: start through: end "Loop over bytecodes, dispatching to the generator for each bytecode, handling fixups in due course." | nextOpcodeIndex descriptor nExts fixup result | <var: #descriptor type: #'BytecodeDescriptor *'> <var: #fixup type: #'BytecodeFixup *'> self traceSimStack. bytecodePC := start. nExts := result := 0. descriptor := nil. deadCode := false. + [self maybeHaltIfDebugPC. - [self cCode: '' inSmalltalk: [self isDebugPC ifTrue: [self halt]]. fixup := self fixupAt: bytecodePC - initialPC. self mergeWithFixupIfRequired: fixup. self assertCorrectSimStackPtr. descriptor := self loadBytesAndGetDescriptor. nextOpcodeIndex := opcodeIndex. result := deadCode ifTrue: [self mapDeadDescriptorIfNeeded: descriptor] ifFalse: [self perform: descriptor generator]. self assertExtsAreConsumed: descriptor. self traceDescriptor: descriptor; traceSimStack. self patchFixupTargetIfNeeded: fixup nextOpcodeIndex: nextOpcodeIndex. self maybeDumpLiterals: descriptor. bytecodePC := self nextBytecodePCFor: descriptor exts: nExts. result = 0 and: [bytecodePC <= end]] whileTrue: [nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]]. self checkEnoughOpcodes. ^result! Item was changed: ----- Method: StackToRegisterMappingCogit>>compileBlockBodies (in category 'compile abstract instructions') ----- compileBlockBodies <inline: false> | result compiledBlocksCount blockStart savedNeedsFrame savedNumArgs savedNumTemps initialStackPtr initialOpcodeIndex initialIndexOfIRC initialCounterIndex | <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 := InVanillaBlock. compiledBlocksCount := 0. [compiledBlocksCount < blockCount] whileTrue: [blockPass := 1. blockStart := self blockStartAt: compiledBlocksCount. (result := self scanBlock: blockStart) < 0 ifTrue: [^result]. initialOpcodeIndex := opcodeIndex. initialCounterIndex := self maybeCounterIndex."for SistaCogit" literalsManager saveForBlockCompile. 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 or: [deadCode]). - [self assert: initialStackPtr > simStackPtr. blockPass := blockPass + 1. "for asserts :-(" 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. self maybeSetCounterIndex: initialCounterIndex. "For SistaCogit" literalsManager resetForBlockCompile. NewspeakVM ifTrue: [indexOfIRC := initialIndexOfIRC]]. compiledBlocksCount := compiledBlocksCount + 1]. needsFrame := savedNeedsFrame. methodOrBlockNumArgs := savedNumArgs. methodOrBlockNumTemps := savedNumTemps. ^0! Item was added: + ----- Method: StackToRegisterMappingCogit>>eventualTargetOf: (in category 'peephole optimizations') ----- + eventualTargetOf: targetBytecodePC + "Attempt to follow a branch to a pc. Handle branches to unconditional jumps + and branches to push: aBoolean; conditional branch pairs. If the branch cannot + be followed answer targetBytecodePC." + + | currentTarget nextPC nExts descriptor span cond | + <var: #descriptor type: #'BytecodeDescriptor *'> + nextPC := currentTarget := targetBytecodePC. + [[nExts := 0. + descriptor := self generatorAt: bytecodeSetOffset + + (objectMemory fetchByte: nextPC ofObject: methodObj). + descriptor isReturn ifTrue: [^currentTarget]. "avoid stepping off the end of methods" + descriptor isExtension] + whileTrue: + [nExts := nExts + 1. + nextPC := nextPC + descriptor numBytes]. + descriptor isUnconditionalBranch + ifTrue: + [span := self spanFor: descriptor at: nextPC exts: nExts in: methodObj. + span < 0 ifTrue: "Do *not* follow backward branches; these are interrupt points and should not be elided." + [^currentTarget]. + nextPC := nextPC + descriptor numBytes + span] + ifFalse: + [descriptor generator + caseOf: { + [#genPushConstantTrueBytecode] -> [cond := true]. + [#genPushConstantFalseBytecode] -> [cond := false] } + otherwise: [^currentTarget]. + "Don't step into loops across a pushTrue; jump:if: boundary, so as not to confuse stack depth fixup." + (fixups at: nextPC - initialPC) isBackwardBranchFixup ifTrue: + [^currentTarget]. + nextPC := self eventualTargetOf: nextPC + descriptor numBytes. + nExts := 0. + [descriptor := self generatorAt: bytecodeSetOffset + + (objectMemory fetchByte: nextPC ofObject: methodObj). + descriptor isReturn ifTrue: [^currentTarget]. "avoid stepping off the end of methods" + descriptor isExtension] + whileTrue: + [nExts := nExts + 1. + nextPC := nextPC + descriptor numBytes]. + descriptor isBranch ifFalse: + [^currentTarget]. + descriptor isUnconditionalBranch ifTrue: + [^currentTarget]. + nextPC := cond == descriptor isBranchTrue + ifTrue: [nextPC + + descriptor numBytes + + (self spanFor: descriptor at: nextPC exts: nExts in: methodObj)] + ifFalse: [nextPC + descriptor numBytes]]. + currentTarget := nextPC] + repeat! Item was changed: ----- Method: StackToRegisterMappingCogit>>extractMaybeBranchDescriptorInto: (in category 'bytecode generator support') ----- extractMaybeBranchDescriptorInto: fourArgBlock "Looks one instruction ahead of the current bytecodePC and answers its bytecode descriptor and its pc. + If the instruction found is a branch, also answers the pc after the branch and the pc targeted by the branch." - If the instruction found is a branch, also answers the pc after the branch and the pc targetted by the branch" | primDescriptor nextPC nExts branchDescriptor targetBytecodePC postBranchPC | <inline: true> <var: #primDescriptor type: #'BytecodeDescriptor *'> <var: #branchDescriptor type: #'BytecodeDescriptor *'> primDescriptor := self generatorAt: byte0. nextPC := bytecodePC + primDescriptor numBytes. nExts := 0. + [[branchDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset. + branchDescriptor isExtension] whileTrue: - [branchDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset. - branchDescriptor isExtension] whileTrue: [nExts := nExts + 1. nextPC := nextPC + branchDescriptor numBytes]. + branchDescriptor isUnconditionalBranch] + whileTrue: + [nextPC := self eventualTargetOf: nextPC + + branchDescriptor numBytes + + (self spanFor: branchDescriptor at: nextPC exts: nExts in: methodObj)]. targetBytecodePC := postBranchPC := 0. + (branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse]) + ifTrue: + [targetBytecodePC := self eventualTargetOf: nextPC + + branchDescriptor numBytes + + (self spanFor: branchDescriptor at: nextPC exts: nExts in: methodObj). + postBranchPC := self eventualTargetOf: nextPC + branchDescriptor numBytes] + ifFalse: + [branchDescriptor isReturn ifFalse: + [postBranchPC := self eventualTargetOf: nextPC + branchDescriptor numBytes. + nextPC := self eventualTargetOf: bytecodePC + primDescriptor numBytes]]. + - (branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse]) ifTrue: - [ targetBytecodePC := nextPC - + branchDescriptor numBytes - + (self spanFor: branchDescriptor at: nextPC exts: nExts in: methodObj). - postBranchPC := nextPC + branchDescriptor numBytes ]. - fourArgBlock value: branchDescriptor value: nextPC value: postBranchPC value: targetBytecodePC! Item was changed: ----- Method: StackToRegisterMappingCogit>>genJumpIf:to: (in category 'bytecode generator support') ----- genJumpIf: boolean to: targetBytecodePC <inline: false> + | desc fixup ok eventualTarget | - | desc fixup ok | <var: #desc type: #'CogSimStackEntry *'> <var: #fixup type: #'BytecodeFixup *'> <var: #ok type: #'AbstractInstruction *'> + eventualTarget := self eventualTargetOf: targetBytecodePC. 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: eventualTarget - initialPC. - 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: eventualTarget - initialPC). - 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 genCallMustBeBooleanFor: boolean. ok jmpTarget: (self annotateBytecode: self Label). ^0! Item was changed: ----- Method: StackToRegisterMappingCogit>>genJumpTo: (in category 'bytecode generator support') ----- genJumpTo: targetBytecodePC self ssFlushTo: simStackPtr. deadCode := true. "can't fall through" + self Jump: (self ensureFixupAt: (self eventualTargetOf: targetBytecodePC) - initialPC). + ^0! - ^super genJumpTo: targetBytecodePC! Item was added: + ----- Method: StackToRegisterMappingCogit>>generateInstructionsAt: (in category 'generate machine code') ----- + generateInstructionsAt: eventualAbsoluteAddress + "Size pc-dependent instructions and assign eventual addresses to all instructions. + Answer the size of the code. + Compute forward branches based on virtual address (abstract code starts at 0), + assuming that any branches branched over are long. + Compute backward branches based on actual address. + Reuse the fixups array to record the pc-dependent instructions that need to have + their code generation postponed until after the others. + + Override to andd handling for null branches (branches to the immediately following + instruction) occasioned by StackToRegisterMapping's following of jumps." + | absoluteAddress pcDependentIndex abstractInstruction fixup | + <var: #abstractInstruction type: #'AbstractInstruction *'> + <var: #fixup type: #'BytecodeFixup *'> + absoluteAddress := eventualAbsoluteAddress. + pcDependentIndex := 0. + 0 to: opcodeIndex - 1 do: + [:i| + self maybeBreakGeneratingAt: absoluteAddress. + abstractInstruction := self abstractInstructionAt: i. + abstractInstruction isPCDependent + ifTrue: + [abstractInstruction sizePCDependentInstructionAt: absoluteAddress. + (abstractInstruction isJump + and: [i + 1 < opcodeIndex + and: [abstractInstruction getJmpTarget == (self abstractInstructionAt: i + 1)]]) + ifTrue: + [abstractInstruction + opcode: Nop; + concretizeAt: absoluteAddress] + ifFalse: + [fixup := self fixupAt: pcDependentIndex. + pcDependentIndex := pcDependentIndex + 1. + fixup instructionIndex: i]. + absoluteAddress := absoluteAddress + abstractInstruction machineCodeSize] + ifFalse: + [absoluteAddress := abstractInstruction concretizeAt: absoluteAddress]]. + 0 to: pcDependentIndex - 1 do: + [:j| + fixup := self fixupAt: j. + abstractInstruction := self abstractInstructionAt: fixup instructionIndex. + self maybeBreakGeneratingAt: abstractInstruction address. + abstractInstruction concretizeAt: abstractInstruction address]. + ^absoluteAddress - eventualAbsoluteAddress! Item was changed: ----- Method: StackToRegisterMappingCogit>>mergeWithFixupIfRequired: (in category 'simulation stack') ----- mergeWithFixupIfRequired: fixup "If this bytecode has a fixup, some kind of merge needs to be done. There are 4 cases: 1) the bytecode has no fixup (fixup isNotAFixup) do nothing 2) the bytecode has a non merge fixup the fixup has needsNonMergeFixup. The code generating non merge fixup (currently only special selector code) is responsible for the merge so no need to do it. We set deadCode to false as the instruction can be reached from jumps. 3) the bytecode has a merge fixup, but execution flow *cannot* fall through to the merge point. the fixup has needsMergeFixup and deadCode = true. ignores the current simStack as it does not mean anything restores the simStack to the state the jumps to the merge point expects it to be. 4) the bytecode has a merge fixup and execution flow *can* fall through to the merge point. the fixup has needsMergeFixup and deadCode = false. flushes the stack to the stack pointer so the fall through execution path simStack is in the state the merge point expects it to be. restores the simStack to the state the jumps to the merge point expects it to be. In addition, if this is a backjump merge point, we patch the fixup to hold the current simStackPtr for later assertions." <var: #fixup type: #'BytecodeFixup *'> "case 1" + fixup notAFixup ifTrue: + [^0]. - fixup notAFixup ifTrue: [^ 0]. "case 2" + fixup isNonMergeFixup ifTrue: + [deadCode := false. ^0]. - fixup isNonMergeFixup ifTrue: [deadCode := false. ^ 0 ]. "cases 3 and 4" self assert: fixup isMergeFixup. self traceMerge: fixup. + deadCode + ifTrue: "case 3" + ["Would like to assert fixup simStackPtr >= (methodOrBlockNumTemps - 1) but can't because + a) the initialNils hack, b) deadCode removal allows arriving at an isBackwardBranchFixup." + self assert: (fixup simStackPtr >= (methodOrBlockNumTemps - 1) or: [inBlock = InVanillaBlock or: [fixup isBackwardBranchFixup]]). + fixup isBackwardBranchFixup ifFalse: + [simStackPtr := fixup simStackPtr]. + LowcodeVM ifTrue: + [simNativeStackPtr := fixup simNativeStackPtr. + simNativeStackSize := fixup simNativeStackSize]] + ifFalse: "case 4" + [self ssFlushTo: simStackPtr]. - deadCode ifTrue: [ - "case 3" - simStackPtr := fixup simStackPtr. - LowcodeVM ifTrue: [ - simNativeStackPtr := fixup simNativeStackPtr. - simNativeStackSize := fixup simNativeStackSize. - ] - ] ifFalse: [ - "case 4" - self ssFlushTo: simStackPtr - ]. "cases 3 and 4" deadCode := false. + fixup isBackwardBranchFixup ifTrue: + [fixup simStackPtr: simStackPtr. + LowcodeVM ifTrue: + [fixup simNativeStackPtr: simNativeStackPtr. + fixup simNativeStackSize: simNativeStackSize]]. - fixup isBackwardBranchFixup ifTrue: [ - fixup simStackPtr: simStackPtr. - LowcodeVM ifTrue: [ - fixup simNativeStackPtr: simNativeStackPtr. - fixup simNativeStackSize: simNativeStackSize. - ] - ]. fixup targetInstruction: self Label. self assert: simStackPtr = fixup simStackPtr. + LowcodeVM ifTrue: + [self assert: simNativeStackPtr = fixup simNativeStackPtr. + self assert: simNativeStackSize = fixup simNativeStackSize]. - LowcodeVM ifTrue: [ - self assert: simNativeStackPtr = fixup simNativeStackPtr. - self assert: simNativeStackSize = fixup simNativeStackSize. - ]. self cCode: '' inSmalltalk: [self assert: fixup simStackPtr = (self debugStackPointerFor: bytecodePC)]. self restoreSimStackAtMergePoint: fixup. + - ^0! |
Hi Eliot, With this commit I got the following compilation error on Mac OS X 64. I made a quick fix by replacing the case into ifTrue:iFalse: ../../spur64src/vm/cogitX64.c:29060:4: error: statement requires expression of integer type ('sqInt (*)(void)' (aka 'long (*)(void)') invalid) switch ((descriptor->generator)) { ^ ~~~~~~~~~~~~~~~~~~~~~~~ ../../spur64src/vm/cogitX64.c:29061:9: error: expression is not an integer constant expression case genPushConstantTrueBytecode: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ ../../spur64src/vm/cogitX64.c:29064:9: error: expression is not an integer constant expression case genPushConstantFalseBytecode: (spur64src because I am using the Pharo branch, where I can load the LowcodeOpalCompiler for testing) Best regards, Ronie 2017-01-17 1:21 GMT-03:00 <[hidden email]>:
|
Free forum by Nabble | Edit this page |