Index: include/llvm/IR/BasicBlock.h =================================================================== --- include/llvm/IR/BasicBlock.h +++ include/llvm/IR/BasicBlock.h @@ -433,6 +433,10 @@ // Create wrappers for C Binding types (see CBindingWrapping.h). DEFINE_SIMPLE_CONVERSION_FUNCTIONS(BasicBlock, LLVMBasicBlockRef) +/// Advance \p It while it points to a meta-instruction and return the result. +/// This assumes that \p It is not at the end of a block. +BasicBlock::iterator skipMetaInstructions(BasicBlock::iterator It); + } // end namespace llvm #endif // LLVM_IR_BASICBLOCK_H Index: include/llvm/IR/Instruction.h =================================================================== --- include/llvm/IR/Instruction.h +++ include/llvm/IR/Instruction.h @@ -556,6 +556,23 @@ } } + /// Return true if the instruction has no effect other than describing + /// program properties. + /// + /// Meta-instructions do not affect program execution and are ignored by the + /// optimizer. + /// + /// This includes llvm.dbg.*, llvm.donothing, and llvm.*.annotation.*. + bool isMeta() const; + + /// Return a pointer to the next non-meta instruction in the same basic + /// block as 'this', or nullptr if no such instruction exists. + const Instruction *getNextNonMetaInstruction() const; + Instruction *getNextNonMetaInstruction() { + return const_cast( + static_cast(this)->getNextNonMetaInstruction()); + } + /// Create a copy of 'this' instruction that is identical in all ways except /// the following: /// * The instruction has no parent Index: lib/IR/BasicBlock.cpp =================================================================== --- lib/IR/BasicBlock.cpp +++ lib/IR/BasicBlock.cpp @@ -479,3 +479,9 @@ } return Optional(); } + +BasicBlock::iterator llvm::skipMetaInstructions(BasicBlock::iterator It) { + while (It->isMeta()) + ++It; + return It; +} Index: lib/IR/Instruction.cpp =================================================================== --- lib/IR/Instruction.cpp +++ lib/IR/Instruction.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/Instruction.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/ADT/DenseSet.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" @@ -594,6 +595,32 @@ !isa(this); } +bool Instruction::isMeta() const { + auto *II = dyn_cast(this); + if (!II) + return false; + + if (isa(II)) + return true; + + switch (II->getIntrinsicID()) { + default: + return false; + case Intrinsic::donothing: + case Intrinsic::annotation: + case Intrinsic::var_annotation: + case Intrinsic::ptr_annotation: + return true; + } +} + +const Instruction *Instruction::getNextNonMetaInstruction() const { + for (const Instruction *I = getNextNode(); I; I = I->getNextNode()) + if (!I->isMeta()) + return I; + return nullptr; +} + bool Instruction::isAssociative() const { unsigned Opcode = getOpcode(); if (isAssociative(Opcode)) Index: lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCalls.cpp +++ lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3488,13 +3488,8 @@ // happen when variable allocas are DCE'd. if (IntrinsicInst *SS = dyn_cast(II->getArgOperand(0))) { if (SS->getIntrinsicID() == Intrinsic::stacksave) { - // Skip over debug info instructions. - // FIXME: This should be an utility in Instruction.h - auto It = SS->getIterator(); - It++; - while (isa(*It)) - It++; - if (&*It == II) { + // Skip over meta-instructions. + if (SS->getNextNonMetaInstruction() == II) { return eraseInstFromFunction(CI); } } @@ -3678,10 +3673,7 @@ // Fence instruction simplification Instruction *InstCombiner::visitFenceInst(FenceInst &FI) { // Remove identical consecutive fences. - Instruction *Next = FI.getNextNode(); - while (Next != nullptr && isa(Next)) - Next = Next->getNextNode(); - + Instruction *Next = FI.getNextNonMetaInstruction(); if (auto *NFI = dyn_cast(Next)) if (FI.isIdenticalTo(NFI)) return eraseInstFromFunction(FI); Index: lib/Transforms/Scalar/LoopRerollPass.cpp =================================================================== --- lib/Transforms/Scalar/LoopRerollPass.cpp +++ lib/Transforms/Scalar/LoopRerollPass.cpp @@ -1160,22 +1160,9 @@ } static bool isIgnorableInst(const Instruction *I) { - if (isa(I)) - return true; - const IntrinsicInst* II = dyn_cast(I); - if (!II) - return false; - switch (II->getIntrinsicID()) { - default: - return false; - case Intrinsic::annotation: - case Intrinsic::ptr_annotation: - case Intrinsic::var_annotation: - // TODO: the following intrinsics may also be whitelisted: - // lifetime_start, lifetime_end, invariant_start, invariant_end - return true; - } - return false; + // TODO: the following intrinsics may also be whitelisted: + // lifetime_start, lifetime_end, invariant_start, invariant_end + return I->isMeta(); } bool LoopReroll::DAGRootTracker::validate(ReductionTracker &Reductions) { Index: unittests/IR/InstructionsTest.cpp =================================================================== --- unittests/IR/InstructionsTest.cpp +++ unittests/IR/InstructionsTest.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/AsmParser/Parser.h" #include "llvm/IR/Instructions.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Analysis/ValueTracking.h" @@ -21,6 +22,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/NoFolder.h" #include "llvm/IR/Operator.h" +#include "llvm/Support/SourceMgr.h" #include "gmock/gmock-matchers.h" #include "gtest/gtest.h" #include @@ -28,6 +30,14 @@ namespace llvm { namespace { +static std::unique_ptr parseIR(LLVMContext &C, const char *IR) { + SMDiagnostic Err; + std::unique_ptr Mod = parseAssemblyString(IR, Err, C); + if (!Mod) + Err.print("InstructionsTests", errs()); + return Mod; +} + TEST(InstructionsTest, ReturnInst) { LLVMContext C; @@ -747,5 +757,53 @@ EXPECT_THAT(Indices, testing::ContainerEq(ArrayRef({-1, 4, 3}))); } +TEST(InstructionsTest, SkipMeta) { + LLVMContext C; + std::unique_ptr M = parseIR(C, + R"( + declare void @llvm.dbg.value(metadata, metadata, metadata) + declare void @llvm.donothing() + declare i32 @llvm.annotation.i32(i32, i8*, i8*, i32) + declare void @llvm.var.annotation(i8*, i8*, i8*, i32) + declare i8* @llvm.ptr.annotation.p0i8(i8*, i8*, i8*, i32) + + define void @f() { + entry: + call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !13 + call void @llvm.donothing() + %0 = call i32 @llvm.annotation.i32(i32 0, i8* undef, i8* undef, i32 0) + call void @llvm.var.annotation(i8* undef, i8* undef, i8* undef, i32 0) + %1 = call i8* @llvm.ptr.annotation.p0i8(i8* undef, i8* undef, i8* undef, i32 0) + ret void + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4} + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "t2.c", directory: "foo") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2) + !9 = !DISubroutineType(types: !10) + !10 = !{null} + !11 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !12) + !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !13 = !DILocation(line: 2, column: 7, scope: !8) + !14 = !DILocation(line: 3, column: 1, scope: !8) + )"); + ASSERT_TRUE(M); + Function *F = cast(M->getNamedValue("f")); + BasicBlock &BB = F->front(); + + // The first non-meta instruction is the terminator. + auto *Term = BB.getTerminator(); + EXPECT_EQ(Term, BB.begin()->getNextNonMetaInstruction()); + EXPECT_EQ(Term->getIterator(), skipMetaInstructions(BB.begin())); + + // After the terminator, there are no non-meta instructions. + EXPECT_EQ(nullptr, Term->getNextNonMetaInstruction()); +} + } // end anonymous namespace } // end namespace llvm