Eliot Miranda uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-eem.1702.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.1702 Author: eem Time: 26 February 2016, 1:00:09.123061 pm UUID: ca289ff1-8bc0-40d7-a4bb-42b583f02dd1 Ancestors: VMMaker.oscog-eem.1701
Eliminate some C compiler warnings in the generated code. Add returns at the end of sqInt methods that don't end with a return. Improve the variable pruning to mirror dead code elimination.
Make the leak checker operational in the non-assert (& non-debug) VM.
=============== Diff against VMMaker.oscog-eem.1701 ===============
Item was changed: ----- Method: CCodeGenerator>>addMethodFor:selector: (in category 'utilities') ----- addMethodFor: aClass selector: selector "Add the given method to the code base and answer its translation or nil if it shouldn't be translated."
| method tmethod | method := aClass compiledMethodAt: selector. (method pragmaAt: #doNotGenerate) ifNotNil: ["only remove a previous method if this one overrides it, i.e. this is a subclass method. If the existing method is in a different hierarchy this method must be merely a redeirect." (methods at: selector ifAbsent: []) ifNotNil: [:tm| (aClass includesBehavior: tm definingClass) ifTrue: [self removeMethodForSelector: selector]]. ^nil]. method isSubclassResponsibility ifTrue: [^nil]. (self shouldIncludeMethodFor: aClass selector: selector) ifFalse: [^nil]. tmethod := self compileToTMethodSelector: selector in: aClass. "Even thoug we exclude initialize methods, we must consider their global variable usage, otherwise globals may be incorrectly localized." selector == #initialize ifTrue: + [self checkForGlobalUsage: (tmethod allReferencedVariablesUsing: self) in: tmethod. - [self checkForGlobalUsage: (tmethod allReferencedVariablesIn: self) in: tmethod. ^nil]. self addMethod: tmethod. "If the method has a macro then add the macro. But keep the method for analysis purposes (e.g. its variable accesses)." (method pragmaAt: #cmacro:) ifNotNil: [:pragma| self addMacro: (pragma argumentAt: 1) for: selector. (inlineList includes: selector) ifTrue: [inlineList := inlineList copyWithout: selector]]. (method pragmaAt: #cmacro) ifNotNil: [:pragma| | literal | "Method should be just foo ^const" self assert: (method numArgs = 0 and: [method numLiterals = 3 or: [method isQuick]]). literal := method isQuick ifTrue: [method decompile block statements last expr key] ifFalse: [method literalAt: 1]. self addMacro: '() ', (self cLiteralFor: literal value name: method selector) for: selector. (inlineList includes: selector) ifTrue: [inlineList := inlineList copyWithout: selector]]. ^tmethod!
Item was changed: ----- Method: CCodeGenerator>>nilOrBooleanConditionFor: (in category 'utilities') ----- nilOrBooleanConditionFor: nodeOrNil "If nodeOrNil is one of the conditional sends for which we do translation-time dead code elimination (i.e. cppIf:ifTrue: et al or ifTrue: et al) and the conditional does evaluate to a translation-time boolean constant, answer that constant, otherwise answer nil. Used to prune dead code, either for code generaton or dead variable elimination." generateDeadCode ifTrue: [^nil]. nodeOrNil ifNil: [^nil]. nodeOrNil isSend ifFalse: [^nil]. + (#(ifTrue: ifFalse: ifTrue:ifFalse: ifFalse:ifTrue:) includes: nodeOrNil selector) ifTrue: + [^self nilOrBooleanConstantReceiverOf: nodeOrNil receiver]. + (#(and: or:) includes: nodeOrNil selector) ifTrue: + [^self nilOrBooleanConstantReceiverOf: nodeOrNil]. (#(cppIf:ifTrue: cppIf:ifTrue:ifFalse:) includes: nodeOrNil selector) ifTrue: [(vmClass notNil and: [nodeOrNil args first isConstant and: [nodeOrNil args first value isSymbol and: [(VMBasicConstants defineAtCompileTime: nodeOrNil args first value) not and: [(vmClass bindingOf: nodeOrNil args first value) notNil]]]]) ifTrue: [self logger nextPutAll: 'Warning: cppIf: reference to '; store: nodeOrNil args first value; nextPutAll: ' when variable of same name exists.'; cr].
^(optionsDictionary notNil and: [nodeOrNil args first isConstant and: [#(true false) includes: (optionsDictionary at: nodeOrNil args first name ifAbsent: [nil])]]) ifTrue: [optionsDictionary at: nodeOrNil args first name]]. + ^nil! - - ^(#(ifTrue: ifFalse: ifTrue:ifFalse: ifFalse:ifTrue:) includes: nodeOrNil selector) ifTrue: - [self nilOrBooleanConstantReceiverOf: nodeOrNil]!
Item was added: + ----- Method: CCodeGenerator>>nodeIsDeadCode:withParent: (in category 'utilities') ----- + nodeIsDeadCode: aNode withParent: parentNode + "Answer if aNode would not be generated due to dead code elimination." + ^(self nilOrBooleanConditionFor: parentNode) + ifNil: [false] + ifNotNil: + [:cond| | filter | + filter := parentNode selector caseOf: + { "First element is accessor for filtered (eliminated) node if expression is true. + Second element is accessor for filtered (eliminated) node if expression is false." + [#ifFalse:] -> [#(first nil)]. + [#ifFalse:ifTrue:] -> [#(first last)]. + [#ifTrue:] -> [#(nil first)]. + [#ifTrue:ifFalse:] -> [#(last first)]. + [#and:] -> [#(nil first)]. + [#or:] -> [#(last nil)]. + [#cppIf:ifTrue:] -> [#(nil #second)]. + [#cppIf:ifTrue:ifFalse:] -> [#(third #second)] }. + (cond ifTrue: [filter first] ifFalse: [filter last]) + ifNil: [false] + ifNotNil: [:accessor| aNode == (parentNode args perform: accessor)]]!
Item was changed: ----- Method: CoInterpreter>>commenceCogCompiledCodeCompaction (in category 'process primitive support') ----- commenceCogCompiledCodeCompaction | startTime | <var: #startTime type: #usqLong> cogCompiledCodeCompactionCalledFor := false. cogit recordEventTrace ifTrue: [self recordTrace: TraceCodeCompaction thing: TraceCodeCompaction source: 0]. cogit recordPrimTrace ifTrue: [self fastLogPrim: TraceCodeCompaction]. startTime := self ioUTCMicrosecondsNow.
"This can be called in a number of circumstances. The instructionPointer may contain a native pc that must be relocated. There may already be a pushed instructionPointer on stack. Clients ensure that instructionPointer is 0 if it should not be pushed and/or relocated. Pushing twice is a mistake because only the top one will be relocated." instructionPointer ~= 0 ifTrue: ["better not have already been pushed" self assert: self stackTop asUnsignedInteger ~= instructionPointer. self push: instructionPointer. self externalWriteBackHeadStackPointer]. self assertValidStackedInstructionPointers: #'__LINE__'. cogit compactCogCompiledCode. instructionPointer ~= 0 ifTrue: [instructionPointer := self popStack. self externalWriteBackHeadStackPointer]. self assertValidStackedInstructionPointers: #'__LINE__'.
statCodeCompactionCount := statCodeCompactionCount + 1. statCodeCompactionUsecs := statCodeCompactionUsecs + (self ioUTCMicrosecondsNow - startTime).
objectMemory checkForLeaks ~= 0 ifTrue: [objectMemory clearLeakMapAndMapAccessibleObjects. + self asserta: (self checkCodeIntegrity: false)]! - self assert: (self checkCodeIntegrity: false)]!
Item was changed: ----- Method: Cogit>>markAndTraceMachineCodeForNewSpaceGC (in category 'jit - api') ----- markAndTraceMachineCodeForNewSpaceGC "Free any methods that refer to unmarked objects, unlinking sends to freed methods." | pointer cogMethod | <var: #cogMethod type: #'CogMethod *'> objectMemory leakCheckNewSpaceGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. - [self assert: self allMachineCodeObjectReferencesValid]. codeModified := false. pointer := methodZone youngReferrers. [pointer < methodZone zoneEnd] whileTrue: [cogMethod := coInterpreter cCoerceSimple: (objectMemory longAt: pointer) to: #'CogMethod *'. cogMethod cmRefersToYoung ifTrue: [self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0. self assert: (cogMethod cmType = CMMethod or: [cogMethod cmType = CMOpenPIC]). (objectMemory isYoung: cogMethod selector) ifTrue: [objectMemory markAndTrace: cogMethod selector]. cogMethod cmType = CMMethod ifTrue: [(objectMemory isYoung: cogMethod methodObject) ifTrue: [objectMemory markAndTrace: cogMethod methodObject]. self markYoungObjectsIn: cogMethod]]. pointer := pointer + objectMemory wordSize]. objectMemory leakCheckNewSpaceGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. - [self assert: self allMachineCodeObjectReferencesValid]. codeModified ifTrue: "After updating oops in inline caches we need to flush the icache." [processor flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed: ----- Method: Cogit>>markAndTraceMachineCodeOfMarkedMethods (in category 'jit - api') ----- markAndTraceMachineCodeOfMarkedMethods "Mark objects in machine-code of marked methods (or open PICs with marked selectors)." <api> <option: #SpurObjectMemory> | cogMethod | <var: #cogMethod type: #'CogMethod *'> objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. - [self assert: self allMachineCodeObjectReferencesValid]. codeModified := false. self markAndTraceObjectReferencesInGeneratedRuntime. cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'. [cogMethod < methodZone limitZony] whileTrue: [(cogMethod cmType = CMMethod and: [objectMemory isMarked: cogMethod methodObject]) ifTrue: [self markAndTraceLiteralsIn: cogMethod]. (cogMethod cmType = CMOpenPIC and: [(objectMemory isImmediate: cogMethod selector) or: [objectMemory isMarked: cogMethod selector]]) ifTrue: [self markAndTraceLiteralsIn: cogMethod]. cogMethod := methodZone methodAfter: cogMethod]. objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. - [self assert: self allMachineCodeObjectReferencesValid]. codeModified ifTrue: "After updating oops in inline caches we need to flush the icache." [processor flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed: ----- Method: Cogit>>markAndTraceOrFreeMachineCodeForFullGC (in category 'jit - api') ----- markAndTraceOrFreeMachineCodeForFullGC "Free any methods that refer to unmarked objects, unlinking sends to freed methods." | cogMethod | <var: #cogMethod type: #'CogMethod *'> objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. - [self assert: self allMachineCodeObjectReferencesValid]. codeModified := false. self markAndTraceObjectReferencesInGeneratedRuntime. cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'. [cogMethod < methodZone limitZony] whileTrue: [self markAndTraceOrFreeCogMethod: cogMethod firstVisit: true. cogMethod := methodZone methodAfter: cogMethod]. objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. - [self assert: self allMachineCodeObjectReferencesValid]. codeModified ifTrue: "After updating oops in inline caches we need to flush the icache." [processor flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone limitZony asUnsignedInteger]!
Item was changed: ----- Method: SpurMemoryManager>>inLineRunLeakCheckerFor:excludeUnmarkedNewSpaceObjs:classIndicesShouldBeValid: (in category 'debug support') ----- inLineRunLeakCheckerFor: gcModes excludeUnmarkedNewSpaceObjs: excludeUnmarkedNewSpaceObjs classIndicesShouldBeValid: classIndicesShouldBeValid <inline: true> (gcModes anyMask: checkForLeaks) ifTrue: [(gcModes anyMask: GCModeFull) ifTrue: [coInterpreter reverseDisplayFrom: 0 to: 7] ifFalse: [coInterpreter reverseDisplayFrom: 8 to: 15]. self clearLeakMapAndMapAccessibleObjects. + self asserta: (self checkHeapIntegrity: excludeUnmarkedNewSpaceObjs classIndicesShouldBeValid: classIndicesShouldBeValid). + self asserta: coInterpreter checkInterpreterIntegrity. + self asserta: coInterpreter checkStackIntegrity. + self asserta: (coInterpreter checkCodeIntegrity: gcModes)]! - self assert: (self checkHeapIntegrity: excludeUnmarkedNewSpaceObjs classIndicesShouldBeValid: classIndicesShouldBeValid). - self assert: coInterpreter checkInterpreterIntegrity. - self assert: coInterpreter checkStackIntegrity. - self assert: (coInterpreter checkCodeIntegrity: gcModes)]!
Item was changed: ----- Method: SpurMemoryManager>>runLeakCheckerForFreeSpace: (in category 'debug support') ----- runLeakCheckerForFreeSpace: gcModes <inline: false> (gcModes anyMask: GCModeFreeSpace) ifTrue: [coInterpreter reverseDisplayFrom: 16 to: 19. self clearLeakMapAndMapAccessibleFreeSpace. + self asserta: self checkHeapFreeSpaceIntegrity]! - self assert: self checkHeapFreeSpaceIntegrity]!
Item was changed: ----- Method: StackInterpreter class>>declareCVarsIn: (in category 'translation') ----- declareCVarsIn: aCCodeGenerator | vmClass | self class == thisContext methodClass ifFalse: [^self]. "Don't duplicate decls in subclasses" vmClass := aCCodeGenerator vmClass. "Generate primitiveTable etc based on vmClass, not just StackInterpreter" aCCodeGenerator addHeaderFile:'<stddef.h> /* for e.g. alloca */'; addHeaderFile:'<setjmp.h>'; addHeaderFile:'<wchar.h> /* for wint_t */'; addHeaderFile:'"vmCallback.h"'; addHeaderFile:'"sqMemoryFence.h"'; addHeaderFile:'"dispdbg.h"'. vmClass declareInterpreterVersionIn: aCCodeGenerator defaultName: 'Stack'. aCCodeGenerator var: #interpreterProxy type: #'struct VirtualMachine*'. aCCodeGenerator declareVar: #sendTrace type: 'volatile int'; declareVar: #byteCount type: 'unsigned long'. "These need to be pointers or unsigned." self declareC: #(instructionPointer method newMethod) as: #usqInt in: aCCodeGenerator. "These are all pointers; char * because Slang has no support for C pointer arithmetic." + self declareC: #(localIP localSP localFP stackPointer framePointer stackLimit breakSelector) - self declareC: #(localIP localSP localFP stackPointer framePointer stackLimit stackMemory breakSelector) as: #'char *' in: aCCodeGenerator. aCCodeGenerator var: #breakSelectorLength declareC: 'sqInt breakSelectorLength = MinSmallInteger'. self declareC: #(stackPage overflowedPage) as: #'StackPage *' in: aCCodeGenerator. aCCodeGenerator removeVariable: 'stackPages'. "this is an implicit receiver in the translated code." NewspeakVM ifFalse: [aCCodeGenerator removeVariable: 'localAbsentReceiver'; removeVariable: 'localAbsentReceiverOrZero'; removeVariable: 'nsMethodCache']. "This defines bytecodeSetSelector as 0 if MULTIPLEBYTECODESETS is not defined, for the benefit of the interpreter on slow machines." aCCodeGenerator addConstantForBinding: (self bindingOf: #MULTIPLEBYTECODESETS). MULTIPLEBYTECODESETS == false ifTrue: [aCCodeGenerator removeVariable: 'extA'; removeVariable: 'extB'; removeVariable: 'bytecodeSetSelector']. aCCodeGenerator var: #methodCache declareC: 'long methodCache[MethodCacheSize + 1 /* ', (MethodCacheSize + 1) printString, ' */]'. aCCodeGenerator var: #nsMethodCache declareC: 'long nsMethodCache[NSMethodCacheSize + 1 /* ', (NSMethodCacheSize + 1) printString, ' */]'. AtCacheTotalSize isInteger ifTrue: [aCCodeGenerator var: #atCache declareC: 'sqInt atCache[AtCacheTotalSize + 1 /* ', (AtCacheTotalSize + 1) printString, ' */]']. aCCodeGenerator var: #primitiveTable declareC: 'void (*primitiveTable[MaxPrimitiveIndex + 2 /* ', (MaxPrimitiveIndex + 2) printString, ' */])(void) = ', vmClass primitiveTableString. vmClass primitiveTable do: [:symbolOrNot| (symbolOrNot isSymbol and: [symbolOrNot ~~ #primitiveFail]) ifTrue: [(aCCodeGenerator methodNamed: symbolOrNot) ifNotNil: [:tMethod| tMethod returnType: #void]]]. vmClass objectMemoryClass hasSpurMemoryManagerAPI ifTrue: [aCCodeGenerator var: #primitiveAccessorDepthTable type: 'signed char' sizeString: 'MaxPrimitiveIndex + 2 /* ', (MaxPrimitiveIndex + 2) printString, ' */' array: vmClass primitiveAccessorDepthTable] ifFalse: [aCCodeGenerator removeVariable: #primitiveAccessorDepthTable]. aCCodeGenerator var: #primitiveFunctionPointer declareC: 'void (*primitiveFunctionPointer)()'. aCCodeGenerator var: #externalPrimitiveTable declareC: 'void (*externalPrimitiveTable[MaxExternalPrimitiveTableSize + 1 /* ', (MaxExternalPrimitiveTableSize + 1) printString, ' */])(void)'. aCCodeGenerator var: #showSurfaceFn type: #'void *'. aCCodeGenerator var: #jmpBuf declareC: 'jmp_buf jmpBuf[MaxJumpBuf + 1 /* ', (MaxJumpBuf + 1) printString, ' */]'. aCCodeGenerator var: #suspendedCallbacks declareC: 'usqInt suspendedCallbacks[MaxJumpBuf + 1 /* ', (MaxJumpBuf + 1) printString, ' */]'. aCCodeGenerator var: #suspendedMethods declareC: 'usqInt suspendedMethods[MaxJumpBuf + 1 /* ', (MaxJumpBuf + 1) printString, ' */]'. aCCodeGenerator var: #interruptCheckChain declareC: 'void (*interruptCheckChain)(void) = 0'.
self declareCAsUSqLong: #(nextPollUsecs nextWakeupUsecs longRunningPrimitiveGCUsecs longRunningPrimitiveStartUsecs longRunningPrimitiveStopUsecs "these are high-frequency enough that they're overflowing quite quickly on modern hardware" statProcessSwitch statIOProcessEvents statForceInterruptCheck statCheckForEvents statStackOverflow statStackPageDivorce) in: aCCodeGenerator. aCCodeGenerator var: #nextProfileTick type: #sqLong!
Item was removed: - ----- Method: TMethod>>allReferencedVariablesIn: (in category 'accessing') ----- - allReferencedVariablesIn: aCodeGen - "Answer the set of all variables referenced in the receiver." - | refs | - refs := Set new. - "Find all the variable names referenced in this method. - Don't descend into " - parseTree - nodesWithParentsDo: - [:node :parent| - node isVariable ifTrue: [refs add: node name asString]. - node isStmtList ifTrue: [refs addAll: node args]. - (node isSend - and: [node selector beginsWith: #cCode:]) ifTrue: - [aCodeGen addVariablesInVerbatimCIn: node to: refs]] - unless: - [:node :parent| - (aCodeGen nilOrBooleanConditionFor: parent) - ifNil: [false] - ifNotNil: - [:condition| "double negatives are confusing, aren't they ;-)" - node = parent args second == condition not]]. - ^refs!
Item was added: + ----- Method: TMethod>>allReferencedVariablesUsing: (in category 'accessing') ----- + allReferencedVariablesUsing: aCodeGen + "Answer the set of all variables referenced in the receiver." + | refs | + refs := Set new. + "Find all the variable names referenced in this method. + Don't descend into conditionals that won't be generated." + parseTree + nodesWithParentsDo: + [:node :parent| + node isVariable ifTrue: [refs add: node name asString]. + node isStmtList ifTrue: [refs addAll: node args]. + (node isSend + and: [node selector beginsWith: #cCode:]) ifTrue: + [aCodeGen addVariablesInVerbatimCIn: node to: refs]] + unless: + [:node :parent| + parent notNil + and: [parent isSend + and: [aCodeGen nodeIsDeadCode: node withParent: parent]]]. + ^refs!
Item was changed: ----- Method: TMethod>>removeUnusedTempsIn: (in category 'utilities') ----- removeUnusedTempsIn: aCodeGen "Remove all of the unused temps in this method. Answer a set of the references." "After inlining some variable references are now obsolete, we could fix them there but the code seems a bit complicated, the other choice to to rebuild the locals before extruding. This is done here" + | usedVariables | + usedVariables := self allReferencedVariablesUsing: aCodeGen. - | refs | - refs := self allReferencedVariablesIn: aCodeGen. "reset the locals to be only those still referred to" + locals do: + [:local| + ((usedVariables includes: local) not + and: [((declarations at: local ifAbsent: ['']) includesSubString: 'static') not + and: [((declarations at: local ifAbsent: ['']) includesSubString: 'extern') not]]) ifTrue: + [locals remove: local. + declarations removeKey: local ifAbsent: []]]. + ^usedVariables! - locals := locals select: [:e| refs includes: e]. - ^refs!
vm-dev@lists.squeakfoundation.org