summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/CodeGen/CGCall.cpp6
-rw-r--r--clang/test/CodeGenCXX/ubsan-unreachable.cpp33
-rw-r--r--compiler-rt/test/ubsan/TestCases/Misc/unreachable_asan-compatibility.c16
-rw-r--r--llvm/docs/LangRef.rst4
-rw-r--r--llvm/include/llvm/Bitcode/LLVMBitCodes.h1
-rw-r--r--llvm/include/llvm/IR/Attributes.td4
-rw-r--r--llvm/lib/AsmParser/LLLexer.cpp1
-rw-r--r--llvm/lib/AsmParser/LLParser.cpp4
-rw-r--r--llvm/lib/AsmParser/LLToken.h1
-rw-r--r--llvm/lib/Bitcode/Reader/BitcodeReader.cpp6
-rw-r--r--llvm/lib/Bitcode/Writer/BitcodeWriter.cpp2
-rw-r--r--llvm/lib/IR/Attributes.cpp2
-rw-r--r--llvm/lib/IR/Verifier.cpp1
-rw-r--r--llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp1
-rw-r--r--llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp3
-rw-r--r--llvm/lib/Transforms/Utils/CodeExtractor.cpp1
-rw-r--r--llvm/test/Bitcode/attributes.ll11
-rw-r--r--llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll48
18 files changed, 104 insertions, 41 deletions
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index a650493c5ba..bebefe5384a 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4401,12 +4401,16 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
if (UnusedReturnSizePtr)
PopCleanupBlock();
- // Strip away the noreturn attribute to better diagnose unreachable UB.
+ // Replace the noreturn attribute to better diagnose unreachable UB.
if (SanOpts.has(SanitizerKind::Unreachable)) {
+ // Also remove from function since CS.hasFnAttr(..) also checks attributes
+ // of the called function.
if (auto *F = CS.getCalledFunction())
F->removeFnAttr(llvm::Attribute::NoReturn);
CS.removeAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::NoReturn);
+ CS.addAttribute(llvm::AttributeList::FunctionIndex,
+ llvm::Attribute::ExpectNoReturn);
}
EmitUnreachable(Loc);
diff --git a/clang/test/CodeGenCXX/ubsan-unreachable.cpp b/clang/test/CodeGenCXX/ubsan-unreachable.cpp
index 32a78048cfd..9ef613e630c 100644
--- a/clang/test/CodeGenCXX/ubsan-unreachable.cpp
+++ b/clang/test/CodeGenCXX/ubsan-unreachable.cpp
@@ -2,38 +2,35 @@
extern void __attribute__((noreturn)) abort();
-// CHECK-LABEL: define void @_Z14calls_noreturnv
+// CHECK-LABEL: define void @_Z14calls_noreturnv()
void calls_noreturn() {
+ // CHECK: call void @_Z5abortv() [[CALL_SITE_ATTR:#[0-9]+]]
abort();
- // Check that there are no attributes on the call site.
- // CHECK-NOT: call void @_Z5abortv{{.*}}#
-
// CHECK: __ubsan_handle_builtin_unreachable
// CHECK: unreachable
}
struct A {
- // CHECK: declare void @_Z5abortv{{.*}} [[ABORT_ATTR:#[0-9]+]]
+ // CHECK: declare void @_Z5abortv() [[EXTERN_FN_ATTR:#[0-9]+]]
// CHECK-LABEL: define linkonce_odr void @_ZN1A5call1Ev
void call1() {
- // CHECK-NOT: call void @_ZN1A16does_not_return2Ev{{.*}}#
+ // CHECK: call void @_ZN1A16does_not_return2Ev({{.*}}) [[CALL_SITE_ATTR]]
does_not_return2();
// CHECK: __ubsan_handle_builtin_unreachable
// CHECK: unreachable
}
- // Test static members.
+ // Test static members. Checks are below after `struct A` scope ends.
static void __attribute__((noreturn)) does_not_return1() {
- // CHECK-NOT: call void @_Z5abortv{{.*}}#
abort();
}
// CHECK-LABEL: define linkonce_odr void @_ZN1A5call2Ev
void call2() {
- // CHECK-NOT: call void @_ZN1A16does_not_return1Ev{{.*}}#
+ // CHECK: call void @_ZN1A16does_not_return1Ev() [[CALL_SITE_ATTR]]
does_not_return1();
// CHECK: __ubsan_handle_builtin_unreachable
@@ -46,18 +43,18 @@ struct A {
// CHECK-LABEL: define linkonce_odr void @_ZN1A5call3Ev
void call3() {
MemFn MF = &A::does_not_return2;
+ // CHECK: call void %{{[0-9]+\(.*}}) [[CALL_SITE_ATTR]]
(this->*MF)();
- // CHECK-NOT: call void %{{.*}}#
// CHECK: __ubsan_handle_builtin_unreachable
// CHECK: unreachable
}
// Test regular members.
// CHECK-LABEL: define linkonce_odr void @_ZN1A16does_not_return2Ev({{.*}})
- // CHECK-SAME: [[DOES_NOT_RETURN_ATTR:#[0-9]+]]
+ // CHECK-SAME: [[USER_FN_ATTR:#[0-9]+]]
void __attribute__((noreturn)) does_not_return2() {
- // CHECK-NOT: call void @_Z5abortv(){{.*}}#
+ // CHECK: call void @_Z5abortv() [[CALL_SITE_ATTR]]
abort();
// CHECK: call void @__ubsan_handle_builtin_unreachable
@@ -68,7 +65,9 @@ struct A {
}
};
-// CHECK: define linkonce_odr void @_ZN1A16does_not_return1Ev() [[DOES_NOT_RETURN_ATTR]]
+// CHECK-LABEL: define linkonce_odr void @_ZN1A16does_not_return1Ev()
+// CHECK-SAME: [[USER_FN_ATTR]]
+// CHECK: call void @_Z5abortv() [[CALL_SITE_ATTR]]
void force_irgen() {
A a;
@@ -77,5 +76,9 @@ void force_irgen() {
a.call3();
}
-// CHECK-NOT: [[ABORT_ATTR]] = {{[^}]+}}noreturn
-// CHECK-NOT: [[DOES_NOT_RETURN_ATTR]] = {{[^}]+}}noreturn
+// 1) 'noreturn' should be removed from functions and call sites
+// 2) 'expect_noreturn' added to call sites
+// CHECK-LABEL: attributes
+// CHECK: [[USER_FN_ATTR]] = { {{.*[^noreturn].*}} }
+// CHECK: [[EXTERN_FN_ATTR]] = { {{.*[^noreturn].*}} }
+// CHECK: [[CALL_SITE_ATTR]] = { expect_noreturn }
diff --git a/compiler-rt/test/ubsan/TestCases/Misc/unreachable_asan-compatibility.c b/compiler-rt/test/ubsan/TestCases/Misc/unreachable_asan-compatibility.c
new file mode 100644
index 00000000000..473e32b872e
--- /dev/null
+++ b/compiler-rt/test/ubsan/TestCases/Misc/unreachable_asan-compatibility.c
@@ -0,0 +1,16 @@
+// Ensure compatiblity of UBSan unreachable with ASan in the presence of
+// noreturn functions
+// RUN: %clang -O2 -fsanitize=address,unreachable %s -emit-llvm -S -o - | FileCheck %s
+// REQUIRES: ubsan-asan
+
+void bar(void) __attribute__((noreturn));
+
+void foo() {
+ bar();
+}
+// CHECK-LABEL: define void @foo()
+// CHECK: call void @__asan_handle_no_return
+// CHECK-NEXT: call void @bar
+// CHECK-NEXT: call void @__asan_handle_no_return
+// CHECK-NEXT: call void @__ubsan_handle_builtin_unreachable
+// CHECK-NEXT: unreachable
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 65c8c75a22d..32c4d458e4e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1458,6 +1458,10 @@ example:
This function attribute indicates that the function never returns
normally. This produces undefined behavior at runtime if the
function ever does dynamically return.
+``expect_noreturn``
+ This function attribute indicates that the function is unlikely to return
+ normally, but that it still allowed to do so. This is useful in cases where
+ ``noreturn`` is too strong a guarantee.
``norecurse``
This function attribute indicates that the function does not call itself
either directly or indirectly down any possible call path. This produces
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index ce853cd3998..0015668e383 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -603,6 +603,7 @@ enum AttributeKindCodes {
ATTR_KIND_OPT_FOR_FUZZING = 57,
ATTR_KIND_SHADOWCALLSTACK = 58,
ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
+ ATTR_KIND_EXPECT_NO_RETURN = 60,
};
enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index e786d85d05a..dc972cf5a09 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -106,6 +106,10 @@ def NoRedZone : EnumAttr<"noredzone">;
/// Mark the function as not returning.
def NoReturn : EnumAttr<"noreturn">;
+/// Mark the function as unlikely to return. This is useful in cases where
+/// `noreturn` is too strong a guarantee.
+def ExpectNoReturn : EnumAttr<"expect_noreturn">;
+
/// Disable Indirect Branch Tracking.
def NoCfCheck : EnumAttr<"nocf_check">;
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index b543115a88e..a60639bef0c 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -656,6 +656,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nonnull);
KEYWORD(noredzone);
KEYWORD(noreturn);
+ KEYWORD(expect_noreturn);
KEYWORD(nocf_check);
KEYWORD(nounwind);
KEYWORD(optforfuzzing);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 855c5d26500..9167694607f 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1248,6 +1248,8 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break;
+ case lltok::kw_expect_noreturn:
+ B.addAttribute(Attribute::ExpectNoReturn); break;
case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break;
case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;
@@ -1611,6 +1613,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_nonlazybind:
case lltok::kw_noredzone:
case lltok::kw_noreturn:
+ case lltok::kw_expect_noreturn:
case lltok::kw_nocf_check:
case lltok::kw_nounwind:
case lltok::kw_optforfuzzing:
@@ -1708,6 +1711,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_nonlazybind:
case lltok::kw_noredzone:
case lltok::kw_noreturn:
+ case lltok::kw_expect_noreturn:
case lltok::kw_nocf_check:
case lltok::kw_nounwind:
case lltok::kw_optforfuzzing:
diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h
index 41899b29ce5..b90394f4c1f 100644
--- a/llvm/lib/AsmParser/LLToken.h
+++ b/llvm/lib/AsmParser/LLToken.h
@@ -200,6 +200,7 @@ enum Kind {
kw_nonnull,
kw_noredzone,
kw_noreturn,
+ kw_expect_noreturn,
kw_nocf_check,
kw_nounwind,
kw_optforfuzzing,
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 0b93a61dc40..d377b6ae515 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -1186,8 +1186,8 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
case Attribute::NoCfCheck: return 1ULL << 57;
case Attribute::OptForFuzzing: return 1ULL << 58;
case Attribute::ShadowCallStack: return 1ULL << 59;
- case Attribute::SpeculativeLoadHardening:
- return 1ULL << 60;
+ case Attribute::SpeculativeLoadHardening: return 1ULL << 60;
+ case Attribute::ExpectNoReturn: return 1ULL << 61;
case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format");
break;
@@ -1366,6 +1366,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::NoRedZone;
case bitc::ATTR_KIND_NO_RETURN:
return Attribute::NoReturn;
+ case bitc::ATTR_KIND_EXPECT_NO_RETURN:
+ return Attribute::ExpectNoReturn;
case bitc::ATTR_KIND_NOCF_CHECK:
return Attribute::NoCfCheck;
case bitc::ATTR_KIND_NO_UNWIND:
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index f4a539e51f7..f16cdacaae3 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -654,6 +654,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_NO_RED_ZONE;
case Attribute::NoReturn:
return bitc::ATTR_KIND_NO_RETURN;
+ case Attribute::ExpectNoReturn:
+ return bitc::ATTR_KIND_EXPECT_NO_RETURN;
case Attribute::NoCfCheck:
return bitc::ATTR_KIND_NOCF_CHECK;
case Attribute::NoUnwind:
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index a474d5740fe..ab49adba81c 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -298,6 +298,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "noredzone";
if (hasAttribute(Attribute::NoReturn))
return "noreturn";
+ if (hasAttribute(Attribute::ExpectNoReturn))
+ return "expect_noreturn";
if (hasAttribute(Attribute::NoCfCheck))
return "nocf_check";
if (hasAttribute(Attribute::NoRecurse))
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 1000e210533..3b3e3158059 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1477,6 +1477,7 @@ void Verifier::visitModuleFlagCGProfileEntry(const MDOperand &MDO) {
static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
switch (Kind) {
case Attribute::NoReturn:
+ case Attribute::ExpectNoReturn:
case Attribute::NoCfCheck:
case Attribute::NoUnwind:
case Attribute::NoInline:
diff --git a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
index cd1fc379820..7300ca06ce7 100644
--- a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
@@ -41,6 +41,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) {
.Case("nonlazybind", Attribute::NonLazyBind)
.Case("noredzone", Attribute::NoRedZone)
.Case("noreturn", Attribute::NoReturn)
+ .Case("expect_noreturn", Attribute::ExpectNoReturn)
.Case("nocf_check", Attribute::NoCfCheck)
.Case("norecurse", Attribute::NoRecurse)
.Case("nounwind", Attribute::NoUnwind)
diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index 1a007b4258f..8d27ee9d1e1 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -2568,7 +2568,8 @@ bool AddressSanitizer::runOnFunction(Function &F) {
if (CS) {
// A call inside BB.
TempsToInstrument.clear();
- if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction());
+ if (CS.doesNotReturn() || CS.hasFnAttr(Attribute::ExpectNoReturn))
+ NoReturnCalls.push_back(CS.getInstruction());
}
if (CallInst *CI = dyn_cast<CallInst>(&Inst))
maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI);
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 03e2b9db078..3cf26b5e0c3 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -779,6 +779,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
case Attribute::NoBuiltin:
case Attribute::NoCapture:
case Attribute::NoReturn:
+ case Attribute::ExpectNoReturn:
case Attribute::None:
case Attribute::NonNull:
case Attribute::ReadNone:
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index de3cf8dd4d7..e96007e5629 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -204,7 +204,7 @@ define void @f34()
; CHECK: define void @f34()
{
call void @nobuiltin() nobuiltin
-; CHECK: call void @nobuiltin() #36
+; CHECK: call void @nobuiltin() #37
ret void;
}
@@ -351,6 +351,12 @@ define void @f59() shadowcallstack
ret void
}
+; CHECK: define void @f60() #36
+define void @f60() expect_noreturn
+{
+ ret void
+}
+
; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
@@ -387,4 +393,5 @@ define void @f59() shadowcallstack
; CHECK: attributes #33 = { speculatable }
; CHECK: attributes #34 = { sanitize_hwaddress }
; CHECK: attributes #35 = { shadowcallstack }
-; CHECK: attributes #36 = { nobuiltin }
+; CHECK: attributes #36 = { expect_noreturn }
+; CHECK: attributes #37 = { nobuiltin }
diff --git a/llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll b/llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll
index 2e90bfc64b2..b8e6aef9848 100644
--- a/llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll
+++ b/llvm/test/Instrumentation/AddressSanitizer/instrument-no-return.ll
@@ -1,37 +1,45 @@
-; RUN: opt < %s -asan -asan-module -S | FileCheck %s
+; RUN: opt < %s -asan -S | FileCheck %s
; AddressSanitizer must insert __asan_handle_no_return
; before every noreturn call or invoke.
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-unknown-linux-gnu"
-declare void @MyNoReturnFunc(i32) noreturn
+declare void @NormalFunc()
+declare void @NoReturnFunc() noreturn
-define i32 @Call1(i8* nocapture %arg) uwtable sanitize_address {
-entry:
- call void @MyNoReturnFunc(i32 1) noreturn ; The call insn has noreturn attr.
-; CHECK: @Call1
-; CHECK: call void @__asan_handle_no_return
-; CHECK-NEXT: call void @MyNoReturnFunc
-; CHECK-NEXT: unreachable
+; Instrument calls to noreturn functions (regardless of callsite)
+define i32 @Call1() sanitize_address {
+ call void @NoReturnFunc()
unreachable
}
-
-define i32 @Call2(i8* nocapture %arg) uwtable sanitize_address {
-entry:
- call void @MyNoReturnFunc(i32 1) ; No noreturn attribure on the call.
-; CHECK: @Call2
+; CHECK-LABEL: @Call1
; CHECK: call void @__asan_handle_no_return
-; CHECK-NEXT: call void @MyNoReturnFunc
-; CHECK-NEXT: unreachable
+; CHECK-NEXT: call void @NoReturnFunc
+
+; Instrument noreturn call sites (regardless of function)
+define i32 @Call2() sanitize_address {
+ call void @NormalFunc() noreturn
unreachable
}
+; CHECK-LABEL: @Call2
+; CHECK: call void @__asan_handle_no_return
+; CHECK-NEXT: call void @NormalFunc
+
+; Also instrument expect_noreturn call sites
+define i32 @Call3() sanitize_address {
+ call void @NormalFunc() expect_noreturn
+ ret i32 0
+}
+; CHECK-LABEL: @Call3
+; CHECK: call void @__asan_handle_no_return
+; CHECK-NEXT: call void @NormalFunc
declare i32 @__gxx_personality_v0(...)
-define i64 @Invoke1(i8** %esc) nounwind uwtable ssp sanitize_address personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+define i64 @Invoke1(i8** %esc) sanitize_address personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
entry:
- invoke void @MyNoReturnFunc(i32 1)
+ invoke void @NoReturnFunc()
to label %invoke.cont unwind label %lpad
invoke.cont:
@@ -42,8 +50,8 @@ lpad:
filter [0 x i8*] zeroinitializer
ret i64 1
}
-; CHECK: @Invoke1
+; CHECK-LABEL: @Invoke1
; CHECK: call void @__asan_handle_no_return
-; CHECK-NEXT: invoke void @MyNoReturnFunc
+; CHECK-NEXT: invoke void @NoReturnFunc
; CHECK: ret i64 0
; CHECK: ret i64 1
OpenPOWER on IntegriCloud