Simplify and improve readability.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Do we want to land this before D82085?
Otherwise, LGTM
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
774 | I'm pretty sure the address-taken check here is wrong, and this doesn't interact correctly with unreachable code. Probably we should teach eliminateCall to RAUW away dead uses instead, and then let some other code call removeUnreachableBlocks to do the cleanup properly. I guess this isn't new code, though. |
I'll wait and rebase after D82085.
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
774 | I don't think there is anything we could use to RAUW that would be semantic preserving, not that it matters much I guess since this block is now dead. I looked at the other uses of FoldReturnIntoUncondBranch (there are only 2) to see how other passes handle the possibility of creating a dead block. SimplifyCFG just checks the predecessors and deletes the block. CodeGenPrepare checks the predecessors and the address-taken and deletes the block. The BasicBlock destructor seems to correctly handle the case where a dead block has its address taken, and even a nice comment explaining how you could end up in that situation. Therefore, I think we are safe to remove the address-taken check and just delete the block if it has no predecessors. |
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
774 |
Exactly, the value doesn't matter because it's dead.
Consider the following example, which currently crashes tailcallelim: define i32 @foo() { %z = call i32 @foo() ret i32 %z ret i32 %z } removeUnreachableBlocks avoids the problem by calling dropAllReferences() (which only works because it's erasing all the unreachable blocks at once). CodeGenPrepare avoids the problem by accident, I think; some of the checks implicitly exclude or manipulate dead code. |
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
774 | I feel like I may be misunderstanding what you're saying. In your example you are passing in a function with a dead block. Where as this only applies to something like the following: define i32 @foo() { %z = call i32 @foo() br label %retblock retblock: ret i32 %z } Where we transform to this: define i32 @foo() { %z = call i32 @foo() ret i32 %z retblock: ret i32 %z } In order to eliminate the call. Is it not valid to just delete the now dead block? It seems a little bug prone to just RAUW when the replacement doesn't maintain the semantics, but that is just my opinion. |
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp | ||
---|---|---|
774 | The general point is just that if you haven't run removeUnreachableBlocks, there can unreachable uses floating around of any instruction that defines a value. Before you erase an instruction, you have to erase/fix any uses of that instruction. To erase a basic block, you have to erase every instruction in that basic block. Actually, taking another look at this, I guess it isn't possible to have an instruction with uses at this point. We checked that the only instructions in the block are phi, debug intrinsics, and return. FoldReturnIntoUncondBranch eliminates any phi nodes, and debug intrinsics and return don't define a value. So I guess you're right, we're okay here. |
I'm pretty sure the address-taken check here is wrong, and this doesn't interact correctly with unreachable code. Probably we should teach eliminateCall to RAUW away dead uses instead, and then let some other code call removeUnreachableBlocks to do the cleanup properly.
I guess this isn't new code, though.