4

I'd like to clean up my assembly code and povide a way to call "NOP" multiple times through a macro:

#define NOP() asm(" nop")

#define NOP_N( N ) \
    NOP(); \
    NOP(); \
    .... call NOP() N times

I can't figure if this is possible in a macro.

Obviously, for performance reasons, I don't want something like this:

#define NOP_N( n ) { register int i; for(i=0;i<n;i++) asm(" nop"); }

Which defeats the purpose of NOP:

L17:                                    ; NOP_N(3);
        nop
        addi      1,r0                  ; Unsigned
        cmpi      3,r0
        blo       L17

The code is in C and assembly, so no C++ can be involved in here. Also, the compiler is fairly old and doesn't support variadic macros...

Gui13
  • 12,993
  • 17
  • 57
  • 104
  • how do you write it as macro for `N`, `call NOP() N times`, N has to be know at coding time – Grijesh Chauhan May 17 '13 at 08:59
  • 1
    @Mogria: Well if xgbi can use C++, then this could be done fairly simply via template metaprogramming, and no preprocessor trickery would be necessary at all. – jamesdlin May 17 '13 at 09:08
  • Possible duplicate: http://stackoverflow.com/q/11532883/946850 – krlmlr May 17 '13 at 09:11
  • Also related: http://stackoverflow.com/q/319328/946850 – krlmlr May 17 '13 at 09:16
  • The code is in C and assembly, so no C++ can be involved in here. Also, the compiler is fairly old and doesn't support variadic macros... – Gui13 May 17 '13 at 09:26

3 Answers3

7

I don't think a solution for unbounded N is possible. For bounded N you could do something along the following lines:

#define REPEAT_0(WHAT)
#define REPEAT_1(WHAT) WHAT REPEAT_0(WHAT)
#define REPEAT_2(WHAT) WHAT REPEAT_1(WHAT)
#define REPEAT_3(WHAT) WHAT REPEAT_2(WHAT)

#define NOP_N(N) REPEAT_##N(asm("nop");)

The first part can be autogenerated easily. The technique employed for the second part is sometimes called token pasting.

krlmlr
  • 25,056
  • 14
  • 120
  • 217
2

what about this and Is the C preprocessor Turing complete?:

#define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__

AND REPEAT_INDIRECT

#define REPEAT(count, macro, ...) \
    WHEN(count) \
    ( \
        DEFER(REPEAT_INDIRECT) () \
        ( \
            DEC(count), macro, __VA_ARGS__ \
        ) \
        DEFER(macro) \
        ( \
            DEC(count), __VA_ARGS__ \
        ) \
    )
#define REPEAT_INDIRECT() REPEAT

//An example of using this macro
#define M(s, i, _) i
EVAL(REPEAT(8, M, ~)) // 0 1 2 3 4 5 6 7

AND FOREVER:

#define FOREVER() \
    ? \
    DEFER(FOREVER_INDIRECT) () ()
#define FOREVER_INDIRECT() FOREVER
// Outputs question marks forever
EVAL(FOREVER())
Community
  • 1
  • 1
0x90
  • 39,472
  • 36
  • 165
  • 245
  • 1
    Care to elaborate what this code does? Otherwise the link is great in a comment but does not qualify for an answer. – krlmlr May 17 '13 at 09:14
0

If you really want to do this in the preprocessor (and have a C99 compliant compiler) you could use P99_UNROLL from P99.

But you are completely underestimating the optimization that modern compilers are capable. As long as the bounds are compile time constants, a good compiler with optimization should just unroll the code for you. Look into the assembler to be sure (gcc has -S for that).

But you probably could help the compiler a bit by coding this "correctly":

#define NOP_N(N) for(register unsigned i=0; i < (N); i++) asm(" nop")

That is, have the loop counter local to the for and use an unsigned type such that there can't be theoretical issues with overflow.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • The thing is that the compiler *is* old. I checked that using a for loop will actually initialize the integer and not unroll the loop. – Gui13 May 17 '13 at 10:17