This is an archive of the discontinued LLVM Phabricator instance.

[ConstantFolding] Handle leading zero-length elements in load folding
ClosedPublic

Authored by nikic on Dec 1 2018, 2:25 PM.

Details

Summary

Struct types may have leading zero-length elements like [0 x i32], in which case the "real" element at offset 0 will not necessarily coincide with the 0th element of the aggregate. ConstantFoldLoadThroughBitcast() wants to drill down the element at offset 0, but currently always picks the 0th aggregate element to do so. This patch changes the code to find the first non-zero-length element instead.

The motivation behind this change is https://github.com/rust-lang/rust/issues/48627. Rust is fond of emitting [0 x iN] separators between struct elements to enforce alignment, which prevents constant folding in this particular case.

Diff Detail

Repository
rL LLVM

Event Timeline

nikic created this revision.Dec 1 2018, 2:25 PM
rkruppe added a subscriber: rkruppe.Dec 1 2018, 2:31 PM
spatel edited reviewers, added: efriedma, hfinkel, rnk, dblaikie; removed: spatel.Dec 10 2018, 2:41 PM

I don't have a good understanding of the potential edge cases with aggregates, so adding some other potential reviewers.

spatel added a subscriber: spatel.Dec 10 2018, 2:41 PM
efriedma added inline comments.Dec 10 2018, 3:41 PM
lib/Analysis/ConstantFolding.cpp
352 ↗(On Diff #176269)

This is probably an infinite loop on something like [4294967296 x [0 x i32]]. (An LLVM array can have up to 2^64 elements.) Not sure how much we care... it looks like there are overflows like this all over the place in LLVM.

Otherwise looks fine.

nikic marked an inline comment as done.Dec 11 2018, 2:24 AM
nikic added inline comments.
lib/Analysis/ConstantFolding.cpp
352 ↗(On Diff #176269)

I tried

@g8 = constant [4294967296 x [0 x i32]] zeroinitializer
define i64 @test_leading_zero_size_elems_big2() {
  %v = load i64, i64* bitcast ([4294967296 x [0 x i32]]* @g8 to i64*)
  ret i64 %v
}

which did not result in an infinite loop ... because ConstantAggregateZero::getNumElements() also returns unsigned, so the number of elements is truncated to 0 :/

Still, using

@g8 = constant [4294967295 x [0 x i32]] zeroinitializer
define i64 @test_leading_zero_size_elems_big2() {
  %v = load i64, i64* bitcast ([4294967295 x [0 x i32]]* @g8 to i64*)
  ret i64 %v
}

ends up looping over array elements for no good reason, it's not like there is a change of finding a non-zero size elements by looking at further elements.

I think I'll change this code to handle the struct case separately, as I think it's the only one where this really makes sense.

nikic updated this revision to Diff 177675.Dec 11 2018, 2:35 AM

Only skip zero-size elements for struct types. Add tests for large arrays of zero-size elements.

nikic marked an inline comment as done.Dec 11 2018, 2:38 AM
nikic added inline comments.
test/Transforms/ConstProp/loads.ll
309 ↗(On Diff #177675)

This result is unrelated to this patch, but I'm wondering if it's correct. Is this folding to zero based on UB, as this is effectively an out-of-bounds access to a constant array?

efriedma accepted this revision.Dec 11 2018, 11:30 AM

LGTM. (We should fix the constant stuff to be consistent with the type system at some point, but not urgent, I guess.)

test/Transforms/ConstProp/loads.ll
309 ↗(On Diff #177675)

Yes, it's ignoring the offset because any well-defined load from a zeroinitializer constant must return zero.

This revision is now accepted and ready to land.Dec 11 2018, 11:30 AM
This revision was automatically updated to reflect the committed changes.