0

I have some doubts about ARM registers.

  • What is the main aim of r12? Is the same as FP in other arquitectures?
  • Some of them are used for "scratch", does it mean temporary storage / caller saving?
  • r13 and r14 have differente names depending on the mode. If I use # MOV R0, R14_svc, will I get its content in R0? Or is it just a way to distinguish R14 in different modes and there's not a harware difference?
auselen
  • 27,577
  • 7
  • 73
  • 114
adrian
  • 317
  • 2
  • 4
  • 11
  • [ARM register saving](http://stackoverflow.com/questions/261419/arm-to-c-calling-convention-registers-to-save), [ARM Link and frame](http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer), etc. See: [ask], especially, I think only point 3 hasn't been addressed. However, the notation `R14_svc` is for a particular ARM, not the generic ones. Ie, In a more advanced version and you do this. – artless noise Oct 08 '14 at 20:11
  • version ARM7TDMI? just for testing, I'd like to apply the code in raspberry B, version 11 – adrian Oct 08 '14 at 20:12
  • I don't believe the ARM7TDMI can access the `R14_svc` directly. It is often used in a manual to describe how an interrupt and/or SVC call may do something. From one privileged mode you can temporarily change to another and move the register to a **non-banked** register. – artless noise Oct 08 '14 at 20:14

1 Answers1

1

You are looking at the results not the symptom.

go to arms website (infocenter.arm.com) and find the arm architectural reference manual for armv5, you might have to give up an email address, but those are easy to come by, no big deal.

So there is the arm instruction set and the arm architecture that goes with it. Some registers are special and some are not. Obviously r15 is very special it is the program counter and there are some instructions that have addressing modes that only support r15 (pc relative addressing). Likewise r14 is special it is also hardwired for the branch link instructions, the return is generic but the call isnt, sure you could make calls without using r14, but that doesnt matter r14 is special because it is hardwired onto one/some instructions.

In thumb mode r13 is hardcoded into the push and pop instructions (in arm mode ldm and stm can technically use any register although r15 is probably a bad idea).

And there may be others...

So some registers are hardwired into some instructions or addressing modes. So in addition to all of this, probably for performance reasons, there are different processor modes, supervisor, interrupt, etc...And the arm docs show you that some of these registers have a _svc version or _und version, etc. Yes, the r14_svc bits are not the same bits as r14_abt, if you modify r14_abt you cant read the vale back with r14_svc or r14_irq. When a particular instruction goes to read or write r14 or r13, etc depending on the processor mode a different bank is selected and the mode specific bank of registers is used. For r0-r7 the same actual registers/bits/ram is used in all modes but starting with r8 to know which registers are used you have to look at the mode. Depending on the ARM ARM this statement may be different but the one I am looking at right now says:

The ARM processor has a total of 37 registers:

and they show you a picture of r0-r15, it adds up to 37 and not 16 because they are separate registers.

Now you get into calling convention which is not tied to the hardware at all it is simply a habit or a convention that folks have agreed to use. if you hop on your wayback machine you may learn that some x86 compilers used stack based parameter passing and some used register based and they were incompatible with each other, and sometimes you could specify which convention to use when compiling. The same is true for some others, but not all. If you look at mips documentation, the hardware folks there like to define the calling convention by renaming the registers based on the convention, this is not carved in silicon you dont have to conform to it. Same with the arm calling convention defined in other places than the architectural manual because that never makes sense, but it is defined somewhere, and it was sane enough that the compiler writers whether they work for arm or not conformed to those standards and even changed when arm changed the convention.

This is where you begin to see the convention of using r0-r3 to pass parameters then after those are consumed you use the stack, if your computer feels the need to have a frame pointer (lazy programmers, easier to debug compiler output) they pick r12 or whatever. The convention defines which registers a function needs to preserve and which ones it doesnt have to preserve and it defines how to return values. Although the convention in some respects is arbitrary and if you were making your own compiler you could invent your own or use stack all the time and not register, etc, the compilers you are likely using are conforming to the arm calling convention (abi/eabi) in one of its evolving versions.

yes that is why one or more of the upper registers is not preserved in the convention so that you can use it as a frame pointer if you feel the need to have a frame pointer.

What does preserved mean? If you look at older days with fewer registers and less mature compilers and stack parameter passing you basically preserved every register you were going to touch on entry into a function and restored them on exit, the stuff you modified (return value) was on the stack or you used the top of the stack for your volatile stuff in the function (local variables, intermediate values, etc). Even with 16 registers you can start to talk about using registers for parameter passing and even going so far as to make a convention where you dont have to preserve all of them. That is what the arm convention does and some others.

unsigned int fun ( unsigned int a, unsigned int b )
{  
  return(a+b+7);
}

a is going to be passed to the function in register r0, b in register r1, because the convention says so. the return value is in r0 because the convention says so so the optimized version of the code about would be to either destroy r0 by adding 7 to it, so the a value is gone, we can see from the code we only need the a value long enough to do one math operation with it. so we could

r0 = r0 + 7;
r0 = r0 + r1;

and return

or we could

r1 = r1 + 7;
r0 = r0 + r1;
return

or other variations on the theme. in either of the above cases we have not preserved r0 because the convention basically says we have to destroy the contents because that is our return. But we are also allowed to modify at a minimum r0,r1,r2,r3 so we could also do this:

r2 = r1+7;
r3 = r2+r0;
r0 = r3;
return

and that would be legal because nobody but us cares of we mess with r0-r3 (and sometimes others).

if you did this

r4 = r1+7;
r0 = r0 + r4;
return

you might get lucky and not crash or you might crash right a way or maybe not crash for a long long time but then crash. Why because the rule says we are not supposed to modify r4, we must preserve it:

push {r4}
r4 = r1+7;
r0 = r0 + r4;
pop {r4}
return

it must be the same value when we exit the function as when we entered. To illustrate this:

unsigned int fun ( unsigned int a, unsigned int b )
{  
  return(more_fun(a,b)+a+b+7);
}

to implement this we might do this

push {r4,r14}
r4 = r0 + r1;
r4 = r4 + 7;
call more_fun();
r0 = r0 + r4;
pop {r4,r14}
return

we need to remember what a+b+7 is based on the values in r0 and r1, but since r0 and r1 can be modified by more_fun() we either need to save them as is, or do the math first then save the result. either way we need to either put them on the stack to save them or put them in a non-volatile register, and to use a non-volatile register we need to save its prior contents.

Now if more_fun() broke the rules and modified r4 then our result of fun() would be incorrect and that incorrect value might cause callers of fun() to do something wrong or crash or whatever, or maybe you get lucky sometimes and it modifies r4 but it modifies it in a way that it is either the same value or a safe value. Then maybe you change a few lines of code and now r4 upstream is used to save something else, you hose that and now you do crash.

So long as every implementation of every function follows the convention then it all works out. (assuming the convention is sane which the arm one is).

old_timer
  • 69,149
  • 8
  • 89
  • 168