1

I'm wondering, is there a limitation on reading input variables based on the actual instruction?

I'm trying to read an input variable in inline assembly for a .rept directive to repeat an instruction x number of times.

However, the assembler complains with: Error: negative count for REPT - ignored

Here's my approach:

const int x = 42;
__asm__ volatile (".rept %[in]" :: [in] "m" (x));
__asm__ volatile (".endr");

Trying to load the variable value into a register works just as expected:

int function(void) {
    const int x = 42;
    __asm__ volatile ("mov %[in], %%eax" :: [in] "m" (x));
    __asm__ volatile ("ret");
}

returns 42, disassembly looks as expected.

I tried writing this in assembly to see if a constant can be used with .rept directive and it does so indeed

global _start

section .data
    nvalue  equ     39

section .text

_start:
    push    rbp
    mov     rbp,    rsp

    %rep nvalue
        nop
    %endrep

    mov     rax,    60
    mov     rdi,    nvalue
    syscall

Disassembly looks as expected:

Disassembly of section .text:

0000000000401000 <_start>:
  401000:       55                      push   rbp
  401001:       48 89 e5                mov    rbp,rsp
  401004:       90                      nop
  ...
  40102d:       90                      nop
  40102e:       b8 3c 00 00 00          mov    eax,0x3c
  401033:       bf 2a 00 00 00          mov    edi,0x2a
  401038:       0f 05                   syscall

Am I confusing .rept with %rep and do they not represent identical operation?

Any help will be greatly appreciated.

6
  • 2
    .rept is a directive to the assembler to repeat code. It is not going to take a memory reference, which you specify with "m". The compiler probably writes the memory reference as something like -4(%rbp), hence the error message about a negative value. I do not think GCC or Clang has a constraint for this. You can use "i" for an immediate operand, but that comes with the marker that denotes an immediate operand in an instruction, $ in Intel assembly, so it puts $42 in the assembly, whereas you need a plain 42. There may not be a way to do this. Commented Apr 6, 2021 at 22:29
  • 1
    You can't safely ret in an asm() statement, except as part of the body of an __attribute__((naked)) function. (And in that case, the entire body has to be a Basic Asm statement; no input/output constraints. i.e. it's a way to let the compiler do name mangling while you write the whole function body in asm.) Other than that, you can't ret; that will break when the compiler inlines your function into some caller. Commented Apr 6, 2021 at 22:46
  • That makes a lot of sense. Looking at the disassembly of int function(), I can see that [rbp-0x4] is indeed the product of the memory reference. Thank you very much for your explanation Eric. Commented Apr 6, 2021 at 23:00
  • Oh wow, thank you for pointing that out Peter! Indeed, I just tried returning the return value of function() from main() and all hell broke loose. Commented Apr 6, 2021 at 23:08
  • 1
    Similarly, if you write a register in an Extended asm statement, you need to mention it as a clobber or an output operand. i.e. your asm that writes EAX steps on the compiler's toes, and you should expect breakage. Commented Apr 7, 2021 at 3:06

1 Answer 1

4

As mentioned in comments, an i operand will normally be inserted with a leading $ sign, which is correct as an immediate operand in AT&T syntax but not good for an assembler directive like .rept. You can suppress this with the c modifier, see Section 6.47.2.8 of the GCC manual:

void many_nops(void) {
    const int num = 42;
    asm(".rept %c0 ; nop ; .endr" : : "i" (num));
}

This will inline 42 nop instructions. Of course this will only work if num can be folded as a compile-time constant; in particular you must compile with optimization for it to work.

Try on godbolt

You could also use Intel asm syntax with -masm=intel, in which constants don't get a leading $ in the first place.

Note that there are a number of other problems with your inline asm (you can't ret from inline asm, shouldn't split code between multiple asm statements, etc.) so you might want to start by reading some of the FAQs at https://stackoverflow.com/tags/inline-assembly/info.

The issue with .rept versus %rep: the first is the correct syntax for the GNU assembler gas which is normally used for assembling GCC output, and the second is for the nasm assembler.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.