Reading a register value into a C variable

96,632

Solution 1

Editor's note: this way of using a local register-asm variable is now documented by GCC as "not supported". It still usually happens to work on GCC, but breaks with clang. (This wording in the documentation was added after this answer was posted, I think.)

The global fixed-register variable version has a large performance cost for 32-bit x86, which only has 7 GP-integer registers (not counting the stack pointer). This would reduce that to 6. Only consider this if you have a global variable that all of your code uses heavily.


Going in a different direction than other answers so far, since I'm not sure what you want.

GCC Manual § 5.40 Variables in Specified Registers

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.

GCC Manual § 3.18 Options for Code Generation Conventions

-ffixed-reg

Treat the register named reg as a fixed register; generated code should never refer to it (except perhaps as a stack pointer, frame pointer or in some other fixed role).

This can replicate Richard's answer in a simpler way,

int main() {
    register int i asm("ebx");
    return i + 1;
}

although this is rather meaningless, as you have no idea what's in the ebx register.

If you combined these two, compiling this with 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;
}

you can ensure that a C variable always uses resides in a register for speedy access and also will not get clobbered by other generated code. (Handily, ebx is callee-save under usual x86 calling conventions, so even if it gets clobbered by calls to other functions compiled without -ffixed-*, it should get restored too.)

On the other hand, this definitely isn't portable, and usually isn't a performance benefit either, as you're restricting the compiler's freedom.

Solution 2

Here is a way to get ebx:

int main()
{
    int i;
    asm("\t movl %%ebx,%0" : "=r"(i));
    return i + 1;
}

The result:

main:
    subl    $4, %esp
    #APP
             movl %ebx,%eax
    #NO_APP
    incl    %eax
    addl    $4, %esp
    ret


Edit:

The "=r"(i) is an output constraint, telling the compiler that the first output (%0) is a register that should be placed in the variable "i". At this optimization level (-O5) the variable i never gets stored to memory, but is held in the eax register, which also happens to be the return value register.

Solution 3

I don't know about gcc, but in VS this is how:

int data = 0;   
__asm
{
    mov ebx, 30
    mov data, ebx
}
cout<<data;

Essentially, I moved the data in ebx to your variable data.

Solution 4

This will move the stack pointer register into the sp variable.

intptr_t sp;
asm ("movl %%esp, %0" : "=r" (sp) );

Just replace 'esp' with the actual register you are interested in (but make sure not to lose the %%) and 'sp' with your variable.

Solution 5

From the GCC docs itself: http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

Share:
96,632

Related videos on Youtube

Brian
Author by

Brian

Updated on August 14, 2020

Comments

  • Brian
    Brian over 3 years

    I remember seeing a way to use extended gcc inline assembly to read a register value and store it into a C variable.

    I cannot though for the life of me remember how to form the asm statement.

    • florin
      florin over 14 years
      And how do you know what is in EBX when your C code starts executing?
    • Peter Cordes
      Peter Cordes about 6 years
      You can't know what value compiler-generated code will have stored in any register when your inline asm statement runs, so the value is usually meaningless, and you'd be much better off using a debugger to look at register values when stopped at a breakpoint. It might make sense for a stack pointer, but there's __builtin_frame_address(0) to get a stack address (and IIRC, cause that function to make a full stack frame, even when -fomit-frame-pointer is enabled, like it is by default on x86.)
    • Peter Cordes
      Peter Cordes almost 3 years
      Closing as a duplicate of a new Q&A because the highest-voted answer here is outdated (broken with clang, unsupported by GCC). (It still happens to work with GCC, at least in simple cases.) An asm statement that does a mov %%reg, %0 to an "=r"(var) output is safe, too, that answer is fine.
  • R Samuel Klatchko
    R Samuel Klatchko over 14 years
    You do realize that's going to read one variable, compute the sine and then store the result in a second variable.
  • Kornel Kisielewicz
    Kornel Kisielewicz over 14 years
    @Samuel: That was an example of the syntax only.
  • ephemient
    ephemient over 14 years
    x86-only, of course. Microsoft's compilers for x64 and Itanium do not support inline assembly.
  • Sridarshan
    Sridarshan about 12 years
    I think the assembly will get translated to mov ebx, 30 mov dword ptr[data], ebx
  • Admin
    Admin about 10 years
    Why not just mov data, 30 ?
  • Michael Petch
    Michael Petch almost 8 years
    I would use the =rm constraint rather than =r. The compiler's optimizer will attempt to choose the best path. If the inline assembler happened to be in a register starved situation =r may force it to generate less than optimal code. =rm would give the optimizer a chance to use a memory reference if it happened to be the best choice. In this simple example it won't be an issue, but if the code is in a more complex situation then giving options to the compiler could be beneficial.
  • Matthew Cole
    Matthew Cole almost 7 years
    Downvoted because this is an example of how to use extended assembly in GCC, not how to get the value of a specific register into a specific variable, which is what the OP asked. By specifying the registers using %0 and %1, GCC will choose the register in question on your behalf. There's no assurance it will choose the register you're hoping for.
  • David Wohlferd
    David Wohlferd about 6 years
    Quoting from the current docs describing local registers The only supported use for this feature is to specify registers for input and output operands when calling Extended asm. So putting i within main() like this is unsupported. And to emphasize your point: x86 only has a limited number of registers. Removing one from general use via global register variable might slow down other critical parts of your code. Some discussion here.
  • David Wohlferd
    David Wohlferd about 6 years
    @MichaelPetch How about "=b" and an empty template string?
  • David Wohlferd
    David Wohlferd about 6 years
    Quoting from the current docs describing local register variables The only supported use for this feature is to specify registers for input and output operands when calling Extended asm. So putting i and ii within gav() like this is unsupported.
  • Peter Cordes
    Peter Cordes about 6 years
    I'd highly recommend not using a global register variable, except maybe in one .c file containing one function as a hack. Expect a significant performance cost, especially on 32-bit x86.
  • Peter Cordes
    Peter Cordes almost 3 years
    Note that clang will typically pick memory if you use "=rm", even if it actually needs the value in a register. It will end up storing and reloading. This is a longstanding missed-optimization in clang's inline asm support. Using "=b"(i) should also work, just telling the compiler that the EBX holds the value of i after the asm statement. You may want asm volatile if you use this in more than one place, otherwise the compiler can assume that the asm statement always produces the same output (because the input is always the same: the empty set of inputs.)
  • Ilan Schemoul
    Ilan Schemoul almost 2 years
    -O5 optimization ? I've read O3 is the maximum ?