[LLVMbugs] [Bug 14116] New: Inliner incorrectly combines cleanup and catch landing pads
bugzilla-daemon at llvm.org
bugzilla-daemon at llvm.org
Thu Oct 18 05:05:58 PDT 2012
http://llvm.org/bugs/show_bug.cgi?id=14116
Bug #: 14116
Summary: Inliner incorrectly combines cleanup and catch landing
pads
Product: new-bugs
Version: trunk
Platform: PC
OS/Version: All
Status: NEW
Severity: enhancement
Priority: P
Component: new bugs
AssignedTo: unassignedbugs at nondot.org
ReportedBy: csdavec at swan.ac.uk
CC: llvmbugs at cs.uiuc.edu
Classification: Unclassified
The LLVM inliner combines catch and cleanups into a single landing pad and then
merges their tails. This is problematic, because some personality functions
(e.g. GNU Objective-C) return different values in exception register 0
depending on whether the landing pad is a catch or a cleanup. Consider the
following example:
define i32 @x() uwtable {
entry:
%retval = alloca i32, align 4
%x = alloca i32, align 4
%exn.slot = alloca i8*
%ehselector.slot = alloca i32
invoke void @objc_exception_throw(i8* null)
to label %invoke.cont unwind label %lpad
invoke.cont: ; preds = %entry
unreachable
lpad: ; preds = %entry
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
@__gnu_objc_personality_v0 to i8*)
cleanup
%1 = extractvalue { i8*, i32 } %0, 0
store i8* %1, i8** %exn.slot
%2 = extractvalue { i8*, i32 } %0, 1
store i32 %2, i32* %ehselector.slot
%3 = bitcast i32* %x to i8*
invoke void @foo(i8* %3)
to label %invoke.cont1 unwind label %terminate.lpad
invoke.cont1: ; preds = %lpad
br label %eh.resume
return: ; No predecessors!
%4 = load i32* %retval
ret i32 %4
eh.resume: ; preds = %invoke.cont1
%exn = load i8** %exn.slot
%sel = load i32* %ehselector.slot
%lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0
%lpad.val2 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1
resume { i8*, i32 } %lpad.val2
terminate.lpad: ; preds = %lpad
%5 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
@__gnu_objc_personality_v0 to i8*)
catch i8* null
call void @abort() noreturn nounwind
unreachable
}
declare void @foo(i8*)
declare void @objc_exception_throw(i8*)
declare i32 @__gnu_objc_personality_v0(...)
declare void @abort()
define i32 @y() uwtable {
entry:
%finally.exn = alloca i8*
%finally.for-eh = alloca i1
%exn.slot = alloca i8*
%ehselector.slot = alloca i32
%cleanup.dest.slot = alloca i32
store i1 false, i1* %finally.for-eh
%call = invoke i32 @x()
to label %invoke.cont unwind label %lpad
invoke.cont: ; preds = %entry
store i32 0, i32* %cleanup.dest.slot
br label %cleanup
cleanup: ; preds = %invoke.cont,
%finally.catchall
%cleanup.dest.saved = load i32* %cleanup.dest.slot
%0 = load i32* @z, align 4
%inc = add nsw i32 %0, 1
store i32 %inc, i32* @z, align 4
%finally.shouldthrow = load i1* %finally.for-eh
br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont
finally.rethrow: ; preds = %cleanup
%1 = load i8** %finally.exn
call void @objc_exception_throw(i8* %1)
unreachable
finally.cont: ; preds = %cleanup
store i32 %cleanup.dest.saved, i32* %cleanup.dest.slot
%cleanup.dest = load i32* %cleanup.dest.slot
switch i32 %cleanup.dest, label %unreachable [
i32 0, label %cleanup.cont
i32 2, label %unreachable
]
cleanup.cont: ; preds = %finally.cont
ret i32 1
lpad: ; preds = %entry
%2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
@__gnu_objc_personality_v0 to i8*)
catch i8* null
%3 = extractvalue { i8*, i32 } %2, 0
store i8* %3, i8** %exn.slot
%4 = extractvalue { i8*, i32 } %2, 1
store i32 %4, i32* %ehselector.slot
br label %finally.catchall
finally.catchall: ; preds = %lpad
%exn = load i8** %exn.slot
store i8* %exn, i8** %finally.exn
store i1 true, i1* %finally.for-eh
store i32 2, i32* %cleanup.dest.slot
br label %cleanup
unreachable: ; preds = %finally.cont,
%finally.cont
unreachable
}
The inner function, x(), has a cleanup. The outer has a catch. The cleanup
rethrows the exception with a resume instruction. The outer function, y(), has
a catchall. This rethrows the exception with objc_exception_throw().
When the inliner has run, the following version of y() is produced:
define i32 @y() uwtable optsize {
entry:
%x.i = alloca i32, align 4
%0 = bitcast i32* %x.i to i8*
call void @llvm.lifetime.start(i64 -1, i8* %0)
invoke void @objc_exception_throw(i8* null)
to label %invoke.cont.i unwind label %lpad.i
invoke.cont.i: ; preds = %entry
unreachable
lpad.i: ; preds = %entry
%1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
@__gnu_objc_personality_v0 to i8*)
cleanup
catch i8* null
invoke void @foo(i8* %0) optsize
to label %finally.rethrow unwind label %terminate.lpad.i
terminate.lpad.i: ; preds = %lpad.i
%2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
@__gnu_objc_personality_v0 to i8*)
catch i8* null
call void @abort() noreturn nounwind
unreachable
finally.rethrow: ; preds = %lpad.i
%3 = extractvalue { i8*, i32 } %1, 0
%4 = load i32* @z, align 4, !tbaa !0
%inc = add nsw i32 %4, 1
store i32 %inc, i32* @z, align 4, !tbaa !0
call void @objc_exception_throw(i8* %3)
unreachable
}
x() has been inlined into the first basic block, so the invoke of the function
throwing the exception (objc_exception_throw()) becomes the terminator for that
block. So far, so good. The landing pad for this now has a cleanup and then a
catchall, which is a bit redundant because either of these will always be
matched. The call to foo() in the landing pad is the cleanup code from x(),
which is correct. This then branches to finally.rethrow, however, which then
calls objc_exception_throw(). This call is only valid if we landed with the
catchall, but because a cleanup will always be matched first we actually landed
with the cleanup.
The resume instruction has been completely removed. The correct behaviour in
this case would be simply to remove the cleanup - there is a catchall and so
the cleanup is completely redundant. In the more general case, if we have
multiple rethrow blocks with different mechanisms then we should be wrapping
them in a branch. The finally.rethrow block in this case should look like
something like this:
finally.rethrow: ; preds = %lpad.i
%3 = extractvalue { i8*, i32 } %1, 0
%4 = load i32* @z, align 4, !tbaa !0
%inc = add nsw i32 %4, 1
store i32 %inc, i32* @z, align 4, !tbaa !0
%5 = extractvalue { i8*, i32 } %1, 1
switch i32 %5, label %unreachable [
i32 0, label %cleanup.cont
i32 1, label %rethrow
]
rethrow:
call void @objc_exception_throw(i8* %3)
unreachable
cleanup.cont:
resume { i8*, i32 } %1
--
Configure bugmail: http://llvm.org/bugs/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are on the CC list for the bug.
More information about the llvm-bugs
mailing list