mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 08:30:47 +03:00
Propagate "nothrow" to LLVM IR (#1202)
Invoke nothrow callees only in try-blocks with at least 1 catch-block, otherwise call them directly. Errors thrown by nothrow callees can thus still be caught inside a try-catch-statement (and this is apparently required for release builds too). Most calls will be direct calls though, and this small change will lead to substantially less IR as we then skip the clean-ups after an Error. Proper clean-up when unwinding after an Error seems not to be guaranteed anyway. There are apparent RAII front-end optimizations for structs with nothrow dtor - see PR #1656.
This commit is contained in:
parent
798cda0649
commit
f4a22f232b
5 changed files with 97 additions and 5 deletions
|
@ -375,6 +375,20 @@ void ScopeStack::popCatch() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScopeStack::hasCatches() const {
|
||||||
|
if (useMSVCEH()) {
|
||||||
|
#if LDC_LLVM_VER >= 308
|
||||||
|
for (const auto &c : cleanupScopes) {
|
||||||
|
if (isCatchSwitchBlock(c.beginBlock))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !catchScopes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void ScopeStack::pushLoopTarget(Statement *loopStatement,
|
void ScopeStack::pushLoopTarget(Statement *loopStatement,
|
||||||
llvm::BasicBlock *continueTarget,
|
llvm::BasicBlock *continueTarget,
|
||||||
llvm::BasicBlock *breakTarget) {
|
llvm::BasicBlock *breakTarget) {
|
||||||
|
|
|
@ -260,6 +260,9 @@ public:
|
||||||
/// Unregisters the last registered catch block.
|
/// Unregisters the last registered catch block.
|
||||||
void popCatch();
|
void popCatch();
|
||||||
|
|
||||||
|
/// Indicates whether there are any registered catch blocks.
|
||||||
|
bool hasCatches() const;
|
||||||
|
|
||||||
size_t currentCatchScope() { return catchScopes.size(); }
|
size_t currentCatchScope() { return catchScopes.size(); }
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 308
|
#if LDC_LLVM_VER >= 308
|
||||||
|
@ -305,7 +308,7 @@ public:
|
||||||
/// are catches/cleanups active or not.
|
/// are catches/cleanups active or not.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
llvm::CallSite callOrInvoke(llvm::Value *callee, const T &args,
|
llvm::CallSite callOrInvoke(llvm::Value *callee, const T &args,
|
||||||
const char *name = "");
|
const char *name = "", bool isNothrow = false);
|
||||||
|
|
||||||
/// Terminates the current basic block with an unconditional branch to the
|
/// Terminates the current basic block with an unconditional branch to the
|
||||||
/// given label, along with the cleanups to execute on the way there.
|
/// given label, along with the cleanups to execute on the way there.
|
||||||
|
@ -406,14 +409,20 @@ private:
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args,
|
llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args,
|
||||||
const char *name) {
|
const char *name, bool isNothrow) {
|
||||||
// If this is a direct call, we might be able to use the callee attributes
|
// If this is a direct call, we might be able to use the callee attributes
|
||||||
// to our advantage.
|
// to our advantage.
|
||||||
llvm::Function *calleeFn = llvm::dyn_cast<llvm::Function>(callee);
|
llvm::Function *calleeFn = llvm::dyn_cast<llvm::Function>(callee);
|
||||||
|
|
||||||
|
// Ignore 'nothrow' inside try-blocks with at least 1 catch block to allow
|
||||||
|
// catching Errors.
|
||||||
|
if (isNothrow)
|
||||||
|
isNothrow = !hasCatches();
|
||||||
|
|
||||||
// Intrinsics don't support invoking and 'nounwind' functions don't need it.
|
// Intrinsics don't support invoking and 'nounwind' functions don't need it.
|
||||||
const bool doesNotThrow =
|
const bool doesNotThrow =
|
||||||
calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow());
|
isNothrow ||
|
||||||
|
(calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()));
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 308
|
#if LDC_LLVM_VER >= 308
|
||||||
// calls inside a funclet must be annotated with its value
|
// calls inside a funclet must be annotated with its value
|
||||||
|
|
|
@ -886,7 +886,8 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval,
|
||||||
}
|
}
|
||||||
|
|
||||||
// call the function
|
// call the function
|
||||||
LLCallSite call = gIR->funcGen().scopes.callOrInvoke(callable, args);
|
LLCallSite call =
|
||||||
|
gIR->funcGen().scopes.callOrInvoke(callable, args, "", tf->isnothrow);
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 309
|
#if LDC_LLVM_VER >= 309
|
||||||
// PGO: Insert instrumentation or attach profile metadata at indirect call
|
// PGO: Insert instrumentation or attach profile metadata at indirect call
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "gen/tollvm.h"
|
#include "gen/tollvm.h"
|
||||||
#include "ir/irdsymbol.h"
|
#include "ir/irdsymbol.h"
|
||||||
|
|
||||||
|
|
||||||
IrFunction::IrFunction(FuncDeclaration *fd) : FMF() {
|
IrFunction::IrFunction(FuncDeclaration *fd) : FMF() {
|
||||||
decl = fd;
|
decl = fd;
|
||||||
|
|
||||||
|
|
69
tests/codegen/nothrow.d
Normal file
69
tests/codegen/nothrow.d
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// RUN: %ldc -c -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
|
||||||
|
|
||||||
|
struct S
|
||||||
|
{
|
||||||
|
~this() nothrow {}
|
||||||
|
void foo() nothrow { throw new Error("foo"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Throwing
|
||||||
|
{
|
||||||
|
~this() {}
|
||||||
|
void bar() { throw new Exception("bar"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define{{.*}} @{{.*}}_D7nothrow10inTryCatchFZv
|
||||||
|
void inTryCatch()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// make sure the nothrow functions S.foo() and S.~this()
|
||||||
|
// are invoked in try-blocks with at least 1 catch block
|
||||||
|
S a;
|
||||||
|
// CHECK: invoke {{.*}}_D7nothrow1S3fooMFNbZv{{.*}} %a
|
||||||
|
a.foo();
|
||||||
|
// CHECK: invoke {{.*}}_D7nothrow1S6__dtorMFNbZv{{.*}} %a
|
||||||
|
}
|
||||||
|
catch (Error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define{{.*}} @{{.*}}_D7nothrow12inTryFinallyFZv
|
||||||
|
void inTryFinally()
|
||||||
|
{
|
||||||
|
// make sure the nothrow functions are never invoked
|
||||||
|
// CHECK-NOT: invoke {{.*}}_D7nothrow1S3fooMFNbZv
|
||||||
|
// CHECK-NOT: invoke {{.*}}_D7nothrow1S6__dtorMFNbZv
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
S a;
|
||||||
|
a.foo();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
S b;
|
||||||
|
b.foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define{{.*}} @{{.*}}_Dmain
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// make sure the nothrow functions are never invoked
|
||||||
|
// CHECK-NOT: invoke {{.*}}_D7nothrow1S3fooMFNbZv
|
||||||
|
// CHECK-NOT: invoke {{.*}}_D7nothrow1S6__dtorMFNbZv
|
||||||
|
|
||||||
|
Throwing t;
|
||||||
|
|
||||||
|
S a;
|
||||||
|
a.foo();
|
||||||
|
t.bar();
|
||||||
|
|
||||||
|
{
|
||||||
|
S b;
|
||||||
|
t.bar();
|
||||||
|
b.foo();
|
||||||
|
|
||||||
|
S().foo();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue