0
#include <cstdio>
int main() {
    int a, b, c;
    const char* str = "hello world";
    asm volatile (
     R"(mov   %0, 1
        mov   %1, 2
        mov   %2, 3
        call  puts)"
        :"=&r"(a),
         "=&r"(b),
         "=&r"(c)
        :"D"(str)
        :"cc","memory"
    );
    printf("%d, %d, %d\n", a, b, c);
}

and the complier result is like this(by gcc12.2, the options are -O3 -masm=intel)

.LC0:
        .string "hello world"
.LC1:
        .string "%d, %d, %d\n"
main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:.LC0
        mov   esi, 1
        mov   edx, 2
        mov   ecx, 3
        call  puts
        mov     edi, OFFSET FLAT:.LC1
        xor     eax, eax
        call    printf
        xor     eax, eax
        add     rsp, 8
        ret

the values (1, 2, 3 in this code) were not saved to memory from the register and they were not successfully passed to printf as well, even if when I turned off the optimization.

so the output is like this, which is obviously undefined-behaviour.

hello world
4202511, 0, 4096

Why does this problem occur?


#include <cstdio>
int main() {
    int a, b, c;
    const char* str = "hello world";
    asm volatile (
     R"(mov     %0, 1
        mov     %1, 2
        mov     %2, 3
        call    puts)"
        :"=&m"(a),
         "=&m"(b),
         "=&m"(c)
        :"D"(str)
        :"cc","memory"
    );
    printf("%d, %d, %d\n", a, b, c);
}

If I change the code from "=&r" to "=&m", it can work well as expected. but is there any possibility to solve this problem while passing the a, b, c through register?

the short link of the code above is https://godbolt.org/z/W6xYcdd9c and https://godbolt.org/z/9dceGrfM1

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
fantasy
  • 53
  • 4
  • Hello and welcome to Stack Overflow! Please read [ask] and take the [tour]. Note that questions on Stack Overflow must be self-contained. They must not rely on external links to be understandable. Also, do not post pictures of code! I have downvoted your question and will remove the downvotes once you rectify these issues. – fuz Dec 22 '22 at 16:01
  • 2
    The likely problems is probably related to the fact you call `puts` in inline assembly which can clobber all or some of the volatile registers. Calling functions within inline assembly is frowned upon if you don't get things right which can be difficult for a novice. Remove the `call puts` would probably solve the problem. – Michael Petch Dec 22 '22 at 16:02
  • Are you passing any options to the compiler? `mov %0, 1` tries to store `%0` into memory at address 1 in the default AT&T syntax. Do you perhaps pass some sort of option to switch to Intel syntax? Also, is this C or C++ code? You have tagged both with one of the two tags likely being irrelevant and wrong. Do not add tags for languages that are not relevant to your question. Your question also still has links to external sites. Integrate the content of these links into your question, then remove the links or clarify that the point to the same content. – fuz Dec 22 '22 at 16:07
  • @fuz If you check out Godbolt they are using `-masm=intel` – Michael Petch Dec 22 '22 at 16:09
  • @MichaelPetch I don't look at external links as Stack Overflow questions must be self contained. Good to know though. – fuz Dec 22 '22 at 16:10
  • @MichaelPetch So is there any possibility to solve this problem? I think It is possible that I have ignored some feature of gcc inline assembly. – fantasy Dec 22 '22 at 16:24
  • You would have to save all the volatile registers prior to calling `puts`. But that's easier said than done because to get it right you also have to account for things like the red zone in 64-bit user mode code. The best thing is not to do it. To get a sense of what calliing `printf` from inline assembly looks like you can see this answer: https://stackoverflow.com/a/37503773/3857942 . The best thing is not to call functions in inline assembly. If you want to use assembly create a separate assembly file `.s`; assemble it to an object; link it into your executable and forget inline assembly. – Michael Petch Dec 22 '22 at 16:32
  • With separate assembly you can more easily manage things as you aren't at the mercy of the rules of inline assembly which are overly complicated for reliably calling functions. With separate assembly module you just have to ensure you follow the System V AMD64 ABI calling convention https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI – Michael Petch Dec 22 '22 at 16:35
  • @MichaelPetch Thank you for your answer. Actually the main reason why I use inline assembly is to reduce the need of considerations of the calling convention. But I didn't consider about the extra problems brought by it. – fantasy Dec 22 '22 at 17:07
  • @MichaelPetch Actually I am a Chinese student, this is my first time for me to ask question on the stackflow, which might be a bit difficult for me. Is my expression clear enough during this problem? – fantasy Dec 22 '22 at 17:09
  • 1
    Yeah basically the moment you call a function from within inline assembly you open a whole other can of worms. – Michael Petch Dec 22 '22 at 17:10
  • 1
    Once you edited your question to provide the code and details within the question itself I felt your question was understandable and readable. – Michael Petch Dec 22 '22 at 17:12
  • @MichaelPetch However, the Link-Time optimization is IR-based. While creating a separate assembly file.s and assemble it to an object, there is not enough IR for linkers to optimize the code, such as inlining the function written in that separate assembly file. And this might bring loss of efficiency to some extent. In contrast, using the inline assembly might avoid this, so create separate assembly file might not be perfect as well – fantasy Dec 23 '22 at 14:13
  • @fantasy : When calling functions within inline assembly you are already going to have to be inefficient by marking everything that may change as being clobbered, adjust for the red zone, keep the stack aligned. While it is possible to do (I linked to another answer that shows the complexities with calling `printf`) you start having to become inefficient and fight both the rules for the ABI and the inline assembly requirements. The reality is calling functions within inline assembly is a perilous journey. – Michael Petch Dec 23 '22 at 14:20
  • Either put it in an external module or don't call functions in inline assembly if you don't understand the intricacies of inline assembly and the ABI requirement. It is just way to easy to get this kind of inline assembly wrong with calling a function (even as basic as `puts`) – Michael Petch Dec 23 '22 at 14:21
  • 1
    @MichaelPetch Ok, I see what you main. Maybe that's why msvc do not support inline assembly for x86-64 and arm. – fantasy Dec 23 '22 at 14:50

0 Answers0