I'm interested, why for Cortex M3 microcontroller (stm32f103) compiler sometimes generates a NOP instruction after branch. And why it sometimes doesn't.

For example:

0x08000496 2400      MOVS     r4,#0x00
0x08000498 4625      MOV      r5,r4
0x0800049A E006      B        0x080004AA
    64: res=res+a[i];
    65: }
0x0800049C F85A0034  LDR      r0,[r10,r4,LSL #3] // No NOP after B
0x080004A0 EB100808  ADDS     r8,r0,r8
0x080004A4 1C64      ADDS     r4,r4,#1
0x080004A6 F1450500  ADC      r5,r5,#0x00
0x080004AA 1BA0      SUBS     r0,r4,r6
0x080004AC EB750007  SBCS     r0,r5,r7
0x080004B0 DBF4      BLT      0x0800049C
    66: int64_t avg=res/x;
0x080004B2 BF00      NOP      // <------------------- NOP after BLT
    69: int v=countbits1(5);
0x080004B4 2005      MOVS     r0,#0x05
0x080004B6 F7FFFFA2  BL.W     countbits1 (0x080003FE)
0x080004BA 9001      STR      r0,[sp,#0x04]     // No NOP after BL.W
    72: unsigned int b=countLeadingZeros(5);
0x080004BC 2005      MOVS     r0,#0x05

My initial guess was that long instruction needs word-alignment but BL.W after NOP actually doesn't have it. If this NOP is related to pipeline somehow than why there are branches without nop's after them?

I'm confused.


It turns out that branch may be not relevant at all. I tried to moving declaration of unused local variable int64_t avg - and NOP moved along with it. So I beleive pjc50 comment is right and this NOP is there just to let debugger put a breakpoint on this line.

  • 3
    \$\begingroup\$ Try moving the int64_t line of C up or down a bit. The first two are clearly part of a for loop; but line 66 has generated no code at all. My guess is that the NOP is for the benefit of the debugger so that every line of C generates at least one instruction. \$\endgroup\$
    – pjc50
    Oct 28, 2014 at 23:45
  • \$\begingroup\$ @pjc50 now that's interesting! This NOP is indeed connected with int64_t avg; moving line 66 moved the NOP. But if I change type of avg to int32_t there is no NOP. This local variable is, actually, unused, so compiler doesn't generate any code at all for it.. Apart from this NOP for 64-bit type. If I change type I can still put a breakpoint on that line (and it will be on the MOVS). Very interesting. \$\endgroup\$
    – Amomum
    Oct 29, 2014 at 0:24
  • \$\begingroup\$ Any ideas why 64-bit variable is honored with a NOP? \$\endgroup\$
    – Amomum
    Oct 29, 2014 at 0:26
  • \$\begingroup\$ ARM and LinkedIn both have great forums for this sort of question - you could try there. I have a couple of comments to add: sometimes both outcomes of a branch are fetched and depending on the result of the branch, one alternative gets NOPed out. Also, the Cortex M3 may choose not to run NOPs, an interesting point if you are using them for short delays. I hope those comments are relevant. \$\endgroup\$
    – Ant
    Nov 4, 2014 at 13:08
  • \$\begingroup\$ Since this is mostly a compiler question that has little to do with EE... you may have better luck asking it on SO. \$\endgroup\$
    – Fizz
    Jan 7, 2015 at 18:27

3 Answers 3


Try moving the int64_t line of C up or down a bit. The first two are clearly part of a for loop; but line 66 has generated no code at all. My guess is that the NOP is for the benefit of the debugger so that every line of C generates at least one instruction.

(Not all debuggers do this on all platforms; Visual Studio will simply move your breakpoint to the nearest line that has code associated with it.)

  • \$\begingroup\$ You are right, moving declaration of the variable moved the NOP instruction; branch instruction turned out to be irrelevant. \$\endgroup\$
    – Amomum
    Jan 22, 2015 at 11:03

Lots (most? all?) compilers end up putting NOP instructions after some (but not other) jump/branch type instructions.

When the compiler sees a "jump" type instruction, it has two different instructions that can do the job. One is relative, one is absolute.

One is a relative jump, and one is an absolute jump. The relative jump is quicker, and specifies a jump relative to the current instruction - the address to jump to is a single byte, so it can jump forward 128 bytes, or backwards 127 bytes.

The other is an absolute jump - this is slower, and specifies the address to jump to. This can jump to anywhere.

The problem is, when jumping forward, the destination address may not be known yet - it would have to compile the code up to the jump destination, then work out if it is less than 128 bytes. Of course, to work out how many bytes to jump forward, you need to know how many bytes THIS instruction takes up, as well as every instruction between here and there.

This is way outside the paygrade of a compiler; it leaves space for an absolute jump, then on the second pass, when it knows where all addresses are, it fills the gaps - either putting in a more efficient relative jump (+ a NOP, because it has to take up the same number of bytes!), or putting in an absolute jump.


Cortex-M3 microcontrollers are built with a 32-bit architecture but they do not limit inputs to this bus size (see http://www.silabs.com/Support%20Documents/TechnicalDocs/EFM32-Cortex-M3-RM.pdf). The architecture has a small prefetch buffer to handle the case of the unaligned word instructions so when the branch is taken you don't have a problem. When the branch is not taken it will need to flush the pipeline to remove the mistaken choice and fetch again. It cannot fetch directly from 0x080004B2 as this is not 4-byte aligned and if it fetched from 0x080004B0 it would re-read the branch. The NOP is then essential for the extra padding it needs to fetch from the word aligned 0x080004B4 and continue from there.


  • \$\begingroup\$ I'm afraid that's not the case. Please see update to the question. \$\endgroup\$
    – Amomum
    Nov 8, 2014 at 17:21

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.