How to align stack at 32 byte boundary in GCC?

后端 未结 3 1755
轻奢々
轻奢々 2020-12-31 18:33

I\'m using MinGW64 build based on GCC 4.6.1 for Windows 64bit target. I\'m playing around with the new Intel\'s AVX instructions. My command line arguments are -march=

相关标签:
3条回答
  • 2020-12-31 18:52

    You can get the effect you want by

    1. Declaring your variables not as variables, but as fields in a struct
    2. Declaring an array that is larger than the structure by an appropriate amount of padding
    3. Doing pointer/address arithmetic to find a 32 byte aligned address in side the array
    4. Casting that address to a pointer to your struct
    5. Finally using the data members of your struct

    You can use the same technique when malloc() does not align stuff on the heap appropriately.

    E.g.

    void foo() {
        struct I_wish_these_were_32B_aligned {
              vec32B foo;
              char bar[32];
        }; // not - no variable definition, just the struct declaration.
        unsigned char a[sizeof(I_wish_these_were_32B_aligned) + 32)];
        unsigned char* a_aligned_to_32B = align_to_32B(a);
        I_wish_these_were_32B_aligned* s = (I_wish_these_were_32B_aligned)a_aligned_to_32B;
        s->foo = ...
    }
    

    where

    unsigned char* align_to_32B(unsiged char* a) {
         uint64_t u = (unit64_t)a;
         mask_aligned32B = (1 << 5) - 1;
         if (u & mask_aligned32B == 0) return (unsigned char*)u;
         return (unsigned char*)((u|mask_aligned_32B) + 1);
    }
    
    0 讨论(0)
  • 2020-12-31 18:56

    I have been exploring the issue, filed a GCC bug report, and found out that this is a MinGW64 related problem. See GCC Bug#49001. Apparently, GCC doesn't support 32-byte stack alignment on Windows. This effectively prevents the use of 256-bit AVX instructions.

    I investigated a couple ways how to deal with this issue. The simplest and bluntest solution is to replace of aligned memory accesses VMOVAPS/PD/DQA by unaligned alternatives VMOVUPS etc. So I learned Python last night (very nice tool, by the way) and pulled off the following script that does the job with an input assembler file produced by GCC:

    import re
    import fileinput
    import sys
    
    # fix aligned stack access
    # replace aligned vmov* by unaligned vmov* with 32-byte aligned operands 
    # see Intel's AVX programming guide, page 39
    vmova = re.compile(r"\s*?vmov(\w+).*?((\(%r.*?%ymm)|(%ymm.*?\(%r))")
    aligndict = {"aps" : "ups", "apd" : "upd", "dqa" : "dqu"};
    for line in fileinput.FileInput(sys.argv[1:],inplace=1):
        m = vmova.match(line)
        if m and m.group(1) in aligndict:
            s = m.group(1)
            print line.replace("vmov"+s, "vmov"+aligndict[s]),
        else:
            print line,
    

    This approach is pretty safe and foolproof. Though I observed a performance penalty on rare occasions. When the stack is unaligned, the memory access crosses the cache line boundary. Fortunately, the code performs as fast as aligned accesses most of the time. My recommendation: inline functions in critical loops!

    I also attempted to fix the stack allocation in every function prolog using another Python script, trying to align it always at the 32-byte boundary. This seems to work for some code, but not for other. I have to rely on the good will of GCC that it will allocate aligned local variables (with respect to the stack pointer), which it usually does. This is not always the case, especially when there is a serious register spilling due to the necessity to save all ymm register before a function call. (All ymm registers are callee-save). I can post the script if there's an interest.

    The best solution would be to fix GCC MinGW64 build. Unfortunately, I have no knowledge of its internal workings, just started using it last week.

    0 讨论(0)
  • 2020-12-31 18:57

    I just ran in the same issue of having segmentation faults when using AVX inside my functions. And it was also due to the stack misalignment. Given the fact that this is a compiler issue (and the options that could help are not available in Windows), I worked around the stack usage by:

    1. Using static variables (see this issue). Given the fact that they are not stored in the stack, you can force their alignment by using __attribute__((align(32))) in your declaration. For example: static __m256i r __attribute__((aligned(32))).

    2. Inlining the functions/methods receiving/returning AVX data. You can force GCC to inline your function/method by adding inline and __attribute__((always_inline)) to your function prototype/declaration. Inlining your functions increase the size of your program, but they also prevent the function from using the stack (and hence, avoids the stack-alignment issue). Example: inline __m256i myAvxFunction(void) __attribute__((always_inline));.

    Be aware that the usage of static variables is no thread-safe, as mentioned in the reference. If you are writing a multi-threaded application you may have to add some protection for your critical paths.

    0 讨论(0)
提交回复
热议问题