6

I am trying to compile the below code with ICC 2018:

__asm {
        mov ebx, xx              ;xx address to registers
}

where xx is of type int16. This is the first instruction inside my function.

I get the below warning with the above assembly code: warning #13212: Reference to ebx in function requiring stack alignment

Surprisingly, when I replaced ebx with eax or esi, I saw the warning go away. I am not able to understand why I am seeing the issue only with ebx, as far as I know, both ebx and eax has same architecture(32 bit registers).

Also, I didn't see the warning when I compiled the same code with ICC 2013.

Can anyone help me resolve this warning?

Thanks!

TobiSH
  • 2,833
  • 3
  • 23
  • 33
  • Can you provide a full snippet of code, so we can copy paste in order to compile? – JVApen Feb 28 '18 at 07:38
  • 2
    I'm guessing ICC uses `ebx` to hold the old stack pointer when it needs to align the stack by more than the default, and won't let you clobber it with inline asm. This makes me think of the weird code gcc emits using `r10` in functions with `__m256` locals (on x86-64). – Peter Cordes Feb 28 '18 at 08:42
  • And BTW, if `xx` is an `int16_t`, you should load it with `movsx eax, word [xx]`, not `mov` – Peter Cordes Feb 28 '18 at 08:44
  • As Peter mentioned, check if ICC has any particular stack conventions involving `ebx` specifically. It might let you go ahead and store something into `ebx` but you'd want to make sure you aren't clobbering something important, or you may need to align whatever address `xx` is to a particular boundary. – lurker Feb 28 '18 at 12:55
  • 2
    This is part of the convention related to usage of Microsoft's `__asm` directives (also applies to ICC) Microsoft has a [warning about using _EBX_ in asm inline](https://msdn.microsoft.com/en-us/library/k1a8ss06.aspx) – Michael Petch Feb 28 '18 at 18:55
  • 4
    To quote: _Some SSE types require eight-byte stack alignment, forcing the compiler to emit dynamic stack-alignment code. To be able to access both the local variables and the function parameters after the alignment, the compiler maintains two frame pointers. If the compiler performs frame pointer omission (FPO), it will use EBP and ESP. If the compiler does not perform FPO, it will use EBX and EBP. To ensure code runs correctly, do not modify EBX in asm code if the function requires dynamic stack alignment as it could modify the frame pointer._" – Michael Petch Feb 28 '18 at 18:55
  • And then says _"Either move the eight-byte aligned types out of the function, or avoid using EBX."_ – Michael Petch Feb 28 '18 at 18:56
  • EBX is a data register right? I don't understand why compiler will use it to store the stack pointer. Do you guys have any idea? – Nayanika Ghosh Jul 05 '18 at 11:57

1 Answers1

1

