[llvm-dev] Semantics for non-byte-sized stores? (or whenever "store size in bits" is different than "size in bits")
Eli Friedman via llvm-dev
llvm-dev at lists.llvm.org
Mon May 20 12:20:53 PDT 2019
Thanks for filing a clear bug.
For the union example, the LangRef rule you cite isn't supposed to mean you can't copy the bits of a union. It's basically just an optimization for non-integral loads: the backend isn't required to explicitly mask away non-value bits of the load. So for example, you can't store i8 -1 to an address, then load it as an i1.
-Eli
> -----Original Message-----
> From: Björn Pettersson A <bjorn.a.pettersson at ericsson.com>
> Sent: Monday, May 20, 2019 11:52 AM
> To: Eli Friedman <efriedma at quicinc.com>; llvm-dev <llvm-dev at lists.llvm.org>
> Subject: [EXT] RE: [llvm-dev] Semantics for non-byte-sized stores? (or whenever
> "store size in bits" is different than "size in bits")
>
> Thanks Eli!
>
> I was looking for the information in the LangRef, but I guess I was stupid enough
> not to read the semantics for load/store (I thought that I would find the
> information in the earlier parts before the description of operations).
>
> Afaict we can end up with different kinds of problems in DeadStoreElimination
> today.
> Worst case scenario includes miscompiles.
> So, I wrote a PR here:
> https://bugs.llvm.org/show_bug.cgi?id=41949
>
>
>
> One part in the LangRef baffled me:
>
> "When loading a value of a type like ``i20`` with a size that is not an integral
> number
> of bytes, the result is undefined if the value was not originally
> written using a store of the same type."
>
> I think that when using unions it is common that there could be different types
> used in the store and the load. Example first using two separate i16 stores to
> store an { i16, i16 } structure, followed by an i32 load that loads all the 32 bits
> in one single operation.
>
> %a = alloca i32
> %b = bitcast i32* %a to { i16, i16 }*
> %b0 = getelementptr inbounds { i16, i16 }, { i16, i16 }* %b, i32 0, i32 0
> %b1 = getelementptr inbounds { i16, i16 }, { i16, i16 }* %b, i32 0, i32 1
> store i16 10, i16* %b0
> store i16 20, i16* %b1
> ...
> %x = load i32, i32* %a
>
> Is the result of the load really undefined for such IR?
> (Or maybe this is something that would need to be formalized as well?)
>
> /Björn
>
> > -----Original Message-----
> > From: Eli Friedman <efriedma at quicinc.com>
> > Sent: den 17 maj 2019 21:09
> > To: Björn Pettersson A <bjorn.a.pettersson at ericsson.com>; llvm-dev <llvm-
> > dev at lists.llvm.org>
> > Subject: RE: [llvm-dev] Semantics for non-byte-sized stores? (or whenever
> > "store size in bits" is different than "size in bits")
> >
> > Comments inline
> >
> > > -----Original Message-----
> > > From: llvm-dev <llvm-dev-bounces at lists.llvm.org> On Behalf Of Björn
> > > Pettersson A via llvm-dev
> > > Sent: Friday, May 17, 2019 7:34 AM
> > > To: llvm-dev at lists.llvm.org
> > > Subject: [EXT] [llvm-dev] Semantics for non-byte-sized stores? (or
> > whenever
> > > "store size in bits" is different than "size in bits")
> > >
> > > Assume that I have LLVM IR for a union between two types that are mapped
> > to
> > > i32 and i19.
> > > And then I have two overlapping stores to this union such as:
> > >
> > > %u = alloca %union
> > > %u32 = bitcast %union* %u to i32*
> > > %u19 = bitcast %union* %u to i19*
> > > store i32 -1, i32* %u32
> > > store i19 0, i19* %u19
> > > %result = load i32, i32* %u32
> > >
> > > How many bits are guaranteed to be zero in %result?
> >
> > According to LangRef, 19. In practice, 24.
> >
> > > How many bits are guaranteed to be one in %result?
> >
> > 8.
> >
> > > To be more specific:
> > >
> > > a) Is the i19 store defined as only writing 19 bits (even if store size
> > is larger, so it
> > > will become a load-modify-write after legalization)?
> >
> > Loads and stores only operate on a whole number of bytes, determined by
> > getTypeStoreSize. So 24 bits are stored, and no bits are loaded.
> >
> > > b) Is the i19 store defined as touching 24 bits, so we get 5 bits that
> > are
> > > undefined (or always zero-extended/sign-extended)?
> >
> > LangRef says the 5 bits are determined by the target: an i19 load is only
> > defined if the value was constructed using an i19 store. But in practice,
> > SelectionDAG legalization always zero-extends stores, and loads assume the
> > value is zero-extended.
> >
> > > %u = alloca %union
> > > %u32 = bitcast %union* %u to i32*
> > > %u19 = bitcast %union* %u to i19*
> > > %u16 = bitcast %union* %u to i16*
> > > store i19 0, i19* %u19
> > > store i16 -1, i16* %u16
> > > %result = load i32, i32* %u32
> >
> > The first store stores three bytes, the second store stores two bytes, the
> > load loads four bytes. So there's one byte of data from the first store,
> > and two bytes from the second.
> >
> > > Yet another question is if the i19 value is guaranteed to be placed in
> > the least
> > > significant end of the 3 bytes that are tainted by the store?
> >
> > IIRC, this isn't formally specified in LangRef, but in practice an i19 is
> > laid out as if it were zero-extended to i24. I'd suggest bailing out of
> > any transform that would care, though.
> >
> > I've been considering formalizing the current SelectionDAG behavior in
> > LangRef, but I haven't written a patch.
> >
> > -Eli
More information about the llvm-dev
mailing list