Eliot Miranda uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog-eem.230.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.230 Author: eem Time: 11 December 2012, 3:16:18.03 pm UUID: 735cb4f0-6d2a-4916-bbd0-cd517917da4e Ancestors: VMMaker.oscog-eem.229
Fix stackPage headroom calculation in CoInterpreter. Was getting things backward. This increases the stack page headroom by 62% from 1564 bytes to 2532 bytes. Shootout benchmarks unchanged, so reduction in frames per page is not an issue for typical code.
This should result in fewer crashes on linux where the dynamic linker, if it kicked in during a signal handler, could cause a deep call chain at interrupt time and trample the start of the adjoining stack page.
Use self alloca: nbytes over cCode: 'alloca(nbytes)'
=============== Diff against VMMaker.oscog-eem.229 ===============
Item was changed: ----- Method: CoInterpreter>>initStackPagesAndInterpret (in category 'initialization') ----- initStackPagesAndInterpret "Initialize the stack pages and enter interpret. Use alloca'ed memory so that when we have a JIT its stack pointer will be on the native stack since alloca allocates memory on the stack. Certain thread systems use the native stack pointer as the frame ID so putting the stack anywhere else can confuse the thread system."
"Override to establish the setjmp/longjmp handler for reentering the interpreter from machine code, and disable executablity on the heap and stack pages."
"This should be in its own initStackPages method but Slang can't inline C code strings." | stackPageBytes stackPagesBytes theStackMemory | <var: #theStackMemory type: #'char *'> stackPageBytes := self stackPageByteSize. stackPagesBytes := self computeStackZoneSize. theStackMemory := self + cCode: [self alloca: stackPagesBytes] - cCode: 'alloca(stackPagesBytes)' inSmalltalk: [stackPages := self stackPagesClass new. stackPages initializeWithByteSize: stackPagesBytes for: self]. self sqMakeMemoryNotExecutableFrom: objectMemory startOfMemory asUnsignedInteger To: objectMemory memoryLimit asUnsignedInteger. self sqMakeMemoryNotExecutableFrom: theStackMemory asUnsignedInteger To: theStackMemory asUnsignedInteger + stackPagesBytes. stackPages initializeStack: theStackMemory numSlots: stackPagesBytes / BytesPerWord pageSize: stackPageBytes / BytesPerWord stackLimitOffset: self stackLimitOffset stackPageHeadroom: self stackPageHeadroom.
"Once the stack pages are initialized we can continue to bootstrap the system." self loadInitialContext. "We're ready for the heartbeat (poll interrupt)" self ioInitHeartbeat. self initialEnterSmalltalkExecutive. ^nil!
Item was changed: ----- Method: CoInterpreterStackPages>>initializeStack:numSlots:pageSize:stackLimitOffset:stackPageHeadroom: (in category 'initialization') ----- initializeStack: theStackPages numSlots: stackSlots pageSize: slotsPerPage stackLimitOffset: stackLimitOffsetBytes stackPageHeadroom: stackPageHeadroomBytes "Initialize the stack pages. In the C VM theStackPages will be alloca'ed memory to hold the stack pages on the C stack. In the simulator they are housed in the memory between the cogMethodZone and the heap."
<var: #theStackPages type: #'char *'> <returnTypeC: #void> | numPages page structStackPageSize pageStructBase count | <var: #page type: #'StackPage *'> <var: #pageStructBase type: #'char *'> + self cCode: [] - self cCode: '' inSmalltalk: [self assert: objectMemory startOfMemory - coInterpreter cogCodeSize - coInterpreter methodCacheSize - coInterpreter primTraceLogSize - coInterpreter rumpCStackSize = (stackSlots * BytesPerWord)]. structStackPageSize := coInterpreter sizeof: CogStackPage. bytesPerPage := slotsPerPage * BytesPerWord. numPages := coInterpreter numStkPages.
"Because stack pages grow down baseAddress is at the top of a stack page and so to avoid subtracting BytesPerWord from baseAddress and lastAddress in the init loop below we simply push the stackPage array up one word to avoid the overlap. This word is extraStackBytes." pageStructBase := theStackPages + (numPages * bytesPerPage) + BytesPerWord. pages := self cCode: [self cCoerceSimple: pageStructBase to: #'StackPage *'] inSmalltalk: [pageMap := Dictionary new. ((0 to: numPages - 1) collect: [:i| CogStackPage surrogateClass new address: pageStructBase + (i * structStackPageSize) simulator: coInterpreter zoneBase: coInterpreter stackZoneBase zoneLimit: objectMemory startOfMemory]) do: [:pageSurrogate| pageMap at: pageSurrogate address put: pageSurrogate]; yourself]. 0 to: numPages - 1 do: [:index| page := self stackPageAt: index. page lastAddress: theStackPages + (index * bytesPerPage); + baseAddress: page lastAddress + bytesPerPage; + stackLimit: page baseAddress + - stackLimitOffsetBytes + - stackPageHeadroomBytes; - baseAddress: (page lastAddress + bytesPerPage); - stackLimit: page lastAddress - + stackLimitOffsetBytes - + stackPageHeadroomBytes; realStackLimit: page stackLimit; baseFP: 0; nextPage: (self stackPageAt: (index = (numPages - 1) ifTrue: [0] ifFalse: [index + 1])); prevPage: (self stackPageAt: (index = 0 ifTrue: [numPages - 1] ifFalse: [index - 1]))].
"Now compute stackBasePlus1 so that the pageIndexFor: call maps all addresses from aPage baseAddress to aBase limitAddress + 1 to the same index (stacks grow down)" stackBasePlus1 := (self cCoerceSimple: theStackPages to: #'char *') + 1. + self cCode: [] - self cCode: '' inSmalltalk: [minStackAddress := theStackPages. maxStackAddress := theStackPages + (numPages * bytesPerPage) + BytesPerWord - 1].
"The overflow limit is the amount of stack to retain when moving frames from an overflowing stack to reduce thrashing. See stackOverflowOrEvent:mayContextSwitch:" page := self stackPageAt: 0. overflowLimit := page baseAddress - page realStackLimit * 3 // 5. 0 to: numPages - 1 do: [:index| page := self stackPageAt: index. self assert: (self pageIndexFor: page baseAddress) == index. self assert: (self pageIndexFor: page baseAddress - (slotsPerPage - 1 * BytesPerWord)) == index. self assert: (self stackPageFor: page baseAddress) == page. self assert: (self stackPageFor: page stackLimit) == page. + self cCode: [] - self cCode: '' inSmalltalk: [| memIndex | memIndex := index * slotsPerPage + 1. "this is memIndex in the block above" self assert: (self memIndexFor: (self oopForPointer: page baseAddress)) == (memIndex + slotsPerPage - 1). index < (numPages - 1) ifTrue: [self assert: (self stackPageFor: page baseAddress + BytesPerWord) == (self stackPageAt: index + 1)]]. self assert: (page trace: -1) ~= 0 "for assert checking of the page tracing flags. -1 == invalid state"].
mostRecentlyUsedPage := self stackPageAt: 0. page := mostRecentlyUsedPage. count := 0. [| theIndex | count := count + 1. theIndex := self pageIndexFor: page baseAddress. self assert: (self stackPageAt: theIndex) == page. self assert: (self pageIndexFor: page baseAddress) == theIndex. self assert: (self pageIndexFor: page stackLimit) == theIndex. self assert: (self pageIndexFor: page lastAddress + 1) == theIndex. (page := page nextPage) ~= mostRecentlyUsedPage] whileTrue. self assert: count == numPages. self assert: self pageListIsWellFormed!
Item was changed: ----- Method: StackInterpreter>>initStackPages (in category 'initialization') ----- initStackPages "Initialize the stackPages. This version is only for simulation because Slang refuses to inline it, which makes the alloca invalid." | stackPageBytes stackPagesBytes theStackMemory | stackPageBytes := self stackPageByteSize. stackPagesBytes := self computeStackZoneSize. theStackMemory := self + cCode: [self alloca: stackPagesBytes] - cCode: 'alloca(stackPagesBytes)' inSmalltalk: [stackPages := self stackPagesClass new. stackPages initializeWithByteSize: stackPagesBytes for: self]. stackPages initializeStack: theStackMemory numSlots: stackPagesBytes / BytesPerWord pageSize: stackPageBytes / BytesPerWord stackLimitOffset: self stackLimitOffset stackPageHeadroom: self stackPageHeadroom!
Item was changed: ----- Method: StackInterpreter>>initStackPagesAndInterpret (in category 'initialization') ----- initStackPagesAndInterpret "Initialize the stack pages and enter interpret. Use alloca'ed memory so that when we have a JIT its stack pointer will be on the native stack since alloca allocates memory on the stack. Certain thread systems use the native stack pointer as the frame ID so putting the stack anywhere else can confuse the thread system."
"This should be in its own initStackPages method but Slang can't inline C code strings." | stackPageBytes stackPagesBytes theStackMemory | <var: #theStackMemory type: #'void *'> stackPageBytes := self stackPageByteSize. stackPagesBytes := self computeStackZoneSize. theStackMemory := self + cCode: [self alloca: stackPagesBytes] - cCode: 'alloca(stackPagesBytes)' inSmalltalk: [stackPages := self stackPagesClass new. stackPages initializeWithByteSize: stackPagesBytes for: self]. stackPages initializeStack: theStackMemory numSlots: stackPagesBytes / BytesPerWord pageSize: stackPageBytes / BytesPerWord stackLimitOffset: self stackLimitOffset stackPageHeadroom: self stackPageHeadroom.
"Once the stack pages are initialized we can continue to bootstrap the system." self loadInitialContext. "We're ready for the heartbeat (poll interrupt)" self ioInitHeartbeat. self interpret. ^nil!
Item was removed: - ----- Method: ThreadedFFIPlugin>>alloca: (in category 'simulation') ----- - alloca: size - <doNotGenerate> - ^ByteArray new: size!
Item was added: + ----- Method: VMClass>>alloca: (in category 'C library simulation') ----- + alloca: size + "Simulation of alloca(3)" + <doNotGenerate> + ^ByteArray new: size!
vm-dev@lists.squeakfoundation.org