[cfe-dev] How to tell if a class contains tail padding?
Han Zhu via cfe-dev
cfe-dev at lists.llvm.org
Wed Jul 21 13:43:35 PDT 2021
After clang codegen, is the class layout final? Does the optimizer modify
the class layout?
On Wed, Jul 21, 2021 at 12:31 PM Eli Friedman <efriedma at quicinc.com> wrote:
> I suspect the transform you’re trying to do is more complicated than
> you’re making it out to be.
>
>
>
> In general, if you have a class that isn’t “POD for the purpose of layout”
> (https://itanium-cxx-abi.github.io/cxx-abi/abi.html), derived classes can
> store data in the tail padding. So the “padding” might contain data the
> program cares about. If you want to overwrite that space, you need to
> prove there isn’t a derived class storing data there.
>
>
>
> Possible proof approaches:
>
>
>
> 1. If the class is marked “final”, there aren’t any derived classes.
> 2. Array indexing with the wrong pointer type might be illegal.
>
>
>
> -Eli
>
>
>
> *From:* cfe-dev <cfe-dev-bounces at lists.llvm.org> *On Behalf Of *Han Zhu
> via cfe-dev
> *Sent:* Wednesday, July 21, 2021 11:46 AM
> *To:* cfe-dev at lists.llvm.org; llvm-dev at lists.llvm.org
> *Subject:* [EXT] [cfe-dev] How to tell if a class contains tail padding?
>
>
>
> Hi,
>
> I'm working on an optimization to improve LoopIdiomRecognize pass. For a
> trivial loop like this:
>
> ```
> struct S {
> int a;
> int b;
> char c;
> // 3 bytes padding
> };
>
> unsigned copy_noalias(S* __restrict__ a, S* b, int n) {
> for (int i = 0; i < n; i++) {
> a[i] = b[i];
> }
> return sizeof(a[0]);
> }
> ```
>
> Clang generates the below loop (some parts of IR omitted):
> ```
> %struct.S = type { i32, i32, i8 }
>
> for.body: ; preds = %for.cond
> %2 = load %struct.S*, %struct.S** %b.addr, align 8
> %3 = load i32, i32* %i, align 4
> %idxprom = sext i32 %3 to i64
> %arrayidx = getelementptr inbounds %struct.S, %struct.S* %2, i64 %idxprom
> %4 = load %struct.S*, %struct.S** %a.addr, align 8
> %5 = load i32, i32* %i, align 4
> %idxprom1 = sext i32 %5 to i64
> %arrayidx2 = getelementptr inbounds %struct.S, %struct.S* %4, i64
> %idxprom1
> %6 = bitcast %struct.S* %arrayidx2 to i8*
> %7 = bitcast %struct.S* %arrayidx to i8*
> call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %6, i8* align 4 %7, i64
> 12, i1 false)
> br label %for.inc
> ```
>
> It can be transformed into a single memcpy:
>
> ```
> for.body.preheader: ; preds = %entry
> %b10 = bitcast %struct.S* %b to i8*
> %a9 = bitcast %struct.S* %a to i8*
> %0 = zext i32 %n to i64
> %1 = mul nuw nsw i64 %0, 12
> call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %a9, i8* align 4 %b10,
> i64 %1, i1 false)
> br label %for.cond.cleanup
> ```
>
> The problem is, if the copied elements are a class, this doesn't work. For
> a
> class with the same members:
> ```
> %class.C = type <{ i32, i32, i8, [3 x i8] }>
> ```
>
> Clang does some optimization to generate a memcpy of nine bytes, omitting
> the
> tail padding:
>
> ```
> call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %6, i8* align 4 %7, i64
> 9, i1 false)
> ```
>
> Then in LLVM, we find the memcpy is not touching every byte of the array,
> so
> we abort the transformation.
>
> If we could tell the untouched three bytes are padding, we should be able
> to
> still do the optimization, but LLVM doesn't seem to have this information.
> I
> tried using `DataLayout::getTypeStoreSize()`, and it returned 12 bytes. I
> also
> tried `StructLayout`, and it treats the tail padding as a regular class
> member.
>
> Is there an API in LLVM to tell if a class has tail padding? If not, would
> it
> be useful to add this feature?
>
> Thanks,
> Han
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20210721/9723be9b/attachment-0001.html>
More information about the cfe-dev
mailing list