Files
clang-p2996/llvm/test/Transforms/FunctionAttrs/nounwind.ll
Nikita Popov 9fe78db4cd [FunctionAttrs] Fix nounwind inference for landingpads
Currently, FunctionAttrs treats landingpads as non-throwing, and
will infer nounwind for functions with landingpads (assuming they
can't unwind in some other way, e.g. via resum). There are two
problems with this:

* Non-cleanup landingpads with catch/filter clauses do not
  necessarily catch all exceptions. Unless there are catch ptr null
  or filter [0 x ptr] zeroinitializer clauses, we should assume
  that we may unwind past this landingpad. This seems like an
  outright bug.
* Cleanup landingpads are skipped during phase one unwinding, so
  we effectively need to support unwinding past them. Marking these
  nounwind is technically correct, but not compatible with how
  unwinding works in reality.

Fixes https://github.com/llvm/llvm-project/issues/61945.

Differential Revision: https://reviews.llvm.org/D147694
2023-04-14 11:46:00 +02:00

336 lines
9.3 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
; TEST 1
define i32 @foo1() {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@foo1
; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: ret i32 1
;
ret i32 1
}
; TEST 2
define i32 @scc1_foo() {
; CHECK: Function Attrs: nofree nosync nounwind memory(none)
; CHECK-LABEL: define {{[^@]+}}@scc1_foo
; CHECK-SAME: () #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @scc1_bar()
; CHECK-NEXT: ret i32 1
;
%1 = call i32 @scc1_bar()
ret i32 1
}
; TEST 3
define i32 @scc1_bar() {
; CHECK: Function Attrs: nofree nosync nounwind memory(none)
; CHECK-LABEL: define {{[^@]+}}@scc1_bar
; CHECK-SAME: () #[[ATTR1]] {
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @scc1_foo()
; CHECK-NEXT: ret i32 1
;
%1 = call i32 @scc1_foo()
ret i32 1
}
declare i32 @non_nounwind()
; TEST 4
define void @call_non_nounwind(){
; CHECK-LABEL: define {{[^@]+}}@call_non_nounwind() {
; CHECK-NEXT: [[TMP1:%.*]] = tail call i32 @non_nounwind()
; CHECK-NEXT: ret void
;
tail call i32 @non_nounwind()
ret void
}
; TEST 5 - throw
; int maybe_throw(bool canThrow) {
; if (canThrow)
; throw;
; else
; return -1;
; }
define i32 @maybe_throw(i1 zeroext %0) {
; CHECK-LABEL: define {{[^@]+}}@maybe_throw
; CHECK-SAME: (i1 zeroext [[TMP0:%.*]]) {
; CHECK-NEXT: br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
; CHECK: 2:
; CHECK-NEXT: tail call void @__cxa_rethrow()
; CHECK-NEXT: unreachable
; CHECK: 3:
; CHECK-NEXT: ret i32 -1
;
br i1 %0, label %2, label %3
2: ; preds = %1
tail call void @__cxa_rethrow() #1
unreachable
3: ; preds = %1
ret i32 -1
}
declare void @__cxa_rethrow()
; TEST 6 - catch
; int catch_thing() {
; try {
; int a = doThing(true);
; }
; catch(...) { return -1; }
; return 1;
; }
define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @__cxa_rethrow()
; CHECK-NEXT: to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
; CHECK: 1:
; CHECK-NEXT: unreachable
; CHECK: 2:
; CHECK-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: catch ptr null
; CHECK-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
; CHECK-NEXT: [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
; CHECK-NEXT: tail call void @__cxa_end_catch()
; CHECK-NEXT: ret i32 -1
;
invoke void @__cxa_rethrow() #1
to label %1 unwind label %2
1: ; preds = %0
unreachable
2: ; preds = %0
%3 = landingpad { ptr, i32 }
catch ptr null
%4 = extractvalue { ptr, i32 } %3, 0
%5 = tail call ptr @__cxa_begin_catch(ptr %4) #2
tail call void @__cxa_end_catch()
ret i32 -1
}
define i32 @catch_thing_user() {
; CHECK-LABEL: define {{[^@]+}}@catch_thing_user() {
; CHECK-NEXT: [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
; CHECK-NEXT: ret i32 [[CATCH_THING_CALL]]
;
%catch_thing_call = call i32 @catch_thing()
ret i32 %catch_thing_call
}
declare void @do_throw()
declare void @abort() nounwind
@catch_ty = external global ptr
define void @catch_specific_landingpad() personality ptr @__gxx_personality_v0 {
; CHECK: Function Attrs: noreturn
; CHECK-LABEL: define {{[^@]+}}@catch_specific_landingpad
; CHECK-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @do_throw()
; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; CHECK: lpad:
; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: catch ptr @catch_ty
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: unreachable:
; CHECK-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
lpad:
%lp = landingpad { ptr, i32 }
catch ptr @catch_ty
call void @abort()
unreachable
unreachable:
unreachable
}
define void @catch_all_landingpad() personality ptr @__gxx_personality_v0 {
; CHECK: Function Attrs: noreturn nounwind
; CHECK-LABEL: define {{[^@]+}}@catch_all_landingpad
; CHECK-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @do_throw()
; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; CHECK: lpad:
; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: catch ptr null
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: unreachable:
; CHECK-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
lpad:
%lp = landingpad { ptr, i32 }
catch ptr null
call void @abort()
unreachable
unreachable:
unreachable
}
define void @filter_specific_landingpad() personality ptr @__gxx_personality_v0 {
; CHECK: Function Attrs: noreturn
; CHECK-LABEL: define {{[^@]+}}@filter_specific_landingpad
; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @do_throw()
; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; CHECK: lpad:
; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: filter [1 x ptr] [ptr @catch_ty]
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: unreachable:
; CHECK-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
lpad:
%lp = landingpad { ptr, i32 }
filter [1 x ptr] [ptr @catch_ty]
call void @abort()
unreachable
unreachable:
unreachable
}
define void @filter_none_landingpad() personality ptr @__gxx_personality_v0 {
; CHECK: Function Attrs: noreturn nounwind
; CHECK-LABEL: define {{[^@]+}}@filter_none_landingpad
; CHECK-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @do_throw()
; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; CHECK: lpad:
; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: filter [0 x ptr] zeroinitializer
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: unreachable:
; CHECK-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
lpad:
%lp = landingpad { ptr, i32 }
filter [0 x ptr] zeroinitializer
call void @abort()
unreachable
unreachable:
unreachable
}
define void @cleanup_landingpad() personality ptr @__gxx_personality_v0 {
; CHECK: Function Attrs: noreturn
; CHECK-LABEL: define {{[^@]+}}@cleanup_landingpad
; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @do_throw()
; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
; CHECK: lpad:
; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: unreachable:
; CHECK-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %lpad
lpad:
%lp = landingpad { ptr, i32 }
cleanup
call void @abort()
unreachable
unreachable:
unreachable
}
define void @cleanuppad() personality ptr @__gxx_personality_v0 {
; CHECK: Function Attrs: noreturn
; CHECK-LABEL: define {{[^@]+}}@cleanuppad
; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @do_throw()
; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
; CHECK: cpad:
; CHECK-NEXT: [[CP:%.*]] = cleanuppad within none []
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: unreachable:
; CHECK-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %cpad
cpad:
%cp = cleanuppad within none []
call void @abort()
unreachable
unreachable:
unreachable
}
define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 {
; CHECK: Function Attrs: noreturn
; CHECK-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: invoke void @do_throw()
; CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
; CHECK: cs:
; CHECK-NEXT: [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
; CHECK: catch:
; CHECK-NEXT: [[C:%.*]] = catchpad within [[TOK]] [ptr @catch_ty, i32 0, ptr null]
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: cpad:
; CHECK-NEXT: [[CP:%.*]] = cleanuppad within none []
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: unreachable:
; CHECK-NEXT: unreachable
;
invoke void @do_throw()
to label %unreachable unwind label %cs
cs:
%tok = catchswitch within none [label %catch] unwind label %cpad
catch:
%c = catchpad within %tok [ptr @catch_ty, i32 0, ptr null]
call void @abort()
unreachable
cpad:
%cp = cleanuppad within none []
call void @abort()
unreachable
unreachable:
unreachable
}
declare i32 @__gxx_personality_v0(...)
declare ptr @__cxa_begin_catch(ptr)
declare void @__cxa_end_catch()