我记得曾经看到一种使用扩展的gcc内嵌汇编来读取寄存器值并将其存储到C变量中的方法。
但是,我无论如何都记不起如何形成asm语句了。
我记得曾经看到一种使用扩展的gcc内嵌汇编来读取寄存器值并将其存储到C变量中的方法。
但是,我无论如何都记不起如何形成asm语句了。
和其他答案不同,我想走一个不同的方向,因为我不确定你想要什么。
register int *foo asm ("a5");
Here
a5
is the name of the register which should be used…Naturally the register name is cpu-dependent, but this is not a problem, since specific registers are most often useful with explicit assembler instructions (see Extended Asm). Both of these things generally require that you conditionalize your program according to cpu type.
Defining such a register variable does not reserve the register; it remains available for other uses in places where flow control determines the variable's value is not live.
-ffixed-
reg将名为reg的寄存器视为固定寄存器;生成的代码不应该引用它(除了作为堆栈指针、帧指针或其他固定角色之一)。
这可以以更简单的方式复制Richard的答案。
int main() {
register int i asm("ebx");
return i + 1;
}
ebx
寄存器中有什么。gcc -ffixed-ebx
编译。#include <stdio.h>
register int counter asm("ebx");
void check(int n) {
if (!(n % 2 && n % 3 && n % 5)) counter++;
}
int main() {
int i;
counter = 0;
for (i = 1; i <= 100; i++) check(i);
printf("%d Hamming numbers between 1 and 100\n", counter);
return 0;
}
ebx 在通常的x86调用约定下是被调用者保存的,因此即使它被编译为没有使用 -ffixed- * 的其他函数调用所破坏,它也应该恢复。)另一方面,这绝对不是可移植的,通常也不是性能上的优势,因为你限制了编译器的自由。
以下是获取 ebx 的方法:
int main()
{
int i;
asm("\t movl %%ebx,%0" : "=r"(i));
return i + 1;
}
main:
subl $4, %esp
#APP
movl %ebx,%eax
#NO_APP
incl %eax
addl $4, %esp
ret
"=r"(i)"是一个输出约束,告诉编译器第一个输出(%0)是一个应该被放置在变量“i”中的寄存器。在这个优化级别(-O5)下,变量i永远不会被存储到内存中,而是保存在eax寄存器中,这也恰好是返回值寄存器。
=rm
约束而不是=r
。编译器的优化器将尝试选择最佳路径。如果内联汇编器恰好处于寄存器饥饿状态,则=r
可能会强制其生成不太优化的代码。如果=rm
恰好是最佳选择,那么它将给优化器一个使用内存引用的机会。在这个简单的例子中,这不会成为问题,但如果代码处于更复杂的情况下,给编译器提供选项可能会有益。 - Michael Petch__asm volatile
而不是asm
。 - pmor我不知道gcc,但在VS中是这样的:
int data = 0;
__asm
{
mov ebx, 30
mov data, ebx
}
cout<<data;
基本上,我将ebx
中的数据移动到您的变量data
中。
mov data, 30
呢? - user2742371intptr_t sp;
asm ("movl %%esp, %0" : "=r" (sp) );
只需将“esp”替换为您感兴趣的实际寄存器(但确保不要丢失%%),并将“sp”替换为您的变量。
#include <stdio.h>
void gav(){
//rgv_t argv = get();
register unsigned long long i asm("rax");
register unsigned long long ii asm("rbx");
printf("I`m gav - first arguman is: %s - 2th arguman is: %s\n", (char *)i, (char *)ii);
}
int main(void)
{
char *test = "I`m main";
char *test1 = "I`m main2";
printf("0x%llx\n", (unsigned long long)&gav);
asm("call %P0" : :"i"((unsigned long long)&gav), "a"(test), "b"(test1));
return 0;
}
i
和ii
放在gav()中是不受支持的。 - David Wohlferd当您的内联asm
语句运行时,您无法知道编译器生成的代码将在任何寄存器中存储什么值,因此该值通常是无意义的,您最好使用调试器在断点处查看寄存器的值。
话虽如此,如果您要执行这个奇怪的任务,那么最好高效地完成它。
在某些目标(例如x86)上,您可以使用特定寄存器输出约束来告诉编译器输出将在哪个寄存器中。使用一个空的asm模板(零条指令)和一个特定寄存器输出约束来告诉编译器,您的asm语句不关心输入的寄存器值,但是给定的C变量将在该寄存器中。
#include <stdint.h>
int foo() {
uint64_t rax_value; // type width determines register size
asm("" : "=a"(rax_value)); // =letter determines which register (or partial reg)
uint32_t ebx_value;
asm("" : "=b"(ebx_value));
uint16_t si_value;
asm("" : "=S"(si_value) );
uint8_t sil_value; // x86-64 required to use the low 8 of a reg other than a-d
// With -m32: error: unsupported size for integer register
asm("# Hi mom, my output constraint picked %0" : "=S"(sil_value) );
return sil_value + ebx_value;
}
#APP
/ #NO_APP
编译器生成的汇编注释对(这些对将汇编器切换到/进入快速解析模式,或者至少曾经是这样)。这是因为我没有使用asm volatile
,它们具有输出操作数,因此它们不是隐式的volatile
。foo(): # @foo()
# BB#0:
push rbx
#APP
#NO_APP
#DEBUG_VALUE: foo:ebx_value <- %EBX
#APP
# Hi mom, my output constraint picked %sil
#NO_APP
#DEBUG_VALUE: foo:sil_value <- %SIL
movzx eax, sil
add eax, ebx
pop rbx
ret
# -- End function
# DW_AT_GNU_pubnames
# DW_AT_external
b
(bl/bx/ebx/rbx)、c
(.../rcx)、d
(.../rdx)、S
(sil/si/esi/rsi)和D
(.../rdi)。尽管在没有帧指针的函数中bpl/bp/ebp/rbp不是特殊的,但没有特定的约束。(也许是因为使用它会使您的代码无法与-fno-omit-frame-pointer
编译。)register uint64_t rbp_var asm ("rbp")
,这样 asm("" : "=r" (rbp_var));
会确保 "=r"
约束将选择 rbp
。对于没有任何显式约束的 r8-r15,也是同样的道理。在一些体系结构中,例如 ARM,asm 寄存器变量是指定要用于 asm 输入/输出约束的唯一方法。(并且请注意,asm 约束是 register asm
变量的唯一支持用法;不能保证变量的值在其他时间在该寄存器中。)
uint32_t ebx_value;
asm("" : "=b"(ebx_value), "+rm"(some_used_variable) );
some_used_variable
可能是一个函数的返回值,并且(经过一些处理后)作为另一个函数的参数传递。或者在循环中计算,将作为函数的返回值返回。在这种情况下,汇编语句保证在循环结束后的某个时刻出现,在依赖于该变量后续值的任何代码之前。asm
语句是否有零条指令。
__builtin_frame_address(0)
获取堆栈地址。(但是我记得,即使在启用了-x86默认情况下的-fomit-frame-pointer时,该函数也会产生完整的堆栈帧。)
asm
语句运行时,你无法知道编译器生成的代码将在任何寄存器中存储什么值,因此该值通常是无意义的,最好使用调试器在断点处停止并查看寄存器值。对于堆栈指针可能有意义,但是可以使用__builtin_frame_address(0)
来获取堆栈地址(如果我没记错的话,这个函数会生成完整的堆栈帧,即使启用了-fomit-frame-pointer
,例如默认情况下在x86上启用)。 - Peter Cordes