In this patch, I'm adding an extra check to the Latch's terminator in llvm::UnrollRuntimeLoopRemainder,
similar to how it is already done in the llvm::UnrollLoop.
The compiler would crash if this function is called with a malformed loop.
Differential D51486
Add check to Latch's terminator in UnrollRuntimeLoopRemainder rcorcs on Aug 30 2018, 6:17 AM. Authored by
Details In this patch, I'm adding an extra check to the Latch's terminator in llvm::UnrollRuntimeLoopRemainder, The compiler would crash if this function is called with a malformed loop.
Diff Detail
Event TimelineComment Actions Hello. Please upload with full context to make patched easier to read. See https://llvm.org/docs/Contributing.html
Comment Actions Sorry, I'll make sure to upload with a full context in the next time. The comment partially covers my concern. The comment and the assertion that you've pointed out only covers the case where the latch block has a conditional branch that does not exit the loop. Since the latch has an unconditional branch, it means that:
The way I see it, the two problems are related. For this reason, I think that it would be better if we catch both cases of malformed loops. But since the comment that you mention says that "This needs to be guaranteed by the callers of UnrollRuntimeLoopRemainder", If you agree with my point, I can update the patch with the assertion (instead of return false). Thanks for the feedback. Comment Actions We do seem to return false in every other case, I'm not very sure why this would be different and considered a precondition we would assert on. Perhaps it's best to update the assert to a return false too. Mind changing it that way instead? Comment Actions I completely agree with your assessment. I've done the suggested change and tested. All the LLVM tests pass as expected. Comment Actions Please upload patch with complete context. Could you also add a test case show casing your problem (which you've described earlier): Just a loop with unconditional latch terminator and run through runtime-unroll. I'd added this as assert because upstream passes guarantees that by the time we reach unroll remainder, we will have a latch with conditional terminator. Comment Actions I think that this patch is mainly important because these functions are externally visible in the library's API. I've added a test case. Please make sure that it follows the testing standards. Comment Actions I'm happy with this as is, but if you want the API to be tested and ensure it keeps working, you could add a unittest. Something like unittests/Transforms/Utils/BasicBlockUtilsTest.cpp or the ones is CloningTest.cpp.
Comment Actions Because I wanted to make sure it worked, I wrote this. Feel free to clean it up and use it as a unittest if you like: //===- BasicBlockUtils.cpp - Unit tests for BasicBlockUtils ---------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/UnrollLoop.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" using namespace llvm; static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C); if (!Mod) Err.print("BasicBlockUtilsTests", errs()); return Mod; } TEST(LoopUnrollRuntime, Latch) { LLVMContext C; std::unique_ptr<Module> M = parseIR( C, R"(define i32 @test(i32* %a, i32* %b, i32* %c, i64 %n) { entry: br label %while.cond while.cond: ; preds = %while.body, %entry %i.0 = phi i64 [ 0, %entry ], [ %inc, %while.body ] %cmp = icmp slt i64 %i.0, %n br i1 %cmp, label %while.body, label %while.end while.body: ; preds = %while.cond %arrayidx = getelementptr inbounds i32, i32* %b, i64 %i.0 %0 = load i32, i32* %arrayidx %arrayidx1 = getelementptr inbounds i32, i32* %c, i64 %i.0 %1 = load i32, i32* %arrayidx1 %mul = mul nsw i32 %0, %1 %arrayidx2 = getelementptr inbounds i32, i32* %a, i64 %i.0 store i32 %mul, i32* %arrayidx2 %inc = add nsw i64 %i.0, 1 br label %while.cond while.end: ; preds = %while.cond ret i32 0 })" ); auto *F = M->getFunction("test"); DominatorTree DT(*F); LoopInfo LI(DT); AssumptionCache AC(*F); TargetLibraryInfoImpl TLII; TargetLibraryInfo TLI(TLII); ScalarEvolution SE(*F, TLI, AC, DT, LI); bool ret = UnrollRuntimeLoopRemainder(*LI.begin(), 4, true, true, false, &LI, &SE, &DT, &AC, true); EXPECT_FALSE(ret); } Comment Actions Thanks for the suggestion and the unit-test code. I'll add it to the patch. About the check in the test file, I don't want to prevent the pass to unroll this loop.
Comment Actions Hi, I'd like to confirm that someone can commit this patch for me because I believe I don't have commit access. Thank you all for the review. Comment Actions Sorry for the delay. Sure, I can do that. Can you rebase this on trunk first? It looks like the unit test files were renamed. Comment Actions I've rebased to the patch and changed the name of the unit-test UnrollLoop.cpp to UnrollLoopTest.cpp, similar to the other files in the same folder. |