This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Fix big-endian miscompile of (bitcast (zext/trunc (bitcast)))
ClosedPublic

Authored by bjope on Nov 29 2019, 1:39 AM.

Details

Summary

optimizeVectorResize is rewriting patterns like:

%1 = bitcast vector %src to integer
%2 = trunc/zext %1
%dst = bitcast %2 to vector

Since bitcasting between integer an vector types gives
different integer values depending on endianness, we need
to take endianness into account. As it happens the old
implementation only gave the correct result for little
endian targets.

Fixes: https://bugs.llvm.org/show_bug.cgi?id=44178

Event Timeline

bjope created this revision.Nov 29 2019, 1:39 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 29 2019, 1:39 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
lebedev.ri added inline comments.
llvm/test/Transforms/InstCombine/cast.ll
2–4

I think this needs two runlines:

; RUN: opt < %s -instcombine -S --data-layout=<> | FileCheck %s --check-prefixes=ALL,LE
; RUN: opt < %s -instcombine -S --data-layout=<> | FileCheck %s --check-prefixes=ALL,BE

and drop hardcoded datalayout.

bjope marked an inline comment as done.Nov 29 2019, 4:11 AM
bjope added inline comments.
llvm/test/Transforms/InstCombine/cast.ll
2–4

Sure, I'll precommit an update to make this test case run with both big/little endian.

bjope updated this revision to Diff 231521.Nov 29 2019, 4:58 AM

Rebase after update to run cast.ll for both big and little endian.

The test change looks correct to me, but i'm wondering whether there is
any cleaner way to structure those optimizeVectorResize() changes.
Or maybe that code is missing comments.

bjope updated this revision to Diff 231547.Nov 29 2019, 8:17 AM

Minor refactoring and added some more code comments.

bjope updated this revision to Diff 231549.Nov 29 2019, 8:30 AM

Realized it was stupid to use a temporary vector when it is possible to simply use append. So a yet another minor refactoring.

This LGTM but I'm not an active reviewer for this area, so I'd get a second opinion :)

lebedev.ri added inline comments.Nov 29 2019, 10:01 AM
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
1851–1894

I personally find this to be a somewhat too convoluted approach.
What do you think about: (didn't compile-test)

SmallVector<uint32_t, 16> ShuffleMaskStorage;
ArrayRef<uint32_t> ShuffleMask;
Value *V2;

bool IsBigEndian = IC.getDataLayout().isBigEndian();
unsigned SrcElts = SrcTy->getNumElements();
unsigned DestElts = DestTy->getNumElements();

// Produce identity shuffle mask for src vector
ShuffleMaskStorage.resize(SrcElts);
std::iota(ShuffleMaskStorage.begin(), ShuffleMaskStorage.end(), 0);

if (SrcElts > DestElts) {
  // If we're shrinking the number of elements, just shuffle in the elements
  // from the input and use undef as the second shuffle input.
  V2 = UndefValue::get(SrcTy);
  ShuffleMask = ShuffleMaskStorage;
  unsigned EltDelta = SrcElts - DestElts;
  // Vector in-memory layout differs between little- and big- endian machines.
  if(IsBigEndian)
    ShuffleMask.drop_front(EltDelta)
  else
    ShuffleMask.drop_back(EltDelta)
} else {
  // If we're increasing the number of elements, shuffle in all of the
  // elements from InVal. Fill the rest of the result elements with zeros
  // from a constant zero.
  V2 = Constant::getNullValue(SrcTy);

  // Vector in-memory layout differs between little- and big- endian machines.
  decltype(ShuffleMaskStorage)::iterator InsertPos = IsBigEndian ?
                                                     ShuffleMaskStorage.begin() :
                                                     ShuffleMaskStorage.end()
  
  // And pad with zeros
  unsigned EltDelta = DestElts - SrcElts;
  ShuffleMaskStorage.insert(InsertPos, EltDelta, /*first element of second shuffle input*/SrcElts)
  
  ShuffleMask = ShuffleMaskStorage;
}

return new ShuffleVectorInst(InVal, V2,
                             ConstantDataVector::get(V2->getContext(),
                                                     ShuffleMask));
bjope updated this revision to Diff 231576.Nov 29 2019, 2:52 PM

Some more refactoring, very much inspired by the ideas from Roman.

lebedev.ri accepted this revision.Nov 29 2019, 11:08 PM

LGTM now, thank you.
Up to you if you want to wait for yet another review (@spatel ?)

llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
1872

maybe let's do

ShuffleMaskStorage.reserve(std::max(SrcElts, DestElts));
ShuffleMaskStorage.resize(SrcElts);

to try to avoid allocs in widening case.
Though we then waste memory in truncating case.

This revision is now accepted and ready to land.Nov 29 2019, 11:08 PM
spatel accepted this revision.Dec 1 2019, 7:59 AM

LGTM

llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
1866–1867

Add an assert that SrcElts != DestElts? Make sure the calling code doesn't get out-of-sync from the assumption in this function.

This revision was automatically updated to reflect the committed changes.