The compiler on the select platform (ICC, because it mimics the behavior of MSVC) uses EBX to preserve the original value of the stack pointer if additional alignment is required. Therefore, you cannot overwrite it safely. The behavior of the program will become undefined. A compiler warning simply reports this.
To help with saving / restoring all registers affected by assembly blocks, an extended syntax with the so-called clobber lists is recommended. Your example uses the __asm{...} syntax in MSVC style. In the MSVC style syntax, the compiler detects which 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 the simpler GCC asm("...") without part of the clobber list. See question for details (thanks to Peter Cordes for the link and explanation).
The documentation 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 unfriendly syntax):
Simple, built-in assembly units without mug lists can only be used safely in the following situations:
- Block instructions do not change the registers defined in the ABI. Therefore, GPR, stack counter, flags should be untouched; if the function has floating point calculations, FPU / vector registers also do not work. Even writes to memory can lead to errors because the compiler relies on known values ββto store in memory. On the contrary, you can issue the
INT3 , HLT , WRMSR etc WRMSR , which either do not concern registers, or affect only system registers that the compiler does not use. However, most of these instructions are privileged and cannot be used in user applications. You can also read all available registers if there are no side effects of such readings. - The assembler block is the only statement in the function body. In this case, he must observe the calls on the chosen platform: how the arguments of the function are transferred, where the exit code should be placed, etc. The block will also need to cope with the compiler-generated blocks of prolog and epilogue code, which have their own assumptions about registers. Their code is not strictly stable, not portable, and not guaranteed at different optimization levels. With GCC on x86, I was not able to disable prolog / epilogue generation, so there is a risk of breaking compiler assumptions.
- You save all the lost registers yourself and restore them later. This is relatively simple because you can see your own assembler code and can determine if it has been modified or not. However, make a mistake and the compiler will not be here for you to point this out. It is very good that ICC 2018 does give a warning, although it could just treat the asm block as a black box.
- You "stole" the register from the compiler. GCC allows you to do this with the
register asm operator (don't remember if the same trick works with other compilers). This way you can declare that the variable is bound to a specific case. Keep in mind that this technique reduces the number of registers available to the compiler for the register allocation phase, and this will degrade the quality of the generated code. Ask for too many registers and the compiler will be helpless and refuse to work. Likewise, you cannot request registers with a dedicated role that must be removed from the compiler, such as a stack pointer or program counter.
However, the extended asm syntax with clobber lists provides a nice alternative. It turns an asm section from a black box into a built-in internal βfunctionβ that declares its own inputs, outputs, and resources that it overwrites that are shared by an external function.
source share