Ilya Zakharevich on Mon, 30 Oct 2023 00:08:40 +0100 |
[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]
ABI of “return objects on the bottom of the stack, continuously” (Re: Two stacks at once) |
The quoted message was written ∼19 years ago. — And then I found a solution “quite soon” — but could not find toits to write this down. The old discussion is on https://pari.math.u-bordeaux.fr/archives/pari-dev-0412/msg00016.html > On Thu, Dec 16, 2004 at 11:49:19PM +0100, Bill Allombert wrote: > > That said, the idea of several stacks has a lot of merit, but I don't > > think we can retrofit it at this stage without major though. ⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜ grepile() is considered harmful I remind that the idea was to (almost?) completely eliminate grepile() — which is the most confusing — and thoroughly unneeded and wasteful — part of the implementation of PARI. The **need** for grepile() comes from the PARI’s ABI, whose idea is that [[[ objects returned from a subroutine call are allocated on stack ]]] Moreover, these returned objects should better (for the purpose of efficiency of the stack usage) be allocated continuously. Likewise, they should better be at the bottom of the stack. However, the components of recursively built objects are often constructed using OTHER ancillary subroutine calls. After such a call to construct (say) the 3rd component, the stack looks like BEFORE … COMPONENT1 COMPONENT2 RESULT-OF-CALL Now we may need to post-process the RESULT-OF-CALL, and when COMPONENT3 is finally returned by yet another ancillary call, the stack becomes BEFORE … COMPONENT1 COMPONENT2 RESULT-OF-CALL COMPONENT3 — and this means that our 3 components are now not adjacent. ⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜ The proposed solution of 2004 Use two stacks alternately: when our caller expects our result to be on STACKα, we call our ancillary routines when being switched to STACKβ, and only the FINAL ancillary call during the calculation of COMPONENT3 is made switching back to STACKα. Of course, there is a disadvantage: this may IN SOME CASES result in a waste of memory: it may happen that we need a lot of memory on STACKβ — while STACKα has a lot of unused memory. ⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜ WORKAROUND — of about 2005 ;—( Have both stacks use the same chunk of memory! To avoid collisions, one of them should grow-down-from-top, the other one grow-up-from-bottom. ⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜⁜ IMPLEMENTATION • Have a new variable stack_dir. • Make the stack allocation routines pay attention to stack_dir. • (As a stopgap measure) make gerepile() (and friends) pay attention to stack_dir. • Make the starting value for stack_dir to be settable at the start of runtime. After this, check that the current code works with the “non-standard” value of stack_dir. If this ACTUALLY turns out to be feasible: • Add a new function flip_stack_dir(). • Use this function in new code (as described above). • (Leisurely,) insert flip_stack_dir() in suitable places in old code and remove gerepile() code. The bookkeeping data for the stack is currently stack_bot avma stack_top (if I did not mix up the names bot/top!). avma grows when one allocates objects on stack. The free area is between avma and stack_top. The proposed data for the stack is stack_min avma_min avma_max stack_max The free area is between avma_min and avma_max. Depending on stack_dir, avma is aliased to one of avma_min and avma_max, and stack_top is aliased to the other one of these two. (The code taking into account stack_bot needs to be rewritten.) (One way to do this is that flip_stack_dir() flips stack_dir, and exchanges avma and stack_top.) Hope this helps, Ilya