0

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 */                             
                        );            
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • 1
    Understanding *why* you need your output to go in a specific register might help us give better answers. While Grigory's answer might do what you need, it sounds like what you are trying to do is a bad idea. But it's hard to say without more detail. – David Wohlferd Apr 25 '18 at 03:21

2 Answers2

2

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.

The only supported use for this feature is to specify registers for input and output operands when calling Extended asm (see Extended Asm). This may be necessary if the constraints for a particular machine don’t provide sufficient control to select the desired register. To force an operand into a register, create a local variable and specify the register name after the variable’s declaration. Then use the local variable for the asm operand and specify any constraint letter that matches the register

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
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Grigory Rechistov
  • 2,104
  • 16
  • 25
  • 1
    This isn't entirely true _In that way, if it works, you will always be sure that the variable is backed by the register:_ A lot of people think that the `foo` variable will always be backed by the register. The only guarantee is that whatever `r8` had in it at the end of the inline assembly will have its contents moved to the variable `foo`. Before or after the template nothing else can really be said about `r8` in general. In your example you probably should drop the pointer `int *foo` and use `int foo` and to match the question maybe use `my_var` instead of foo. – Michael Petch Apr 24 '18 at 20:58
  • Basically by itself `register int foo asm ("r8");` doesn't have to move the contents of `r8` to `foo`. When this variable is applied to an extended inline assembly statement then it has meaning. – Michael Petch Apr 24 '18 at 21:00
  • @MichaelPetch This is exactly what the quote I gave says: "Then use the local variable for the asm operand and **specify any constraint letter that matches the register**" – Grigory Rechistov Apr 24 '18 at 21:09
  • Yes, thats what the quote says but that's not what your own words say. The docs you quote are correct but you may give someone the false impression about what is actually going on. Your example actually says nothing about have to use `foo` in an assemblytemplate to actually do anything. You leave it dangling. – Michael Petch Apr 24 '18 at 21:11
  • So to get the value of `r8` (as you say doing this is rather useless) you could declare `my_var` as `register long long my_var asm ("r8");` and then later on have an assembly template that looks like `__asm__ ("" :"=r"(my_var));` . The `=r` will use the register `r8` and transfer its contents to `my_var` when the assembly template is complete. – Michael Petch Apr 24 '18 at 21:13
  • @MichaelPetch Indeed, thanks. I tried to complete my answer now. – Grigory Rechistov Apr 24 '18 at 21:14
  • @MichaelPetch: I fixed this answer so it puts the entire recipe in one place, without giving any impression that `asm("r8")` might be safe on its own. I could have just posted my own, but this answer did already identify the right gcc feature, it just needed improvement. – Peter Cordes Apr 25 '18 at 07:10
1

It should be possible, based on the answer here: https://stackoverflow.com/a/43197401/3569229

#include <stdint.h>

uint64_t getsp( void )
{
    uint64_t sp;
    asm( "mov %%r8, %0" : "=rm" ( sp ));
    return sp;
}

You can find a list of register names here: https://www3.nd.edu/~dthain/courses/cse40243/fall2015/intel-intro.html

So your code above would be changed to:

__asm__ __volatile__ ("mov %%r8, %0"  
                        :"=rm"(my_var) 
                        : /* no input */                             
                        );            
Chris Weber
  • 81
  • 1
  • 4