1

I am in a situation where I need to store a pointer to the next instruction in Thumb-2 assembly code. Thumb-2 instructions can be 16- and 32-bit, and are thus halfword-aligned. When I use PC as the source operand in an add, I actually read the word-aligned program counter plus 4 (that is, either 2 or 4 more than the current instruction). Therefore, I need to add either 0 or 2 to the current program counter to get the next instruction address.

Now, I could use .align to get this instruction on a word boundary, in which case I can simply add 0. However, I would like to not use nops if not necessary.

Is something like the following possible?

.if alignment_of_next_instruction % 4 == 2
        add r12,pc,#2
.else
        add r12,pc,#0       @ just an example, mov would be better
.endif
        str r12,[sp,#-4]!

The reason I cannot use nop is that in some cases I don't need to get the address of the next instruction, but the instruction after that, or even one further. In those cases, an add is necessary, but whether I need to add 10 or 12 (for example) depends on alignment.

If this isn't clear, here is what .align would look like with my proposed syntax:

.if alignment_of_next_instruction % 4 == 2
        nop
.endif

I cannot find anything in the ARM SDK reference guide, section Assembler expressions and operators (5.9), but perhaps I should be looking elsewhere.

artless noise
  • 21,212
  • 6
  • 68
  • 105
  • Using a longer but equivalent instruction instead of padding with NOPs is a known technique on x86 (where it's often possible to use a different encoding for an exactly identical instruction), but seldom used because assemblers won't do it for you. Hopefully someone knows or wants to implement something like this in an open-source assembler, because it's a cood idea for a viable syntax for this sort of thing. – Peter Cordes Sep 19 '16 at 21:46
  • @PeterCordes I don't think there is a longer (or shorter) equivalent instruction, these `add`s are already 32 bits and I don't see a way to make them 16 bits - and on ARM those are all the options you get. I will see in the morning what the `as` source code looks like or perhaps write an email on the binutils list. –  Sep 19 '16 at 22:01
  • Can you do something with an instruction somewhere *before* the add, so it's always aligned the way you want for a `mov`? (For safely against future breakage, put a `.p2align 2` before it, so you'll get a NOP if someone changes the code between where you create the alignment and where it's needed. So for now, it will not need to pad, but in future you'll get padding instead of breakage. Or use this conditional thing to create a build-time error if the requirement is violated.) – Peter Cordes Sep 19 '16 at 22:08
  • @PeterCordes well, in the real code in some cases we need to store the address of the next-next instruction, so then a `mov` won't work. Sorry, perhaps I should have written that in the question. In any case, adding `.align` will already add at most one `nop`. It's really that in cases where an `add` is actually necessary (cannot be replaced by `mov`), the immediate value added should be different depending on the alignment. –  Sep 19 '16 at 22:11
  • I guess there are multiple use-cases for an assemble-time query of the current alignment. One of them is what I suggested; using alternate instructions to control alignment at a later point. Your use-case might not be solved by that exactly (depending on what you want the alignment of every instruction to be), so it counts as different. – Peter Cordes Sep 19 '16 at 22:14
  • @PeterCordes ah, yes. In general what you suggest may be useful :) –  Sep 19 '16 at 22:16
  • What's wrong with `mov.w r12, pc`? Note that the premise of the question is incorrect, as it's not "When I read PC" that gives the word-aligned value so much as "when I use the PC as the operand in an `add`/`sub` immediate instruction" (which are, for legacy reasons, actually aliases of `adr`). – Notlikethat Sep 19 '16 at 23:51
  • @Notlikethat that it doesn't work if the offset needs to be different. I will add that to the question (it was originally left out because I didn't think it was relevant). –  Sep 20 '16 at 08:12
  • So `mov.n r12, pc; add.w r12, #offset-2`? Or if you can use a low register instead of r12, `mov.n r6, pc; add.n r6, #offset` is still 4 bytes. – Notlikethat Sep 20 '16 at 09:07
  • @Notlikethat that's possible, but then I need an extra instruction, while I was just trying to save the nop. (I cannot use a low register) –  Sep 20 '16 at 09:12

1 Answers1

2

Rather than faffing about trying to work in terms of addresses and offsets, just specify things in terms of instructions, i.e. with a label:

   adr r12, 1f
   ...
1: <instruction of interest>

The assembler and linker know what to do.

Notlikethat
  • 20,095
  • 3
  • 40
  • 77