Inspired from https://github.com/llvm/llvm-project/issues/63337.
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
After @arsenm's change, InstSimplify should no longer work with instructions without parent. Can you explain how we end up working on an assume without parent in NewGVN? It's not really obvious to me.
I traced back the calls and they start from NewGVN::ExprResult NewGVN::performSymbolicEvaluation(Value *V, SmallPtrSetImpl<Value *> &Visited) const . I tried inserting the getParent() check in various places in NewGVN but it causes 5 LIT tests to fail for generating a few more basic blocks.
I believe this is related to the "phi of op" feature in NewGVN: https://github.com/llvm/llvm-project/blob/6954cb54252b50df95eea05c513e739b3ad2c8a0/llvm/lib/Transforms/Scalar/NewGVN.cpp#L2767 These cloned instructions won't have a parent.
I'm not entirely sure what to do here. I don't think that this fix is sufficient: If we need support for instructions without parent for NewGVN, then we need to revert @arsenm's change entirely, not just this part. Alternatively we need a different way to handle this on the NewGVN side. It may be possible to insert the instructions, as long as we are sure to later remove them again. I'm not sure if this would cause problems with NewGVNs design though, which is not supposed to do IR modifications during the analysis phase.
NewGVN makes use of instructions without parents in multiple places; I'm surprised that there isn't more fallout from this. I am not sure if inserting the instructions in the existing IR would work well, but inserting them in a temporary block may work as workaround.
This probably went unnoticed because we only assert that the instruction is inserted in the primary simplifyInstruction() entry point, but not the helpers for individual instruction types.
I am not sure if inserting the instructions in the existing IR would work well, but inserting them in a temporary block may work as workaround.
This seems like it might result in incorrect folds based on dominance relationships, especially is the temporary block is not connected to the CFG.
The problems I ran into were from missing the parent context function. If we had the context function passed in separately from the instruction, I think it would matter less if they were attached to a block
Inserting the cloned instruction ValueOp after I at https://github.com/llvm/llvm-project/blob/6954cb54252b50df95eea05c513e739b3ad2c8a0/llvm/lib/Transforms/Scalar/NewGVN.cpp#L2767 incorrectly deduces values in tests involving PHI of ops.
Perhaps we could add this case as a check to OpIsSafeForPHIOfOps. While walking the operands we could check if they are used in an assume until we hit a dominator. Not sure this makes complete sense, but we already do something similar to prevent InstSimplify from code walking through non-dominating phis.
@kmitropoulou noted that the PHI of ops transformation in the test is completely valid. This test might be a nice addition with llvm.assume() intrinsic in the LIT test suite.
I don't think doing phi-of-ops on the following is valid :
%f.0 = phi i32 [ undef, %entry ], [ %xor, %if.then ], [ %xor, %if.then2 ]
...
%tobool9.not = icmp eq i32 %f.0, 0
entry: %tobool9.not = icmp eq i32 undef, 0 -> undef if.then: %tobool9.not = icmp eq i32 %xor, 0 -> false (wrong! No way of knowing this!) if.then2: %tobool9.not = icmp eq i32 %xor, 0 -> false ( correct!! From the assume) %tobool9.not -> phi [undef, false, false] -> false ( wrong because of if.then value)
Alive2 proof: https://alive2.llvm.org/ce/z/V5ANuZ
Am I misunderstanding something?
llvm/test/Transforms/NewGVN/assume-dominating-icmp.ll | ||
---|---|---|
5 | Do you need the local_unnamed_addr and dso_local? |
@ManuelJBrito You are right :) I missed this. NewGVN checks if it can simplify icmp eq i32 %xor, 0. For this reason, it calls simplifyICmpInst() from InstructionSimplify.cpp. This function tries to find if the compare is dependent on a llvm.assume() intrinsic. Hence, it calls isValidAssumeForContext() which returns true for both cases! The isValidAssumeForContext() checks if the llvm.assume() dominates the icmp eq i32 %xor, 0. The problem is that icmp eq i32 %xor, 0 does not have a parent. Because of that, dominates() get confused that icmp eq i32 %xor, 0 is part of an unreachable block and it returns true.
Do you need the local_unnamed_addr and dso_local?