One thing that is constantly frustrating when golfing ARM Thumb-2 code is generating constants.
Since Thumb only has 16-bit and 32-bit instructions, it is impossible to encode every immediate 32-bit value into a single instruction.
Additionally, since it is a variable length instruction set, some methods of generating a constant are smaller than others.
I hate that. Write a program or function that does this work for me so I don't have to think about it. (*shudder*)
There are four different variants of the mov
instruction as well as a pseudo instruction which loads from a constant pool.
They are either 2 bytes, 4 bytes, or 6 bytes long.
These are the following:
movs
: 2 bytes. Generates any 8-bit unsigned value from0x00
to0xFF
in the low 8 bits, setting the other bits to0
.mov
: 4 bytes. Generates any 8-bit unsigned value bitwise rotated any number of bits,† filling the rest with0
bits.mvn
: 4 bytes. The same asmov
, but generates the one's complement of that rotated 8-bit value, filling the rest with1
bits.movw
: 4 bytes. Can set the low 16 bits to any 16-bit unsigned value from0x0000
-0xFFFF
, and the high 16 bits to 0.ldr
: 6 bytes. The last resort. Can generate any 32-bit value, but is slow and larger than the other options.
†: Note that this is dramatically oversimplified, the real encoding would make this challenge quite frustrating.
The input will be a 32-bit integer, either by string (base 10 signed/unsigned or hex), stdin
, or value.
Every value from 0x00000000
to 0xFFFFFFFF
must be handled. It is a critical part of the challenge. Watch out for signed shift issues.
However, you can safely assume all inputs will fit into 32 bits, since ARM is a 32-bit architecture.
The output will be the shortest instruction to encode this constant.
The output format is whatever is easiest, as long as the values are distinct and you indicate which instruction corresponds to each output.
There will be a few cases where multiple answers are correct. You may either output one of the correct answers, or all of them. However, you must only display the shortest answers. So, for example, you must only output ldr
when none of the other instructions can generate it.
Test cases (values are in hex to show the bit patterns)
0x00000000 -> movs
0x00000013 -> movs
0x000000ff -> movs
0x12345678 -> ldr
0x02300000 -> mov
0xffffffff -> mvn
0x00ffffff -> mvn
0x00001200 -> mov or movw
0x11800000 -> mov
0x80000000 -> mov
0x00000100 -> mov or movw
0x0000f00d -> movw
0xfff3dfff -> mvn
0x03dc0000 -> mov
0xf000000f -> mov
0x00003dc0 -> mov or movw
0x0021f300 -> ldr
0x00100010 -> ldr
0xffff0000 -> ldr
Standard loopholes apply.
Note that as
and objdump
will not help; they will use the real encoding which is wrong. 😏
Being code-golf, the smallest answer in bytes per language wins.
as
andobjdump
are not built-ins. \$\endgroup\$mov
is eitherx * y
forx
a 8-bit value andy
in (0x00000001, 0x00010001, 0x01000100, 0x01010101); orx << y
forx
in [0x80..=0xFF] andy
in [1..=24] (which covers most useful integer literals, as numbers like0xF000000F
are rarely used) \$\endgroup\$