diff --git a/test/Transforms/GlobalDCE/comdats.ll b/test/Transforms/GlobalDCE/comdats.ll new file mode 100644 index 00000000000..29ef848a586 --- /dev/null +++ b/test/Transforms/GlobalDCE/comdats.ll @@ -0,0 +1,178 @@ +; 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 +} diff --git a/test/Transforms/GlobalDCE/pr20981.ll b/test/Transforms/GlobalDCE/pr20981.ll deleted file mode 100644 index c3e06699da7..00000000000 --- a/test/Transforms/GlobalDCE/pr20981.ll +++ /dev/null @@ -1,17 +0,0 @@ -; RUN: opt < %s -globaldce -S | FileCheck %s - -$c1 = comdat any -; CHECK: $c1 = comdat any - -@a1 = linkonce_odr alias void (), void ()* @f1 -; CHECK: @a1 = linkonce_odr alias void (), void ()* @f1 - -define linkonce_odr void @f1() comdat($c1) { - ret void -} -; CHECK: define linkonce_odr void @f1() comdat($c1) - -define void @g() { - call void @f1() - ret void -}