adjust address calculus for an architecture that does not address bytes

Hi,
my target architecture has a kind of "16bit addressing mode", i.e. one address does not address 8 bit but a 16bit chunk. Consequently, every constant used to calculate effective addresses must be divided by two.
So far this is not such a problem for stack objects since FrameIndexes, function arguments etc. have a lot of custom lowering code where this can be done.
But when it comes to pointer arithmetic resulting from the GetElementPtr instruction this is less obvious.
At first I thought this could be handled when lowering loads and stores, but I realize that I can only catch the targeted addresses of loads/stores here - however address calculation nodes may occur anywhere in a DAG.

So my first impulse would be to adjust the constants when the GEP instructions are transformed to ADDs. Afaics his would mean to change the TargetData class, which is not meant to be subclassed.
Is there a cleaner solution without modifying llvm?

Regards, Christian

Christian Sayer wrote:

Hi,
my target architecture has a kind of "16bit addressing mode", i.e. one address does not address 8 bit but a 16bit chunk. Consequently, every constant used to calculate effective addresses must be divided by two.
So far this is not such a problem for stack objects since FrameIndexes, function arguments etc. have a lot of custom lowering code where this can be done.
But when it comes to pointer arithmetic resulting from the GetElementPtr instruction this is less obvious.
At first I thought this could be handled when lowering loads and stores, but I realize that I can only catch the targeted addresses of loads/stores here - however address calculation nodes may occur anywhere in a DAG.

So my first impulse would be to adjust the constants when the GEP instructions are transformed to ADDs. Afaics his would mean to change the TargetData class, which is not meant to be subclassed.
Is there a cleaner solution without modifying llvm?

Regards, Christian

The XCore has loads / stores where the offset is scaled by the size of the load or store. For example the load word instruction LDW takes an offset which is multiplied by 4 and added to the base pointer. This is dealt with in the patterns defined in XCoreInstrInfo.td. The following pattern is used for LDW:

def : Pat<(load (add GRRegs:$addr, immUs4:$offset)),
          (LDW_2rus GRRegs:$addr, (div4_xform immUs4:$offset))>;

immUs4 is true when offset is a multiple of 4 and the offset divided by 4 fits in an immediate. The div4_xform xform divides a constant by 4. These are both defined in XCoreInstrInfo.td.

It sounds like your target may be able to use a similar approach.

> At first I thought this could be handled when lowering
loads and stores, but I realize that I can only catch the
targeted addresses of loads/stores here - however address
calculation nodes may occur anywhere in a DAG.
>
> So my first impulse would be to adjust the constants when
the GEP instructions are transformed to ADDs. Afaics his
would mean to change the TargetData class, which is not meant
to be subclassed.
> Is there a cleaner solution without modifying llvm?
>
> Regards, Christian
The XCore has loads / stores where the offset is scaled by the size of
the load or store. For example the load word instruction LDW takes an
offset which is multiplied by 4 and added to the base pointer. This is
dealt with in the patterns defined in XCoreInstrInfo.td. The following
pattern is used for LDW:

def : Pat<(load (add GRRegs:$addr, immUs4:$offset)),
          (LDW_2rus GRRegs:$addr, (div4_xform immUs4:$offset))>;

Richard,

thanks for your suggestion.
However, I think what you describe is what I meant by 'only catch
the targeted addresses of loads/stores', i.e. the address the instruction
is reading from/writing to.

However, if I have e.g. a chunk of code which stores the address of the second
element of struct str somewhere into another struct pstr:

        %0 = getelementptr %struct.str* %s, i32 0, i32 1 ; <i16*> [#uses=1]
        %1 = getelementptr %struct.pstr* %ps, i32 0, i32 5 ; <i16**> [#uses=1]
        store i16* %0, i16** %1, align 4

Your suggestion allows to store the i32* at the right place in %struct.pstr, but
the first GEP instruction adds the size of the first element - in bytes! - of str to the
address of str to get the address of the second element. I need that size (aka offset)
to be in respect of 16bit.
(Note also that e.g. arbitrary pointer arithmetics could be done on %0 - when it comes
to lowering, we cannot know if an ADD node comes from a GEP or not)

Or to put it shortly, regarding the property of char being the smallest addressable piece
of data, sizeof(char) is 16 on my architecture, but "8" is hard coded in TargetData (maybe
'char' should rather be replaced by 'byte' in the last sentence).

Well my current workaround is just to divide offsets by 2 when resolving GEP instructions
but I still hope there is a good way to do this in the backend.
I think this would be to subclass TargetData in the backend - I'd appreciate if someone
could confirm or not that there isn't another option within the current way of designing
a backend.

Thanks a lot,
Christian

Your suggestion allows to store the i32* at the right place
in %struct.pstr, but

this should be 'store the i16*'

Or to put it shortly, regarding the property of char being
the smallest addressable piece
of data, sizeof(char) is 16 on my architecture, but "8" is
hard coded in TargetData (maybe

my wording is quite a mess here...
well the goal is of course that sizeof(char) is 1 [byte], what I mean
is that the size of a char (rather a byte) is 16 bit :wink:

(8-bit) byte addressibility is a fairly widespread assumption in LLVM,
reaching beyond just GEP lowering. Fixing this will require more
work than just adding a subclass, though it ought to be doable.

Go through the TargetData class and generalize anything that works
in terms of bytes to work in terms of address units. I think you'll need
to extend the datalayout string syntax to support an address unit
size (in bits), and to add an address unit size field to the TargetData
class. You'll also need to change APIs like getSizeInBytes to
getSizeInAddressUnits.

Then, you can change your target datalayout string to be in terms of
16-bit address units instead of 8-bit address units.

Then there are other places in LLVM which hardcode "8" constants
and "i8*" types; these can be fixed once the TargetData changes are
in place.

Dan