如何将32位字符与内联assembelyc++中的32位字符进行比较
how to compare 32 bit char against 32 bit char in, inline assembely c++
我想比较两个4字符字符串。例如"A"、"T"、"T"、"C"反对"A"、"T"、"T"、"c"。我已经将这些字符存储在c++中的一个数组中,我想在指令中比较这两个字。此外,我不想使用循环进行比较。如何将这些单词存储在"eax"answers"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;
}
如果有其他在一个指令中比较两个单词的想法,请分享,谢谢。
这个答案的其余部分是假设您需要使用内联asm来完成一些家庭作业(因为它不会比智能编译器为4字节memcmp
内联更高效)。请参阅@MartinYork的答案,了解gcc/clang对4字节memcmp
的作用。但令人惊讶的是,只有gcc7和更高版本内联了常量大小memcmp
。Clang至少回到了3.5。
MSVC 2017还为恒定的4字节大小内联memcmp
,并且std::array
运算符==,生成与gcc/clang相同的asm。(我没有测试早期版本)。请参阅Godbolt编译器资源管理器上的纯C++版本。
从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
}
作为一个完整的函数,此编译如下,使用MSVC 192017,在Godbolt编译器资源管理器上使用-Ox
:
;; 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
指令由编译器生成,将4字节数组存储到具有双字MOV立即数的堆栈中。
如果要返回0/non-0int
而不是0/1bool
,可以使用@p_J__对mov
/sub
的建议,而不是检查cmp
之后的标志。两个相等的双字将离开寄存器0,其他任何字都不会。(xor
具有相同的性质。)
如果你想比较作为函数arg获得的char*
的4个字节,它将是一个指针,而不是一个C数组,所以你必须在内联asm中自己将指针加载到寄存器中。(即使编译器已经在寄存器中有了指针;MSVC内联asm语法基本上对小块来说很糟糕,因为它强制输入和输出进行存储/重载往返(约5个延迟周期),除非你可以使用明显支持的破解方法,在EAX中留下一些东西,并从非void函数的末尾掉下来。另请参阅';asm''__asm';和'__asm__';?与GNU C内联asm进行比较,后者使在寄存器中请求输入和在寄存器中产生多个输出变得容易,从而允许编译器尽可能优化。当然,它仍然会战胜不断的传播;如果使用memcmp
,编译器只能使用return 0
,因为数组具有编译时间常量内容。https://gcc.gnu.org/wiki/DontUseInlineAsm)
不管怎样,这就是比较函数参数的前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
或其他方法进行编译,以启用在寄存器中传递参数的更好调用约定,情况会更糟:编译器必须将指针参数溢出到堆栈中,以便asm重新加载它们,而不是变成reg reg移动。AFAIK,没有办法通过强制转换或其他方式让编译器为您将指针加载到寄存器中,这样您就可以直接在内联asm中引用数组内容。
首先,您的数组存在严重问题。您将数组定义为包含3个元素,但尝试将4个元素填充到数组中。这真的很糟糕,会导致不明确的行为。
除此之外。。。放下装配!lib函数将(在几乎所有情况下)执行您在汇编中可以执行的操作。换句话说,只需使用memcmp
类似:
int main()
{
char b[4],a[4];
b[0]='A',b[1]='T',b[2]='C',b[3]='G';
a[0]='A',a[1]='T',a[2]='C',a[3]='G';
if (memcmp(a, b, sizeof(a)) == 0)
printf("Equaln");
else
printf("Different");
return 0;
}
我要说的是,在汇编中这样做是个坏主意。
您应该使用高级语言构造。这将允许代码是可移植的,当事态发展到紧要关头时,编译器将在任何像这样的窥视孔优化中击败"大多数"人。
所以我检查了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 << "Equaln";
}
}
{
std::array<char, 4> a {'A', 'T', 'C', 'G'};
std::array<char, 4> b {'A', 'T', 'C', 'G'};
if (testY(a, b)) {
std::cout << "Equaln";
}
}
{
char a[] = {'A', 'T', 'C', 'G'};
char b[] = {'A', 'T', 'C', 'G'};
if (testZ(a, b)) {
std::cout << "Equaln";
}
}
}
启用优化后,我们可以从clang中获得不错的asm,通常是从Godbolt编译器资源管理器上最近的gcc中获得的。(如果函数可以内联,上面的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
。但是,如果编译器能够内联这些函数,那么开销就会消失。
类似这样的东西:
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;
int main()
{
volatile char b[4],a[4];
b[0]='A';b[1]='T';b[2]='C';b[3]='G';
a[0]='A';a[1]='T';a[2]='C';a[3]='G';
uint32_t val;
__asm__("movl %0, %%eax;" : "=m" (a) : "m" (a));
__asm__ ( "subl %1, %%eax;" : "=a" (val) : "m" (b) );
printf("%sn", !val ? "Equal" : "Not equal");
}
- 将应用程序从32位移植到64位时出现问题
- 正在解码MSVC 32位版本的程序集(作业).没有手术做什么
- qmake:检测目标位宽(32 位或 64 位)
- 如何在 64 位 vb.net Windows 应用程序中引用 32 位 dll
- 浮点数为 32 位和 64 位二进制表示形式
- C++易失性:保证 32 位访问?
- C++将 16 位值转换为 32 位值
- 如何在 64 位平台上计算 32 位哈希C++?
- C++中的24位到32位转换
- 在机器字大小等于 32 位的计算机上int64_t如何工作?
- uint32_t如何保证 32 位?
- 如何将32位字符与内联assembelyc++中的32位字符进行比较
- Visual C 32位整数从文件到8位字符到文件 - 程序在某些整数上崩溃
- 为什么字符指针会得到一个 32 位值 (ffffff88)
- 建筑物32位openssl fips(nmake f ms ntdll.mak):宏中的非法字符
- 在 C 语言中将 32 位 int 块连接在 16 个字符数组中
- 为什么 printf 将 8 位字符填充到 32 位
- 需要帮助将32位整数r转换为字符
- 在32位操作系统下查询的垃圾字符,在64位操作系统中没有问题
- 将1到32位的数字附加到字符缓冲区