本文介绍了使用内联asm检索x64寄存器值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否有任何方法可以让我指定eax,ebx,ecx和edx以外的任何东西作为输出操作数.

让我们说我想将r8的内容放在一个变量中,是否可以编写这样的内容:

  __asm__ __volatile__ (""
                        :"=r8"(my_var)
                        : /* no input */
                        );
解决方案

鉴于大多数寄存器的易变性,目前尚不清楚为什么需要将特定寄存器的内容放入变量中.

GNU C仅对原始8个寄存器具有特定寄存器约束,类似于"=S"(rsi) .对于r8..r15,唯一的选择(为避免在asm语句内需要mov指令)是register-asm变量.

 register long long my_var __asm__ ("r8");
 __asm__ ("" :"=r"(my_var));               // guaranteed that r chooses r8

您可能希望使用额外的输入/输出约束来控制对的值进行采样的 where . (例如,"+rm"(some_other_var)将使该asm语句成为函数中数据依赖关系链的一部分,但这也将阻止常量传播和其他优化.)asm volatile可能有助于控制顺序,但这不能保证. >


有时 可以使用本地寄存器作为操作数来省略__asm__ ("" :"=r"(my_var));语句,但是只有在使用它的情况下,它才能保证有效: https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables. html#Local-Register-Variables . (并查看该答案的先前版本的注释中的讨论,该建议建议您可以跳过该部分.)这不会使您的代码变慢,因此请不要跳过该部分以确保您的代码总体上是安全的./p>

P.S.这是一个GCC扩展,可能无法移植,但应该在所有支持GNU C内联asm语法的编译器上都可用.

对于某些体系结构,例如ARM,gcc完全没有特定寄存器约束,因此,这种技术是在极少数情况下要为输入或输出操作数强制使用特定寄存器的唯一方法.


示例:

int get_r8d(void) {
     register long long my_var __asm__ ("r8");
     __asm__ ("" :"=r"(my_var));               // guaranteed that r chooses r8
     return my_var * 2;         // do something interesting with the value
}

编译为 gcc7.3 -O3在G上odbolt编译器资源管理器

get_r8d():
    lea     eax, [r8+r8]        # gcc can use it directly without a MOV first
    ret

I was wondering if there was any way that would allow me to specify anything other than eax, ebx, ecx and edx as output operands.

Lets say I want to put the content of r8 in a variable, Is it possible to write something like this :

  __asm__ __volatile__ (""
                        :"=r8"(my_var)
                        : /* no input */
                        );
解决方案

It is not clear why would you need to put contents of a specific register into a variable, given a volatile nature of the most of them.

GNU C only has specific-register constraints for the original 8 registers, like "=S"(rsi). For r8..r15, your only option (to avoid needing a mov instruction inside the asm statement) is a register-asm variable.

 register long long my_var __asm__ ("r8");
 __asm__ ("" :"=r"(my_var));               // guaranteed that r chooses r8

You may want to use an extra input/output constraint to control where you sample the value of r8. (e.g. "+rm"(some_other_var) will make this asm statement part of a data dependency chain in your function, but that will also prevent constant-propagation and other optimizations.) asm volatile may help with controlling the ordering, but that's not guaranteed.


It sometimes works to omit the __asm__ ("" :"=r"(my_var)); statement using the register local as an operand, but it's only guaranteed to work if you do use it: https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html#Local-Register-Variables. (And see discussion in comments on a previous version of this answer which suggested you could skip that part.) It doesn't make your code any slower, so don't skip that part to make sure your code is safe in general.

P.S. This is a GCC extension that may not be portable, but should be available on all compilers that support GNU C inline asm syntax.

gcc doesn't have specific-register constraints at all for some architectures, like ARM, so this technique is the only way for rare cases where you want to force specific registers for input or output operands.


Example:

int get_r8d(void) {
     register long long my_var __asm__ ("r8");
     __asm__ ("" :"=r"(my_var));               // guaranteed that r chooses r8
     return my_var * 2;         // do something interesting with the value
}

compiled with gcc7.3 -O3 on the Godbolt compiler explorer

get_r8d():
    lea     eax, [r8+r8]        # gcc can use it directly without a MOV first
    ret

这篇关于使用内联asm检索x64寄存器值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-19 16:21
查看更多