Page 1 of 1

What is the purpose of the nop in this code?

Posted: Thu Nov 13, 2025 3:12 am
by sharpice
Hi everyone, firs post here.

I am exploring the RISC-V ISA and in particular assembly while also catching up on C. I have a setup where I write simple C programs and compile them to assembly.

I compiled the following program:

Code: Select all

void bob() {
    static int z = 0;
    z += 1;
}

int valio() {
    auto int a = 6;

    bob();
    bob();

    return a;
}
and got the following assembly code (snippet):

Code: Select all

...
bob:
	addi	sp,sp,-16
	sw	ra,12(sp)
	sw	s0,8(sp)
	addi	s0,sp,16
	lui	a5,%hi(z.0)
	lw	a5,%lo(z.0)(a5)
	addi	a4,a5,1
	lui	a5,%hi(z.0)
	sw	a4,%lo(z.0)(a5)
	nop
	lw	ra,12(sp)
	lw	s0,8(sp)
	addi	sp,sp,16
	jr	ra
...
What is the purpose of this "nop" here? I understand that it is supposed to do nothing and consume 1 cycle, if I am not mistaken. What I am trying to learn more about it why GCC decided to put it there and what purpose it servers.
I compiled it via:

Code: Select all

riscv64-unknown-elf-gcc -O0 -nostdlib -march=rv32i -mabi=ilp32 -Wl,-Tm.ld c-asm.c -S
What do I need to read more about to understand this?

Re: What is the purpose of the nop in this code?

Posted: Thu Nov 13, 2025 7:10 am
by Demindiro
AFAICT it serves no purpose. clang doesn't generate it and it's gone with -O1 or higher.

It is strange it is emitted at all though. You can reproduce it with just void empty() {}:

Code: Select all

empty:
        addi    sp,sp,-16
        sd      ra,8(sp)
        sd      s0,0(sp)
        addi    s0,sp,16
        nop
        ld      ra,8(sp)
        ld      s0,0(sp)
        addi    sp,sp,16
        jr      ra
Function prologue, body then epilogue. Must be a detail of the compilation process leaking through.

I guess you'll only find a real answer by looking at GCC source.

Re: What is the purpose of the nop in this code?

Posted: Thu Nov 13, 2025 7:53 am
by Demindiro
Did a bit more research and it turns out the same happens on x86-64:

Code: Select all

empty:
        push    rbp
        mov     rbp, rsp
        nop
        pop     rbp
        ret
bob:
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR z.0[rip]
        add     eax, 1
        mov     DWORD PTR z.0[rip], eax
        nop
        pop     rbp
        ret
I guess I never noticed because I don't bother looking at unoptimized assembly :P

Re: What is the purpose of the nop in this code?

Posted: Thu Nov 13, 2025 8:50 am
by sharpice
Huh, interesting! I guess I put too much meaning into it. It did disappear with -O1.

One thing I noticed initially when compiling with -O0 is that the resulting assembly, although technically correct, is pretty bad. I am guessing this level of optimization is not used at all? Should I avoid using it even for learning purposes? Want to avoid creating bad habits from what I am reading.

Re: What is the purpose of the nop in this code?

Posted: Thu Nov 13, 2025 9:25 am
by Demindiro
-O0 applies next to no optimizations. Only very trivial ones like substituting mul/div with shifts.

If I read compiled assembly it's usually to figure out if it optimizes well or if there's a miscompilation. -O0 might be useful for debugging, though even then you're probably better of with -Og.

I first learned to read x86-64 assembly by trying to optimizing interpreters. I found that at some point optimizing turned into guesswork and I had to understand what was being generated before I could go any further.

I then learned to write RISC-V assembly by consulting the specification and writing my own OS for it.