如何将32位char与32位char进行比较,内联汇编C ++

时间:2018-07-31 20:18:05

标签: c++ assembly visual-c++ x86 inline-assembly

我想比较两个4字符的字符串。例如“ A”,“ T”,“ T”,“ C” 反对“ A”,“ T”,“ T”,“ c”。我将这些字符存储在c ++中的数组中,我想在指令中比较这两个单词。此外,我不想使用循环进行比较。如何将这些单词存储在“ eax”和“ ebx”寄存器中并相互比较?

int _tmain()
{
char b[3],a[3];
b[0]='A',b[1]='T',b[2]='C',b[3]='G';
a[0]='A',a[1]='T',a[2]='C',a[3]='G';
__asm
{
    movzx eax,b[1]  //here i want to load b to eax
}
getchar();
return 0;
}

如果在同一条指令中还有比较两个单词的想法,请分享谢谢。

5 个答案:

答案 0 :(得分:3)

此答案的其余部分假设您 使用内联asm进行某些作业(因为它不会比智能编译器内联的效率更高)。 4字节的memcmp)。有关4字节memcmp的gcc / clang的信息,请参见@MartinYork的答案。但是令人惊讶的是,只有gcc7和更高版本内联了恒定大小的memcmp。用Clang至少可以恢复到3.5。

MSVC 2017还内联memcmp以获得恒定的4字节大小,并内联std::array运算符==,从而产生与gcc / clang相同的asm。 (我没有测试早期版本)。请参见纯C ++版本on the Godbolt compiler explorer


从char数组中加载dword的必要语法是dword ptr大小覆盖。

// true for equal, false for not-equal
bool foo()
{
    //char a[] = "ACTG";
    char a[] = {'A', 'C', 'T', 'G'};
    char b[] = {'A', 'T', 'T', 'G'};
    _asm {
        mov eax, dword ptr a       // mov eax, a   would complain 
        cmp eax, dword ptr b
        sete al                    // al= 0 or 1 depending on ZF, the "e" condition like je
    }
    // falling off the end of a non-void function implicitly returns EAX
    // apparently this is supported in MSVC even when inlining
}

作为一个完整的功能,该编译如下, with MSVC 19, 2017, with -Ox on the Godbolt compiler explorer

 ;; define a couple assembler constants for use
_a$ = -8                                                ; size = 4
_b$ = -4                                                ; size = 4
foo PROC
        sub      esp, 8
        mov      DWORD PTR _a$[esp+8], 1196704577 ; 47544341H
        mov      DWORD PTR _b$[esp+8], 1196708929 ; 47545441H
  ;; inline asm block starts here
        mov      eax, DWORD PTR _a$[esp+8]
        cmp      eax, DWORD PTR _b$[esp+8]
        sete     al
  ;; and ends here
        add      esp, 8
        ret      0
foo ENDP

前2条mov指令由编译器生成,使用dword MOV立即将4字节数组存储到堆栈中。

如果您要返回0 /非0 int而不是0/1 bool,则可以使用@P__J__建议mov / sub cmp之后检查标志。两个相等的双字将使寄存器为0,其他情况则不会。 (xor具有相同的属性。)


如果您想比较作为函数arg获得的char*的4个字节,它将是一个指针,而不是C数组,因此您必须自己将指针加载到内联asm中的寄存器中。 (即使编译器已经在寄存器中包含了指针; MSVC内联汇编语法也基本上会占用较小的块,因为它会强制对输入和输出进行存储/重载往返(约5个周期的延迟),除非您可以明显使用支持的将EAX保留下来并掉落非无效函数的末尾的方法,另请参见What is the difference between 'asm', '__asm' and '__asm__'?与GNU C内联汇编的比较,这使得在寄存器中请求输入并产生多个输出变得容易在寄存器中,允许编译器进行尽可能多的优化,当然它仍然会破坏常量的传播;如果您使用memcmp,则编译器只能return 0,因为数组具有编译时常量内容。 {3}})

无论如何,这是比较函数args的前4个字节所得到的:

char bar(char *a, char *b)
{
    // a and b are pointers, not arrays
    _asm {
        mov eax, a              // loads the address
        mov eax, [eax]          // loads 4 bytes of data
        mov ecx, b
        cmp eax, [ecx]
        sete al
    }
}

bar PROC
        mov      eax, DWORD PTR _a$[esp-4]
        mov      eax, DWORD PTR [eax]
        mov      ecx, DWORD PTR _b$[esp-4]
        cmp      eax, DWORD PTR [ecx]
        sete     al
        ret      0

如果您使用-Gv进行编译或使用更好的调用约定以在寄存器中传递args的方式进行编译,实际上是更糟糕的:编译器必须将指针args溢出到asm的堆栈中重新加载它们,而不是将其转换为reg-reg动作。 AFAIK,无法通过强制转换或任何方式使编译器为您将指针加载到寄存器中,因此您可以直接在嵌入式asm中引用数组内容。

答案 1 :(得分:2)

首先,您的阵列存在严重问题。您将数组定义为容纳3个元素,但是尝试将4个元素填充到数组中。这真的很糟糕,并且会导致不确定的行为。

