[context] How to set stack pointer in a platform-independent way?
Hi, The problem had been discussed here: https://groups.google.com/forum/#!topic/boost-devel-archive/cl8B1oUPrVM But it's still not clear how to set the stack pointer portably, even after examining the code of Boost.Coroutine. For now, Boost.Context only supports downward stack, and Boost.Coroutine, which happened to be written by the same author, just assumes that the stack always grows downward, which is not really a portable implementation, if future Boost.Context supports upward stack, Boost.Coroutine has to be changed as well. Even than that, what does "beginning of the stack" really means? Suppose we have a stack of size 3, starting at [s]: [s-1][s][s+1][s+2][s+3] Why the beginning of a downward stack is [s+3], not [s+2]? If it's a upward stack, is beginning [s] or [s-1]? Thanks.
But it's still not clear how to set the stack pointer portably, even after examining the code of Boost.Coroutine. For now, Boost.Context only supports downward stack, and Boost.Coroutine, which happened to be written by the same author, just assumes that the stack always grows downward, which is not really a portable implementation, if future Boost.Context
boost.context is implemented for specific architectures/platforms, e.g. it does not contain generic code which will work in general. that said - you can not have a 'portable' implementation. an architecture/platform determines in which direction the stack grows - at the moment all architectures/platforms supported by boost.context have downward growing stack.
supports upward stack, Boost.Coroutine has to be changed as well.
yes, if in the future an architecture/platform with upward growing stack will be supported, the stack allocator classes have to be modified
2014-09-19 14:10 GMT+08:00 Oliver Kowalke
But it's still not clear how to set the stack pointer portably, even after
examining the code of Boost.Coroutine. For now, Boost.Context only supports downward stack, and Boost.Coroutine, which happened to be written by the same author, just assumes that the stack always grows downward, which is not really a portable implementation, if future Boost.Context
boost.context is implemented for specific architectures/platforms, e.g. it does not contain generic code which will work in general. that said - you can not have a 'portable' implementation. an architecture/platform determines in which direction the stack grows - at the moment all architectures/platforms supported by boost.context have downward growing stack.
I'm not familiar with the asm enough, but can't you adjust the sp in the asm so that in user code it always has to be the lowest position? supports upward stack, Boost.Coroutine has to be changed as well.
yes, if in the future an architecture/platform with upward growing stack will be supported, the stack allocator classes have to be modified
So how would you modify them to support both upward and downward stack? preprocessor branch? runtime check? My prevoius question stays unanswered, so let me repeat here: what does "beginning of the stack" really means? Suppose we have a stack of size 3, starting at [s]: [s-1][s][s+1][s+2][s+3] Why the beginning of a downward stack is [s+3], not [s+2]? If it's a upward stack, is beginning [s] or [s-1]? Thanks.
2014-09-19 8:53 GMT+02:00 TONGARI J
I'm not familiar with the asm enough, but can't you adjust the sp in the asm so that in user code it always has to be the lowest position?
no idea what you want - fcontext_t is a pointer containing the address of the lowest address of the stack - but it is not the stack point position if the context is resumed
So how would you modify them to support both upward and downward stack? preprocessor branch? runtime check?
preprocessor - because it is determined at compile time
My prevoius question stays unanswered, so let me repeat here: what does "beginning of the stack" really means? Suppose we have a stack of size 3, starting at [s]:
[s-1][s][s+1][s+2][s+3]
Why the beginning of a downward stack is [s+3], not [s+2]? If it's a upward stack, is beginning [s] or [s-1]?
stackallocator using malloc(): void * limit = std::malloc( size); if ( ! limit) throw std::bad_alloc(); ctx.size = size; ctx.sp = static_cast< char * >( limit) + ctx.size; if limit is at x and you allocated 64byte of memory for the stack you have to adjust your stack base for downward growing stacks to address y=x+64, e.g. starting at y your stack has 64bytes to grow.
2014-09-19 15:21 GMT+08:00 Oliver Kowalke
2014-09-19 8:53 GMT+02:00 TONGARI J
: I'm not familiar with the asm enough, but can't you adjust the sp in the asm so that in user code it always has to be the lowest position?
no idea what you want - fcontext_t is a pointer containing the address of the lowest address of the stack - but it is not the stack point position if the context is resumed
To be clear, I was talking about `make_fcontext(void* sp,std::size_t size,void(*fn)(intptr_t))`. Currently `sp` requires the user to know the whether stack grows downwards or upwards, but for a specific architecture/platform, you have to write an asm for it, and you know how the stack grows on this architecture/platform, don't you? Or are those asms independent of how the stack grows? If the asm knows how the stack grows, you can offset the `sp` internally, leave it transparent to the user.
So how would you modify them to support both upward and downward stack?
preprocessor branch? runtime check?
preprocessor - because it is determined at compile time
My prevoius question stays unanswered, so let me repeat here: what does "beginning of the stack" really means? Suppose we have a stack of size 3, starting at [s]:
[s-1][s][s+1][s+2][s+3]
Why the beginning of a downward stack is [s+3], not [s+2]? If it's a upward stack, is beginning [s] or [s-1]?
stackallocator using malloc(): void * limit = std::malloc( size); if ( ! limit) throw std::bad_alloc(); ctx.size = size; ctx.sp = static_cast< char * >( limit) + ctx.size;
if limit is at x and you allocated 64byte of memory for the stack you have to adjust your stack base for downward growing stacks to address y=x+64, e.g. starting at y your stack has 64bytes to grow.
So how about upward stack, is it x or (x-1)? Thanks.
2014-09-19 10:12 GMT+02:00 TONGARI J
Currently `sp` requires the user to know the whether stack grows downwards or upwards, but for a specific architecture/platform, you have to write an asm for it, and you know how the stack grows on this architecture/platform, don't you?
yes, but at the moment for all supported platforms the stack grows downward
Or are those asms independent of how the stack grows?
no
If the asm knows how the stack grows, you can offset the `sp` internally, leave it transparent to the user.
boost.context is low level and the design decision was that higher level libraries using boost.context have to take care about the stack, e.g. boost.context is not responsible for allocating/deallocating the stack.
stackallocator using malloc():
void * limit = std::malloc( size); if ( ! limit) throw std::bad_alloc(); ctx.size = size; ctx.sp = static_cast< char * >( limit) + ctx.size;
if limit is at x and you allocated 64byte of memory for the stack you have to adjust your stack base for downward growing stacks to address y=x+64, e.g. starting at y your stack has 64bytes to grow.
So how about upward stack, is it x or (x-1)?
of course x for upward growing stacks x is the lowest address of your stack and y is the highest address of your stack
2014-09-19 16:19 GMT+08:00 Oliver Kowalke
boost.context is low level and the design decision was that higher level libraries using boost.context have to take care about the stack, e.g. boost.context is not responsible for allocating/deallocating the stack.
I know boost.context is low level, and I don't expect it to alloc/dealloc the stack, what I proposed is: ```c++ fcontext_t BOOST_CONTEXT_CALLDECL make_fcontext_portable(void* sp, std::size_t size, void (*fn)(intptr_t)) { #if STACK_GROWS_UPWARD return make_fcontext(sp, size, fn); #else return make_fcontext(sp + size, size, fn); } ... char stack[1024]; // works for both downward/upward stacks auto ctx = make_fcontext_portable(stack, 1024, f); ``` Does that make sense? Is it possible to embed the logic in asm so you don't have to use preprocessor condition at all? Thanks.
On 09/18/2014 10:02 PM, TONGARI J wrote:
Hi,
The problem had been discussed here: https://groups.google.com/forum/#!topic/boost-devel-archive/cl8B1oUPrVM
But it's still not clear how to set the stack pointer portably, even after examining the code of Boost.Coroutine. For now, Boost.Context only supports downward stack, and Boost.Coroutine, which happened to be written by the same author, just assumes that the stack always grows downward, which is not really a portable implementation, if future Boost.Context supports upward stack, Boost.Coroutine has to be changed as well.
Several years ago, I had a look at the Boehm garbage collector: http://www.hboehm.info/gc/ IIRC, it had to determine whether the stack grew upward or downward. I can't remember where in the code that was, but I also vaguely remember reading (I think in the code commets) that the method was not completely portable. HTH. -regards, Larry
2014-09-19 22:03 GMT+08:00 Larry Evans
On 09/18/2014 10:02 PM, TONGARI J wrote:
Hi,
The problem had been discussed here: https://groups.google.com/forum/#!topic/boost-devel-archive/cl8B1oUPrVM
But it's still not clear how to set the stack pointer portably, even after examining the code of Boost.Coroutine. For now, Boost.Context only supports downward stack, and Boost.Coroutine, which happened to be written by the same author, just assumes that the stack always grows downward, which is not really a portable implementation, if future Boost.Context supports upward stack, Boost.Coroutine has to be changed as well.
Several years ago, I had a look at the Boehm garbage collector:
IIRC, it had to determine whether the stack grew upward or downward. I can't remember where in the code that was, but I also vaguely remember reading (I think in the code commets) that the method was not completely portable.
I guess something like below should work? #include <iostream> __attribute__((noinline)) bool is_upward_test(char* a) { char b[32]; return a < b; } bool is_upward() { char a[32]; return is_upward_test(a); } int main(int argc, char** argv) { std::cout << is_upward(); return 0; }
On 09/20/2014 02:20 AM, TONGARI J wrote:
2014-09-19 22:03 GMT+08:00 Larry Evans
: On 09/18/2014 10:02 PM, TONGARI J wrote:
Hi,
The problem had been discussed here: https://groups.google.com/forum/#!topic/boost-devel-archive/cl8B1oUPrVM
But it's still not clear how to set the stack pointer portably, even after examining the code of Boost.Coroutine. For now, Boost.Context only supports downward stack, and Boost.Coroutine, which happened to be written by the same author, just assumes that the stack always grows downward, which is not really a portable implementation, if future Boost.Context supports upward stack, Boost.Coroutine has to be changed as well.
Several years ago, I had a look at the Boehm garbage collector:
IIRC, it had to determine whether the stack grew upward or downward. I can't remember where in the code that was, but I also vaguely remember reading (I think in the code commets) that the method was not completely portable.
I guess something like below should work?
#include <iostream>
__attribute__((noinline)) bool is_upward_test(char* a) { char b[32]; return a < b; }
bool is_upward() { char a[32]; return is_upward_test(a); }
int main(int argc, char** argv) { std::cout << is_upward(); return 0; }
Yep. Something like that. I, again, vaguely remember seeing code something like that in the Boehm GC source code. -regards, Larry
On Sat, Sep 20, 2014 at 3:20 AM, TONGARI J
I guess something like below should work?
#include <iostream>
__attribute__((noinline)) bool is_upward_test(char* a) { char b[32]; return a < b; }
bool is_upward() { char a[32]; return is_upward_test(a); }
int main(int argc, char** argv) { std::cout << is_upward(); return 0; }
Doesn't the standard say pointer comparisons are only defined between pointers into the same container? Therefore wouldn't the above be UB?
2014-09-21 1:48 GMT+08:00 Nat Goodspeed
On Sat, Sep 20, 2014 at 3:20 AM, TONGARI J
wrote: I guess something like below should work?
#include <iostream>
__attribute__((noinline)) bool is_upward_test(char* a) { char b[32]; return a < b; }
bool is_upward() { char a[32]; return is_upward_test(a); }
int main(int argc, char** argv) { std::cout << is_upward(); return 0; }
Doesn't the standard say pointer comparisons are only defined between pointers into the same container? Therefore wouldn't the above be UB?
o_O!? That would be scary, do you really mean pointer or iterator? Where's the paragraph?
On 09/21/2014 07:06 AM, TONGARI J wrote:
2014-09-21 1:48 GMT+08:00 Nat Goodspeed
mailto:nat@lindenlab.com>:
Doesn't the standard say pointer comparisons are only defined between pointers into the same container? Therefore wouldn't the above be UB?
o_O!? That would be scary, do you really mean pointer or iterator? Where's the paragraph?
Paragraph [expr.rel]/3 does not specify this general case. Think of near pointers in the segmented memory model. For an in-depth discussion see commentary 1209 in: http://c0x.coding-guidelines.com/6.5.8.pdf
participants (5)
-
Bjorn Reese
-
Larry Evans
-
Nat Goodspeed
-
Oliver Kowalke
-
TONGARI J