problem with inlining pass

Hi!

My llvm version is 3.0 release.
I have a module generated by clang. When I optimize it, I first add an
inlining pass (llvm::createFunctionInliningPass), then these passes:
- own FunctionPass
- llvm::createPromoteMemoryToRegisterPass
- llvm::createInstructionCombiningPass
- llvm::createDeadInstEliminationPass
- llvm::createDeadStoreEliminationPass
- new llvm::DominatorTree()
- new llvm::LoopInfo()
- llvm::createLoopSimplifyPass()
- own FunctionPass

The problem is that the last function pass (and maybe the others too) gets called
with functions that the inlining pass is supposed to delete. this behavior I only saw
on windows and macos in 64 bit release mode. In debug mode it works and I think
at least on windows it works in 32 bit release mode.

My current workaround is creating a new pass manager for the last pass and let it
run separately. At this time all functions with linkonce_odr got deleted by the inlining
pass and everything is ok.

-Jochen

Hi Jochen,

My llvm version is 3.0 release.
I have a module generated by clang. When I optimize it, I first add an
inlining pass (llvm::createFunctionInliningPass), then these passes:
- own FunctionPass
- llvm::createPromoteMemoryToRegisterPass
- llvm::createInstructionCombiningPass
- llvm::createDeadInstEliminationPass
- llvm::createDeadStoreEliminationPass
- new llvm::DominatorTree()
- new llvm::LoopInfo()
- llvm::createLoopSimplifyPass()
- own FunctionPass

The problem is that the last function pass (and maybe the others too)
gets called
with functions that the inlining pass is supposed to delete.

I think this is a normal consequence of how the pass manager schedules
passes. Suppose you have the following call graph: A --> B, i.e. function
A calls function B. Passes will be scheduled as follows:

First, the inliner will be run on function B. This won't do anything
since B doesn't call anything. Then each function pass will be run on
B in turn. That means that your function pass will be run on B. Then
the inliner will be run on function A. This may inline function B into
function A then delete B. Then each function pass will be run on A.
In short the inliner works its way up the callgraph, and after it runs
on each function, all other function passes are run on that function.
That's why I think it is normal for your pass to be run on function B
even if the inliner is going to delete B after inlining it into A.

Ciao, Duncan.

Hello everyone,

  I am working on some changes to the Hexagon VLIW PreRA scheduler, and as a
part of it need to test aliasing properties of two instruction.
What it boils down to is the following code:

char a[20];
char s;
char *p, *q; // p == &a[0]; q == &s;

void test()
{
  register char reg;

  s = 0;
  reg = p[0] + p[1];
  s = q[0] + reg;

  return;
}

When I ask the question whether "&s" and "&q[0]" may potentially alias, I
got negative affirmation.
In the full test (not presented) they indeed may and do in fact alias,
resulting in incorrect VLIW schedule.

My question - is it a feature or a bug :slight_smile:

Here is somewhat more info:

Before lowering begins:
*** IR Dump After Remove sign extends ***
define void @test() nounwind {
entry:
  store i8 0, i8* @s, align 1, !tbaa !0
  %0 = load i8** @p, align 4, !tbaa !2
  %1 = load i8* %0, align 1, !tbaa !0
  %conv = zext i8 %1 to i32
  %arrayidx1 = getelementptr inbounds i8* %0, i32 1
  %2 = load i8* %arrayidx1, align 1, !tbaa !0
  %conv2 = zext i8 %2 to i32
  %3 = load i8** @q, align 4, !tbaa !2 <<< Can this load be bypassed by the
store below?
  %4 = load i8* %3, align 1, !tbaa !0
  %conv5 = zext i8 %4 to i32
  %add = add i32 %conv2, %conv
  %add7 = add i32 %add, %conv5
  %conv8 = trunc i32 %add7 to i8
  store i8 %conv8, i8* @s, align 1, !tbaa !0 <<< Can this store bypass the
above load?
  ret void
}

At the point of enquiry I have the following (lowered) instructions:

x3df7900: i32,ch = LDw_GP_V4 0x3df4c70, 0x3df5470<Mem:LD4[@q](tbaa=!"any
pointer")> [ORD=8] [ID=6] // This is Load from q[0]
0x3df5470: ch = STb_GP_V4 0x3df5170, 0x3df4e70, 0x3d9c130<Mem:ST1[@s]>
[ID=4] // This is a store to s

Underlying Values:

@q = common global i8* null, align 4
@s = common global i8 0, align 1

The way inquiry is made is similar to DAGCombiner::isAlias()

SDNode *SDN1;
SDNode *SDN2;
MachineMemOperand *MMOa;
MachineMemOperand *MMOb;
...
    const MachineSDNode *MNb = dyn_cast<MachineSDNode>(SDN2);
    const MachineSDNode *MNa = dyn_cast<MachineSDNode>(SDN1);
...
    MMOa = !MNa->memoperands_empty() ?
                    (*MNa->memoperands_begin()) : NULL;
    MMOb = !MNb->memoperands_empty() ?
                    (*MNb->memoperands_begin()) : NULL;

    if (MMOa && MMOa->getValue() && MMOb && MMOb->getValue()) {

...
      int64_t MinOffset = std::min(MMOa->getOffset(), MMOb->getOffset());
      int64_t Overlapa = MMOa->getSize() + MMOa->getOffset() - MinOffset;
      int64_t Overlapb = MMOb->getSize() + MMOb->getOffset() - MinOffset;
      
      AliasAnalysis::AliasResult AAResult = AA->alias(
            AliasAnalysis::Location(MMOa->getValue(), Overlapa,
                                    MMOa->getTBAAInfo()),
            AliasAnalysis::Location(MMOb->getValue(), Overlapb,
                                    MMOb->getTBAAInfo()));

Quick debug of BasicAliasAnalysis::aliasCheck() points to this code:

if (isIdentifiedObject(O1) && isIdentifiedObject(O2))
      return NoAlias;

And in llvm::isIdentifiedObject() this is true:
if (isa<GlobalValue>(V) && !isa<GlobalAlias>(V))

Any qlues/suggestions are welcome. Thanks.

Sergei Larin

Err, are you sure you're asking the right question? Given the loads
you're pointing at, you're asking whether &s and &q alias.

-Eli

Err, are you sure you're asking the right question? Given the loads
you're pointing at, you're asking whether &s and &q alias.

Yes. And I am pretty sure this enquiry works fine for 99.9999% of cases, but
has some issue with this one...
Scheduling is notorious for exposing latent bugs way later after they have
been introduced :frowning:

Sergei

Ah, that explains it: you've only attached one memory operand to an
instruction that reads from two memory locations. You probably just
need to add the second memory operand.

-Eli