2011-02-20 16:38:20 +08:00
//===- Local.cpp - Unit tests for Local -----------------------------------===//
//
2019-01-19 16:50:56 +08:00
// 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
2011-02-20 16:38:20 +08:00
//
//===----------------------------------------------------------------------===//
2018-06-05 05:23:21 +08:00
# include "llvm/Transforms/Utils/Local.h"
2019-02-06 10:52:52 +08:00
# include "llvm/Analysis/DomTreeUpdater.h"
2018-08-03 13:08:17 +08:00
# include "llvm/Analysis/PostDominators.h"
2019-12-08 00:47:00 +08:00
# include "llvm/Analysis/TargetTransformInfo.h"
2017-09-22 03:52:03 +08:00
# include "llvm/AsmParser/Parser.h"
2013-01-02 19:36:10 +08:00
# include "llvm/IR/BasicBlock.h"
2017-09-22 03:52:03 +08:00
# include "llvm/IR/DIBuilder.h"
2013-01-02 19:36:10 +08:00
# include "llvm/IR/IRBuilder.h"
# include "llvm/IR/Instructions.h"
2017-09-22 03:52:03 +08:00
# include "llvm/IR/IntrinsicInst.h"
2013-01-02 19:36:10 +08:00
# include "llvm/IR/LLVMContext.h"
2018-07-07 01:32:39 +08:00
# include "llvm/IR/Verifier.h"
2017-09-22 03:52:03 +08:00
# include "llvm/Support/SourceMgr.h"
2012-06-29 20:38:19 +08:00
# include "gtest/gtest.h"
2011-02-20 16:38:20 +08:00
using namespace llvm ;
TEST ( Local , RecursivelyDeleteDeadPHINodes ) {
2016-04-15 05:59:01 +08:00
LLVMContext C ;
2011-02-20 16:38:20 +08:00
IRBuilder < > builder ( C ) ;
// Make blocks
BasicBlock * bb0 = BasicBlock : : Create ( C ) ;
BasicBlock * bb1 = BasicBlock : : Create ( C ) ;
builder . SetInsertPoint ( bb0 ) ;
2011-03-30 19:28:46 +08:00
PHINode * phi = builder . CreatePHI ( Type : : getInt32Ty ( C ) , 2 ) ;
2011-02-20 16:38:20 +08:00
BranchInst * br0 = builder . CreateCondBr ( builder . getTrue ( ) , bb0 , bb1 ) ;
builder . SetInsertPoint ( bb1 ) ;
BranchInst * br1 = builder . CreateBr ( bb0 ) ;
phi - > addIncoming ( phi , bb0 ) ;
phi - > addIncoming ( phi , bb1 ) ;
// The PHI will be removed
EXPECT_TRUE ( RecursivelyDeleteDeadPHINode ( phi ) ) ;
// Make sure the blocks only contain the branches
EXPECT_EQ ( & bb0 - > front ( ) , br0 ) ;
EXPECT_EQ ( & bb1 - > front ( ) , br1 ) ;
2011-02-21 02:05:56 +08:00
builder . SetInsertPoint ( bb0 ) ;
2011-03-30 19:28:46 +08:00
phi = builder . CreatePHI ( Type : : getInt32Ty ( C ) , 0 ) ;
2011-02-21 02:05:56 +08:00
EXPECT_TRUE ( RecursivelyDeleteDeadPHINode ( phi ) ) ;
2011-02-22 00:27:36 +08:00
builder . SetInsertPoint ( bb0 ) ;
2011-03-30 19:28:46 +08:00
phi = builder . CreatePHI ( Type : : getInt32Ty ( C ) , 0 ) ;
2011-02-22 00:27:36 +08:00
builder . CreateAdd ( phi , phi ) ;
EXPECT_TRUE ( RecursivelyDeleteDeadPHINode ( phi ) ) ;
2011-02-20 16:38:20 +08:00
bb0 - > dropAllReferences ( ) ;
bb1 - > dropAllReferences ( ) ;
delete bb0 ;
delete bb1 ;
}
[RemoveDuplicatePHINodes] Start over after removing a PHI.
This makes RemoveDuplicatePHINodes more effective and fixes an assertion
failure. Triggering the assertions requires a DenseSet reallocation
so this change only contains a constructive test.
I'll explain the issue with a small example. In the following function
there's a duplicate PHI, %4 and %5 are identical. When this is found
the DenseSet in RemoveDuplicatePHINodes contains %2, %3 and %4.
define void @F() {
br label %1
; <label>:1 ; preds = %1, %0
%2 = phi i32 [ 42, %0 ], [ %4, %1 ]
%3 = phi i32 [ 42, %0 ], [ %5, %1 ]
%4 = phi i32 [ 42, %0 ], [ 23, %1 ]
%5 = phi i32 [ 42, %0 ], [ 23, %1 ]
br label %1
}
after RemoveDuplicatePHINodes runs the function looks like this. %3 has
changed and is now identical to %2, but RemoveDuplicatePHINodes never
saw this.
define void @F() {
br label %1
; <label>:1 ; preds = %1, %0
%2 = phi i32 [ 42, %0 ], [ %4, %1 ]
%3 = phi i32 [ 42, %0 ], [ %4, %1 ]
%4 = phi i32 [ 42, %0 ], [ 23, %1 ]
br label %1
}
If the DenseSet does a reallocation now it will reinsert all
keys and stumble over %3 now having a different hash value than it had
when inserted into the map for the first time. This change clears the
set whenever a PHI is deleted and starts the progress from the
beginning, allowing %3 to be deleted and avoiding inconsistent DenseSet
state. This potentially has a negative performance impact because
it rescans all PHIs, but I don't think that this ever makes a difference
in practice.
llvm-svn: 246694
2015-09-03 03:52:23 +08:00
TEST ( Local , RemoveDuplicatePHINodes ) {
2016-04-15 05:59:01 +08:00
LLVMContext C ;
[RemoveDuplicatePHINodes] Start over after removing a PHI.
This makes RemoveDuplicatePHINodes more effective and fixes an assertion
failure. Triggering the assertions requires a DenseSet reallocation
so this change only contains a constructive test.
I'll explain the issue with a small example. In the following function
there's a duplicate PHI, %4 and %5 are identical. When this is found
the DenseSet in RemoveDuplicatePHINodes contains %2, %3 and %4.
define void @F() {
br label %1
; <label>:1 ; preds = %1, %0
%2 = phi i32 [ 42, %0 ], [ %4, %1 ]
%3 = phi i32 [ 42, %0 ], [ %5, %1 ]
%4 = phi i32 [ 42, %0 ], [ 23, %1 ]
%5 = phi i32 [ 42, %0 ], [ 23, %1 ]
br label %1
}
after RemoveDuplicatePHINodes runs the function looks like this. %3 has
changed and is now identical to %2, but RemoveDuplicatePHINodes never
saw this.
define void @F() {
br label %1
; <label>:1 ; preds = %1, %0
%2 = phi i32 [ 42, %0 ], [ %4, %1 ]
%3 = phi i32 [ 42, %0 ], [ %4, %1 ]
%4 = phi i32 [ 42, %0 ], [ 23, %1 ]
br label %1
}
If the DenseSet does a reallocation now it will reinsert all
keys and stumble over %3 now having a different hash value than it had
when inserted into the map for the first time. This change clears the
set whenever a PHI is deleted and starts the progress from the
beginning, allowing %3 to be deleted and avoiding inconsistent DenseSet
state. This potentially has a negative performance impact because
it rescans all PHIs, but I don't think that this ever makes a difference
in practice.
llvm-svn: 246694
2015-09-03 03:52:23 +08:00
IRBuilder < > B ( C ) ;
std : : unique_ptr < Function > F (
Function : : Create ( FunctionType : : get ( B . getVoidTy ( ) , false ) ,
GlobalValue : : ExternalLinkage , " F " ) ) ;
BasicBlock * Entry ( BasicBlock : : Create ( C , " " , F . get ( ) ) ) ;
BasicBlock * BB ( BasicBlock : : Create ( C , " " , F . get ( ) ) ) ;
BranchInst : : Create ( BB , Entry ) ;
B . SetInsertPoint ( BB ) ;
AssertingVH < PHINode > P1 = B . CreatePHI ( Type : : getInt32Ty ( C ) , 2 ) ;
P1 - > addIncoming ( B . getInt32 ( 42 ) , Entry ) ;
PHINode * P2 = B . CreatePHI ( Type : : getInt32Ty ( C ) , 2 ) ;
P2 - > addIncoming ( B . getInt32 ( 42 ) , Entry ) ;
AssertingVH < PHINode > P3 = B . CreatePHI ( Type : : getInt32Ty ( C ) , 2 ) ;
P3 - > addIncoming ( B . getInt32 ( 42 ) , Entry ) ;
P3 - > addIncoming ( B . getInt32 ( 23 ) , BB ) ;
PHINode * P4 = B . CreatePHI ( Type : : getInt32Ty ( C ) , 2 ) ;
P4 - > addIncoming ( B . getInt32 ( 42 ) , Entry ) ;
P4 - > addIncoming ( B . getInt32 ( 23 ) , BB ) ;
P1 - > addIncoming ( P3 , BB ) ;
P2 - > addIncoming ( P4 , BB ) ;
BranchInst : : Create ( BB , BB ) ;
// Verify that we can eliminate PHIs that become duplicates after chaning PHIs
// downstream.
EXPECT_TRUE ( EliminateDuplicatePHINodes ( BB ) ) ;
EXPECT_EQ ( 3U , BB - > size ( ) ) ;
}
2017-09-22 03:52:03 +08:00
2018-02-01 06:54:37 +08:00
static std : : unique_ptr < Module > parseIR ( LLVMContext & C , const char * IR ) {
2017-09-22 03:52:03 +08:00
SMDiagnostic Err ;
std : : unique_ptr < Module > Mod = parseAssemblyString ( IR , Err , C ) ;
if ( ! Mod )
Err . print ( " UtilsTests " , errs ( ) ) ;
return Mod ;
}
TEST ( Local , ReplaceDbgDeclare ) {
LLVMContext C ;
// Original C source to get debug info for a local variable:
// void f() { int x; }
2018-03-03 05:36:33 +08:00
std : : unique_ptr < Module > M = parseIR ( C ,
R " (
define void @ f ( ) ! dbg ! 8 {
entry :
% x = alloca i32 , align 4
call void @ llvm . dbg . declare ( metadata i32 * % x , metadata ! 11 , metadata ! DIExpression ( ) ) , ! dbg ! 13
call void @ llvm . dbg . declare ( metadata i32 * % x , metadata ! 11 , metadata ! DIExpression ( ) ) , ! dbg ! 13
ret void , ! dbg ! 14
}
declare void @ llvm . dbg . declare ( metadata , metadata , metadata )
! 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 }
[DebugInfo] Add DILabel metadata and intrinsic llvm.dbg.label.
In order to set breakpoints on labels and list source code around
labels, we need collect debug information for labels, i.e., label
name, the function label belong, line number in the file, and the
address label located. In order to keep these information in LLVM
IR and to allow backend to generate debug information correctly.
We create a new kind of metadata for labels, DILabel. The format
of DILabel is
!DILabel(scope: !1, name: "foo", file: !2, line: 3)
We hope to keep debug information as much as possible even the
code is optimized. So, we create a new kind of intrinsic for label
metadata to avoid the metadata is eliminated with basic block.
The intrinsic will keep existing if we keep it from optimized out.
The format of the intrinsic is
llvm.dbg.label(metadata !1)
It has only one argument, that is the DILabel metadata. The
intrinsic will follow the label immediately. Backend could get the
label metadata through the intrinsic's parameter.
We also create DIBuilder API for labels to be used by Frontend.
Frontend could use createLabel() to allocate DILabel objects, and use
insertLabel() to insert llvm.dbg.label intrinsic in LLVM IR.
Differential Revision: https://reviews.llvm.org/D45024
Patch by Hsiangkai Wang.
llvm-svn: 331841
2018-05-09 10:40:45 +08:00
! 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 )
2018-03-03 05:36:33 +08:00
! 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 )
) " );
2017-09-22 03:52:03 +08:00
auto * GV = M - > getNamedValue ( " f " ) ;
ASSERT_TRUE ( GV ) ;
auto * F = dyn_cast < Function > ( GV ) ;
ASSERT_TRUE ( F ) ;
Instruction * Inst = & F - > front ( ) . front ( ) ;
auto * AI = dyn_cast < AllocaInst > ( Inst ) ;
ASSERT_TRUE ( AI ) ;
Inst = Inst - > getNextNode ( ) - > getNextNode ( ) ;
ASSERT_TRUE ( Inst ) ;
auto * DII = dyn_cast < DbgDeclareInst > ( Inst ) ;
ASSERT_TRUE ( DII ) ;
Value * NewBase = Constant : : getNullValue ( Type : : getInt32PtrTy ( C ) ) ;
DIBuilder DIB ( * M ) ;
2020-02-11 07:37:56 +08:00
replaceDbgDeclare ( AI , NewBase , DIB , DIExpression : : ApplyOffset , 0 ) ;
2017-09-22 03:52:03 +08:00
// There should be exactly two dbg.declares.
int Declares = 0 ;
for ( const Instruction & I : F - > front ( ) )
if ( isa < DbgDeclareInst > ( I ) )
Declares + + ;
EXPECT_EQ ( 2 , Declares ) ;
}
2017-10-26 23:04:53 +08:00
/// Build the dominator tree for the function and run the Test.
static void runWithDomTree (
Module & M , StringRef FuncName ,
function_ref < void ( Function & F , DominatorTree * DT ) > Test ) {
auto * F = M . getFunction ( FuncName ) ;
ASSERT_NE ( F , nullptr ) < < " Could not find " < < FuncName ;
// Compute the dominator tree for the function.
DominatorTree DT ( * F ) ;
Test ( * F , & DT ) ;
}
TEST ( Local , MergeBasicBlockIntoOnlyPred ) {
LLVMContext C ;
2018-08-03 13:08:17 +08:00
std : : unique_ptr < Module > M ;
auto resetIR = [ & ] ( ) {
M = parseIR ( C ,
R " (
2018-03-03 05:36:33 +08:00
define i32 @ f ( i8 * % str ) {
entry :
br label % bb2 . i
bb2 . i : ; preds = % bb4 . i , % entry
br i1 false , label % bb4 . i , label % base2flt . exit204
bb4 . i : ; preds = % bb2 . i
br i1 false , label % base2flt . exit204 , label % bb2 . i
bb10 . i196 . bb7 . i197_crit_edge : ; No predecessors !
br label % bb7 . i197
bb7 . i197 : ; preds = % bb10 . i196 . bb7 . i197_crit_edge
% . reg2mem .0 = phi i32 [ % . reg2mem .0 , % bb10 . i196 . bb7 . i197_crit_edge ]
br i1 undef , label % base2flt . exit204 , label % base2flt . exit204
base2flt . exit204 : ; preds = % bb7 . i197 , % bb7 . i197 , % bb2 . i , % bb4 . i
ret i32 0
}
) " );
2018-08-03 13:08:17 +08:00
} ;
auto resetIRReplaceEntry = [ & ] ( ) {
M = parseIR ( C ,
R " (
define i32 @ f ( ) {
entry :
br label % bb2 . i
bb2 . i : ; preds = % entry
ret i32 0
}
) " );
} ;
auto Test = [ & ] ( Function & F , DomTreeUpdater & DTU ) {
for ( Function : : iterator I = F . begin ( ) , E = F . end ( ) ; I ! = E ; ) {
BasicBlock * BB = & * I + + ;
BasicBlock * SinglePred = BB - > getSinglePredecessor ( ) ;
if ( ! SinglePred | | SinglePred = = BB | | BB - > hasAddressTaken ( ) )
continue ;
BranchInst * Term = dyn_cast < BranchInst > ( SinglePred - > getTerminator ( ) ) ;
if ( Term & & ! Term - > isConditional ( ) )
MergeBasicBlockIntoOnlyPred ( BB , & DTU ) ;
}
2018-09-16 20:30:41 +08:00
if ( DTU . hasDomTree ( ) ) {
2018-08-03 13:08:17 +08:00
EXPECT_TRUE ( DTU . getDomTree ( ) . verify ( ) ) ;
2018-09-16 20:30:41 +08:00
}
if ( DTU . hasPostDomTree ( ) ) {
2018-08-03 13:08:17 +08:00
EXPECT_TRUE ( DTU . getPostDomTree ( ) . verify ( ) ) ;
2018-09-16 20:30:41 +08:00
}
2018-08-03 13:08:17 +08:00
} ;
// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with
// both DT and PDT.
resetIR ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with
// DT.
resetIR ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
DomTreeUpdater DTU ( * DT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with
// PDT.
resetIR ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( PDT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with
// both DT and PDT.
resetIR ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with
// PDT.
resetIR ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( PDT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with DT.
resetIR ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
DomTreeUpdater DTU ( * DT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with
// both DT and PDT.
resetIRReplaceEntry ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with
// DT.
resetIRReplaceEntry ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
DomTreeUpdater DTU ( * DT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Eager UpdateStrategy with
// PDT.
resetIRReplaceEntry ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( PDT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with
// both DT and PDT.
resetIRReplaceEntry ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with
// PDT.
resetIRReplaceEntry ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( PDT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
Test ( F , DTU ) ;
} ) ;
// Test MergeBasicBlockIntoOnlyPred working under Lazy UpdateStrategy with DT.
resetIRReplaceEntry ( ) ;
runWithDomTree ( * M , " f " , [ & ] ( Function & F , DominatorTree * DT ) {
DomTreeUpdater DTU ( * DT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
Test ( F , DTU ) ;
} ) ;
2017-10-26 23:04:53 +08:00
}
2018-01-18 00:27:17 +08:00
TEST ( Local , ConstantFoldTerminator ) {
LLVMContext C ;
2018-03-03 05:36:33 +08:00
std : : unique_ptr < Module > M = parseIR ( C ,
R " (
define void @ br_same_dest ( ) {
entry :
br i1 false , label % bb0 , label % bb0
bb0 :
ret void
}
define void @ br_different_dest ( ) {
entry :
br i1 true , label % bb0 , label % bb1
bb0 :
br label % exit
bb1 :
br label % exit
exit :
ret void
}
define void @ switch_2_different_dest ( ) {
entry :
switch i32 0 , label % default [ i32 0 , label % bb0 ]
default :
ret void
bb0 :
ret void
}
define void @ switch_2_different_dest_default ( ) {
entry :
switch i32 1 , label % default [ i32 0 , label % bb0 ]
default :
ret void
bb0 :
ret void
}
define void @ switch_3_different_dest ( ) {
entry :
switch i32 0 , label % default [ i32 0 , label % bb0
i32 1 , label % bb1 ]
default :
ret void
bb0 :
ret void
bb1 :
ret void
}
define void @ switch_variable_2_default_dest ( i32 % arg ) {
entry :
switch i32 % arg , label % default [ i32 0 , label % default ]
default :
ret void
}
define void @ switch_constant_2_default_dest ( ) {
entry :
switch i32 1 , label % default [ i32 0 , label % default ]
default :
ret void
}
define void @ switch_constant_3_repeated_dest ( ) {
entry :
switch i32 0 , label % default [ i32 0 , label % bb0
i32 1 , label % bb0 ]
bb0 :
ret void
default :
ret void
}
define void @ indirectbr ( ) {
entry :
indirectbr i8 * blockaddress ( @ indirectbr , % bb0 ) , [ label % bb0 , label % bb1 ]
bb0 :
ret void
bb1 :
ret void
}
define void @ indirectbr_repeated ( ) {
entry :
indirectbr i8 * blockaddress ( @ indirectbr_repeated , % bb0 ) , [ label % bb0 , label % bb0 ]
bb0 :
ret void
}
define void @ indirectbr_unreachable ( ) {
entry :
indirectbr i8 * blockaddress ( @ indirectbr_unreachable , % bb0 ) , [ label % bb1 ]
bb0 :
ret void
bb1 :
ret void
}
) " );
2018-01-18 00:27:17 +08:00
2018-08-03 13:08:17 +08:00
auto CFAllTerminatorsEager = [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
2018-01-18 00:27:17 +08:00
for ( Function : : iterator I = F . begin ( ) , E = F . end ( ) ; I ! = E ; ) {
BasicBlock * BB = & * I + + ;
2018-08-03 13:08:17 +08:00
ConstantFoldTerminator ( BB , true , nullptr , & DTU ) ;
2018-01-18 00:27:17 +08:00
}
2018-08-03 13:08:17 +08:00
EXPECT_TRUE ( DTU . getDomTree ( ) . verify ( ) ) ;
EXPECT_TRUE ( DTU . getPostDomTree ( ) . verify ( ) ) ;
2018-01-18 00:27:17 +08:00
} ;
2018-08-03 13:08:17 +08:00
auto CFAllTerminatorsLazy = [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
for ( Function : : iterator I = F . begin ( ) , E = F . end ( ) ; I ! = E ; ) {
BasicBlock * BB = & * I + + ;
ConstantFoldTerminator ( BB , true , nullptr , & DTU ) ;
}
EXPECT_TRUE ( DTU . getDomTree ( ) . verify ( ) ) ;
EXPECT_TRUE ( DTU . getPostDomTree ( ) . verify ( ) ) ;
} ;
// Test ConstantFoldTerminator under Eager UpdateStrategy.
runWithDomTree ( * M , " br_same_dest " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " br_different_dest " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " switch_2_different_dest " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " switch_2_different_dest_default " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " switch_3_different_dest " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " switch_variable_2_default_dest " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " switch_constant_2_default_dest " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " switch_constant_3_repeated_dest " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " indirectbr " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " indirectbr_repeated " , CFAllTerminatorsEager ) ;
runWithDomTree ( * M , " indirectbr_unreachable " , CFAllTerminatorsEager ) ;
// Test ConstantFoldTerminator under Lazy UpdateStrategy.
runWithDomTree ( * M , " br_same_dest " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " br_different_dest " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " switch_2_different_dest " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " switch_2_different_dest_default " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " switch_3_different_dest " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " switch_variable_2_default_dest " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " switch_constant_2_default_dest " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " switch_constant_3_repeated_dest " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " indirectbr " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " indirectbr_repeated " , CFAllTerminatorsLazy ) ;
runWithDomTree ( * M , " indirectbr_unreachable " , CFAllTerminatorsLazy ) ;
2018-01-18 00:27:17 +08:00
}
2018-03-03 05:36:35 +08:00
2018-03-03 06:46:48 +08:00
struct SalvageDebugInfoTest : : : testing : : Test {
2018-03-03 05:36:35 +08:00
LLVMContext C ;
2018-03-03 06:46:48 +08:00
std : : unique_ptr < Module > M ;
Function * F = nullptr ;
2018-03-03 05:36:35 +08:00
2020-07-17 11:36:46 +08:00
void SetUp ( ) override {
2018-03-03 06:46:48 +08:00
M = parseIR ( C ,
R " (
2018-03-03 05:36:35 +08:00
define void @ f ( ) ! dbg ! 8 {
entry :
% x = add i32 0 , 1
% y = add i32 % x , 2
call void @ llvm . dbg . value ( metadata i32 % x , metadata ! 11 , metadata ! DIExpression ( ) ) , ! dbg ! 13
call void @ llvm . dbg . value ( metadata i32 % y , metadata ! 11 , metadata ! DIExpression ( ) ) , ! dbg ! 13
ret void , ! dbg ! 14
}
declare void @ llvm . dbg . value ( metadata , metadata , metadata )
! 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 }
[DebugInfo] Add DILabel metadata and intrinsic llvm.dbg.label.
In order to set breakpoints on labels and list source code around
labels, we need collect debug information for labels, i.e., label
name, the function label belong, line number in the file, and the
address label located. In order to keep these information in LLVM
IR and to allow backend to generate debug information correctly.
We create a new kind of metadata for labels, DILabel. The format
of DILabel is
!DILabel(scope: !1, name: "foo", file: !2, line: 3)
We hope to keep debug information as much as possible even the
code is optimized. So, we create a new kind of intrinsic for label
metadata to avoid the metadata is eliminated with basic block.
The intrinsic will keep existing if we keep it from optimized out.
The format of the intrinsic is
llvm.dbg.label(metadata !1)
It has only one argument, that is the DILabel metadata. The
intrinsic will follow the label immediately. Backend could get the
label metadata through the intrinsic's parameter.
We also create DIBuilder API for labels to be used by Frontend.
Frontend could use createLabel() to allocate DILabel objects, and use
insertLabel() to insert llvm.dbg.label intrinsic in LLVM IR.
Differential Revision: https://reviews.llvm.org/D45024
Patch by Hsiangkai Wang.
llvm-svn: 331841
2018-05-09 10:40:45 +08:00
! 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 )
2018-03-03 05:36:35 +08:00
! 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 )
) " );
2018-03-03 06:46:48 +08:00
auto * GV = M - > getNamedValue ( " f " ) ;
ASSERT_TRUE ( GV ) ;
F = dyn_cast < Function > ( GV ) ;
ASSERT_TRUE ( F ) ;
}
bool doesDebugValueDescribeX ( const DbgValueInst & DI ) {
const auto & CI = * cast < ConstantInt > ( DI . getValue ( ) ) ;
if ( CI . isZero ( ) )
return DI . getExpression ( ) - > getElements ( ) . equals (
{ dwarf : : DW_OP_plus_uconst , 1 , dwarf : : DW_OP_stack_value } ) ;
else if ( CI . isOneValue ( ) )
return DI . getExpression ( ) - > getElements ( ) . empty ( ) ;
return false ;
}
bool doesDebugValueDescribeY ( const DbgValueInst & DI ) {
const auto & CI = * cast < ConstantInt > ( DI . getValue ( ) ) ;
if ( CI . isZero ( ) )
return DI . getExpression ( ) - > getElements ( ) . equals (
{ dwarf : : DW_OP_plus_uconst , 1 , dwarf : : DW_OP_plus_uconst , 2 ,
dwarf : : DW_OP_stack_value } ) ;
else if ( CI . isOneValue ( ) )
return DI . getExpression ( ) - > getElements ( ) . equals (
{ dwarf : : DW_OP_plus_uconst , 2 , dwarf : : DW_OP_stack_value } ) ;
return false ;
}
void verifyDebugValuesAreSalvaged ( ) {
// Check that the debug values for %x and %y are preserved.
bool FoundX = false ;
bool FoundY = false ;
for ( const Instruction & I : F - > front ( ) ) {
auto DI = dyn_cast < DbgValueInst > ( & I ) ;
if ( ! DI ) {
// The function should only contain debug values and a terminator.
2018-08-26 17:51:22 +08:00
ASSERT_TRUE ( I . isTerminator ( ) ) ;
2018-03-03 06:46:48 +08:00
continue ;
}
EXPECT_EQ ( DI - > getVariable ( ) - > getName ( ) , " x " ) ;
FoundX | = doesDebugValueDescribeX ( * DI ) ;
FoundY | = doesDebugValueDescribeY ( * DI ) ;
}
ASSERT_TRUE ( FoundX ) ;
ASSERT_TRUE ( FoundY ) ;
}
} ;
TEST_F ( SalvageDebugInfoTest , RecursiveInstDeletion ) {
2018-03-03 05:36:35 +08:00
Instruction * Inst = & F - > front ( ) . front ( ) ;
2018-03-03 06:46:48 +08:00
Inst = Inst - > getNextNode ( ) ; // Get %y = add ...
2018-03-03 05:36:35 +08:00
ASSERT_TRUE ( Inst ) ;
bool Deleted = RecursivelyDeleteTriviallyDeadInstructions ( Inst ) ;
ASSERT_TRUE ( Deleted ) ;
2018-03-03 06:46:48 +08:00
verifyDebugValuesAreSalvaged ( ) ;
}
2018-03-03 05:36:35 +08:00
2018-03-03 06:46:48 +08:00
TEST_F ( SalvageDebugInfoTest , RecursiveBlockSimplification ) {
BasicBlock * BB = & F - > front ( ) ;
ASSERT_TRUE ( BB ) ;
bool Deleted = SimplifyInstructionsInBlock ( BB ) ;
ASSERT_TRUE ( Deleted ) ;
verifyDebugValuesAreSalvaged ( ) ;
2018-03-03 05:36:35 +08:00
}
2018-07-07 01:32:39 +08:00
2018-08-08 04:21:56 +08:00
TEST ( Local , ChangeToUnreachable ) {
LLVMContext Ctx ;
std : : unique_ptr < Module > M = parseIR ( Ctx ,
R " (
define internal void @ foo ( ) ! dbg ! 6 {
entry :
ret void , ! dbg ! 8
}
! llvm . dbg . cu = ! { ! 0 }
! llvm . debugify = ! { ! 3 , ! 4 }
! 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 : " test.ll " , directory : " / " )
! 2 = ! { }
! 3 = ! { i32 1 }
! 4 = ! { i32 0 }
! 5 = ! { i32 2 , ! " Debug Info Version " , i32 3 }
! 6 = distinct ! DISubprogram ( name : " foo " , linkageName : " foo " , scope : null , file : ! 1 , line : 1 , type : ! 7 , isLocal : true , isDefinition : true , scopeLine : 1 , isOptimized : true , unit : ! 0 , retainedNodes : ! 2 )
! 7 = ! DISubroutineType ( types : ! 2 )
! 8 = ! DILocation ( line : 1 , column : 1 , scope : ! 6 )
) " );
bool BrokenDebugInfo = true ;
verifyModule ( * M , & errs ( ) , & BrokenDebugInfo ) ;
ASSERT_FALSE ( BrokenDebugInfo ) ;
Function & F = * cast < Function > ( M - > getNamedValue ( " foo " ) ) ;
BasicBlock & BB = F . front ( ) ;
Instruction & A = BB . front ( ) ;
DebugLoc DLA = A . getDebugLoc ( ) ;
ASSERT_TRUE ( isa < ReturnInst > ( & A ) ) ;
// One instruction should be affected.
EXPECT_EQ ( changeToUnreachable ( & A , /*UseLLVMTrap*/ false ) , 1U ) ;
Instruction & B = BB . front ( ) ;
// There should be an uncreachable instruction.
ASSERT_TRUE ( isa < UnreachableInst > ( & B ) ) ;
DebugLoc DLB = B . getDebugLoc ( ) ;
EXPECT_EQ ( DLA , DLB ) ;
}
2018-07-07 01:32:39 +08:00
TEST ( Local , ReplaceAllDbgUsesWith ) {
using namespace llvm : : dwarf ;
LLVMContext Ctx ;
// Note: The datalayout simulates Darwin/x86_64.
std : : unique_ptr < Module > M = parseIR ( Ctx ,
R " (
target datalayout = " e-m:o-i63:64-f80:128-n8:16:32:64-S128 "
declare i32 @ escape ( i32 )
define void @ f ( ) ! dbg ! 6 {
entry :
% a = add i32 0 , 1 , ! dbg ! 15
call void @ llvm . dbg . value ( metadata i32 % a , metadata ! 9 , metadata ! DIExpression ( ) ) , ! dbg ! 15
% b = add i64 0 , 1 , ! dbg ! 16
call void @ llvm . dbg . value ( metadata i64 % b , metadata ! 11 , metadata ! DIExpression ( ) ) , ! dbg ! 16
call void @ llvm . dbg . value ( metadata i64 % b , metadata ! 11 , metadata ! DIExpression ( DW_OP_lit0 , DW_OP_mul ) ) , ! dbg ! 16
call void @ llvm . dbg . value ( metadata i64 % b , metadata ! 11 , metadata ! DIExpression ( DW_OP_lit0 , DW_OP_mul , DW_OP_stack_value ) ) , ! dbg ! 16
call void @ llvm . dbg . value ( metadata i64 % b , metadata ! 11 , metadata ! DIExpression ( DW_OP_LLVM_fragment , 0 , 8 ) ) , ! dbg ! 16
call void @ llvm . dbg . value ( metadata i64 % b , metadata ! 11 , metadata ! DIExpression ( DW_OP_lit0 , DW_OP_mul , DW_OP_LLVM_fragment , 0 , 8 ) ) , ! dbg ! 16
call void @ llvm . dbg . value ( metadata i64 % b , metadata ! 11 , metadata ! DIExpression ( DW_OP_lit0 , DW_OP_mul , DW_OP_stack_value , DW_OP_LLVM_fragment , 0 , 8 ) ) , ! dbg ! 16
% c = inttoptr i64 0 to i64 * , ! dbg ! 17
call void @ llvm . dbg . declare ( metadata i64 * % c , metadata ! 13 , metadata ! DIExpression ( ) ) , ! dbg ! 17
% d = inttoptr i64 0 to i32 * , ! dbg ! 18
call void @ llvm . dbg . addr ( metadata i32 * % d , metadata ! 20 , metadata ! DIExpression ( ) ) , ! dbg ! 18
% e = add < 2 x i16 > zeroinitializer , zeroinitializer
call void @ llvm . dbg . value ( metadata < 2 x i16 > % e , metadata ! 14 , metadata ! DIExpression ( ) ) , ! dbg ! 18
% f = call i32 @ escape ( i32 0 )
call void @ llvm . dbg . value ( metadata i32 % f , metadata ! 9 , metadata ! DIExpression ( ) ) , ! dbg ! 15
% barrier = call i32 @ escape ( i32 0 )
% g = call i32 @ escape ( i32 % f )
call void @ llvm . dbg . value ( metadata i32 % g , metadata ! 9 , metadata ! DIExpression ( ) ) , ! dbg ! 15
ret void , ! dbg ! 19
}
declare void @ llvm . dbg . addr ( metadata , metadata , metadata )
declare void @ llvm . dbg . declare ( metadata , metadata , metadata )
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 : " /Users/vsk/Desktop/foo.ll " , directory : " / " )
! 2 = ! { }
! 5 = ! { i32 2 , ! " Debug Info Version " , i32 3 }
! 6 = distinct ! DISubprogram ( name : " f " , linkageName : " f " , scope : null , file : ! 1 , line : 1 , type : ! 7 , isLocal : false , isDefinition : true , scopeLine : 1 , isOptimized : true , unit : ! 0 , retainedNodes : ! 8 )
! 7 = ! DISubroutineType ( types : ! 2 )
! 8 = ! { ! 9 , ! 11 , ! 13 , ! 14 }
! 9 = ! DILocalVariable ( name : " 1 " , scope : ! 6 , file : ! 1 , line : 1 , type : ! 10 )
! 10 = ! DIBasicType ( name : " ty32 " , size : 32 , encoding : DW_ATE_signed )
! 11 = ! DILocalVariable ( name : " 2 " , scope : ! 6 , file : ! 1 , line : 2 , type : ! 12 )
! 12 = ! DIBasicType ( name : " ty64 " , size : 64 , encoding : DW_ATE_signed )
! 13 = ! DILocalVariable ( name : " 3 " , scope : ! 6 , file : ! 1 , line : 3 , type : ! 12 )
! 14 = ! DILocalVariable ( name : " 4 " , scope : ! 6 , file : ! 1 , line : 4 , type : ! 10 )
! 15 = ! DILocation ( line : 1 , column : 1 , scope : ! 6 )
! 16 = ! DILocation ( line : 2 , column : 1 , scope : ! 6 )
! 17 = ! DILocation ( line : 3 , column : 1 , scope : ! 6 )
! 18 = ! DILocation ( line : 4 , column : 1 , scope : ! 6 )
! 19 = ! DILocation ( line : 5 , column : 1 , scope : ! 6 )
! 20 = ! DILocalVariable ( name : " 5 " , scope : ! 6 , file : ! 1 , line : 5 , type : ! 10 )
) " );
bool BrokenDebugInfo = true ;
verifyModule ( * M , & errs ( ) , & BrokenDebugInfo ) ;
ASSERT_FALSE ( BrokenDebugInfo ) ;
Function & F = * cast < Function > ( M - > getNamedValue ( " f " ) ) ;
DominatorTree DT { F } ;
BasicBlock & BB = F . front ( ) ;
Instruction & A = BB . front ( ) ;
Instruction & B = * A . getNextNonDebugInstruction ( ) ;
Instruction & C = * B . getNextNonDebugInstruction ( ) ;
Instruction & D = * C . getNextNonDebugInstruction ( ) ;
Instruction & E = * D . getNextNonDebugInstruction ( ) ;
Instruction & F_ = * E . getNextNonDebugInstruction ( ) ;
Instruction & Barrier = * F_ . getNextNonDebugInstruction ( ) ;
Instruction & G = * Barrier . getNextNonDebugInstruction ( ) ;
// Simulate i32 <-> i64* conversion. Expect no updates: the datalayout says
// pointers are 64 bits, so the conversion would be lossy.
EXPECT_FALSE ( replaceAllDbgUsesWith ( A , C , C , DT ) ) ;
EXPECT_FALSE ( replaceAllDbgUsesWith ( C , A , A , DT ) ) ;
// Simulate i32 <-> <2 x i16> conversion. This is unsupported.
EXPECT_FALSE ( replaceAllDbgUsesWith ( E , A , A , DT ) ) ;
EXPECT_FALSE ( replaceAllDbgUsesWith ( A , E , E , DT ) ) ;
// Simulate i32* <-> i64* conversion.
EXPECT_TRUE ( replaceAllDbgUsesWith ( D , C , C , DT ) ) ;
2018-08-06 11:59:47 +08:00
SmallVector < DbgVariableIntrinsic * , 2 > CDbgVals ;
2018-07-07 01:32:39 +08:00
findDbgUsers ( CDbgVals , & C ) ;
EXPECT_EQ ( 2U , CDbgVals . size ( ) ) ;
2018-08-06 11:59:47 +08:00
EXPECT_TRUE ( any_of ( CDbgVals , [ ] ( DbgVariableIntrinsic * DII ) {
2018-07-07 01:32:39 +08:00
return isa < DbgAddrIntrinsic > ( DII ) ;
} ) ) ;
2018-08-06 11:59:47 +08:00
EXPECT_TRUE ( any_of ( CDbgVals , [ ] ( DbgVariableIntrinsic * DII ) {
2018-07-07 01:32:39 +08:00
return isa < DbgDeclareInst > ( DII ) ;
} ) ) ;
EXPECT_TRUE ( replaceAllDbgUsesWith ( C , D , D , DT ) ) ;
2018-08-06 11:59:47 +08:00
SmallVector < DbgVariableIntrinsic * , 2 > DDbgVals ;
2018-07-07 01:32:39 +08:00
findDbgUsers ( DDbgVals , & D ) ;
EXPECT_EQ ( 2U , DDbgVals . size ( ) ) ;
2018-08-06 11:59:47 +08:00
EXPECT_TRUE ( any_of ( DDbgVals , [ ] ( DbgVariableIntrinsic * DII ) {
2018-07-07 01:32:39 +08:00
return isa < DbgAddrIntrinsic > ( DII ) ;
} ) ) ;
2018-08-06 11:59:47 +08:00
EXPECT_TRUE ( any_of ( DDbgVals , [ ] ( DbgVariableIntrinsic * DII ) {
2018-07-07 01:32:39 +08:00
return isa < DbgDeclareInst > ( DII ) ;
} ) ) ;
// Introduce a use-before-def. Check that the dbg.value for %a is salvaged.
EXPECT_TRUE ( replaceAllDbgUsesWith ( A , F_ , F_ , DT ) ) ;
auto * ADbgVal = cast < DbgValueInst > ( A . getNextNode ( ) ) ;
EXPECT_EQ ( ConstantInt : : get ( A . getType ( ) , 0 ) , ADbgVal - > getVariableLocation ( ) ) ;
2019-11-25 17:02:05 +08:00
// Introduce a use-before-def. Check that the dbg.values for %f become undef.
2018-07-07 01:32:39 +08:00
EXPECT_TRUE ( replaceAllDbgUsesWith ( F_ , G , G , DT ) ) ;
2019-11-25 17:02:05 +08:00
auto * FDbgVal = cast < DbgValueInst > ( F_ . getNextNode ( ) ) ;
EXPECT_TRUE ( isa < UndefValue > ( FDbgVal - > getVariableLocation ( ) ) ) ;
2018-07-07 01:32:39 +08:00
SmallVector < DbgValueInst * , 1 > FDbgVals ;
2019-11-25 17:02:05 +08:00
findDbgValues ( FDbgVals , & F_ ) ;
2018-07-07 01:32:39 +08:00
EXPECT_EQ ( 0U , FDbgVals . size ( ) ) ;
// Simulate i32 -> i64 conversion to test sign-extension. Here are some
// interesting cases to handle:
// 1) debug user has empty DIExpression
// 2) debug user has non-empty, non-stack-value'd DIExpression
// 3) debug user has non-empty, stack-value'd DIExpression
// 4-6) like (1-3), but with a fragment
EXPECT_TRUE ( replaceAllDbgUsesWith ( B , A , A , DT ) ) ;
SmallVector < DbgValueInst * , 8 > ADbgVals ;
findDbgValues ( ADbgVals , & A ) ;
EXPECT_EQ ( 6U , ADbgVals . size ( ) ) ;
// Check that %a has a dbg.value with a DIExpression matching \p Ops.
auto hasADbgVal = [ & ] ( ArrayRef < uint64_t > Ops ) {
return any_of ( ADbgVals , [ & ] ( DbgValueInst * DVI ) {
assert ( DVI - > getVariable ( ) - > getName ( ) = = " 2 " ) ;
return DVI - > getExpression ( ) - > getElements ( ) = = Ops ;
} ) ;
} ;
// Case 1: The original expr is empty, so no deref is needed.
2019-03-19 21:16:28 +08:00
EXPECT_TRUE ( hasADbgVal ( { DW_OP_LLVM_convert , 32 , DW_ATE_signed ,
DW_OP_LLVM_convert , 64 , DW_ATE_signed ,
DW_OP_stack_value } ) ) ;
2018-07-07 01:32:39 +08:00
// Case 2: Perform an address calculation with the original expr, deref it,
// then sign-extend the result.
2019-03-19 21:16:28 +08:00
EXPECT_TRUE ( hasADbgVal ( { DW_OP_lit0 , DW_OP_mul , DW_OP_deref ,
DW_OP_LLVM_convert , 32 , DW_ATE_signed ,
DW_OP_LLVM_convert , 64 , DW_ATE_signed ,
DW_OP_stack_value } ) ) ;
2018-07-07 01:32:39 +08:00
// Case 3: Insert the sign-extension logic before the DW_OP_stack_value.
2019-03-19 21:16:28 +08:00
EXPECT_TRUE ( hasADbgVal ( { DW_OP_lit0 , DW_OP_mul , DW_OP_LLVM_convert , 32 ,
DW_ATE_signed , DW_OP_LLVM_convert , 64 , DW_ATE_signed ,
DW_OP_stack_value } ) ) ;
2018-07-07 01:32:39 +08:00
// Cases 4-6: Just like cases 1-3, but preserve the fragment at the end.
2019-03-19 21:16:28 +08:00
EXPECT_TRUE ( hasADbgVal ( { DW_OP_LLVM_convert , 32 , DW_ATE_signed ,
DW_OP_LLVM_convert , 64 , DW_ATE_signed ,
DW_OP_stack_value , DW_OP_LLVM_fragment , 0 , 8 } ) ) ;
EXPECT_TRUE ( hasADbgVal ( { DW_OP_lit0 , DW_OP_mul , DW_OP_deref ,
DW_OP_LLVM_convert , 32 , DW_ATE_signed ,
DW_OP_LLVM_convert , 64 , DW_ATE_signed ,
DW_OP_stack_value , DW_OP_LLVM_fragment , 0 , 8 } ) ) ;
EXPECT_TRUE ( hasADbgVal ( { DW_OP_lit0 , DW_OP_mul , DW_OP_LLVM_convert , 32 ,
DW_ATE_signed , DW_OP_LLVM_convert , 64 , DW_ATE_signed ,
DW_OP_stack_value , DW_OP_LLVM_fragment , 0 , 8 } ) ) ;
2018-07-07 01:32:39 +08:00
verifyModule ( * M , & errs ( ) , & BrokenDebugInfo ) ;
ASSERT_FALSE ( BrokenDebugInfo ) ;
}
2018-08-03 13:08:17 +08:00
TEST ( Local , RemoveUnreachableBlocks ) {
LLVMContext C ;
std : : unique_ptr < Module > M = parseIR ( C ,
R " (
define void @ br_simple ( ) {
entry :
br label % bb0
bb0 :
ret void
bb1 :
ret void
}
define void @ br_self_loop ( ) {
entry :
br label % bb0
bb0 :
br i1 true , label % bb1 , label % bb0
bb1 :
br i1 true , label % bb0 , label % bb2
bb2 :
br label % bb2
}
define void @ br_constant ( ) {
entry :
br label % bb0
bb0 :
br i1 true , label % bb1 , label % bb2
bb1 :
br i1 true , label % bb0 , label % bb2
bb2 :
br label % bb2
}
define void @ br_loop ( ) {
entry :
br label % bb0
bb0 :
br label % bb0
bb1 :
br label % bb2
bb2 :
br label % bb1
}
2019-10-03 03:38:24 +08:00
declare i32 @ __gxx_personality_v0 ( . . . )
define void @ invoke_terminator ( ) personality i8 * bitcast ( i32 ( . . . ) * @ __gxx_personality_v0 to i8 * ) {
entry :
br i1 undef , label % invoke . block , label % exit
invoke . block :
% cond = invoke zeroext i1 @ invokable ( )
to label % continue . block unwind label % lpad . block
continue . block :
br i1 % cond , label % if . then , label % if . end
if . then :
unreachable
if . end :
unreachable
lpad . block :
% lp = landingpad { i8 * , i32 }
catch i8 * null
br label % exit
exit :
ret void
}
declare i1 @ invokable ( )
2018-08-03 13:08:17 +08:00
) " );
auto runEager = [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
2019-10-03 00:58:13 +08:00
removeUnreachableBlocks ( F , & DTU ) ;
2018-08-03 13:08:17 +08:00
EXPECT_TRUE ( DTU . getDomTree ( ) . verify ( ) ) ;
EXPECT_TRUE ( DTU . getPostDomTree ( ) . verify ( ) ) ;
} ;
auto runLazy = [ & ] ( Function & F , DominatorTree * DT ) {
PostDominatorTree PDT = PostDominatorTree ( F ) ;
DomTreeUpdater DTU ( * DT , PDT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
2019-10-03 00:58:13 +08:00
removeUnreachableBlocks ( F , & DTU ) ;
2018-08-03 13:08:17 +08:00
EXPECT_TRUE ( DTU . getDomTree ( ) . verify ( ) ) ;
EXPECT_TRUE ( DTU . getPostDomTree ( ) . verify ( ) ) ;
} ;
// Test removeUnreachableBlocks under Eager UpdateStrategy.
runWithDomTree ( * M , " br_simple " , runEager ) ;
runWithDomTree ( * M , " br_self_loop " , runEager ) ;
runWithDomTree ( * M , " br_constant " , runEager ) ;
runWithDomTree ( * M , " br_loop " , runEager ) ;
2019-10-03 03:38:24 +08:00
runWithDomTree ( * M , " invoke_terminator " , runEager ) ;
2018-08-03 13:08:17 +08:00
// Test removeUnreachableBlocks under Lazy UpdateStrategy.
runWithDomTree ( * M , " br_simple " , runLazy ) ;
runWithDomTree ( * M , " br_self_loop " , runLazy ) ;
runWithDomTree ( * M , " br_constant " , runLazy ) ;
runWithDomTree ( * M , " br_loop " , runLazy ) ;
2019-10-03 03:38:24 +08:00
runWithDomTree ( * M , " invoke_terminator " , runLazy ) ;
[Dominators] Make RemoveUnreachableBlocks return false if the BasicBlock is already awaiting deletion
Summary:
Previously, `removeUnreachableBlocks` still returns true (which indicates the CFG is changed) even when all the unreachable blocks found is awaiting deletion in the DDT class.
This makes code pattern like
```
// Code modified from lib/Transforms/Scalar/SimplifyCFGPass.cpp
bool EverChanged = removeUnreachableBlocks(F, nullptr, DDT);
...
do {
EverChanged = someMightHappenModifications();
EverChanged |= removeUnreachableBlocks(F, nullptr, DDT);
} while (EverChanged);
```
become a dead loop.
Fix this by detecting whether a BasicBlock is already awaiting deletion.
Reviewers: kuhar, brzycki, dmgreen, grosser, davide
Reviewed By: kuhar, brzycki
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D49738
llvm-svn: 338882
2018-08-03 20:45:29 +08:00
M = parseIR ( C ,
R " (
define void @ f ( ) {
entry :
ret void
bb0 :
ret void
}
) " );
auto checkRUBlocksRetVal = [ & ] ( Function & F , DominatorTree * DT ) {
DomTreeUpdater DTU ( DT , DomTreeUpdater : : UpdateStrategy : : Lazy ) ;
2019-10-03 00:58:13 +08:00
EXPECT_TRUE ( removeUnreachableBlocks ( F , & DTU ) ) ;
EXPECT_FALSE ( removeUnreachableBlocks ( F , & DTU ) ) ;
[Dominators] Make RemoveUnreachableBlocks return false if the BasicBlock is already awaiting deletion
Summary:
Previously, `removeUnreachableBlocks` still returns true (which indicates the CFG is changed) even when all the unreachable blocks found is awaiting deletion in the DDT class.
This makes code pattern like
```
// Code modified from lib/Transforms/Scalar/SimplifyCFGPass.cpp
bool EverChanged = removeUnreachableBlocks(F, nullptr, DDT);
...
do {
EverChanged = someMightHappenModifications();
EverChanged |= removeUnreachableBlocks(F, nullptr, DDT);
} while (EverChanged);
```
become a dead loop.
Fix this by detecting whether a BasicBlock is already awaiting deletion.
Reviewers: kuhar, brzycki, dmgreen, grosser, davide
Reviewed By: kuhar, brzycki
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D49738
llvm-svn: 338882
2018-08-03 20:45:29 +08:00
EXPECT_TRUE ( DTU . getDomTree ( ) . verify ( ) ) ;
} ;
runWithDomTree ( * M , " f " , checkRUBlocksRetVal ) ;
2018-08-08 04:21:56 +08:00
}
2019-12-08 00:47:00 +08:00
TEST ( Local , SimplifyCFGWithNullAC ) {
LLVMContext Ctx ;
std : : unique_ptr < Module > M = parseIR ( Ctx , R " (
declare void @ true_path ( )
declare void @ false_path ( )
declare void @ llvm . assume ( i1 % cond ) ;
define i32 @ foo ( i1 , i32 ) {
entry :
% cmp = icmp sgt i32 % 1 , 0
br i1 % cmp , label % if . bb1 , label % then . bb1
if . bb1 :
call void @ true_path ( )
br label % test . bb
then . bb1 :
call void @ false_path ( )
br label % test . bb
test . bb :
% phi = phi i1 [ 1 , % if . bb1 ] , [ % 0 , % then . bb1 ]
call void @ llvm . assume ( i1 % 0 )
br i1 % phi , label % if . bb2 , label % then . bb2
if . bb2 :
ret i32 % 1
then . bb2 :
ret i32 0
}
) " );
Function & F = * cast < Function > ( M - > getNamedValue ( " foo " ) ) ;
TargetTransformInfo TTI ( M - > getDataLayout ( ) ) ;
SimplifyCFGOptions Options { } ;
Options . setAssumptionCache ( nullptr ) ;
// Obtain BasicBlock of interest to this test, %test.bb.
BasicBlock * TestBB = nullptr ;
for ( BasicBlock & BB : F ) {
if ( BB . getName ( ) . equals ( " test.bb " ) ) {
TestBB = & BB ;
break ;
}
}
ASSERT_TRUE ( TestBB ) ;
2021-01-02 05:20:50 +08:00
DominatorTree DT ( F ) ;
DomTreeUpdater DTU ( DT , DomTreeUpdater : : UpdateStrategy : : Eager ) ;
2019-12-08 00:47:00 +08:00
// %test.bb is expected to be simplified by FoldCondBranchOnPHI.
2021-01-02 05:20:50 +08:00
EXPECT_TRUE ( simplifyCFG ( TestBB , TTI ,
RequireAndPreserveDomTree ? & DTU : nullptr , Options ) ) ;
2019-12-08 00:47:00 +08:00
}
2019-02-14 07:28:16 +08:00
TEST ( Local , CanReplaceOperandWithVariable ) {
LLVMContext Ctx ;
Module M ( " test_module " , Ctx ) ;
IRBuilder < > B ( Ctx ) ;
FunctionType * FnType =
FunctionType : : get ( Type : : getVoidTy ( Ctx ) , { } , false ) ;
FunctionType * VarArgFnType =
FunctionType : : get ( Type : : getVoidTy ( Ctx ) , { B . getInt32Ty ( ) } , true ) ;
Function * TestBody = Function : : Create ( FnType , GlobalValue : : ExternalLinkage ,
0 , " " , & M ) ;
BasicBlock * BB0 = BasicBlock : : Create ( Ctx , " " , TestBody ) ;
B . SetInsertPoint ( BB0 ) ;
2020-04-04 12:27:00 +08:00
FunctionCallee Intrin = M . getOrInsertFunction ( " llvm.foo " , FnType ) ;
FunctionCallee Func = M . getOrInsertFunction ( " foo " , FnType ) ;
FunctionCallee VarArgFunc
= M . getOrInsertFunction ( " foo.vararg " , VarArgFnType ) ;
FunctionCallee VarArgIntrin
= M . getOrInsertFunction ( " llvm.foo.vararg " , VarArgFnType ) ;
2019-02-14 07:28:16 +08:00
auto * CallToIntrin = B . CreateCall ( Intrin ) ;
auto * CallToFunc = B . CreateCall ( Func ) ;
// Test if it's valid to replace the callee operand.
EXPECT_FALSE ( canReplaceOperandWithVariable ( CallToIntrin , 0 ) ) ;
EXPECT_TRUE ( canReplaceOperandWithVariable ( CallToFunc , 0 ) ) ;
// That it's invalid to replace an argument in the variadic argument list for
// an intrinsic, but OK for a normal function.
auto * CallToVarArgFunc = B . CreateCall (
VarArgFunc , { B . getInt32 ( 0 ) , B . getInt32 ( 1 ) , B . getInt32 ( 2 ) } ) ;
EXPECT_TRUE ( canReplaceOperandWithVariable ( CallToVarArgFunc , 0 ) ) ;
EXPECT_TRUE ( canReplaceOperandWithVariable ( CallToVarArgFunc , 1 ) ) ;
EXPECT_TRUE ( canReplaceOperandWithVariable ( CallToVarArgFunc , 2 ) ) ;
EXPECT_TRUE ( canReplaceOperandWithVariable ( CallToVarArgFunc , 3 ) ) ;
auto * CallToVarArgIntrin = B . CreateCall (
VarArgIntrin , { B . getInt32 ( 0 ) , B . getInt32 ( 1 ) , B . getInt32 ( 2 ) } ) ;
EXPECT_TRUE ( canReplaceOperandWithVariable ( CallToVarArgIntrin , 0 ) ) ;
EXPECT_FALSE ( canReplaceOperandWithVariable ( CallToVarArgIntrin , 1 ) ) ;
EXPECT_FALSE ( canReplaceOperandWithVariable ( CallToVarArgIntrin , 2 ) ) ;
EXPECT_FALSE ( canReplaceOperandWithVariable ( CallToVarArgIntrin , 3 ) ) ;
// Test that it's invalid to replace gcroot operands, even though it can't use
// immarg.
Type * PtrPtr = B . getInt8Ty ( ) - > getPointerTo ( 0 ) ;
Value * Alloca = B . CreateAlloca ( PtrPtr , ( unsigned ) 0 ) ;
CallInst * GCRoot = B . CreateIntrinsic ( Intrinsic : : gcroot , { } ,
{ Alloca , Constant : : getNullValue ( PtrPtr ) } ) ;
EXPECT_TRUE ( canReplaceOperandWithVariable ( GCRoot , 0 ) ) ; // Alloca
EXPECT_FALSE ( canReplaceOperandWithVariable ( GCRoot , 1 ) ) ;
EXPECT_FALSE ( canReplaceOperandWithVariable ( GCRoot , 2 ) ) ;
BB0 - > dropAllReferences ( ) ;
}