blob: 905928819dda807d65e80e2300e8adedbbab4c0a [file] [log] [blame]
//===- llvm/unittest/IR/BasicBlockTest.cpp - BasicBlock unit tests --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/NoFolder.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/SourceMgr.h"
#include "gmock/gmock-matchers.h"
#include "gtest/gtest.h"
#include <memory>
using namespace llvm;
extern cl::opt<bool> UseNewDbgInfoFormat;
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("BasicBlockDbgInfoTest", errs());
return Mod;
}
namespace {
// We can occasionally moveAfter an instruction so that it moves to the
// position that it already resides at. This is fine -- but gets complicated
// with dbg.value intrinsics. By moving an instruction, we can end up changing
// nothing but the location of debug-info intrinsics. That has to be modelled
// by DbgVariableRecords, the dbg.value replacement.
TEST(BasicBlockDbgInfoTest, InsertAfterSelf) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
%c = add i16 %b, 1, !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
// Convert the module to "new" form debug-info.
M->convertToNewDbgValues();
// Fetch the entry block.
BasicBlock &BB = M->getFunction("f")->getEntryBlock();
Instruction *Inst1 = &*BB.begin();
Instruction *Inst2 = &*std::next(BB.begin());
Instruction *RetInst = &*std::next(Inst2->getIterator());
EXPECT_TRUE(Inst1->hasDbgRecords());
EXPECT_TRUE(Inst2->hasDbgRecords());
EXPECT_FALSE(RetInst->hasDbgRecords());
// If we move Inst2 to be after Inst1, then it comes _immediately_ after. Were
// we in dbg.value form we would then have:
// dbg.value
// %b = add
// %c = add
// dbg.value
// Check that this is replicated by DbgVariableRecords.
Inst2->moveAfter(Inst1);
// Inst1 should only have one DbgVariableRecord on it.
EXPECT_TRUE(Inst1->hasDbgRecords());
auto Range1 = Inst1->getDbgRecordRange();
EXPECT_EQ(std::distance(Range1.begin(), Range1.end()), 1u);
// Inst2 should have none.
EXPECT_FALSE(Inst2->hasDbgRecords());
// While the return inst should now have one on it.
EXPECT_TRUE(RetInst->hasDbgRecords());
auto Range2 = RetInst->getDbgRecordRange();
EXPECT_EQ(std::distance(Range2.begin(), Range2.end()), 1u);
M->convertFromNewDbgValues();
UseNewDbgInfoFormat = false;
}
TEST(BasicBlockDbgInfoTest, MarkerOperations) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
// Fetch the entry block,
BasicBlock &BB = M->getFunction("f")->getEntryBlock();
// Convert the module to "new" form debug-info.
M->convertToNewDbgValues();
EXPECT_EQ(BB.size(), 2u);
// Fetch out our two markers,
Instruction *Instr1 = &*BB.begin();
Instruction *Instr2 = Instr1->getNextNode();
DbgMarker *Marker1 = Instr1->DebugMarker;
DbgMarker *Marker2 = Instr2->DebugMarker;
// There's no TrailingDbgRecords marker allocated yet.
DbgMarker *EndMarker = nullptr;
// Check that the "getMarker" utilities operate as expected.
EXPECT_EQ(BB.getMarker(Instr1->getIterator()), Marker1);
EXPECT_EQ(BB.getMarker(Instr2->getIterator()), Marker2);
EXPECT_EQ(BB.getNextMarker(Instr1), Marker2);
EXPECT_EQ(BB.getNextMarker(Instr2), EndMarker); // Is nullptr.
// There should be two DbgVariableRecords,
EXPECT_EQ(Marker1->StoredDbgRecords.size(), 1u);
EXPECT_EQ(Marker2->StoredDbgRecords.size(), 1u);
// Unlink them and try to re-insert them through the basic block.
DbgRecord *DVR1 = &*Marker1->StoredDbgRecords.begin();
DbgRecord *DVR2 = &*Marker2->StoredDbgRecords.begin();
DVR1->removeFromParent();
DVR2->removeFromParent();
EXPECT_TRUE(Marker1->StoredDbgRecords.empty());
EXPECT_TRUE(Marker2->StoredDbgRecords.empty());
// This should appear in Marker1.
BB.insertDbgRecordBefore(DVR1, BB.begin());
EXPECT_EQ(Marker1->StoredDbgRecords.size(), 1u);
EXPECT_EQ(DVR1, &*Marker1->StoredDbgRecords.begin());
// This should attach to Marker2.
BB.insertDbgRecordAfter(DVR2, &*BB.begin());
EXPECT_EQ(Marker2->StoredDbgRecords.size(), 1u);
EXPECT_EQ(DVR2, &*Marker2->StoredDbgRecords.begin());
// Now, how about removing instructions? That should cause any
// DbgVariableRecords to "fall down".
Instr1->removeFromParent();
Marker1 = nullptr;
// DbgVariableRecords should now be in Marker2.
EXPECT_EQ(BB.size(), 1u);
EXPECT_EQ(Marker2->StoredDbgRecords.size(), 2u);
// They should also be in the correct order.
SmallVector<DbgRecord *, 2> DVRs;
for (DbgRecord &DVR : Marker2->getDbgRecordRange())
DVRs.push_back(&DVR);
EXPECT_EQ(DVRs[0], DVR1);
EXPECT_EQ(DVRs[1], DVR2);
// If we remove the end instruction, the DbgVariableRecords should fall down
// into the trailing marker.
EXPECT_EQ(BB.getTrailingDbgRecords(), nullptr);
Instr2->removeFromParent();
EXPECT_TRUE(BB.empty());
EndMarker = BB.getTrailingDbgRecords();
ASSERT_NE(EndMarker, nullptr);
EXPECT_EQ(EndMarker->StoredDbgRecords.size(), 2u);
// Again, these should arrive in the correct order.
DVRs.clear();
for (DbgRecord &DVR : EndMarker->getDbgRecordRange())
DVRs.push_back(&DVR);
EXPECT_EQ(DVRs[0], DVR1);
EXPECT_EQ(DVRs[1], DVR2);
// Inserting a normal instruction at the beginning: shouldn't dislodge the
// DbgVariableRecords. It's intended to not go at the start.
Instr1->insertBefore(BB, BB.begin());
EXPECT_EQ(EndMarker->StoredDbgRecords.size(), 2u);
Instr1->removeFromParent();
// Inserting at end(): should dislodge the DbgVariableRecords, if they were
// dbg.values then they would sit "above" the new instruction.
Instr1->insertBefore(BB, BB.end());
EXPECT_EQ(Instr1->DebugMarker->StoredDbgRecords.size(), 2u);
// We should de-allocate the trailing marker when something is inserted
// at end().
EXPECT_EQ(BB.getTrailingDbgRecords(), nullptr);
// Remove Instr1: now the DbgVariableRecords will fall down again,
Instr1->removeFromParent();
EndMarker = BB.getTrailingDbgRecords();
EXPECT_EQ(EndMarker->StoredDbgRecords.size(), 2u);
// Inserting a terminator, however it's intended, should dislodge the
// trailing DbgVariableRecords, as it's the clear intention of the caller that
// this be the final instr in the block, and DbgVariableRecords aren't allowed
// to live off the end forever.
Instr2->insertBefore(BB, BB.begin());
EXPECT_EQ(Instr2->DebugMarker->StoredDbgRecords.size(), 2u);
EXPECT_EQ(BB.getTrailingDbgRecords(), nullptr);
// Teardown,
Instr1->insertBefore(BB, BB.begin());
UseNewDbgInfoFormat = false;
}
TEST(BasicBlockDbgInfoTest, HeadBitOperations) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%c = add i16 %a, 1, !dbg !11
%d = add i16 %a, 1, !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
// Test that the movement of debug-data when using moveBefore etc and
// insertBefore etc are governed by the "head" bit of iterators.
BasicBlock &BB = M->getFunction("f")->getEntryBlock();
// Convert the module to "new" form debug-info.
M->convertToNewDbgValues();
// Test that the head bit behaves as expected: it should be set when the
// code wants the _start_ of the block, but not otherwise.
EXPECT_TRUE(BB.getFirstInsertionPt().getHeadBit());
BasicBlock::iterator BeginIt = BB.begin();
EXPECT_TRUE(BeginIt.getHeadBit());
// If you launder the instruction pointer through dereferencing and then
// get the iterator again with getIterator, the head bit is lost. This is
// deliberate: if you're calling getIterator, then you're requesting an
// iterator for the position of _this_ instruction, not "the start of this
// block".
BasicBlock::iterator BeginIt2 = BeginIt->getIterator();
EXPECT_FALSE(BeginIt2.getHeadBit());
// Fetch some instruction pointers.
Instruction *BInst = &*BeginIt;
Instruction *CInst = BInst->getNextNode();
Instruction *DInst = CInst->getNextNode();
// CInst should have debug-info.
ASSERT_TRUE(CInst->DebugMarker);
EXPECT_FALSE(CInst->DebugMarker->StoredDbgRecords.empty());
// If we move "c" to the start of the block, just normally, then the
// DbgVariableRecords should fall down to "d".
CInst->moveBefore(BB, BeginIt2);
EXPECT_TRUE(!CInst->DebugMarker ||
CInst->DebugMarker->StoredDbgRecords.empty());
ASSERT_TRUE(DInst->DebugMarker);
EXPECT_FALSE(DInst->DebugMarker->StoredDbgRecords.empty());
// Wheras if we move D to the start of the block with moveBeforePreserving,
// the DbgVariableRecords should move with it.
DInst->moveBeforePreserving(BB, BB.begin());
EXPECT_FALSE(DInst->DebugMarker->StoredDbgRecords.empty());
EXPECT_EQ(&*BB.begin(), DInst);
// Similarly, moveAfterPreserving "D" to "C" should move DbgVariableRecords
// with "D".
DInst->moveAfterPreserving(CInst);
EXPECT_FALSE(DInst->DebugMarker->StoredDbgRecords.empty());
// (move back to the start...)
DInst->moveBeforePreserving(BB, BB.begin());
// Current order of insts: "D -> C -> B -> Ret". DbgVariableRecords on "D".
// If we move "C" to the beginning of the block, it should go before the
// DbgVariableRecords. They'll stay on "D".
CInst->moveBefore(BB, BB.begin());
EXPECT_TRUE(!CInst->DebugMarker ||
CInst->DebugMarker->StoredDbgRecords.empty());
EXPECT_FALSE(DInst->DebugMarker->StoredDbgRecords.empty());
EXPECT_EQ(&*BB.begin(), CInst);
EXPECT_EQ(CInst->getNextNode(), DInst);
// Move back.
CInst->moveBefore(BInst);
EXPECT_EQ(&*BB.begin(), DInst);
// Current order of insts: "D -> C -> B -> Ret". DbgVariableRecords on "D".
// Now move CInst to the position of DInst, but using getIterator instead of
// BasicBlock::begin. This signals that we want the "C" instruction to be
// immediately before "D", with any DbgVariableRecords on "D" now moving to
// "C". It's the equivalent of moving an instruction to the position between a
// run of dbg.values and the next instruction.
CInst->moveBefore(BB, DInst->getIterator());
// CInst gains the DbgVariableRecords.
EXPECT_TRUE(!DInst->DebugMarker ||
DInst->DebugMarker->StoredDbgRecords.empty());
EXPECT_FALSE(CInst->DebugMarker->StoredDbgRecords.empty());
EXPECT_EQ(&*BB.begin(), CInst);
UseNewDbgInfoFormat = false;
}
TEST(BasicBlockDbgInfoTest, InstrDbgAccess) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%c = add i16 %a, 1, !dbg !11
%d = add i16 %a, 1, !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
// Check that DbgVariableRecords can be accessed from Instructions without
// digging into the depths of DbgMarkers.
BasicBlock &BB = M->getFunction("f")->getEntryBlock();
// Convert the module to "new" form debug-info.
M->convertToNewDbgValues();
Instruction *BInst = &*BB.begin();
Instruction *CInst = BInst->getNextNode();
Instruction *DInst = CInst->getNextNode();
ASSERT_FALSE(BInst->DebugMarker);
ASSERT_TRUE(CInst->DebugMarker);
ASSERT_EQ(CInst->DebugMarker->StoredDbgRecords.size(), 1u);
DbgRecord *DVR1 = &*CInst->DebugMarker->StoredDbgRecords.begin();
ASSERT_TRUE(DVR1);
EXPECT_FALSE(BInst->hasDbgRecords());
// Clone DbgVariableRecords from one inst to another. Other arguments to clone
// are tested in DbgMarker test.
auto Range1 = BInst->cloneDebugInfoFrom(CInst);
EXPECT_EQ(BInst->DebugMarker->StoredDbgRecords.size(), 1u);
DbgRecord *DVR2 = &*BInst->DebugMarker->StoredDbgRecords.begin();
EXPECT_EQ(std::distance(Range1.begin(), Range1.end()), 1u);
EXPECT_EQ(&*Range1.begin(), DVR2);
EXPECT_NE(DVR1, DVR2);
// We should be able to get a range over exactly the same information.
auto Range2 = BInst->getDbgRecordRange();
EXPECT_EQ(Range1.begin(), Range2.begin());
EXPECT_EQ(Range1.end(), Range2.end());
// We should be able to query if there are DbgVariableRecords,
EXPECT_TRUE(BInst->hasDbgRecords());
EXPECT_TRUE(CInst->hasDbgRecords());
EXPECT_FALSE(DInst->hasDbgRecords());
// Dropping should be easy,
BInst->dropDbgRecords();
EXPECT_FALSE(BInst->hasDbgRecords());
EXPECT_EQ(BInst->DebugMarker->StoredDbgRecords.size(), 0u);
// And we should be able to drop individual DbgVariableRecords.
CInst->dropOneDbgRecord(DVR1);
EXPECT_FALSE(CInst->hasDbgRecords());
EXPECT_EQ(CInst->DebugMarker->StoredDbgRecords.size(), 0u);
UseNewDbgInfoFormat = false;
}
/* Let's recall the big illustration from BasicBlock::spliceDebugInfo:
Dest
|
this-block: A----A----A ====A----A----A----A---A---A
Src-block ++++B---B---B---B:::C
| |
First Last
in all it's glory. Depending on the bit-configurations for the iterator head
/ tail bits on the three named iterators, there are eight ways for a splice to
occur. To save the amount of thinking needed to pack this into one unit test,
just test the same IR eight times with difference splices. The IR shall be
thus:
define i16 @f(i16 %a) !dbg !6 {
entry:
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
br label %exit, !dbg !11
exit:
call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata !DIExpression()), !dbg !11
%c = add i16 %b, 1, !dbg !11
ret i16 0, !dbg !11
}
The iterators will be:
Dest: exit block, "c" instruction.
First: entry block, "b" instruction.
Last: entry block, branch instruction.
The numbered configurations will be:
| Dest-Head | First-Head | Last-tail
----+----------------+----------------+------------
0 | false | false | false
1 | true | false | false
2 | false | true | false
3 | true | true | false
4 | false | false | true
5 | true | false | true
6 | false | true | true
7 | true | true | true
Each numbered test scenario will also have a short explanation indicating what
this bit configuration represents.
*/
static const std::string SpliceTestIR = R"(
define i16 @f(i16 %a) !dbg !6 {
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
br label %exit, !dbg !11
exit:
call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata !DIExpression()), !dbg !11
%c = add i16 %b, 1, !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)";
class DbgSpliceTest : public ::testing::Test {
protected:
LLVMContext C;
std::unique_ptr<Module> M;
BasicBlock *BBEntry, *BBExit;
BasicBlock::iterator Dest, First, Last;
Instruction *BInst, *Branch, *CInst;
DbgVariableRecord *DVRA, *DVRB, *DVRConst;
void SetUp() override {
UseNewDbgInfoFormat = true;
M = parseIR(C, SpliceTestIR.c_str());
M->convertToNewDbgValues();
BBEntry = &M->getFunction("f")->getEntryBlock();
BBExit = BBEntry->getNextNode();
Dest = BBExit->begin();
First = BBEntry->begin();
Last = BBEntry->getTerminator()->getIterator();
BInst = &*First;
Branch = &*Last;
CInst = &*Dest;
DVRA =
cast<DbgVariableRecord>(&*BInst->DebugMarker->StoredDbgRecords.begin());
DVRB = cast<DbgVariableRecord>(
&*Branch->DebugMarker->StoredDbgRecords.begin());
DVRConst =
cast<DbgVariableRecord>(&*CInst->DebugMarker->StoredDbgRecords.begin());
}
void TearDown() override { UseNewDbgInfoFormat = false; }
bool InstContainsDbgVariableRecord(Instruction *I, DbgVariableRecord *DVR) {
for (DbgRecord &D : I->getDbgRecordRange()) {
if (&D == DVR) {
// Confirm too that the links between the records are correct.
EXPECT_EQ(DVR->Marker, I->DebugMarker);
EXPECT_EQ(I->DebugMarker->MarkedInstr, I);
return true;
}
}
return false;
}
bool CheckDVROrder(Instruction *I,
SmallVector<DbgVariableRecord *> CheckVals) {
SmallVector<DbgRecord *> Vals;
for (DbgRecord &D : I->getDbgRecordRange())
Vals.push_back(&D);
EXPECT_EQ(Vals.size(), CheckVals.size());
if (Vals.size() != CheckVals.size())
return false;
for (unsigned int I = 0; I < Vals.size(); ++I) {
EXPECT_EQ(Vals[I], CheckVals[I]);
// Provide another expectation failure to let us localise what goes wrong,
// by returning a flag to the caller.
if (Vals[I] != CheckVals[I])
return false;
}
return true;
}
};
TEST_F(DbgSpliceTest, DbgSpliceTest0) {
Dest.setHeadBit(false);
First.setHeadBit(false);
Last.setTailBit(false);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from First, not including leading dbg.value, to Last, including the
trailing dbg.value. Place at Dest, between the constant dbg.value and %c.
%b, and the following dbg.value, should move, to:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on Dest, in exit block.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRB));
// DVRA, should have "fallen" onto the branch, remained in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRA));
// DVRConst should be on the moved %b instruction.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRConst));
}
TEST_F(DbgSpliceTest, DbgSpliceTest1) {
Dest.setHeadBit(true);
First.setHeadBit(false);
Last.setTailBit(false);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from First, not including leading dbg.value, to Last, including the
trailing dbg.value. Place at the head of Dest, i.e. at the very start of
BBExit, before any debug-info there. Becomes:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 Last br label %exit, !dbg !11
BBExit exit:
First %b = add i16 %a, 1, !dbg !11
DVRB call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata
!DIExpression()), !dbg !11 DVRConst call void @llvm.dbg.value(metadata i16 0,
metadata !9, metadata !DIExpression()), !dbg !11 Dest %c = add i16 %b, 1,
!dbg !11 ret i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on CInst, in exit block.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRB));
// DVRA, should have "fallen" onto the branch, remained in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRA));
// DVRConst should be behind / after the moved instructions, remain on CInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRConst));
// Order of DVRB and DVRConst should be thus:
EXPECT_TRUE(CheckDVROrder(CInst, {DVRB, DVRConst}));
}
TEST_F(DbgSpliceTest, DbgSpliceTest2) {
Dest.setHeadBit(false);
First.setHeadBit(true);
Last.setTailBit(false);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from head of First, which includes the leading dbg.value, to Last,
including the trailing dbg.value. Place in front of Dest, but after any
debug-info there. Becomes:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 DVRA call void @llvm.dbg.value(metadata i16 %a,
metadata !9, metadata !DIExpression()), !dbg !11 First %b = add i16 %a, 1,
!dbg !11 DVRB call void @llvm.dbg.value(metadata i16 %b, metadata !9,
metadata !DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret
i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
// DVRB: should be on CInst, in exit block.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRB));
// DVRA, should have transferred with the spliced instructions, remains on
// the "b" inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRA));
// DVRConst should be ahead of the moved instructions, ahead of BInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRConst));
// Order of DVRA and DVRConst should be thus:
EXPECT_TRUE(CheckDVROrder(BInst, {DVRConst, DVRA}));
}
TEST_F(DbgSpliceTest, DbgSpliceTest3) {
Dest.setHeadBit(true);
First.setHeadBit(true);
Last.setTailBit(false);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from head of First, which includes the leading dbg.value, to Last,
including the trailing dbg.value. Place at head of Dest, before any
debug-info there. Becomes:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
Last br label %exit, !dbg !11
BBExit exit:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9,
metadata !DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret
i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
// DVRB: should be on CInst, in exit block.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRB));
// DVRA, should have transferred with the spliced instructions, remains on
// the "b" inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRA));
// DVRConst should be behind the moved instructions, ahead of CInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRConst));
// Order of DVRB and DVRConst should be thus:
EXPECT_TRUE(CheckDVROrder(CInst, {DVRB, DVRConst}));
}
TEST_F(DbgSpliceTest, DbgSpliceTest4) {
Dest.setHeadBit(false);
First.setHeadBit(false);
Last.setTailBit(true);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from First, not including the leading dbg.value, to Last, but NOT
including the trailing dbg.value because the tail bit is set. Place at Dest,
after any debug-info there. Becomes:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 DVRB call void @llvm.dbg.value(metadata i16 %b,
metadata !9, metadata !DIExpression()), !dbg !11 Last br label %exit, !dbg
!11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 Dest %c =
add i16 %b, 1, !dbg !11 ret i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on Branch as before, remain in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRB));
// DVRA, should have remained in entry block, falls onto Branch inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRA));
// DVRConst should be ahead of the moved instructions, BInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRConst));
// Order of DVRA and DVRA should be thus:
EXPECT_TRUE(CheckDVROrder(Branch, {DVRA, DVRB}));
}
TEST_F(DbgSpliceTest, DbgSpliceTest5) {
Dest.setHeadBit(true);
First.setHeadBit(false);
Last.setTailBit(true);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from First, not including the leading dbg.value, to Last, but NOT
including the trailing dbg.value because the tail bit is set. Place at head
of Dest, before any debug-info there. Becomes:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 DVRB call void @llvm.dbg.value(metadata i16 %b,
metadata !9, metadata !DIExpression()), !dbg !11 Last br label %exit, !dbg
!11
BBExit exit:
First %b = add i16 %a, 1, !dbg !11
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on Branch as before, remain in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRB));
// DVRA, should have remained in entry block, falls onto Branch inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRA));
// DVRConst should be behind of the moved instructions, on CInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRConst));
// Order of DVRA and DVRB should be thus:
EXPECT_TRUE(CheckDVROrder(Branch, {DVRA, DVRB}));
}
TEST_F(DbgSpliceTest, DbgSpliceTest6) {
Dest.setHeadBit(false);
First.setHeadBit(true);
Last.setTailBit(true);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from First, including the leading dbg.value, to Last, but NOT
including the trailing dbg.value because the tail bit is set. Place at Dest,
after any debug-info there. Becomes:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRB call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata
!DIExpression()), !dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 DVRA call void @llvm.dbg.value(metadata i16 %a,
metadata !9, metadata !DIExpression()), !dbg !11 First %b = add i16 %a, 1,
!dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on Branch as before, remain in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRB));
// DVRA, should have transferred to BBExit, on B inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRA));
// DVRConst should be ahead of the moved instructions, on BInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRConst));
// Order of DVRA and DVRConst should be thus:
EXPECT_TRUE(CheckDVROrder(BInst, {DVRConst, DVRA}));
}
TEST_F(DbgSpliceTest, DbgSpliceTest7) {
Dest.setHeadBit(true);
First.setHeadBit(true);
Last.setTailBit(true);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from First, including the leading dbg.value, to Last, but NOT
including the trailing dbg.value because the tail bit is set. Place at head
of Dest, before any debug-info there. Becomes:
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRB call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata
!DIExpression()), !dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRConst call
void @llvm.dbg.value(metadata i16 0, metadata !9, metadata !DIExpression()),
!dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, First, Last);
EXPECT_EQ(BInst->getParent(), BBExit);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on Branch as before, remain in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRB));
// DVRA, should have transferred to BBExit, on B inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(BInst, DVRA));
// DVRConst should be after of the moved instructions, on CInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRConst));
}
// But wait, there's more! What if you splice a range that is empty, but
// implicitly contains debug-info? In the dbg.value design for debug-info,
// this would be an explicit range, but in DbgVariableRecord debug-info, it
// isn't. Check that if we try to do that, with differing head-bit values, that
// DbgVariableRecords are transferred.
// Test with empty transfers to Dest, with head bit set and not set.
TEST_F(DbgSpliceTest, DbgSpliceEmpty0) {
Dest.setHeadBit(false);
First.setHeadBit(false);
Last.setHeadBit(false);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from BBEntry.getFirstInsertionPt to First -- this implicitly is a
splice of DVRA, but the iterators are pointing at the same instruction. The
only difference is the setting of the head bit. Becomes;
define i16 @f(i16 %a) !dbg !6 {
First %b = add i16 %a, 1, !dbg !11
DVRB call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata
!DIExpression()), !dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 DVRA call void @llvm.dbg.value(metadata i16 %a,
metadata !9, metadata !DIExpression()), !dbg !11 Dest %c = add i16 %b, 1,
!dbg !11 ret i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, BBEntry->getFirstInsertionPt(), First);
EXPECT_EQ(BInst->getParent(), BBEntry);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on Branch as before, remain in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRB));
// DVRA, should have transferred to BBExit, on C inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRA));
// DVRConst should be ahead of the moved DbgVariableRecord, on CInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRConst));
// Order of DVRA and DVRConst should be thus:
EXPECT_TRUE(CheckDVROrder(CInst, {DVRConst, DVRA}));
}
TEST_F(DbgSpliceTest, DbgSpliceEmpty1) {
Dest.setHeadBit(true);
First.setHeadBit(false);
Last.setHeadBit(false);
/*
define i16 @f(i16 %a) !dbg !6 {
BBEntry entry:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 First %b = add i16 %a, 1, !dbg !11 DVRB call
void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()),
!dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRConst call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata
!DIExpression()), !dbg !11 Dest %c = add i16 %b, 1, !dbg !11 ret i16 0,
!dbg !11
}
Splice from BBEntry.getFirstInsertionPt to First -- this implicitly is a
splice of DVRA, but the iterators are pointing at the same instruction. The
only difference is the setting of the head bit. Insert at head of Dest,
i.e. before DVRConst. Becomes;
define i16 @f(i16 %a) !dbg !6 {
First %b = add i16 %a, 1, !dbg !11
DVRB call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata
!DIExpression()), !dbg !11 Last br label %exit, !dbg !11
BBExit exit:
DVRA call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata
!DIExpression()), !dbg !11 DVRConst call void @llvm.dbg.value(metadata i16 0,
metadata !9, metadata !DIExpression()), !dbg !11 Dest %c = add i16 %b, 1,
!dbg !11 ret i16 0, !dbg !11
}
*/
BBExit->splice(Dest, BBEntry, BBEntry->getFirstInsertionPt(), First);
EXPECT_EQ(BInst->getParent(), BBEntry);
EXPECT_EQ(CInst->getParent(), BBExit);
EXPECT_EQ(Branch->getParent(), BBEntry);
// DVRB: should be on Branch as before, remain in entry block.
EXPECT_TRUE(InstContainsDbgVariableRecord(Branch, DVRB));
// DVRA, should have transferred to BBExit, on C inst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRA));
// DVRConst should be ahead of the moved DbgVariableRecord, on CInst.
EXPECT_TRUE(InstContainsDbgVariableRecord(CInst, DVRConst));
// Order of DVRA and DVRConst should be thus:
EXPECT_TRUE(CheckDVROrder(CInst, {DVRA, DVRConst}));
}
// If we splice new instructions into a block with trailing DbgVariableRecords,
// then the trailing DbgVariableRecords should get flushed back out.
TEST(BasicBlockDbgInfoTest, DbgSpliceTrailing) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
entry:
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
br label %exit
exit:
%b = add i16 %a, 1, !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
BasicBlock &Entry = M->getFunction("f")->getEntryBlock();
BasicBlock &Exit = *Entry.getNextNode();
M->convertToNewDbgValues();
// Begin by forcing entry block to have dangling DbgVariableRecord.
Entry.getTerminator()->eraseFromParent();
ASSERT_NE(Entry.getTrailingDbgRecords(), nullptr);
EXPECT_TRUE(Entry.empty());
// Now transfer the entire contents of the exit block into the entry.
Entry.splice(Entry.end(), &Exit, Exit.begin(), Exit.end());
// The trailing DbgVariableRecord should have been placed at the front of
// what's been spliced in.
Instruction *BInst = &*Entry.begin();
ASSERT_TRUE(BInst->DebugMarker);
EXPECT_EQ(BInst->DebugMarker->StoredDbgRecords.size(), 1u);
UseNewDbgInfoFormat = false;
}
// When we remove instructions from the program, adjacent DbgVariableRecords
// coalesce together into one DbgMarker. In "old" dbg.value mode you could
// re-insert the removed instruction back into the middle of a sequence of
// dbg.values. Test that this can be replicated correctly by DbgVariableRecords
TEST(BasicBlockDbgInfoTest, RemoveInstAndReinsert) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
entry:
%qux = sub i16 %a, 0
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%foo = add i16 %a, %a
call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata !DIExpression()), !dbg !11
ret i16 1
}
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
BasicBlock &Entry = M->getFunction("f")->getEntryBlock();
M->convertToNewDbgValues();
// Fetch the relevant instructions from the converted function.
Instruction *SubInst = &*Entry.begin();
ASSERT_TRUE(isa<BinaryOperator>(SubInst));
Instruction *AddInst = SubInst->getNextNode();
ASSERT_TRUE(isa<BinaryOperator>(AddInst));
Instruction *RetInst = AddInst->getNextNode();
ASSERT_TRUE(isa<ReturnInst>(RetInst));
// add and sub should both have one DbgVariableRecord on add and ret.
EXPECT_FALSE(SubInst->hasDbgRecords());
EXPECT_TRUE(AddInst->hasDbgRecords());
EXPECT_TRUE(RetInst->hasDbgRecords());
auto R1 = AddInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R1.begin(), R1.end()), 1u);
auto R2 = RetInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R2.begin(), R2.end()), 1u);
// The Supported (TM) code sequence for removing then reinserting insts
// after another instruction:
std::optional<DbgVariableRecord::self_iterator> Pos =
AddInst->getDbgReinsertionPosition();
AddInst->removeFromParent();
// We should have a re-insertion position.
ASSERT_TRUE(Pos);
// Both DbgVariableRecords should now be attached to the ret inst.
auto R3 = RetInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R3.begin(), R3.end()), 2u);
// Re-insert and re-insert.
AddInst->insertAfter(SubInst);
Entry.reinsertInstInDbgRecords(AddInst, Pos);
// We should be back into a position of having one DbgVariableRecord on add
// and ret.
EXPECT_FALSE(SubInst->hasDbgRecords());
EXPECT_TRUE(AddInst->hasDbgRecords());
EXPECT_TRUE(RetInst->hasDbgRecords());
auto R4 = AddInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R4.begin(), R4.end()), 1u);
auto R5 = RetInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R5.begin(), R5.end()), 1u);
UseNewDbgInfoFormat = false;
}
// Test instruction removal and re-insertion, this time with one
// DbgVariableRecord that should hop up one instruction.
TEST(BasicBlockDbgInfoTest, RemoveInstAndReinsertForOneDbgVariableRecord) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
entry:
%qux = sub i16 %a, 0
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
%foo = add i16 %a, %a
ret i16 1
}
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
BasicBlock &Entry = M->getFunction("f")->getEntryBlock();
M->convertToNewDbgValues();
// Fetch the relevant instructions from the converted function.
Instruction *SubInst = &*Entry.begin();
ASSERT_TRUE(isa<BinaryOperator>(SubInst));
Instruction *AddInst = SubInst->getNextNode();
ASSERT_TRUE(isa<BinaryOperator>(AddInst));
Instruction *RetInst = AddInst->getNextNode();
ASSERT_TRUE(isa<ReturnInst>(RetInst));
// There should be one DbgVariableRecord.
EXPECT_FALSE(SubInst->hasDbgRecords());
EXPECT_TRUE(AddInst->hasDbgRecords());
EXPECT_FALSE(RetInst->hasDbgRecords());
auto R1 = AddInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R1.begin(), R1.end()), 1u);
// The Supported (TM) code sequence for removing then reinserting insts:
std::optional<DbgVariableRecord::self_iterator> Pos =
AddInst->getDbgReinsertionPosition();
AddInst->removeFromParent();
// No re-insertion position as there were no DbgVariableRecords on the ret.
ASSERT_FALSE(Pos);
// The single DbgVariableRecord should now be attached to the ret inst.
EXPECT_TRUE(RetInst->hasDbgRecords());
auto R2 = RetInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R2.begin(), R2.end()), 1u);
// Re-insert and re-insert.
AddInst->insertAfter(SubInst);
Entry.reinsertInstInDbgRecords(AddInst, Pos);
// We should be back into a position of having one DbgVariableRecord on the
// AddInst.
EXPECT_FALSE(SubInst->hasDbgRecords());
EXPECT_TRUE(AddInst->hasDbgRecords());
EXPECT_FALSE(RetInst->hasDbgRecords());
auto R3 = AddInst->getDbgRecordRange();
EXPECT_EQ(std::distance(R3.begin(), R3.end()), 1u);
UseNewDbgInfoFormat = false;
}
// Similar to the above, what if we splice into an empty block with debug-info,
// with debug-info at the start of the moving range, that we intend to be
// transferred. The dbg.value of %a should remain at the start, but come ahead
// of the i16 0 dbg.value.
TEST(BasicBlockDbgInfoTest, DbgSpliceToEmpty1) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
entry:
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
br label %exit
exit:
call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata !DIExpression()), !dbg !11
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 1, metadata !9, metadata !DIExpression()), !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
Function &F = *M->getFunction("f");
BasicBlock &Entry = F.getEntryBlock();
BasicBlock &Exit = *Entry.getNextNode();
M->convertToNewDbgValues();
// Begin by forcing entry block to have dangling DbgVariableRecord.
Entry.getTerminator()->eraseFromParent();
ASSERT_NE(Entry.getTrailingDbgRecords(), nullptr);
EXPECT_TRUE(Entry.empty());
// Now transfer the entire contents of the exit block into the entry. This
// includes both dbg.values.
Entry.splice(Entry.end(), &Exit, Exit.begin(), Exit.end());
// We should now have two dbg.values on the first instruction, and they
// should be in the correct order of %a, then 0.
Instruction *BInst = &*Entry.begin();
ASSERT_TRUE(BInst->hasDbgRecords());
EXPECT_EQ(BInst->DebugMarker->StoredDbgRecords.size(), 2u);
SmallVector<DbgVariableRecord *, 2> DbgVariableRecords;
for (DbgRecord &DVR : BInst->getDbgRecordRange())
DbgVariableRecords.push_back(cast<DbgVariableRecord>(&DVR));
EXPECT_EQ(DbgVariableRecords[0]->getVariableLocationOp(0), F.getArg(0));
Value *SecondDVRValue = DbgVariableRecords[1]->getVariableLocationOp(0);
ASSERT_TRUE(isa<ConstantInt>(SecondDVRValue));
EXPECT_EQ(cast<ConstantInt>(SecondDVRValue)->getZExtValue(), 0ull);
// No trailing DbgVariableRecords in the entry block now.
EXPECT_EQ(Entry.getTrailingDbgRecords(), nullptr);
UseNewDbgInfoFormat = false;
}
// Similar test again, but this time: splice the contents of exit into entry,
// with the intention of leaving the first dbg.value (i16 0) behind.
TEST(BasicBlockDbgInfoTest, DbgSpliceToEmpty2) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
entry:
call void @llvm.dbg.value(metadata i16 %a, metadata !9, metadata !DIExpression()), !dbg !11
br label %exit
exit:
call void @llvm.dbg.value(metadata i16 0, metadata !9, metadata !DIExpression()), !dbg !11
%b = add i16 %a, 1, !dbg !11
call void @llvm.dbg.value(metadata i16 1, metadata !9, metadata !DIExpression()), !dbg !11
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
Function &F = *M->getFunction("f");
BasicBlock &Entry = F.getEntryBlock();
BasicBlock &Exit = *Entry.getNextNode();
M->convertToNewDbgValues();
// Begin by forcing entry block to have dangling DbgVariableRecord.
Entry.getTerminator()->eraseFromParent();
ASSERT_NE(Entry.getTrailingDbgRecords(), nullptr);
EXPECT_TRUE(Entry.empty());
// Now transfer into the entry block -- fetching the first instruction with
// begin and then calling getIterator clears the "head" bit, meaning that the
// range to move will not include any leading DbgVariableRecords.
Entry.splice(Entry.end(), &Exit, Exit.begin()->getIterator(), Exit.end());
// We should now have one dbg.values on the first instruction, %a.
Instruction *BInst = &*Entry.begin();
ASSERT_TRUE(BInst->hasDbgRecords());
EXPECT_EQ(BInst->DebugMarker->StoredDbgRecords.size(), 1u);
SmallVector<DbgVariableRecord *, 2> DbgVariableRecords;
for (DbgRecord &DVR : BInst->getDbgRecordRange())
DbgVariableRecords.push_back(cast<DbgVariableRecord>(&DVR));
EXPECT_EQ(DbgVariableRecords[0]->getVariableLocationOp(0), F.getArg(0));
// No trailing DbgVariableRecords in the entry block now.
EXPECT_EQ(Entry.getTrailingDbgRecords(), nullptr);
// We should have nothing left in the exit block...
EXPECT_TRUE(Exit.empty());
// ... except for some dangling DbgVariableRecords.
EXPECT_NE(Exit.getTrailingDbgRecords(), nullptr);
EXPECT_FALSE(Exit.getTrailingDbgRecords()->empty());
Exit.getTrailingDbgRecords()->eraseFromParent();
Exit.deleteTrailingDbgRecords();
UseNewDbgInfoFormat = false;
}
// What if we moveBefore end() -- there might be no debug-info there, in which
// case we shouldn't crash.
TEST(BasicBlockDbgInfoTest, DbgMoveToEnd) {
LLVMContext C;
UseNewDbgInfoFormat = true;
std::unique_ptr<Module> M = parseIR(C, R"(
define i16 @f(i16 %a) !dbg !6 {
entry:
br label %exit
exit:
ret i16 0, !dbg !11
}
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.ll", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
)");
Function &F = *M->getFunction("f");
BasicBlock &Entry = F.getEntryBlock();
BasicBlock &Exit = *Entry.getNextNode();
M->convertToNewDbgValues();
// Move the return to the end of the entry block.
Instruction *Br = Entry.getTerminator();
Instruction *Ret = Exit.getTerminator();
EXPECT_EQ(Entry.getTrailingDbgRecords(), nullptr);
Ret->moveBefore(Entry, Entry.end());
Br->eraseFromParent();
// There should continue to not be any debug-info anywhere.
EXPECT_EQ(Entry.getTrailingDbgRecords(), nullptr);
EXPECT_EQ(Exit.getTrailingDbgRecords(), nullptr);
EXPECT_FALSE(Ret->hasDbgRecords());
UseNewDbgInfoFormat = false;
}
} // End anonymous namespace.