; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 5 ; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -sink-common-insts -S | FileCheck %s ; RUN: opt < %s -passes='simplifycfg' -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" ; Simple test, nothing interesting happens here. define void @t0_noop() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t0_noop() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C]], label %[[IF_THEN:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c = call i1 @cond() br i1 %c, label %if.then, label %if.end if.then: invoke void @simple_throw() to label %invoke.cont unwind label %lpad invoke.cont: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.end: call void @sideeffect() ret void } ; More interesting test, here we can merge the invokes. define void @t1_mergeable_invoke() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t1_mergeable_invoke() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; normal block is shared, but it is unreachable, so we are fine. define void @t2_shared_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t2_shared_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() to label %invoke.cont unwind label %lpad invoke.cont: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; shared normal destination is not unreachable. define void @t3_shared_identical_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t3_shared_identical_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[INVOKE_CONT:.*]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; normal destinations are not unreachable and not shared and can not be merged. define void @t4_normal_dests() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t4_normal_dests() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: call void @another_sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @maybe_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @maybe_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: call void @another_sideeffect() unreachable if.end: call void @sideeffect() ret void } ; Invokes lead to different landing pads. define void @t5_different_landingpads() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t5_different_landingpads() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD0:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[COMMON_RESUME:.*]]: ; CHECK-NEXT: [[COMMON_RESUME_OP:%.*]] = phi { ptr, i32 } [ [[EH0:%.*]], %[[LPAD0]] ], [ [[EH1:%.*]], %[[LPAD1:.*]] ] ; CHECK-NEXT: resume { ptr, i32 } [[COMMON_RESUME_OP]] ; CHECK: [[LPAD0]]: ; CHECK-NEXT: [[EH0]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: br label %[[COMMON_RESUME]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD1]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD1]]: ; CHECK-NEXT: [[EH1]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @another_destructor() ; CHECK-NEXT: br label %[[COMMON_RESUME]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad0 invoke.cont0: unreachable lpad0: %eh0 = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh0 if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad1 invoke.cont2: unreachable lpad1: %eh1 = landingpad { ptr, i32 } cleanup call void @another_destructor() resume { ptr, i32 } %eh1 if.end: call void @sideeffect() ret void } ; The invoked functions are different define void @t6_different_invokes() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t6_different_invokes() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @another_simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @another_simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Merging of this invoke is disallowed define void @t7_nomerge0() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t7_nomerge0() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR1:[0-9]+]] ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() nomerge to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } define void @t8_nomerge1() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t8_nomerge1() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR1]] ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() nomerge to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } define void @t9_nomerge2() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t9_nomerge2() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR1]] ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR1]] ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() nomerge to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() nomerge to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Just don't deal with inlineasm. define void @t10_inlineasm() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t10_inlineasm() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void asm sideeffect "something bad", ""() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void asm sideeffect "something bad", ""() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void asm sideeffect "something bad", ""() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void asm sideeffect "something bad", ""() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; landingpad has PHI nodes, and the incoming values are incompatible. define void @t11_phi_in_landingpad_incompatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t11_phi_in_landingpad_incompatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[IF_THEN0]] ], [ 1, %[[IF_THEN1:.*]] ] ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @consume(i32 [[PHI]]) ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %phi = phi i32 [ 0, %if.then0 ], [ 1, %if.then1 ] %eh = landingpad { ptr, i32 } cleanup call void @consume(i32 %phi) call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; It is okay for the invoke to take arguments define void @t12_arguments_are_fine() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t12_arguments_are_fine() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw_taking_argument(i32 42) ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw_taking_argument(i32 42) to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw_taking_argument(i32 42) to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; It is okay for the invoke to take different arguments define void @t13_different_arguments_are_fine() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t13_different_arguments_are_fine() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 42, %[[IF_ELSE]] ], [ 0, %[[ENTRY]] ] ; CHECK-NEXT: invoke void @simple_throw_taking_argument(i32 [[TMP0]]) ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw_taking_argument(i32 0) to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw_taking_argument(i32 42) to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; There can be more than two invokes in a set define void @t14_three_invokes() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t14_three_invokes() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN2_INVOKE:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN2_INVOKE]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN2_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN2_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[IF_THEN2_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN2_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.end if.then2: invoke void @simple_throw() to label %invoke.cont3 unwind label %lpad invoke.cont3: unreachable if.end: call void @sideeffect() ret void } ; If not all invokes of landingpad are compatible then we still merge compatible ones. define void @t15_three_invokes_only_two_compatible() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t15_three_invokes_only_two_compatible() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN2:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN2]]: ; CHECK-NEXT: invoke void @another_simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT3:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT3]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.end if.then2: invoke void @another_simple_throw() to label %invoke.cont3 unwind label %lpad invoke.cont3: unreachable if.end: call void @sideeffect() ret void } ; We succeed in merging invokes into two sets define void @t16_four_invokes_forming_two_sets() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t16_four_invokes_forming_two_sets() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN3_INVOKE:.*]], label %[[IF_ELSE2:.*]] ; CHECK: [[IF_ELSE2]]: ; CHECK-NEXT: [[C3:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C3]], label %[[IF_THEN3_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN3_INVOKE]]: ; CHECK-NEXT: invoke void @another_simple_throw() ; CHECK-NEXT: to label %[[IF_THEN3_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN3_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.else2 if.then2: invoke void @another_simple_throw() to label %invoke.cont3 unwind label %lpad invoke.cont3: unreachable if.else2: %c3 = call i1 @cond() br i1 %c3, label %if.then3, label %if.end if.then3: invoke void @another_simple_throw() to label %invoke.cont4 unwind label %lpad invoke.cont4: unreachable if.end: call void @sideeffect() ret void } define void @t17_mismatched_attrs_okay_merge() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t17_mismatched_attrs_okay_merge() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() readnone to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } define void @t17_mismatched_attrs_okay_merge_intersect() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t17_mismatched_attrs_okay_merge_intersect() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR2:[0-9]+]] ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() readnone cold to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() readnone to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } define void @t17_mismatched_attrs_okay_merge_intersect2() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t17_mismatched_attrs_okay_merge_intersect2() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR2]] ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() readnone to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() readnone cold to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } define void @t17_mismatched_attrs_prevent_merge() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t17_mismatched_attrs_prevent_merge() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR3:[0-9]+]] ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() strictfp to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Common attributes are preserved define void @t18_attributes_are_preserved() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t18_attributes_are_preserved() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() #[[ATTR2]] ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() readnone to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() readnone to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Fully identical operand bundles are good. define void @t19_compatible_operand_bundle() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t19_compatible_operand_bundle() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() [ "abc"(i32 42) ] ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() [ "abc"(i32 42) ] to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() [ "abc"(i32 42) ] to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Operand bundles must be compatible, else we can't merge. define void @t20_incompatible_operand_bundle() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t20_incompatible_operand_bundle() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() [ "abc"(i32 42) ] ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() [ "def"(i32 0) ] ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() [ "abc"(i32 42) ] to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() [ "def"(i32 0) ] to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; We need to PHI together the arguments of the operand bundles. define void @t21_semicompatible_operand_bundle(i32 %a, i32 %b) personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t21_semicompatible_operand_bundle( ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ [[B]], %[[IF_ELSE]] ], [ [[A]], %[[ENTRY]] ] ; CHECK-NEXT: invoke void @simple_throw() [ "abc"(i32 [[TMP0]]) ] ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @simple_throw() [ "abc"(i32 %a) ] to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @simple_throw() [ "abc"(i32 %b) ] to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Even though the normal destinations are unreachable, ; they may have (dead) PHI nodes, so we must cleanup them. define void @t22_dead_phi_in_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t22_dead_phi_in_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @maybe_throw() to label %invoke.cont0 unwind label %lpad lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @maybe_throw() to label %invoke.cont2 unwind label %lpad invoke.cont0: %deadphi0 = phi i32 [ 0, %if.then0 ] unreachable invoke.cont2: %deadphi2 = phi i32 [ 0, %if.then1 ] unreachable if.end: call void @sideeffect() ret void } ; landingpad has PHI nodes, and out of three invokes, only two have compatible incoming values. define void @t23_phi_in_landingpad_compatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t23_phi_in_landingpad_compatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ -1, %[[IF_THEN2:.*]] ], [ 0, %[[IF_THEN1_INVOKE]] ] ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @consume(i32 [[PHI]]) ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN2]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN2]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT3:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT3]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %phi = phi i32 [ 0, %if.then0 ], [ 0, %if.then1 ], [ -1, %if.then2 ] %eh = landingpad { ptr, i32 } cleanup call void @consume(i32 %phi) call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.end if.then2: invoke void @simple_throw() to label %invoke.cont3 unwind label %lpad invoke.cont3: unreachable if.end: call void @sideeffect() ret void } ; landingpad has two PHI nodes, but depending on which PHI you look, ; the invoke sets would be different, so we can't merge invokes here. define void @t24_phi_in_landingpad_semi_compatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t24_phi_in_landingpad_semi_compatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[PHI0:%.*]] = phi i32 [ 0, %[[IF_THEN0]] ], [ 0, %[[IF_THEN1:.*]] ], [ -1, %[[IF_THEN2:.*]] ] ; CHECK-NEXT: [[PHI1:%.*]] = phi i32 [ 0, %[[IF_THEN0]] ], [ 1, %[[IF_THEN1]] ], [ 1, %[[IF_THEN2]] ] ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @consume(i32 [[PHI0]]) ; CHECK-NEXT: call void @consume(i32 [[PHI1]]) ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN2]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN2]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT3:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT3]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void @simple_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %phi0 = phi i32 [ 0, %if.then0 ], [ 0, %if.then1 ], [ -1, %if.then2 ] %phi1= phi i32 [ 0, %if.then0 ], [ 1, %if.then1 ], [ 1, %if.then2 ] %eh = landingpad { ptr, i32 } cleanup call void @consume(i32 %phi0) call void @consume(i32 %phi1) call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void @simple_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.end if.then2: invoke void @simple_throw() to label %invoke.cont3 unwind label %lpad invoke.cont3: unreachable if.end: call void @sideeffect() ret void } ; The normal destinations are shared, but the incoming values are incompatible. define void @t25_incompatible_phis_in_normal_destination() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t25_incompatible_phis_in_normal_destination() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT]]: ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[IF_THEN0]] ], [ -1, %[[IF_THEN1:.*]] ] ; CHECK-NEXT: call void @consume(i32 [[PHI]]) ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: %phi = phi i32 [ 0, %if.then0 ], [ -1, %if.then1 ] call void @consume(i32 %phi) call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; shared normal destination has PHI nodes, and out of three invokes, only two have compatible incoming values. define void @t26_phi_in_normal_dest_compatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t26_phi_in_normal_dest_compatible_incoming_values() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[INVOKE_CONT:.*]]: ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ -1, %[[IF_THEN2:.*]] ], [ 0, %[[IF_THEN1_INVOKE]] ] ; CHECK-NEXT: call void @consume(i32 [[PHI]]) ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN2]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN2]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: %phi = phi i32 [ 0, %if.then0 ], [ 0, %if.then1 ], [ -1, %if.then2 ] call void @consume(i32 %phi) call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.end if.then2: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; Invokes return values, but they are unused. define void @t27_invoke_ret_value_is_used() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t27_invoke_ret_value_is_used() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[INVOKE_CONT:.*]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0:%.*]] = invoke i32 @returning_maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: %v0 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: %v1 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; Invokes return values, and they are used in a phi node, making the incoming values incompatible. define void @t28_invoke_ret_value_is_used_in_phi_node() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t28_invoke_ret_value_is_used_in_phi_node() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[INVOKE_CONT:.*]]: ; CHECK-NEXT: call void @consume(i32 [[TMP0:%.*]]) ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0]] = invoke i32 @returning_maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: %v0 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: %phi = phi i32 [ %v0, %if.then0 ], [ %v1, %if.then1 ] call void @consume(i32 %phi) call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: %v1 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; out of three invokes, two share normal destination and another one has unreachable destination define void @t29_common_normal_destination_and_unreachable_normal_destination() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t29_common_normal_destination_and_unreachable_normal_destination() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[INVOKE_CONT0:.*]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0]] unwind label %[[LPAD]] ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN2:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN2]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void @maybe_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void @maybe_throw() to label %invoke.cont0 unwind label %lpad if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.end if.then2: invoke void @maybe_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; normal destinations are not unreachable and different but could be merged define void @t30_completely_different_normal_dests() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t30_completely_different_normal_dests() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT0:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT0]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT2]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @maybe_throw() to label %invoke.cont0 unwind label %lpad invoke.cont0: call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @maybe_throw() to label %invoke.cont2 unwind label %lpad invoke.cont2: call void @sideeffect() unreachable if.end: call void @sideeffect() ret void } ; Even though the normal destinations are unreachable, ; they may have (dead) PHI nodes with incompatible incoming values, ; so we must cleanup them. define void @t31_incompatible_dead_phi_in_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t31_incompatible_dead_phi_in_normal_dest() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void @maybe_throw() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void @maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: %deadphi0 = phi i32 [ 0, %if.then0 ], [ -1, %if.then1 ] unreachable if.end: call void @sideeffect() ret void } ; Invokes return values, and they are used in a phi node, making the incoming values incompatible, ; second phi has compatible incoming values define void @t32_invoke_ret_value_is_used_in_phi_node_other_phi_is_fine() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t32_invoke_ret_value_is_used_in_phi_node_other_phi_is_fine() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[INVOKE_CONT:.*]]: ; CHECK-NEXT: call void @consume(i32 [[TMP0:%.*]]) ; CHECK-NEXT: call void @consume(i32 0) ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0]] = invoke i32 @returning_maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: %v0 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: %phi0 = phi i32 [ %v0, %if.then0 ], [ %v1, %if.then1 ] %phi1 = phi i32 [ 0, %if.then0 ], [ 0, %if.then1 ] call void @consume(i32 %phi0) call void @consume(i32 %phi1) call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: %v1 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; Invokes return values, and they are used in a phi node, making the incoming values incompatible, ; second phi has incompatible incoming values. define void @t33_invoke_ret_value_is_used_in_phi_node_other_phi_is_bad() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t33_invoke_ret_value_is_used_in_phi_node_other_phi_is_bad() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: [[V0:%.*]] = invoke i32 @returning_maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT]]: ; CHECK-NEXT: [[PHI0:%.*]] = phi i32 [ [[V0]], %[[IF_THEN0]] ], [ [[V1:%.*]], %[[IF_THEN1:.*]] ] ; CHECK-NEXT: [[PHI1:%.*]] = phi i32 [ 0, %[[IF_THEN0]] ], [ -1, %[[IF_THEN1]] ] ; CHECK-NEXT: call void @consume(i32 [[PHI0]]) ; CHECK-NEXT: call void @consume(i32 [[PHI1]]) ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: [[V1]] = invoke i32 @returning_maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: %v0 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: %phi0 = phi i32 [ %v0, %if.then0 ], [ %v1, %if.then1 ] %phi1 = phi i32 [ 0, %if.then0 ], [ -1, %if.then1 ] call void @consume(i32 %phi0) call void @consume(i32 %phi1) call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: %v1 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; Invokes return values, and they are used in a phi node, but when coming from different invokes, ; the incoming value isn't always the invoke, which is not okay. define void @t34_invoke_ret_value_maybe_incompatibly_used_in_phi_node() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t34_invoke_ret_value_maybe_incompatibly_used_in_phi_node() personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN0:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_THEN0]]: ; CHECK-NEXT: [[V0:%.*]] = invoke i32 @returning_maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[INVOKE_CONT]]: ; CHECK-NEXT: [[PHI0:%.*]] = phi i32 [ [[V0]], %[[IF_THEN0]] ], [ 0, %[[IF_THEN1:.*]] ] ; CHECK-NEXT: [[PHI1:%.*]] = phi i32 [ 0, %[[IF_THEN0]] ], [ [[V1:%.*]], %[[IF_THEN1]] ] ; CHECK-NEXT: call void @consume(i32 [[PHI0]]) ; CHECK-NEXT: call void @consume(i32 [[PHI1]]) ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1]]: ; CHECK-NEXT: [[V1]] = invoke i32 @returning_maybe_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT]] unwind label %[[LPAD]] ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: %v0 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad invoke.cont: %phi0 = phi i32 [ %v0, %if.then0 ], [ 0, %if.then1 ] %phi1 = phi i32 [ 0, %if.then0 ], [ %v1, %if.then1 ] call void @consume(i32 %phi0) call void @consume(i32 %phi1) call void @sideeffect() unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: %v1 = invoke i32 @returning_maybe_throw() to label %invoke.cont unwind label %lpad if.end: call void @sideeffect() ret void } ; Two mergeable indirect calls, with identical callees. define void @t35_identical_indirect_callees(ptr %callee) personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t35_identical_indirect_callees( ; CHECK-SAME: ptr [[CALLEE:%.*]]) personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void [[CALLEE]]() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void %callee() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void %callee() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Two mergeable indirect calls, with different callees. define void @t36_different_indirect_callees(ptr %callee0, ptr %callee1) personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t36_different_indirect_callees( ; CHECK-SAME: ptr [[CALLEE0:%.*]], ptr [[CALLEE1:%.*]]) personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi ptr [ [[CALLEE1]], %[[IF_ELSE]] ], [ [[CALLEE0]], %[[ENTRY]] ] ; CHECK-NEXT: invoke void [[TMP0]]() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void %callee0() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void %callee1() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; Don't merge direct invoke with indirect ones. define void @t37_three_invokes_two_indirect_one_direct(ptr %callee) personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t37_three_invokes_two_indirect_one_direct( ; CHECK-SAME: ptr [[CALLEE:%.*]]) personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE0:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE0]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_ELSE1:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: invoke void [[CALLEE]]() ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_ELSE1]]: ; CHECK-NEXT: [[C2:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C2]], label %[[IF_THEN2:.*]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN2]]: ; CHECK-NEXT: invoke void @simple_throw() ; CHECK-NEXT: to label %[[INVOKE_CONT3:.*]] unwind label %[[LPAD]] ; CHECK: [[INVOKE_CONT3]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else0 if.then0: invoke void %callee() to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else0: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.else1 if.then1: invoke void %callee() to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.else1: %c2 = call i1 @cond() br i1 %c2, label %if.then2, label %if.end if.then2: invoke void @simple_throw() to label %invoke.cont3 unwind label %lpad invoke.cont3: unreachable if.end: call void @sideeffect() ret void } ; For indirect invokes, different arguments are fine. define void @t38_different_arguments_and_operand_bundes_are_fine(ptr %callee0, ptr %callee1) personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t38_different_arguments_and_operand_bundes_are_fine( ; CHECK-SAME: ptr [[CALLEE0:%.*]], ptr [[CALLEE1:%.*]]) personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 42, %[[IF_ELSE]] ], [ 0, %[[ENTRY]] ] ; CHECK-NEXT: [[TMP1:%.*]] = phi ptr [ [[CALLEE1]], %[[IF_ELSE]] ], [ [[CALLEE0]], %[[ENTRY]] ] ; CHECK-NEXT: invoke void [[TMP1]](i32 [[TMP0]]) ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void %callee0(i32 0) to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void %callee1(i32 42) to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; For indirect invokes, different operand bundle arguments are fine. define void @t39_different_arguments_and_operand_bundes_are_fine(ptr %callee0, ptr %callee1, i32 %a, i32 %b) personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t39_different_arguments_and_operand_bundes_are_fine( ; CHECK-SAME: ptr [[CALLEE0:%.*]], ptr [[CALLEE1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ [[B]], %[[IF_ELSE]] ], [ [[A]], %[[ENTRY]] ] ; CHECK-NEXT: [[TMP1:%.*]] = phi ptr [ [[CALLEE1]], %[[IF_ELSE]] ], [ [[CALLEE0]], %[[ENTRY]] ] ; CHECK-NEXT: invoke void [[TMP1]]() [ "abc"(i32 [[TMP0]]) ] ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void %callee0() [ "abc"(i32 %a) ] to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void %callee1() [ "abc"(i32 %b) ] to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } ; For indirect invokes, both different arguments and operand bundle arguments are fine. define void @t40_different_arguments_and_operand_bundes_are_fine(ptr %callee0, ptr %callee1, i32 %a, i32 %b) personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define void @t40_different_arguments_and_operand_bundes_are_fine( ; CHECK-SAME: ptr [[CALLEE0:%.*]], ptr [[CALLEE1:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C0]], label %[[IF_THEN1_INVOKE:.*]], label %[[IF_ELSE:.*]] ; CHECK: [[LPAD:.*]]: ; CHECK-NEXT: [[EH:%.*]] = landingpad { ptr, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: call void @destructor() ; CHECK-NEXT: resume { ptr, i32 } [[EH]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: br i1 [[C1]], label %[[IF_THEN1_INVOKE]], label %[[IF_END:.*]] ; CHECK: [[IF_THEN1_INVOKE]]: ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 42, %[[IF_ELSE]] ], [ 0, %[[ENTRY]] ] ; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ [[B]], %[[IF_ELSE]] ], [ [[A]], %[[ENTRY]] ] ; CHECK-NEXT: [[TMP2:%.*]] = phi ptr [ [[CALLEE1]], %[[IF_ELSE]] ], [ [[CALLEE0]], %[[ENTRY]] ] ; CHECK-NEXT: invoke void [[TMP2]](i32 [[TMP0]]) [ "abc"(i32 [[TMP1]]) ] ; CHECK-NEXT: to label %[[IF_THEN1_CONT:.*]] unwind label %[[LPAD]] ; CHECK: [[IF_THEN1_CONT]]: ; CHECK-NEXT: unreachable ; CHECK: [[IF_END]]: ; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: ret void ; entry: %c0 = call i1 @cond() br i1 %c0, label %if.then0, label %if.else if.then0: invoke void %callee0(i32 0) [ "abc"(i32 %a) ] to label %invoke.cont0 unwind label %lpad invoke.cont0: unreachable lpad: %eh = landingpad { ptr, i32 } cleanup call void @destructor() resume { ptr, i32 } %eh if.else: %c1 = call i1 @cond() br i1 %c1, label %if.then1, label %if.end if.then1: invoke void %callee1(i32 42) [ "abc"(i32 %b) ] to label %invoke.cont2 unwind label %lpad invoke.cont2: unreachable if.end: call void @sideeffect() ret void } define void @dont_merge_different_immargs(i1 %c1) gc "statepoint-example" personality ptr null { ; CHECK-LABEL: define void @dont_merge_different_immargs( ; CHECK-SAME: i1 [[C1:%.*]]) gc "statepoint-example" personality ptr null { ; CHECK-NEXT: br i1 [[C1]], label %[[IF:.*]], label %[[ELSE:.*]] ; CHECK: [[IF]]: ; CHECK-NEXT: [[T1:%.*]] = invoke token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 1, i32 0, ptr elementtype(void (ptr addrspace(1))) null, i32 1, i32 0, ptr addrspace(1) null, i64 0, i64 0) ; CHECK-NEXT: to label %[[UNREACHABLE:.*]] unwind label %[[LPAD:.*]] ; CHECK: [[ELSE]]: ; CHECK-NEXT: [[T2:%.*]] = invoke token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (ptr addrspace(1))) null, i32 1, i32 0, ptr addrspace(1) null, i64 0, i64 0) ; CHECK-NEXT: to label %[[UNREACHABLE]] unwind label %[[LPAD]] ; CHECK: [[UNREACHABLE]]: ; CHECK-NEXT: unreachable ; CHECK: [[LPAD]]: ; CHECK-NEXT: [[T3:%.*]] = landingpad token ; CHECK-NEXT: cleanup ; CHECK-NEXT: ret void ; br i1 %c1, label %if, label %else if: %t1 = invoke token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 1, i32 0, ptr elementtype(void (ptr addrspace(1))) null, i32 1, i32 0, ptr addrspace(1) null, i64 0, i64 0) to label %unreachable unwind label %lpad else: %t2 = invoke token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (ptr addrspace(1))) null, i32 1, i32 0, ptr addrspace(1) null, i64 0, i64 0) to label %unreachable unwind label %lpad unreachable: unreachable lpad: %t3 = landingpad token cleanup ret void } declare token @llvm.experimental.gc.statepoint.p0(i64 immarg, i32 immarg, ptr, i32 immarg, i32 immarg, ...) declare i1 @cond() declare void @sideeffect() declare void @another_sideeffect() declare void @maybe_throw() declare void @simple_throw() noreturn declare void @another_simple_throw() noreturn declare void @simple_throw_taking_argument(i32) noreturn declare i32 @returning_maybe_throw() declare void @destructor() declare void @another_destructor() declare void @consume(i32) declare dso_local i32 @__gxx_personality_v0(...) ;. ; CHECK: attributes #[[ATTR0:[0-9]+]] = { noreturn } ; CHECK: attributes #[[ATTR1]] = { nomerge } ; CHECK: attributes #[[ATTR2]] = { memory(none) } ; CHECK: attributes #[[ATTR3]] = { strictfp } ;.