assembly 如何将C++函数转换为程序集(x86_64)?

wvmv3b1j  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(119)

这是我的.CPP文件

#include <iostream>
using namespace std;

extern "C" void KeysAsm(int arr[], int n, int thetha, int rho);

// Keep this and call it from assembler
extern "C"
void crim(int *xp, int *yp) {
    int temp = *xp;
    *xp = *yp;
    *yp = temp+2;
}

// Translate this into Intel assembler
void KeysCpp(int arr[], int n, int thetha, int rho){
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                crim(&arr[j], &arr[j + 1]);
            }
        }
        arr[i]= arr[i] + thetha / rho * 2 - 4;
    }
}

// Function to print an array 
void printArray(int arr[], int size){
    int i;
    for (i = 0; i < size; i++)
        cout << arr[i] << "\n";
    cout << endl;
}

int main() {
    int gamma1[]{
        9,
        270,
        88,
        -12,
        456,
        80,
        45,
        123,
        427,
        999
    };

    int gamma2[]{
        900,
        312,
        542,
        234,
        234,
        1,
        566,
        123,
        427,
        111
    };

    printf("Array:\n");
    printArray(gamma1, 10);

    KeysAsm(gamma1, 10, 5, 6);
    printf("Array Result Asm:\n");
    printArray(gamma1, 10);

    KeysCpp(gamma2, 10, 5, 6);
    printf("Array Result Cpp:\n");
    printArray(gamma2, 10);
}

我想做的是,将 KeysCpp 函数转换为汇编语言,并从这个.CPP文件中调用它。我想保留.CPP中的 crim 函数,而只转换 KeysCpp
这是我的.ASM文件

PUBLIC KeysAsm

includelib kernel32.lib

_DATA   SEGMENT
EXTERN crim:PROC
_DATA ENDS

_TEXT   SEGMENT

KeysAsm PROC
    push    rbp
    mov     rbp, rsp
    sub     rsp, 40
    mov     QWORD PTR [rbp-24], rdi
    mov     DWORD PTR [rbp-28], esi
    mov     DWORD PTR [rbp-32], edx
    mov     DWORD PTR [rbp-36], ecx
    mov     DWORD PTR [rbp-4], 0
    jmp      L3

 L3:
    mov     eax, DWORD PTR [rbp-28]
    sub     eax, 1
    cmp     DWORD PTR [rbp-4], eax
    jl       L7

 L4:
    mov     eax, DWORD PTR [rbp-28]
    sub     eax, DWORD PTR [rbp-4]
    sub     eax, 1
    cmp     DWORD PTR [rbp-8], eax
    jl       L6

 L5:
    add     DWORD PTR [rbp-8], 1

 L6:
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    lea     rdx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rax, rdx
    mov     edx, DWORD PTR [rax]
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    add     rax, 1
    lea     rcx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rax, rcx
    mov     eax, DWORD PTR [rax]
    cmp     edx, eax
    jle      L5
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    add     rax, 1
    lea     rdx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rdx, rax
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    lea     rcx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rax, rcx
    mov     rsi, rdx
    mov     rdi, rax
    call    crim

 L7:
    mov     DWORD PTR [rbp-8], 0
    jmp      L4

KeysAsm ENDP

_TEXT   ENDS


END

我正在使用Visual Studio 2017运行此项目。
运行此代码时出现下一个错误。
MatrixMultiplication.exe中0x00007FF74B0E429C处的未处理异常:堆栈Cookie检测代码检测到基于堆栈的缓冲区溢出。发生

y4ekin9u

y4ekin9u1#

您的asm看起来像是预期使用x86-64 System V呼叫惯例,而args则使用RDI、ESI、EDX、ECX。但是您说您是使用Visual Studio进行编译,因此编译器产生的程式码会使用Windows x64呼叫惯例:RCX、EDX、R8D、R9D。
当调用crim时,它可以使用影子空间(返回地址上方的32个字节,您没有为它保留空间)。
看起来您是从未经优化的编译器输出中得到这个asm的,可能是从使用GCC for Linux的https://godbolt.org/z/ea4MPh81r中得到的,在为非Windows目标编译时没有使用-mabi=ms覆盖默认的-mabi=sysv。底部是jmp而不是ret?可能是不同于12.2的GCC版本,因为标签编号和代码不完全匹配。
(The未优化编译器输出的标志是所有从[rbp-whatever]重新加载,并在使用int索引cdqe数组之前重新进行符号扩展。人类应该知道int必须是非负的。特别是GCC,像.L1:等编号标签在您刚刚删除.的地方,以及在调试版本中尽可能多地大量使用RAX。并且像lea rdx, [0+rax*4]这样的选择用于复制和移位,以及它在英特尔语法中用于打印该指令的确切语法与GCC匹配。)

若要编译Windows x64的单一函式,请将它隔离,并只为编译器提供它所呼叫之任何函式的原型

extern "C" void crim(int *xp, int *yp);  // prototype only

void KeysCpp(int arr[], int n, int thetha, int rho){
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                crim(&arr[j], &arr[j + 1]);
            }
        }
        arr[i]= arr[i] + thetha / rho * 2 - 4;
    }
}

然后在Godbolt上,使用gcc -O3 -mabi=ms,或使用MSVC,它总是针对Windows。https://godbolt.org/z/Mj5Gb54b5显示GCC和MSVC,并启用优化。

KeysCpp(int*, int, int, int):    ; demangled name
        cmp     edx, 1
        jle     .L11            ; "shrink wrap" optimization: early-out on n<=1 before saving regs
        push    r15             ; save some call-preserved regs
        push    r14
        lea     r14, [rcx+4]    ; arr + 1
        push    r13
        mov     r13, rcx

不幸的是,GCC没有提升thetha / rho * 2 - 4循环不变式,而是每次都重做idiv。看起来像是一个明显的优化,因为这些都是本地变量,它们的地址根本没有被取到,而且它在寄存器中保留了thetha(拼写错误是theta?)和rho。所以MSVC在这里效率更高。Clang也错过了这个优化。

相关问题