PowerPC Assembly Load Immediate

KeithSmith picture KeithSmith · Jun 21, 2012 · Viewed 12.1k times · Source

I am new to PowerPC assembly. I am working with an MPC8245 (yes, old school). This is a 603e family processor.

I would like to know how to create a load immediate instruction where the immediate 16-bit value is 'unsigned'.

Example: li r3,0xFC10

The gcc cross-compiler, 4.4.5, I am using does not allow this instruction as the value is not a signed constant.

Yes, I could use minus the 2-s complement, but this makes reading and documenting the code more difficult. When loading bit fields for device registers, having the exact bit fields in the instruction is much easier to read.

Answer

Matthew Slattery picture Matthew Slattery · Jun 22, 2012

li is a pseudo-opcode which actually translates as an addi instruction.

addi rD, rA, SIMM adds the signed immediate SIMM to rA, placing the result in rD, except when the rA field is 0, in which case it specifies a literal 0 instead of r0. li rD, SIMM is really addi rD, 0, SIMM.

This is good for loading values from 0-0x7fff and 0xffff8000-0xffffffff into a register, but not other values.

The immediate bitwise operation opcodes (ori etc.) do interpret the 16-bit immediate field as an unsigned value. However they do not have the "r0 means a literal 0" behaviour of addi.

You will need to use two instructions to load a constant of 0xfc10 into a register: load the register with 0 using li (or lis, as if you were loading the top half of an arbitrary 32-bit constant) and then OR in the unsigned 16-bit value with ori.

This is exactly what gcc does under the circumstances:

$ cat test.c
unsigned int test(void)
{
    return 0xfc10;
}
$ gcc -O2 -c test.c
$ objdump -d test.o

test.o:     file format elf32-powerpc

Disassembly of section .text:

00000000 <test>:
   0:   38 60 00 00     li      r3,0
   4:   60 63 fc 10     ori     r3,r3,64528
   8:   4e 80 00 20     blr
   c:   60 00 00 00     nop
$

I don't think the GNU assembler has any way of automatically generating two instructions to load such a value from a single source instruction when assembling for PowerPC. However you can use the @h and @l suffixes to extract the (unsigned) high and low 16-bit halves of a constant, e.g.:

lis r3, 0x12345678@h            /* => li  r3, 0x1234     */
ori r3, r3, 0x12345678@l        /* => ori r3, r3, 0x5678 */

You could use this to write your own macro for arbitrary constant loading...

$ cat test2.s
        .macro  load_const rD, const
        .if (\const >= -0x8000) && (\const <= 0x7fff)
        li      \rD, \const
        .else
        lis     \rD, \const@h
        ori     \rD, \rD, \const@l
        .endif
        .endm

        load_const r3, 0
        load_const r4, 1
        load_const r5, -1
        load_const r6, 0x7fff
        load_const r7, 0x8000
        load_const r8, -32767
        load_const r9, -32768
        load_const r10, -32769
        load_const r11, 0xfc10

$ as -mregnames -o test2.o test2.s
$ objdump -d test2.o

test2.o:     file format elf32-powerpc

Disassembly of section .text:

00000000 <.text>:
   0:   38 60 00 00     li      r3,0
   4:   38 80 00 01     li      r4,1
   8:   38 a0 ff ff     li      r5,-1
   c:   38 c0 7f ff     li      r6,32767
  10:   3c e0 00 00     lis     r7,0
  14:   60 e7 80 00     ori     r7,r7,32768
  18:   39 00 80 01     li      r8,-32767
  1c:   39 20 80 00     li      r9,-32768
  20:   3d 40 ff ff     lis     r10,-1
  24:   61 4a 7f ff     ori     r10,r10,32767
  28:   3d 60 00 00     lis     r11,0
  2c:   61 6b fc 10     ori     r11,r11,64528
$