Hacker News new | ask | show | jobs
by AshleysBrain 4536 days ago
Very cool and clever scheme. But what happens to immediates that can't be encoded that way?
3 comments

There are a few options for producing other values:

1. load them from a constant pool in memory (often this is put nearly inline in the instruction stream so that it can be addressed via a small constant offset from PC). If your code doesn't saturate the LSU locally, this is usually the best option.

2. assemble them via bitwise or arithmetic combinations of literals that you can represent (or already in-register values that you know something about). If you can do it with just a few operations, and the LSU is otherwise occupied, you should do this instead. This is also preferred on some limited cores that can dual-issue logical and arithmetic ops but not LSU ops.

3. in recent revisions of the instruction set, there are a pair of instructions MOVW and MOVT; MOVW conjures a 16b immediate, and MOVT sets the high 16b, so with these two instructions you can conjure any 32b value.

There is a bit of subtlety to choosing the correct approach. Some assemblers provide a "conjure this value" pseudo-operation where the assembler will choose what it thinks is the best option to materialize a constant; that way the programmer doesn't need to worry about these details. This looks something like the following:

    LDR R0, =0xff0000ff
the assembler might actually stash the constant somewhere and generate a load instruction, or it might instead do:

    MOV R0,     0xff000000
    ORR R0, R0, 0x000000ff
and assemble the value via a couple instructions with immediates.
Here's what you can do to add a "complicated" constant stored elsewhere, in hand-crafted assembler:

    add_something:        ; function starts here (argument r0 == some number)
	ldr r1, __tmp     ; get complicated constant, store in r1
	add r0, r0, r1    ; do the addition r0 = r0 + r1
	bx lr            ; == return result (in r0)
    __tmp:
	.word 0x12345678  ; store complicated constant here
You can play with your compiler, if you call gcc as "gcc -Os -S -o- file.c" if will spit out generated assembler code (-S) on stdout "-o-" for the c-code in file.c.

(but then, gcc prefers to have 4 "compact" adds, instead of loading a constant...)

    $ cat dummy.c
    int
    add_random_number(int a)
    {
            return a + 0x12345678; /* guaranteed to be random */
    }

    $ arm-none-eabi-gcc -S -o- -Os dummy.c
    (...)
    add_random_number:
            @ Function supports interworking.
            @ args = 0, pretend = 0, frame = 0
            @ frame_needed = 0, uses_anonymous_args = 0
            @ link register save eliminated.
            add     r0, r0, #301989888
            add     r0, r0, #3424256
            add     r0, r0, #5696
            add     r0, r0, #56
            bx      lr
In at least the ADT assembler and gas a few years back, the assembler provided syntactic sugar:

    ldr r0,=0x123456578
assembles to

    ldr r0,pc+xxx
    ... and then somewhere later ...
    .word 0x12345678
The assembler had some default places it would put constant pools (end of a module?), or you could explicitly tell it to generate a constant pool if the default place would be outside the limit of the pc-relative addressing mode.
From my experience on x86, GCC's -Os isn't that great - looks like it's the same for ARM.
You have to place them in a constant pool and load them.
As others have said, that's not the case. All constants can be constructed by ORing together a small collection of expressible constants. With a little ingenuity "most" require very few instructions to build. FWIW, I have written real-time radar processing embedded software, and rarely had to resort to stored constants.