Search code examples
cinline-assemblypreprocessor

Why are braces needed for preprocessor in order to have statement?


Having this code:

#define GREATER(a, b, res) ( \ /* no braces here */
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b)); )

Error: c.c:4:2: error: expected expression before ‘asm’ asm("cmp %1, %2\n\t" \

But this one (only changed braces, everything else is left - the code is otherwise correct):

#define GREATER(a, b, res) ({ \ /* used braces here - correct */
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b)); })

Will compile with no error. But the only changed is added braces. So why are they needed? What does the preprocessor assume to be statement? (If only means being in braces.) I have seen other macro functions declared only in parenthesis (no curly braces), so why this one should have ones?


Solution

  • The preprocessor directives are not involved in this issue. The issues are:

    • asm(…) is a GCC extension to the C language. GCC treats asm, which must be followed by a ;, as a statement.
    • Parentheses are parts of expressions. When you write (…), the contents of the parentheses should be an expression. Since asm is not an expression, (asm(…);) is an error.
    • GCC has an extension called statement expressions, in which ({…}) has a value like an expression but may contain statements. Inside ({…}), you may put statements inside the braces, and GCC will evaluate them and use the value of the last expression statement1 in them as the value of the ({…}) expression. (The last statement in ({…}) should be an expression statement, as opposed to some other kind of statement, just as a for loop.)

    Thus ({ asm(…); }) is accepted as an expression.

    However, although GCC accepts it, it is violating the statement in GCC’s documentation that “The last thing in the compound statement should be an expression followed by a semicolon…”. It does not look like your macro is intended to be used as an expression; it puts a result in res but does not itself have a value. In this case, you can make it simply a statement by removing the parentheses from your original code:

    #define GREATER(a, b, res) \
        asm("cmp %1, %2\n\t" \
             "jge 0f\n\t" \
             "movl %1, %0\n\t" \
             "jmp 1f\n" \
             "0:\n\t" \
             "movl %2, %0\n" \
             "1:" \
             : "=r"(res) \
             : "r"(a), "r"(b));
    

    Additionally, people often prefer to leave off the final ; in such macros, because then the macro can be written like a statement when used in source code:

    GREATER(a, b, res);
    

    instead of looking odd to people who are accustomed to statements ending with ;:

    GREATER(a, b, res)
    

    (Although with the ; in the definition, you can still write GREATER(a, b, res);, but this expands to have ;;, and that can cause problems, because if (foo) GREATER(a, b, res); else… will fail to associate that else with that if because of the extra ;.)

    Footnote

    1 A statement expression is an expression followed by a ;.