assemblymacrosriscvgnu-assembler

GNU as recursive/loop macro expected output


In this assembly file below, the macro jump_table should automagically create ... a jump table to consecutively numbered labels like jump_0, jump_1, ... jump_<n>.

It seems there is no loop feature in as .macro directive and the official documentation seems to suggest what appears to be a recursive macro.

Anyway, this is the file:

.section .text
.align 4

.macro jump_table name init last
  j \name\()_\init
  .if \last > \init
  jump_table \name \init+1 \last
  .endif
.endm

jump_table jump 0 3

jump_0:
  nop
jump_1:
  nop
  nop
jump_2:
  nop
  nop
  nop
jump_3:
  nop
  nop
  nop
  nop

The resulting binary, once disassembled with objdump, yields this:

0000000000000000 <jump_0-0x1c>:
   0:   00000013            nop
   4:   00000013            nop
   8:   00000013            nop
   c:   0100006f            j   1c <jump_0>
  10:   00c0006f            j   1c <jump_0>
  14:   00a0006f            j   1e <jump_0+0x2>
  18:   0060006f            j   1e <jump_0+0x2>

000000000000001c <jump_0>:
  1c:   00000013            nop

0000000000000020 <jump_1>:
  20:   00000013            nop
  24:   00000013            nop

0000000000000028 <jump_2>:
  28:   00000013            nop
  2c:   00000013            nop
  30:   00000013            nop

0000000000000034 <jump_3>:
  34:   00000013            nop
  38:   00000013            nop
  3c:   00000013            nop
  40:   00000013            nop
  44:   00000013            nop
  48:   00000013            nop
  4c:   00000013            nop

The jump table looks weird to me: offsets are decreasing while I would expect them to increase. Moreover, labelling also looks weird.

If I understood correctly, I should expect something like:

  j   1c <jump_0>
  j   20 <jump_1>
  j   28 <jump_2>
  j   34 <jump_3>

I bet I am missing something and some explanation would help me greatly.

I am using riscv64-elf-as and riscv64-elf-objdump v2.44 on MacOS M4.


Solution

  • \init+1 will not be evaluated before it gets passed recursively, therefore your macro creates something like this:

    j  jump_0
    j  jump_0+1
    j  jump_0+1+1
    j  jump_0+1+1+1
    

    Which the assembler appears to round to the next valid address, i.e., in your case:

    j   1c <jump_0>
    j   1c <jump_0>
    j   1e <jump_0+0x2>
    j   1e <jump_0+0x2>
    

    If you enable .altmacro-mode, you can have expressions evaluated before they get passed as parameters, using %(expr):

    .altmacro
    
    .macro jump_table name init last
      j \name\()_\init
      .if \last > \init
      jump_table \name, %(\init+1), \last
      .endif
    .endm
    

    Godbolt-Demo: https://godbolt.org/z/hGjEG6TE1