Padauk devices have some instructions (idxm, ldtabl, ldtabh, stt, ldt, pushw, popw , igoto, icall) operating on 16-bit values that require their operands to be aligned to 16 bits.
In some cases use of these instruction would help reduce register pressure.
SDCC should try to align 16-bit values (int, generic pointers, etc) on 16-bit boundaries, and make alignment information available to code genration to be able to use those instructions better.
There is a trade-off here, and I'd suggest not to make this a struct alignment requirement - requiring padding in structs would waste too much memory.
So we would just align to 16-bits any variable that has size that is a multiple of 16 bits. This way we would not waste any RAM on gaps. This could be implemented by having a separate segment for aligned variables, that placed before the "normal" DATA segment.
What about the overlay? Order variables in the overlay, so that 16-bit stuff is first, followed by the rest?
So the idea to not waste even a single byte, but still get good alignment would be:
PREG (16 bits, at address 0)
DATA16 (multiple of 16 bits, holds all non-overlay stuff that should be aligned)
OVR (within OVR: 16-bit-aigned stuff first)
DATA (anything else)
For some uses of pushw / popw, we'd also need alignment on the stack, which our Chaitin-style stack allocator is already prepared for to handle, so enabling it shouldn't be much extra effort (but we'd have to check if it is worth it, as it might increase stack memory usage).
Philipp
Diff:
Also very useful would be to have a way to explicitly specify word alignment, i.e. support for _Alignas(2) from the ISO C11 standard. This could require padding bytes in structs.
Would
_Alignas(n)just emit.bndry n?The linker moves an area to an address that satisfies the least common multiple of all
.bndryin a relocatable object file. If an area is defined in multiple relocatable object files, each will be placed separately.I guess it is more complicated: We can't just have .bndry n on the DATA area, or so, since then we'll have some 8-bit objects in between, which will mess up the alignment of subsequent objects. Those 8-bit objects would need "padding" (not meant in the C sense) after them. To avoid wasting space for padding, we'd want to have all 2-aligned objects of size multiple of 2 in the area, and other stuff in a different area. But where the user explicitly requests 2-alignment on an object where size is not a multiple of 2, we'd want to use the space at the end for an 8bit object. Doing this perfectly would be a link-time optimization. But maybe we could just create a DATA2 area for objects of even size, and put all global ints there for a start (and also allow the user to put other objects of even size there).
I would like to add my support and encouragement for this feature.
I am currently writing some code for PDK and am needing to use inline assembly with an
stt16instruction to load a value into the 16-bit timer counter (it's not accessible as a register, only bystt16andldt16special instructions). I'm using a globaluint16_tvariable as the source value.However, the memory location the value is loaded from must be word-aligned, and there is currently no way to explicitly specify this to SDCC. Either you must rely on circumstance that a variable happens to be located at an even address, or indirectly ensure it by making the variable the very first one allocated (so it ends up at 0x2).
In case it helps anybody, I also experimented with creating and initialising the word-aligned source variable in the inline assembly using a
.evendirective, like so:But I stayed with a global variable and didn't use this approach in the end.
By the way, I also experimented using
.bndry, but I don't think it works properly in SDAS. Instead of making the location an integer multiple of N, it makes it the area base addres plus multiple of N. For example, on PDK, DATA area's base address is 0x2; if I specify.bndry 8instead of.evenin the code above, I end up with an address of 0xA (i.e. 2+8). Same for 16, which gives 0x12. This is obviously not the desired effect, especially in the context of ensuring memory alignment.I also think a more general-purpose ability to specify alignment of variables (perhaps via an
__align()attribute) on all platforms would be good. A use case would be for platforms that feature DMA controllers with the ability to make word-size transfers that require word-aligned memory source/target addresses. One example being certain STM8L/ATM8AL devices.AFAIK the .bndry and .even directives work in the assembler, but the linker does not know how to handle them. So .even aligned code may still end up at an odd address.
For pdk, we already have PREG that needs to be at address 0, and have a size of 16 bits. So if we place DATA16 immediately after PREG, the DATA16 area would be at an even address, and all
.evendirectives inside it should align to 16 bits (assuming nothing goes wrong when the linker combines DATA16 from multiple files).P.S.: Would it be sufficient to put an
.evenat the end of DATA16 in each file to ensure that all the DATA16 parts from different files all start at an even address, and thus.evenwould work inside them as expected?Last edit: Philipp Klaus Krause 2024-01-12
It looks like a
.evendirective at the end will indeed insert space to make sure it actually ends at an even address.results in this REL:
I suppose that makes sense. The assembler may be setting the current address according to the directive as far as it knows, but then at linking time the area offset is applied and screws things up.
The current ASxxxx documentation claims that boundary specifications are preserved at linking time. But when looking at the ASxxxx changelog, I see the following note:
So it seems it didn't always do that, and the previous behaviour is what is currently in SDAS. But that ASxxxx change was in v5.2 from January 2017, quite some time ago. Am I mis-remembering, or didn't SDAS sometime in the last few years get features ported across to gain parity with upstream?