2

I would like to add method to my class using assebler language. How can I do it? example:

main.cpp

Struct ex {
    int field1;
    asm_method(char*);
}

add.asm

asm_method: 
    //some asm code
  • cppreference has an example https://en.cppreference.com/w/cpp/language/asm – Aykhan Hagverdili Apr 24 '20 at 10:50
  • I know about asm() directive, but i'd like to do it in two different files – Churkin Aleksey Apr 24 '20 at 10:52
  • That's pretty much it. You'll need to declare the asm_method in your C++ code (as you would forward-declare a C++ method), and declare it as `extern "C"` (which is going to be easier than mangling the name to match your compiler), then assemble and link the assembly code with the normal link step. If you need more specific help can you tell us which CPU, OS (=> calling conventions) and compiler? – Rup Apr 24 '20 at 10:52
  • @ChurkinAleksey The link I shared has an example of defining the function in assembly. Is this not what you were looking for? – Aykhan Hagverdili Apr 24 '20 at 10:53
  • 1
    Oh, I missed you said "add method to my class" - as in this method should have a this pointer? That's going to be more work to set up: you need to use your compiler's name mangling tools to work out what asm_method should be called (to encode the class name, return value and parameter types) and find out what calling convention you will need for the assembly code. e.g. Windows __thiscall passes the this pointer in ECX and the method itself removes its arguments from the stack ("callee clean") – Rup Apr 24 '20 at 10:58

2 Answers2

3

This depends on your target platform and compiler/toolchain and is generally too broad a question for StackOverflow.

For example, the C++ compiler in the GCC toolchain actually generates assembly from C++, and then produces object files from that assembly. Then the linker links together multiple object files to produce an ELF module.

You can bypass the C++ compilation step for a single object file and directly write .asm files.

You can compile it the same way you compile .c: gcc myfile.S -o myfile.o.

Though you should take platform ABI into account such that you can accept function arguments and return values via the correct registers. The platform ABI also specifies the calling convention and which registers should be preserved across function calls. Finally, you need to produce correct function names according to C++ name mangling rules, or use C naming rules (which are simpler) and declare your function extern "C".

For more details see C++ to ASM linkage and for Linux ABI refer to System V ABI.

For Windows start here: calling conventions and compiling assembly in Visual Studio.

rustyx
  • 80,671
  • 25
  • 200
  • 267
3

Get asm output the compiler generates for a non-inline definition of the C++ member function, and use that as a starting point for an asm source file. This works for any ISA with any compiler that can emit valid asm (which is most of them, although apparently MSVC emits a bunch of extra junk that you have to remove.)


Example with GCC (for x86-64 GNU/Linux, but works anywhere)

Also works with clang.

e.g. g++ -O3 -fverbose-asm -masm=intel -S -o foo_func.S foo.cpp (How to remove "noise" from GCC/clang assembly output?)

That .S file is now your asm source file. Remove the compiler-generated instruction lines and insert your own.

Obviously you need to know the calling convention and other stuff like that (e.g. for x86 see https://www.agner.org/optimize/#manuals for a calling convention guide), but this will get the compiler to do the name mangling for you, for that specific target platform's ABI.

struct ex {                 // lower case struct not Struct
    int field1;
    void *asm_method(char*);   // methods need a return type
};     // struct declarations end with a ;

void *ex::asm_method(char*) {
    return this;  // easy way to find out what register `this` is passed in.
}

compiles as follows for x86-64 System V, with g++ -O3 (Godbolt with Linux gcc and Windows MSVC)

# x86-64 System V: GNU/Linux g++ -O3
# This is GAS syntax
        .intel_syntax noprefix
        .text                   # .text section is already the default at top of file

        .align 2
        .p2align 4              # aligning functions by 16 bytes is typical
        .globl  _ZN2ex10asm_methodEPc     # the symbol is global, not private to this file
        .type   _ZN2ex10asm_methodEPc, @function   # (optional) and it's a function.
_ZN2ex10asm_methodEPc:                    # a label defines the symbol
        .cfi_startproc
  ## YOUR CODE GOES HERE ##
  ## RSP-8 is aligned by 16 in x86-64 SysV and Windows ##
        mov     rax, rdi                 # copy first arg (this) to return-value register.
        ret                              # pop into program counter
        .cfi_endproc
        .size   _ZN2ex10asm_methodEPc, .-_ZN2ex10asm_methodEPc    # maybe non-optional for dynamic linking

It's probably fine to omit the .cfi stack-unwind directives from hand-written asm for leaf functions, since you're not going to be throwing C++ exceptions from hand-written asm (I hope).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847