Can I add my own assembly file/instructions to a godbolt project?

279 Views Asked by At

Just like the title suggests. Say, I want to do this with a Godbolt project:

main.cpp

#include <iostream>

extern "C" int test_111();

int main()
{
    int r = test_111();
    
    std::cout << "result=" << r << std::endl;
    return 0;
}

and then in asm1.asm:

.code

test_111 PROC

    ; 64-bit assembly
    xor eax, eax
    inc rax

    ret

test_111 ENDP

END

Trying it under "x86-64 clang" (any version).

But I can't seem to figure out how to do this. Any suggestions?

PS. The goal is to inject my own assembly instructions into the output.


EDIT:

After a suggestion in the comments, I tried the asm volatile key words, but it simply adds whatever gibberish I put in there into the assembly output:

enter image description here

2

There are 2 best solutions below

1
On BEST ANSWER

Using the Tree mode, you can set up a CMake project with multiple files, including asm:

Godbolt Link

To make it work, I had to enable nasm support in the CMakeLists.txt file and manually set the path to the assembler

set(CMAKE_ASM_NASM_COMPILER /opt/compiler-explorer/nasm-2.14.02/bin/nasm)
enable_language(ASM_NASM)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
4
On

I tried the asm volatile key words, but it simply adds whatever gibberish I put in there into the assembly output:

Yes, GNU C inline asm just prints extra text into the asm. And the Godbolt compiler-explorer's default mode of operation is to just show you the compiler's asm text output. (Clang is different: unlike GCC, it has a built-in assembler, so it does require your asm statements to assemble.)

In the dropdown, you can select "compile to binary object" to compile to a .o and show you disassembly of that, or to compile to a linked executable (including a dummy main if your code doesn't have one) and disassemble that with objdump. This will feed the output of the C->asm compiler (cc1 or cc1plus) through the assembler, so you will get errors if there's a problem.


Don't use Basic Asm (no constraints) inside functions, except with __attribute__((naked)) in which case Basic Asm is the only thing you can put there. https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended

You can use Basic Asm at global scope, though, almost exactly like a separate .s file, using the GNU assembler's .intel_syntax noprefix which is MASM-like other than directives.

You can get examples of valid syntax from looking at GCC and Clang output for simple C/C++ functions on Godbolt. (The asm source in your question uses MASM directives like .code and foo PROC. You'll need to change that to just using a label. Since it's in the same compilation unit as the caller, you don't need .global test_111)

// compile this with -masm=intel; the default is att
asm(R"             # multi-line string literal
.text
test_111:
    # 64-bit assembly
    # lea rax, [RIP + main]     # example of its addressing-mode syntax
    xor eax, eax
    inc rax
    ret
");


#include <iostream>

extern "C" long long test_111();
// your asm uses RAX, which would be pointless if you only return a 32-bit type

int main()
{
    long long r = test_111();

    std::cout << "result=" << r << '\n';  // endl sucks and clutters the asm
    return 0;
}

You need to put -masm=intel on the compiler command line; normally compiler-explorer only passes it for builds that are going to stop at the .s output to show that to the user, not for the separate build of an executable to run. But xor eax,eax isn't valid in AT&T syntax; that would be two memory operands and an ambiguous operand-size.

See it on Godbolt with both "execute the code" checked for the compiler-output pane, and a separate "executor" pane, both needing -masm=intel.

Re: using R"( ... )" in C++, see How to write multiline inline assembly code in GCC C++?