cgccassemblyinline-assembly

What is the use of matching constraints in inline assembly


From the following link, https://www.ibm.com/developerworks/library/l-ia/index.html

a single variable may serve as both the input and the output operand.

I wrote the following code:

#include <stdio.h>

int main()
{   
    int num = 1;
    asm volatile ("incl %0"
            :"=a"(num)
            :"0"(num));
    printf("num:%d\n", num);
    return 0;
}

The above code increments the value of num.

What is the use of matching constraints, if i don't use matching constraints, the code does not work as expected.

asm volatile ("incl %0"
                :"=a"(num));

Solution

  • why and when should we use matching constraints

    That's not the question you asked; you asked why you need an input at all, which should be fairly obvious when you know what the syntax actually means. (That "=r"(var) is a pure output, independent of any previous value the C variable had, like var = 123; would be). So "=r" with an inc instruction is like var = stale_garbage + 1;


    But anyway, as I commented, the interesting question is "why do matching constraints exist when you can just use "+r"(var) for a read/write operand, instead of the more complicated matching-constraint syntax?"

    They're rarely useful; usually you can use the same variable for input and output especially if you have your asm inside a C wrapper function. But if you don't want to use the same C var for input and output, but still need them to pick the same register or memory, then you want a matching constraint. One use-case might be wrapping a system call is one use-case; you might want to use a different C variable for the call number vs. the return value. (Except you could just use "=a" and "a" instead of a matching constraint; the compiler doesn't have a choice.) Or maybe an output var of a narrower or different type than the input var could be another use-case.

    IIRC, x87 is another use-case; I seem to recall "+t" not working.

    I think that "+r" RMW constraints are internally implemented as an output with a "hidden" matching constraint. But while %1 normally errors in an asm template that only has one operand, if that operand is an in/out "+something" then GCC doesn't reject %1 as being too high an operand number. And if you look at the asm to see which register or memory it actually chose for that out-of-bounds operand number, it does match the in/out operand.

    So "+r" is basically syntactic sugar for matching constraints. Older versions of gcc did not support this notation for asm statements (it was only available for internal RTL constraints). If we look the the documentation from gcc 2.7.2 (emphasis mine):

    The output operands must be write-only; GNU CC will assume that the values in these operands before the instruction are dead and need not be generated. Extended asm does not support input-output or read-write operands. For this reason, the constraint character `+', which indicates such an operand, may not be used.

    When the assembler instruction has a read-write operand, or an operand in which only some of the bits are to be changed, you must logically split its function into two separate operands, one input operand and one write-only output operand.

    followed by a description of the matching-constraint syntax.

    By version 2.95.3 those paragraphs had changed to:

    The ordinary output operands must be write-only; GNU CC will assume that the values in these operands before the instruction are dead and need not be generated. Extended asm supports input-output or read-write operands. Use the constraint character `+' to indicate such an operand and list it with the output operands.

    When the constraints for the read-write operand (or the operand in which only some of the bits are to be changed) allows a register, you may, as an alternative, logically split its function into two separate operands, one input operand and one write-only output operand.

    For this reason it's not rare to see older tutorial examples that use matching constraints with the same var for both input and output that would simpler to read with "+" RMW constraints.


    Basics:

    With constraints like "a" and "=a" you don't need a matching constraint; the compiler only has 1 choice anyway. Where it's useful is "=r" where the compiler could pick any register, and you need it to pick the same register for an input operand.

    If you just used "=r" and a separate "r" input, you'd be telling the compiler that it can use this as a copy-and-whatever operation, leaving the original input unmodified and producing the output in a new register. Or overwriting the input if it wants to. That would be appropriate for lea 1(%[srcreg]), %[dstreg] but not inc %0. The latter would assume that %0 and %1 are the same register, therefore you need to do something to make sure that's true!