llvm/test/Transforms/GlobalDCE/comdats.ll
Chandler Carruth 28c1ee71f3 Test the different scenarios of GlobalDCE and comdats more
systematically and document in the test what all is going on.

This replaces the PR-named test that was the only coverage for GlobalDCE
and comdats previously. I wrote this because I wasn't certain how
comdat DCE was supposed to work and wanted to step through what
GlobalDCE did to fully understand it. After talking to folks and reading
the code and really staring at things it all makes sense but it seemed
good to help write down some of this in a more explicit and fully
covering test case.

For example, it seemed like a bug that GlobalDCE didn't consider comdat
participation of ifuncs. Specifically it seemed like an accident because
testing didn't really cover that case. But in fact, ifuncs specifically
cannot participate in a comdat despite having that API. The new test
case covers this and explicitly documents that DCE gets to fire here
even though there are comdats involved.

Also, we didn't have any positive tests for the challenging cases such
as usage cycles between comdat participants that might make them seem
alive except that there is no external edge into the cycle.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@290537 91177308-0d34-0410-b5e6-96231b3b80d8
2016-12-26 08:54:01 +00:00

179 lines
5.1 KiB
LLVM

; Test the behavior of GlobalDCE in conjunction with comdats.
;
; RUN: opt < %s -globaldce -S | FileCheck %s
; First test checks that if one function in a comdat group is used, both other
; functions and other globals even if unused will be preserved.
$test1_c = comdat any
; CHECK: $test1_c = comdat any
; Second test checks that if one function in a comdat group is used, both other
; functions and other globals even if unused will be preserved.
$test2_c = comdat any
; CHECK: $test2_c = comdat any
; Third test checks that calling a function in a comdat group with an alias
; preserves the alias.
$test3_c = comdat any
; CHECK: $test3_c = comdat any
; Fourth test checks that calling an alias in a comdat group with a function
; preserves the function. (This is the trivial case as the alias uses the
; function.)
$test4_c = comdat any
; CHECK: $test4_c = comdat any
; Fifth test checks that calling a function in a comdat group that is used as
; the resolver of an ifunc doesn't preserve that ifunc. ifunc symbols don't
; participate in the comdat group of their resolver function as they are
; considered separate objects.
$test5_c = comdat any
; CHECK: $test5_c = comdat any
; Sixth test checks that calling an ifunc whose resolver is in a comdat group
; preserves the resolver. This is the trivial case as the ifunc uses the
; resolver.
$test6_c = comdat any
; CHECK: $test6_c = comdat any
; Seventh test checks that we can eliminate a comdat when it has only one dead function participant.
$test7_c = comdat any
; CHECK-NOT: $test7_c = comdat any
; Eighth test checks that we can eliminate a comdat when it has only one dead global participant.
$test8_c = comdat any
; CHECK-NOT: $test8_c = comdat any
; Ninth test checks that we can eliminate a comdat when there are multiple
; dead participants.
$test9_c = comdat any
; CHECK-NOT: $test9_c = comdat any
; Tenth test checks that we can eliminate a comdat when it has multiple
; participants that form internal cyclic uses but are never used externally and
; thus the entire ifunc can safely be eliminated.
$test10_c = comdat any
; CHECK-NOT: $test10_c = comdat any
@test1_gv = linkonce_odr unnamed_addr global i32 42, comdat($test1_c)
; CHECK: @test1_gv = linkonce_odr unnamed_addr global
@test2_used = linkonce_odr unnamed_addr global i32 42, comdat($test2_c)
; CHECK: @test2_used = linkonce_odr unnamed_addr global
@test2_gv = linkonce_odr unnamed_addr global i32 42, comdat($test2_c)
; CHECK: @test2_gv = linkonce_odr unnamed_addr global
@test8_gv = linkonce_odr unnamed_addr global i32 42, comdat($test8_c)
; CHECK-NOT: @test8_gv
@test9_gv = linkonce_odr unnamed_addr global i32 42, comdat($test9_c)
; CHECK-NOT: @test9_gv
@test10_gv = linkonce_odr unnamed_addr global void ()* @test10_f, comdat($test10_c)
; CHECK-NOT: @test10_gv
@test3_a = linkonce_odr unnamed_addr alias void (), void ()* @test3_f
; CHECK: @test3_a = linkonce_odr unnamed_addr alias
@test4_a = linkonce_odr unnamed_addr alias void (), void ()* @test4_f
; CHECK: @test4_a = linkonce_odr unnamed_addr alias
@test10_a = linkonce_odr unnamed_addr alias void (), void ()* @test10_g
; CHECK-NOT: @test10_a
@test5_if = linkonce_odr ifunc void (), void ()* ()* @test5_f
; CHECK-NOT: @test5_if
@test6_if = linkonce_odr ifunc void (), void ()* ()* @test6_f
; CHECK: @test6_if = linkonce_odr ifunc
; This function is directly used and so cannot be eliminated.
define linkonce_odr void @test1_used() comdat($test1_c) {
; CHECK: define linkonce_odr void @test1_used()
entry:
ret void
}
define linkonce_odr void @test1_f() comdat($test1_c) {
; CHECK: define linkonce_odr void @test1_f()
entry:
ret void
}
; Now test that a function, global variable, alias, and ifunc in the same
; comdat are kept.
define linkonce_odr void @test2_f() comdat($test2_c) {
; CHECK: define linkonce_odr void @test2_f()
entry:
ret void
}
define linkonce_odr void @test3_f() comdat($test3_c) {
; CHECK: define linkonce_odr void @test3_f()
entry:
ret void
}
define linkonce_odr void @test4_f() comdat($test4_c) {
; CHECK: define linkonce_odr void @test4_f()
entry:
ret void
}
declare void @test_external()
define linkonce_odr void ()* @test5_f() comdat($test5_c) {
; CHECK: define linkonce_odr void ()* @test5_f()
entry:
ret void ()* @test_external
}
define linkonce_odr void ()* @test6_f() comdat($test6_c) {
; CHECK: define linkonce_odr void ()* @test6_f()
entry:
ret void ()* @test_external
}
define linkonce_odr void @test7_f() comdat($test7_c) {
; CHECK-NOT: @test7_f
entry:
ret void
}
define linkonce_odr void @test9_f() comdat($test9_c) {
; CHECK-NOT: @test9_f
entry:
ret void
}
define linkonce_odr void @test10_f() comdat($test10_c) {
; CHECK-NOT: @test10_f
entry:
%gv = load void ()*, void ()** @test10_gv
call void @test10_a()
ret void
}
define linkonce_odr void @test10_g() comdat($test10_c) {
; CHECK-NOT: @test10_g
entry:
call void @test10_f()
ret void
}
; An external function to pin as "used" various things above that shouldn't be
; eliminated.
define void @external_user() {
call void @test1_used()
%gv = load i32, i32* @test2_used
call void @test3_f()
call void @test4_a()
%fptr = call void() *@test5_f()
call void @test6_if()
ret void
}