AVR indirect_jump addresses limited to 16 bits
Hello, I'm looking into adding support for ATmega256x and larger devices to the AVR port. This means that program memory addresses no longer fit in 16 bits - and I'm looking how to avoid making pointers larger. Jumps and function calls are no problem as long as they use labels (constant addresses). Indirect jumps and calls (address in a register) are a problem. For function calls, it seems easy - generate each function in two parts, placed by linker script in different sections: - jump to function body - function body and locate the jumps below 128K (64K words), while the function body can be anywhere in the address space. If you need to take a 16-bit address of the function, simply take the address of the jump. The added runtime overhead of the jump is a small price to pay compared to making pointers 3 or 4 bytes long (instead of 2 bytes) on the AVR. But, now there is the "indirect_jump" pattern - mandatory, even though I haven't yet seen it actually used in real applications on the AVR. The question is, how to make sure that if indirect_jump ever happens to be used, its target address is guaranteed to fit in 16 bits? Where do these pointers come from? (Like function pointers come from taking the address of a function, and this may be solved as described above.) Looking at the source, I've found one use of indirect_jump so far - returning from a function with some data still on the stack. I suspect this may not work properly on the AVR anyway, because return address is stored on the stack in the wrong byte order - big endian, even though the AVR is little endian. Anything else? Thanks, Marek
Re: AVR indirect_jump addresses limited to 16 bits
On Sat, Mar 19, 2005 at 01:51:05PM -0500, Paul Schlie wrote: > - Fully agree, just can't keep from wondering if this may be most > efficiency accomplish by simply requiring the alignment of all > function entry points to be two instruction word aligned. Thereby This only doubles the available address space, and I'd rather not do it all again (this time with 4-word alignment) if 512K chips appear. Supporting the maximum possible address space on the AVR would require 64-word function alignment, and shifting the word address by 6. But function entry points are not the only problem - indirect jumps are another (as you can see in the subject of my message), they can point somewhere within a function (so function alignment may not help here). On the other hand, indirect_jump is rarely seen, so it must be correct but doesn't have to be very efficient (OK if it costs even a few more instructions to stay in the low 64K words). So, I'm trying to figure out when the indirect_jump pattern can actually be generated on the AVR (haven't yet seen it in any real application), and where does the pointer comes from (to see if the jump target can be moved to the low 64K words somehow). Marek
Re: AVR indirect_jump addresses limited to 16 bits
On Sat, Mar 19, 2005 at 04:38:28PM -0500, Paul Schlie wrote: > - understood, however unlikely; observing it will likely take Atmel at > least 2-3 years to stabilize production of the 256K devices, and larger > device volume potential vs. competitive offerings couldn't likely justify > their development (as just an opinion). I thought the same about 128K devices, and then 256K devices appeared :) > - Sorry, I'm confused; can you give me an example of legal C expression > specifying an indirect jump to an arbitrary location within a function? Good question - I can't. On the other hand, the manual says: `indirect_jump' An instruction to jump to an address which is operand zero. *This pattern name is mandatory on all machines.* Why would it be mandatory if it was not truly needed? If the manual is correct, it seems this pattern is truly needed (not just an optional optimization like some other patterns). If it is impossible on the AVR, it could be implemented with invalid assembler output (so we get an error if "impossible" ever happens). But I'd like to be sure if this is really the case. GCC is not only a C compiler, perhaps indirect_jump is needed for some other language? Thanks, Marek
Re: AVR indirect_jump addresses limited to 16 bits
On Sat, Mar 19, 2005 at 06:37:54PM -0500, Paul Schlie wrote: > - or possibly GCC may try to be clever by jumping to no-return attribute > function calls? Such "trying to be clever" would most likely be optional. Again, why is indirect_jump _mandatory_ on all machines? Please, don't try to be too clever :) - just look at the GCC source and try to figure out exactly what is indirect_jump used for, and how much of this (if anything) matters for the AVR port. If the address of a label is loaded into a register, then we have to switch to a different section around that label: /* ... */ jmp label .section .text_low,"ax",@progbits label: jmp 1f .text 1: /* ... */ then it will be safe to load the address of the label into a register, no need to do anything with the highest bits as pm_hh8(label) == 0: ldi r30,pm_lo8(label) ldi r31,pm_hi8(label) /* ... */ ijmp; PC <- 0:r31:r30 Note that this adds two jumps around the label, so we only want to do it infrequently - if the address of the label is loaded into a register (not if the only users of the label are direct jumps). Thanks, Marek
Re: AVR indirect_jump addresses limited to 16 bits
On Sun, Mar 20, 2005 at 02:58:29AM +0100, Giovanni Bajo wrote: > It is possible in GNU C at least: > > int foo(int dest) > { >__label__ l1, l2, l3; >void *lb[] = { &&l1, &&l2, &&l3 }; >int x = 0; > >goto *lb[dest]; > > l1: >x += 1; > l2: >x += 1; > l3: >x += 1; >return x; > } Thanks for explaining this. So yes, unfortunately we have to support indirect_jump properly (I was hoping it was only for something like trampolines, which can't work on the AVR anyway). I was wrong about needing two jumps - just one for each label should be enough, because switching to .text_low and back to .text doesn't disrupt the normal flow of instructions in .text. So, it's not that bad, really the same thing for indirect jumps and calls (using function pointers), each affected label needs to be output like this: .section .text_low,"ax",@progbits label: jmp 1f .text 1: This includes all global labels (visible to other object files, like function entry points), and local labels referenced by anything other than direct jumps or calls). On the other hand, branches within the same function should avoid the extra jump and go to "1:" directly. If the same label is used in both ways (direct jump/branch, and address taken), two separate labels (at the same address) should be output for both of these uses, but I'm not sure how to do this in GCC. Any suggestions? (The problem can be seen by adding something like "if (dest > 2) goto l3;" above the goto in the original example: in the assembler output, there is only one label corresponding to l3 which is both 1. referenced with pm() and 2. target of a branch for which .text_low would be out of range.) Marek
Re: AVR indirect_jump addresses limited to 16 bits
On Sun, Mar 20, 2005 at 04:29:01PM -0800, Richard Henderson wrote: > The easiest way is to do this in the linker instead of the compiler. > See the xstormy16 port and how it handles R_XSTORMY16_FPTR16. This > has the distinct advantage that you do not commit to the creation of > an indirect jump until you discover that the target label is outside > the low 64k. Looks perfect to me. So we are not the first architecture needing such tricks... AVR would need 3 new relocs, used like this: .word pm16(label) ldi r30,pm16_lo8(label) ldi r31,pm16_hi8(label) and the linker can do the rest of the magic (add jumps in a section below 64K words if the label is above). Cc: to Denis, as I may need help actually implementing these changes (you know binutils internals much better than I do). Thanks, Marek