The compiler on the platform of choice (ICC as it mimics MSVC's behavior) uses EBX to save the original stack pointer value if additional alignment is required. Therefore you cannot overwrite it safely. The program's behavior would become undefined. The compiler warning just tells you about that.

To help with save/restore of all registers affected by assembly blocks, an extended syntax with so called clobber lists is recommended. Your example uses MSVC-style __asm{...} syntax. In MSVC-style syntax, the compiler detects what registers you touch and saves/restores them for you. ICC also supports GCC-like notation for extended asm with clobber lists: asm("...":::). It also supports simpler GCC asm("...") without the clobber list part. See this question for more details (thanks Peter Cordes for the link and explanation).

Documentation that I found useful when I was learning to use clobber lists (I actually use it all the time because it is impossible to remember its rather human-unfriendly syntax):

  1. https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5
  2. https://software.intel.com/en-us/node/694235

The simple inline assembly blocks without clobber lists can be safely used only in the following situations:

  1. Instructions of the block do not modify registers defined in the ABI. Thus GPRs, stack counter, flags should be untouched; if there are floating-point calculations in the function, FPU/vector registers are off limits as well. Even memory writes can lead to bugs because the compiler relies on known values to reside in memory. In contrast, one can issue INT3, HLT, WRMSR etc instructions which either touch no registers or affect only system registers which the compiler do not use. However, the majority of such instructions are privileged and cannot be used in user applications. One can also read all available registers provided there are no side effects of such reads.
  2. The assembler block is the only statement in a function's body. In this case, it has to abide to calling conventions of the chosen platform: how function's arguments are passed, where its exit code should be placed etc. The block will also need to cope with compiler-generated prologue and epilogue code blocks that have their own assumptions about registers. Their code is not strictly stable, nor portable nor guaranteed to be the same with different optimization levels. With GCC on x86, I was unable to disable prologue/epilogue generation, so there is still some risk to violate compiler assumptions.
  3. You save all clobbered registers yourself and restore them afterwards. This is relatively easy because you can see your own assembler code and can tell if a register gets modified by it or not. However, make a mistake and a compiler will not be here for you to point it out. It is very nice of ICC 2018 to actually give a warning even though it could have just treated the asm block as a black box.
  4. You "stole" a register from compiler. GCC allows doing that with register asm statement (do not remember if the same trick works with other compilers). You can thus declare that a variable is bound to a certain register. Be aware that such technique reduces number of registers available to compiler for its register allocation phase, and that will degrade quality of code it generates. Ask for too many registers, and the compiler will be helpless and refuse to work. Similarly, one cannot ask for registers with a dedicated role to be taken away from a compiler, such as stack pointer or program counter.

That said, the extended asm syntax with clobber lists provides a nice alternative. It turns an asm section from a black box to something of an inline internal "function" that declares its own inputs, outputs and resources it overwrites which are shared with the outer function.

Grigory Rechistov
  • 2,104
  • 16
  • 25
  • 2
    The OP is using MSVC-style `__asm { }` syntax, *not* GNU C Basic Asm `asm( "" )` syntax. In MSVC-style syntax, the compiler detects what registers you touch and saves / restores them (or lets them be clobbered). You should not use `push` / `pop` inside the asm statement, the compiler will still emit its own save/restore around your asm block, even if your block includes push/pop: https://godbolt.org/g/Cebj1b. (So your suggestion only works for GNU C extended asm syntax, yes you could push/pop ebx and not declare a clobber on it in that case.) IDK if the OP cares about compat with MSVC. – Peter Cordes Feb 28 '18 at 19:37
  • But note that push/pop in inline asm is not safe in 64-bit code, because the x86-64 SysV ABI has a red-zone, and you can't declare a clobber on it. https://stackoverflow.com/questions/34520013/using-base-pointer-register-in-c-inline-asm. (You could ask for a dummy output memory operand and use that to save/restore, though. https://stackoverflow.com/questions/48853757/gcc-inline-assembly-with-stack-operation). – Peter Cordes Feb 28 '18 at 19:40
  • @PeterCordes thanks, I haven't used MSVC-style `__asm` much to know that it *attempts* figuring clobber lists on itself. I wonder how it will behave when someone uses `__emit \` or other nasty stuff. I will extend my answer with your comment, with your permission. – Grigory Rechistov Feb 28 '18 at 20:33
  • 1
    Interesting question; I've never used it for anything myself, just learned how it works from other people on SO. I'd guess it doesn't disassemble stuff you `__emit`, so that's a way around the crappy over-simplified design of MSVC syntax for this case, but not the more important problem of not being able to get inputs in registers. (Apparently the implementation in MSVC itself is/was also nasty, and sometimes broke your code in new compiler versions. https://stackoverflow.com/questions/3323445/what-is-the-difference-between-asm-and-asm#comment59576185_35959859) – Peter Cordes Feb 28 '18 at 20:38
  • Your first paragraph is still totally bogus. `ebx` is call-preserved in all the calling conventions the OP might be using, so looking at whether the compiler touches it or not without the `asm` statement is not useful. MSVC syntax will make the compiler save/restore it (or error at compile time if it doesn't want to, apparently), like a correct GNU C extended asm statement with a clobber. Save/restoring registers *inside* inline asm statements is worse than letting the compiler do it except as a workaround; often it's already doing it around the whole function and can let you clobber it. – Peter Cordes Feb 28 '18 at 23:21
  • EBX is a data register right? I don't understand why compiler will use it to store the stack pointer. Do you guys have any idea? – Nayanika Ghosh Jun 18 '18 at 08:34