简体   繁体   中英

Why can't 16-bit instructions access the high registers of the general purpose registers

So right now I read the "Definitive Guide to ARM Cortex-M3/M4" book and can't understand why the 16-bit instructions cannot access the high general purpose registers R8-R12.

It says that very few of them actually can access these registers but mostly not.

16bits mean that the machine code instruction only has 16 bits to encode information. 8 registers take 3 bits to encode. 12 registers need 4 bits to encode. Then we need space for opcodes and other options meaning that extra bit might be one bit too many.

Encoding the address of 8 registers takes 3 bits. Encoding the address of 12 registers takes 4 bits.

If you have a 3-register-instruction, you would need 12 bits to encode the 3 registers, which leaves only 4 bits for the instruction. You could only have a maximum of 16 instructions.

The only actual definitive guides to these cores are the arm documentation. Before reading anything else or learning an instruction set you need to have the documentation; in this case, ARM Architectural Reference Manual for the ARMV7-m as well as either the cortex-m3 or cortex-m4 Technical Reference Manual. If your book makes a statement like that and does not explain it it is far from definitive.

It is technically possible but it would take a lot of space and perhaps defeat the purpose of attempting 16 bit instructions (does not mean a 16-bit processor or registers or anything like that, the instructions themselves are 16 bits). If you look at compiled code and how compilers work a great deal of the code generated uses lower registers or can be made as such. So it is a trade off of richness of the instruction set vs number of instructions, which is true independent of how many registers you have.

Size of register is irrelevant, number of registers is what matters. In order to for example

add r1,r2,r3

You somewhere have to encode which register is which. The comment of course is specific to the old/original 16 bit instructions not the thumb2 extensions.

So you need a pattern, naturally one would:

000 r0
001 r1
010 r2
011 r3
100 r4
101 r5
110 r6
111 r7

If you limit yourself to 8 registers then you only need three bits per register to describe which register is used for each field.

If you want to use 16 registers in your instructions then you need 4 bits:

0000 r0
0001 r1
0010 r2
....
0111 r7
1000 r8
1001 r9
....
1110 r14
1111 r15

If you want 32 registers in your instruction set (generically not ARM thumb) then five bits:

00000 r0
00001 r1
10000 r16
11111 r31

Note

2 to the power 0 is 1
2 to the power 1 is 2
2 to the power 2 is 4
2 to the power 3 is 8
2 to the power 4 is 16
2 to the power 5 is 32
2 to the power 16 is 65536

and so on.

If you want to describe 8 things it takes 3 bits if you want to describe 16 things it takes 4 bits.

16 bits means potentially 65536 unique instructions. ARM's doc does a good job of showing how it lays out its instruction sets, it leans toward maximizing the number of and possible features of instructions compared to say MIPS which leans toward simplicity of decoding logic first then features second (design trade-offs).

Skipping ahead for example the top 6 bits of the instruction are the opcode

00xxxx shift, add, subtract, move, and compare
010000 data processing instructions
010001 special data
01001x ...

and so on. Shown in various ways depending on which ARM ARM you download.

They did not have to implement any three register instructions but they chose to. Had these instructions supported r0 to r15 for each operand then that means 12 bits are needed and four bits are left for encoding including one or more of them to indicate this class of instruction and the rest which operand, you would likely wipe out at least a half of the possible instructions (one bit dedicated to indicating is this a three register instruction or not) leaving 7 possible operations or, one quarter of the instruction space (two bits) leaving 2 bits to choose which operand making it not very feature-rich. Instead what they actually did is take 1/4th of the instruction space but left at least 5 bits for operand and/or other features (immediate, etc).

So the T1 encoding for add is

0001100mmmnnnddd

The top two bits indicate which group of instructions this is, if you look you will see that bit 10 indicates immediate or register the mmm bits are a register or an immediate between 0 and 7, there are lot of times a programmer wants to increment by 1 or 2 or some small number and having to force an instruction and burn a register to put that small number vs the trade-off of burning some instruction space is a balance here.

Anyway you see here that there are three bits for the rm, rn and rd registers for this instruction encoding. (add rd,rn,rm) and that indicates r0-r7 is possible for any of those fields.

To make it more complete there is a mov high register instruction that allows you to move high/low, low/high or high/high, technically low/low but they prefer you use a different encoding as documented. One of those can be add rd,rn,#0 related to the three register above. And that is what you see gnu assembler doing

.thumb
mov r1,r2
mov r10,r2
mov r1,r11
mov r10,r11
    
00000000 <.text>:
   0:   1c11        adds    r1, r2, #0
   2:   4692        mov r10, r2
   4:   4659        mov r1, r11
   6:   46da        mov r10, r11

Point being there is a way to move to and from high registers, that is faster than to/from the stack (no memory cycle) so the compiler can still choose to use one/some high registers (understanding that push/pop is limited to for obvious reasons (read the docs) so there is a cost there to preserve and restore them, it is a trade off).

So you should stick to the actual definitive guide to something rather than something that claims to be.

The one thing you will almost never find anywhere unless you happened to be in the brain/cube/office/conference room at that moment(s) the designer(s) implemented the instruction set or a particular instruction is the "why" did they do it. (why no three register and, xor, etc) So despite the other answers, comments and the above, it is because they wanted to. And if you need to know more you can maybe get a job at arm and maybe they are still there and might spend time with you, but I would expect them to be retired.

And it has been so long the answer from the involved parties would likely vary as to who said what and when all those years ago. But when trying to take a 32 bit instruction set and map a subset to 16 bit instructions to double the number of instructions per unit space, while attempting to be rich enough to not greatly increase the number of instructions per task, limiting most of the instructions to the lower general purpose registers for an architecture like this seems like an obvious design choice. I would have to revisit the MIPS equivalent to thumb to see what they did I would expect them to have done the same thing. Likewise I would also have to revisit risc-v and any other that has done the same thing.

Understand this was a design choice, not a requirement in any way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM