Confused about call-used registers and interrupts on AVR - Page 1
Forum section: electronics / general

author: HwAoRrDk
author_rank: Super Contributor
timestamp: September 05, 2019, 09:19:36 am
content: On the AVR platform, I am confused about call-used and call-saved registers - particularly call-used registers. TheAVR-libC FAQdescribes them as follows:QuoteSo, if I am interpreting this correctly, if any code in a subroutine wants to use R18-R27/R30-31, it does not have to save their contents on the stack before use, because it was the responsibility of any code calling the subroutine to do that. For example: function foo() uses R18-24, and if by itself does not make any calls to other functions, does not need to save R18-24 before use nor restore afterwards. And if foo() is called by function bar(), if the latter is using R18-20, it should save them before calling foo() and restore them afterwards. Am I right?However, does this not apply to interrupt service routines?If it doesn't, is it because an ISR may be branched to at any time, right in the middle of whatever other piece of code happens to running at the time, and so that code is unaware and cannot save/restore call-used registers? So, therefore, if an ISR wants to use any of the call-used registers, itmustsave and restore them itself? And if it does use them without doing that, it might be trampling on the values used by whatever code was interrupted?My confusion comes about because I am trying to determine whether an ISR of mine could be made a 'naked' ISR (with attribute ISR_NAKED) which doesn't implement such register saving and restoring. At first I thought that even though it uses R18-25 (from looking at the assembly generated by the C compiler) it could, because it doesn't call any subroutines, but now I'm not so sure.Might as well ask a related side question while I'm here: why does the standard AVR ISR prologue code useeor r1, r1(XOR-ing it with itself) to clear the zero register instead of simplyclr r1?

author: langwadt
author_rank: Super Contributor
timestamp: September 05, 2019, 09:29:10 am
content: interrupts can happen at any time so the ISR must restore everything to exactly what it was, if the hardware doesn't do it you have to do itafaict, eor r1, r1 and clr r1 results in the same instruction just a different way of writing it

author: HwAoRrDk
author_rank: Super Contributor
timestamp: September 05, 2019, 10:27:29 am
content: Quote from: langwadt on September 05, 2019, 09:29:10 amOh, so you mean they're literally the same at the binary level, just a different mnemonic?And the tool producing the assembly listing (objdump IIRC) can't tell the difference, so lists the former?

author: magic
author_rank: Super Contributor
timestamp: September 05, 2019, 10:47:05 am
content: It's very likely that way. There is no point having a separate CLR instruction in the CPU when EOR achieves the same thing in one clock cycle and one program memory word.In some CPUs even a MOV instruction doesn't really exist and is realized by adding 0 to the source register and writing the result to the destination register because such an operation happens to be already available in the instruction set.editJust checked my AVR instruction set manual. Yep, it's an alias.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: September 05, 2019, 11:46:26 am
content: In extreme cases, you might have an ISR that saves only SREG, and local working variables are processed in dedicated registers. The compiler needs to be told to never touch those registers in the rest of the program.I'm guessing that's also kind of along the lines of what you're thinking, but really, both ways (dedicated registers, naked ISRs, and other unusual strategies), you should really be questioning: do I really need to do it this way at all? Is there a better way? Should I be compiling this at all, or should I be writing in bare assembly?And really, if the answer is yes to that last one, you should ask even further... "should I even be on AVR?" CPU cycles are nearly zero cost: get an ARM and stop worrying about it!Tim

author: westfw
author_rank: Super Contributor
timestamp: September 08, 2019, 06:58:07 am
content: In extreme cases, you don't need to save anything - you can write an ISR that doesn't touch either registers or status bits. For example, on AVR:Code:myISR: sbi PORTB, 1 ; toggle output bit PortB, bit 0 reti ; return from ISR(for more fun, on AVRs with more than 8k of memory (and thus 2-word "vectors"), you can fit both of those instructions in the vector itself, where you'd normally have a "JMP myISR" instruction, saving another couple of cycles.)The AVR ISRs get a little bit weird, at least with avr-gcc. If your ISR code is all within the one ISR function, the compiler knows which registers the function uses, and can save only those (well, and maybe the knows zero reg, and the status, depending on the compiler version (recently improved in avr-gcc)) But if you call a function external to the ISR, suddenly you have to save all theadditionalregisters that those functions are allowed to use without saving them. So that function call picks up a lot of additional overhead...

author: ejeffrey
author_rank: Super Contributor
timestamp: September 09, 2019, 05:59:06 pm
content: Quote from: westfw on September 08, 2019, 06:58:07 amWhy is that weird? What else would you expect? I think there are (or used to be) compilers that simply insisted that ISRs could only call other ISR annotated functions, but generally an ISR that calls an outside function needs to save and restore all caller saved registers.The compiler might be able to optimize away those register saves if the target function can be analyzed either via inlining, because it is in the same translation unit, or via linktime optimization but it will always have to fall back on saving everything in some cases.I think you can allocate register globals that won't be used by the compiler anywhere. This is intended for hand optimizing hot globals not reserving a variable for use in an ISR. The problem is that unless you are 100% sure that you compile all code in your project with that flag, something else can still stomp on your register. This is not a problem for the intended use as the compiler will automatically save and restore the global when calling external library functions but is a problem for ISRs and signal handlers that can be called asynchronously even during a library call. The compiler is free to generate library calls to memset, memcpy, memmove, and others implicitly, and also may implement arithmetic functions not supported by your ISA via function calls. So even if you think you aren't generating library calls you probably need to recompile libgcc and maybe libc if you want to reserve a register for use by an ISA.I personally can't really imagine ever going to this much trouble. If the whole application is simple I would just do it entirely in assembly rather than trick the compiler into avoiding a specific register. If I really needed a more complex C application yet needed to optimize the ISR that heavily I would just get a faster MCU. If I couldn't do that, I would try to figure out how to split the load between two processors, one running an assembly language only program with just the highly optimized ISRs and the minimum communications link and the rest of the application written in C running on a separate processor.

author: magic
author_rank: Super Contributor
timestamp: September 09, 2019, 08:28:26 pm
content: For a single translation unit and functions which never call extern functions or function pointers, this could be made a generic optimization. So that when you call foo from main, main would only save those of its caller-saved registers that will actually be clobbered by foo. Ditto for ISRs. Not a hard optimization to implement.editActually, GCC performs such optimizations on x86.

author: langwadt
author_rank: Super Contributor
timestamp: September 09, 2019, 09:03:42 pm
content: Quote from: magic on September 09, 2019, 08:28:26 pmcortex M4 sorta does automatically for FP registers with the lazy stackinghttp://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0298a/index.html