此外...丢下组件! lib函数将(几乎在所有情况下)执行您可以在汇编中执行的功能。换句话说-只需使用VAR

赞:

memcmp

答案 2 :(得分:2)

我要说的是在汇编中这样做是一个坏主意。

您应该使用高级语言构造。这样可以使代码具有可移植性,并且当需要推送时,编译器将在诸如此类的窥视孔优化中击败“大多数”人。

因此,我检查了g ++的输出以查看其生成的程序集。

main.cpp

#include <array>
#include <iostream>

bool testX(int a, int b);
bool testY(std::array<char, 4> const& a, std::array<char, 4> const& b);
bool testZ(char const(&a)[4], char const(&b)[4]);

int main()
{
    {
        int a = 'ATCG';
        int b = 'ATCG';
        if (testX(a, b)) {
            std::cout << "Equal\n";
        }
    }
    {
        std::array<char, 4> a {'A', 'T', 'C', 'G'};
        std::array<char, 4> b {'A', 'T', 'C', 'G'};
        if (testY(a, b)) {
            std::cout << "Equal\n";
        }
    }
    {
        char    a[] = {'A', 'T', 'C', 'G'};
        char    b[] = {'A', 'T', 'C', 'G'};

        if (testZ(a, b)) {
            std::cout << "Equal\n";
        }
    }
}

启用优化后,我们会从clang(通常是最近的gcc on the Godbolt compiler explorer)获得不错的asm。 (如果输入可以编译,则上面的main可以优化比较,因为输入是编译时常量。)

X.cpp

bool testX(int a, int b)
{
    return a == b;
}

# gcc and clang -O3 asm output
testX(int, int):
    cmpl    %esi, %edi
    sete    %al
    ret

Z.cpp

#include <cstring>

bool testZ(char const(&a)[4], char const(&b)[4])
{
    return std::memcmp(a, b, sizeof(a)) == 0;
}

Z.s

# clang, and gcc7 and newer, -O3
testZ(char const (&) [4], char const (&) [4]):
    movl    (%rdi), %eax
    cmpl    (%rsi), %eax
    sete    %al
    retq

Y.cpp

#include <array>

bool testY(std::array<char, 4> const& a, std::array<char, 4> const& b)
{
    return a == b;
}

Y.s

# only clang does this.  gcc8.2 actually calls memcmp with a constant 4-byte size
testY(std::array<char, 4ul> const&, std::array<char, 4ul> const&):           
    movl    (%rdi), %eax
    cmpl    (%rsi), %eax
    sete    %al
    retq

因此,用于比较4字节对象的std :: array和memcmp都使用clang生成相同的代码,但仅使用gcc memcmp会产生很好的效果。


当然,该函数的独立版本实际上必须产生0/1整数,而不是仅设置jcc的标志以直接分支。这些函数的调用者必须在分支之前先test %eax,%eax。但是,如果编译器可以内联这些函数,那么开销就会消失。

答案 3 :(得分:0)

类似这样的东西:

asm{

mov eax,'A'
mov ebx,'C'

 cmp eax,ebx
 JAE input_a
** here you print that 'A' <= 'C'  **
jump endofMain
input_a:
** here you print that 'A' >= 'C'  **
endofMain: 
}
return 0;

答案 4 :(得分:-1)

** IMPORTANT: UPGRADE PROBLEM: The data files need to be fully upgraded to version 3.6 before attempting an upgrade to 4.0; see http://dochub.mongodb.org/core/4.0-upgrade-fcv for more details.
2018-08-01T00:15:50.220+0200 I NETWORK  [initandlisten] shutdown: going to close listening sockets...
2018-08-01T00:15:50.220+0200 I NETWORK  [initandlisten] removing socket file: /tmp/mongodb-27017.sock
2018-08-01T00:15:50.224+0200 I STORAGE  [initandlisten] WiredTigerKVEngine shutting down
2018-08-01T00:15:50.303+0200 I STORAGE  [initandlisten] Downgrading WiredTiger datafiles.
2018-08-01T00:15:50.501+0200 I STORAGE  [initandlisten] WiredTiger message [1533075350:501686][3594:0x7fffb492e380], txn-recover: Main recovery loop: starting at 14/3712
2018-08-01T00:15:50.598+0200 I STORAGE  [initandlisten] WiredTiger message [1533075350:598867][3594:0x7fffb492e380], txn-recover: Recovering log 14 through 15
2018-08-01T00:15:50.664+0200 I STORAGE  [initandlisten] WiredTiger message [1533075350:663976][3594:0x7fffb492e380], txn-recover: Recovering log 15 through 15
2018-08-01T00:15:50.715+0200 I STORAGE  [initandlisten] WiredTiger message [1533075350:715398][3594:0x7fffb492e380], txn-recover: Set global recovery timestamp: 0
2018-08-01T00:15:51.002+0200 I STORAGE  [initandlisten] shutdown: removing fs lock...
2018-08-01T00:15:51.005+0200 I CONTROL  [initandlisten] now exiting
2018-08-01T00:15:51.005+0200 I CONTROL  [initandlisten] shutting down with code:62