Click here to Skip to main content
15,887,683 members
Please Sign up or sign in to vote.
4.50/5 (2 votes)
See more:
I'm looking to detect whether the stack is 8-byte or 16-byte aligned with some portable code. I know it will be 16-byte aligned on 64bit OS and on recent versions of Linux/BSD/OSX but I'd rather have a reliable empirical way of determining it than rely on an OS based heuristic or lookup. No per-new-OS changes are allowed in the module I'm currently working on.

So far:

bool isStack16ByteAligned()
{
  bool bResult = ( sizeof( Cmp_uint_ptr ) == 8 ) ? true : false;

  //...

  return bResult;
}


Any solution that gets used will be credited in my next QOR series article :-)
Posted
Comments
Sergey Alexandrovich Kryukov 14-Mar-13 12:24pm    
Pretty interesting tricky question, but the idea to write code depending on such things is quite questionable. So, I voted 4 for the question. :-)
—SA

Here is the simple idea: declare some local variables of primitive types (I would suggest a single byte followed by a 16- or 32-bit integer, please see below), so the memory for them will be reserved on a current stack frame. Get pointers to those objects, cast the pointers to unsigned integer type and compare the numbers.

This may be not so simple though. First, those variables may be optimized out, you should prevent it. As you may need this code working even if optimization is applied, you will need to prevent optimizing the variables out by using them somehow. The problem can be more difficult if the optimizer tries to violate ("optimize") 16-bit alignment by packing some memory area (for example, if several separate byte-size stack variables are detected) as it was 8-bit alignment. I don't really know if some compilers do such things, so this is a potential danger to your method portability. You can easily find it out if you experimenting with each particular compiler, but you cannot predict the behavior of any unknown compiler. You also should understand that optimization can reorder the location of objects on stack. I really don't know how likely such behavior might be. Only I remember that when C++ compilers worked not in modern flat model but used real-mode segments/offsets, they did much more complex memory tricks in certain data presentation modes.

To prevent the effect of such byte packing I speculated about above, you could combine the sized of objects. A byte followed by integer as I mentioned in the first paragraph should be a candidate to do the trick, but you should analyze it all more thoroughly them I did and learn disassembled code with or without optimization and other options, so you may want to think of something more reliable. If the optimization reorders object locations on stack, you need take this possibility into account. Just think thoroughly about it.

As a final note, I would say that any technique relying on stack alignment is potentially dangerous in principle and generally should not be used. The issue is not how to determine the alignment type option, but what you are going to do with this result, and how reliable this technique could be. So, I'm just curious about your idea.

—SA
 
Share this answer
 
v2
Comments
Matthew Faithfull 14-Mar-13 12:42pm    
Thanks for the ideas. I might try something like this but as you mention getting it to be robust to different compilers and compiler settings is the issue so I'm going to leave the question open for now.
In this case the alignment size is needed because it's being used by a JIT assembler that's writing stack frames that need to be callable from C++ so knowing what the correct alignment should be is vital.
I'm still porting Petr Koblicek's AsmJit project to the Querysoft Open Runtime and this is one of the few points where his portability requirements are less strict than mine. He can get away with an is-it-Linux-or-Windows check but that's not sufficient for my needs.
Sergey Alexandrovich Kryukov 14-Mar-13 12:56pm    
Well, very interesting, but relying on certain stack layout (I don't even say alignment) still sounds questionable to me. I'm afraid your approach can only be reliable only for some C++ compiler(s) you know well and tested. This is because C++ standard does not guarantee any certain stack layout. Isn't that so?
—SA
Matthew Faithfull 14-Mar-13 13:11pm    
It's true the detailed layout cannot be predicted for novel compilers but that's OK. Petr's code is pretty sophisticated and I'm still understanding it but as long as the JIT generated frame fits within the same stack as the compiler generated frames, i.e. it's aligned correctly then the rest can be taken care of by parameterising the various possible ways of pushing/popping function parameters. If I need to add a novel or variant calling convention to support a new compiler that's possible and the use of that compiler can be detected at compile time so the calling convention default can be set accordingly. The current version works with at least MSVC, GCC and Clang on 32 and 64 bit Windows, Linux, BSD and OSX and part of what I'm doing with the port is factoring out the small number of OS and compiler specific dependencies to broaden that further. Clearly a LOT of testing will be needed.
Thank you for your interest :-)
Sergey Alexandrovich Kryukov 14-Mar-13 13:20pm    
OK, I understand it. In this case, I'm sure the method I described will give you reliable result if you back it with some research, which is not even difficult: you need just disassemble prototypes of this method in all compilation options, to make sure you understand that an optimized compilation does not trick you.
I would even suggest you can accept the answer formally even now, because whatever you do, it should fall into the method I explained. Just think about it.

I forgot to mention that some precompiler definition may "know" about this option, but I don't think you are asking about this approach. If so, I do understand you... Mentioned just for a record.
—SA
Matthew, from your answers in this forum I recall that you are one of the experienced guys around here. So, please treat my answer just as a suggestion and it might just complement what you found out yourself so far.

Let's first clarify, what exactly is meant by stack alignment. I'd define it as:

The minimum size the CPU uses for a push operation.

By that definition, I would stay away from comparing the addresses of stack-based variables. A compiler generally allocates the local variables of a function by just subtracting the required total size from the stack pointer. Inside that memory contingent it arranges variables just as it likes, using packing, reordering, etc. So, the address difference of two single-byte variables might very well be 1, or -1, or anything else.

What I would try to use is the sequence in which the compiler pushed arguments on the stack. Usually that will be either left to right or right to left, but in any case in strict sequence. That you can build upon, because otherwise the var_args mechanism would not work or at least would be very hard to implement.

So I'd write a little function like this:

C++
UINT ArgAddressDiff (char arg1, char arg2, ...)
{
    return abs (&arg1 - &arg2);
}

Optimization might put the arguments into registers, though. That's the reason I included the "..." varg_args declarator. That might prevent such an optimization, but I am not sure.

You could also try to pass the two arguments as the variable arguments of the function, in which the compiler has no chance to optimize them away. That would require some tweeking with the va_list and va_start macros. I haven't tried that yet, though.

I hope that contributes somewhat to your solution.
 
Share this answer
 
Comments
Matthew Faithfull 14-Mar-13 15:58pm    
Thank you that is an interesting idea. I had thought of using local variables like this but not parameters and the use of varargs will force 'C' call conformance so the parameters will go on the stack even if there are registers available. It's sneaky and I like it.
nv3 14-Mar-13 16:02pm    
You're welcome!
Matthew Faithfull 14-Mar-13 16:42pm    
It turns out that accessing the varargs requires macro magic which under the hood is itself based on OS determined alignment constants, 8 for 32 bit Windows, 16 for 64 bit Windows for example so in the end it looks like its the OS library that will need to supply the stack alignment as a readable value. Not too bad a solution I suppose but I would have preferred a bit of old fashioned C style bit manipulation magic.
I'll mark both solutions as accepted as they helped narrow it down. Many thanks.
It turns out that although the stack alignment is controlled by the compiler it is determined ultimately by the target OS. Not entirely surprising when you think about it as this is a process under that OS that has to call and be called by the OS.
It would seem then that the only 'correct' solution is an interface on the OS support library to retrieve the correct value for stack alignment rather then trying to calculate it. Not the solution that I wanted but one at least that will work without breaking the design.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900