mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-02-02 02:14:23 +00:00
[Attributor] Deducing existing nounwind attribute.
Adding nounwind deduction in new attributor framework. Reviewers: jdoerfert, uenoku Subscribers: hiraditya, llvm-commits Differential Revision: https://reviews.llvm.org/D63379 llvm-svn: 364521
This commit is contained in:
parent
dd944bb275
commit
7f06f40e4b
@ -642,6 +642,23 @@ Pass *createAttributorLegacyPass();
|
||||
/// Abstract Attribute Classes
|
||||
/// ----------------------------------------------------------------------------
|
||||
|
||||
struct AANoUnwind : public AbstractAttribute {
|
||||
/// An abstract interface for all nosync attributes.
|
||||
AANoUnwind(Value &V, InformationCache &InfoCache)
|
||||
: AbstractAttribute(V, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getAttrKind()/
|
||||
virtual Attribute::AttrKind getAttrKind() const override { return ID; }
|
||||
|
||||
static constexpr Attribute::AttrKind ID = Attribute::NoUnwind;
|
||||
|
||||
/// Returns true if nounwind is assumed.
|
||||
virtual bool isAssumedNoUnwind() const = 0;
|
||||
|
||||
/// Returns true if nounwind is known.
|
||||
virtual bool isKnownNoUnwind() const = 0;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
|
||||
|
@ -42,6 +42,7 @@ STATISTIC(NumAttributesValidFixpoint,
|
||||
"Number of abstract attributes in a valid fixpoint state");
|
||||
STATISTIC(NumAttributesManifested,
|
||||
"Number of abstract attributes manifested in IR");
|
||||
STATISTIC(NumFnNoUnwind, "Number of functions marked nounwind");
|
||||
|
||||
// TODO: Determine a good default value.
|
||||
//
|
||||
@ -86,10 +87,13 @@ static void bookkeeping(AbstractAttribute::ManifestPosition MP,
|
||||
|
||||
if (!Attr.isEnumAttribute())
|
||||
return;
|
||||
//switch (Attr.getKindAsEnum()) {
|
||||
//default:
|
||||
// return;
|
||||
//}
|
||||
switch (Attr.getKindAsEnum()) {
|
||||
case Attribute::NoUnwind:
|
||||
NumFnNoUnwind++;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to identify the correct offset into an attribute list.
|
||||
@ -241,6 +245,64 @@ const Function &AbstractAttribute::getAnchorScope() const {
|
||||
return const_cast<AbstractAttribute *>(this)->getAnchorScope();
|
||||
}
|
||||
|
||||
/// -----------------------NoUnwind Function Attribute--------------------------
|
||||
|
||||
struct AANoUnwindFunction : AANoUnwind, BooleanState {
|
||||
|
||||
AANoUnwindFunction(Function &F, InformationCache &InfoCache)
|
||||
: AANoUnwind(F, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getState()
|
||||
/// {
|
||||
AbstractState &getState() override { return *this; }
|
||||
const AbstractState &getState() const override { return *this; }
|
||||
/// }
|
||||
|
||||
/// See AbstractAttribute::getManifestPosition().
|
||||
virtual ManifestPosition getManifestPosition() const override {
|
||||
return MP_FUNCTION;
|
||||
}
|
||||
|
||||
virtual const std::string getAsStr() const override {
|
||||
return getAssumed() ? "nounwind" : "may-unwind";
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
virtual ChangeStatus updateImpl(Attributor &A) override;
|
||||
|
||||
/// See AANoUnwind::isAssumedNoUnwind().
|
||||
virtual bool isAssumedNoUnwind() const override { return getAssumed(); }
|
||||
|
||||
/// See AANoUnwind::isKnownNoUnwind().
|
||||
virtual bool isKnownNoUnwind() const override { return getKnown(); }
|
||||
};
|
||||
|
||||
ChangeStatus AANoUnwindFunction::updateImpl(Attributor &A) {
|
||||
Function &F = getAnchorScope();
|
||||
|
||||
// The map from instruction opcodes to those instructions in the function.
|
||||
auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
|
||||
auto Opcodes = {
|
||||
(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr,
|
||||
(unsigned)Instruction::Call, (unsigned)Instruction::CleanupRet,
|
||||
(unsigned)Instruction::CatchSwitch, (unsigned)Instruction::Resume};
|
||||
|
||||
for (unsigned Opcode : Opcodes) {
|
||||
for (Instruction *I : OpcodeInstMap[Opcode]) {
|
||||
if (!I->mayThrow())
|
||||
continue;
|
||||
|
||||
auto *NoUnwindAA = A.getAAFor<AANoUnwind>(*this, *I);
|
||||
|
||||
if (!NoUnwindAA || !NoUnwindAA->isAssumedNoUnwind()) {
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------------------------------
|
||||
/// Attributor
|
||||
/// ----------------------------------------------------------------------------
|
||||
@ -254,8 +316,8 @@ ChangeStatus Attributor::run() {
|
||||
<< AllAbstractAttributes.size()
|
||||
<< " abstract attributes.\n");
|
||||
|
||||
// Now that all abstract attributes are collected and initialized we start the
|
||||
// abstract analysis.
|
||||
// Now that all abstract attributes are collected and initialized we start
|
||||
// the abstract analysis.
|
||||
|
||||
unsigned IterationCounter = 1;
|
||||
|
||||
@ -383,6 +445,9 @@ void Attributor::identifyDefaultAbstractAttributes(
|
||||
Function &F, InformationCache &InfoCache,
|
||||
DenseSet</* Attribute::AttrKind */ unsigned> *Whitelist) {
|
||||
|
||||
// Every function can be nounwind.
|
||||
registerAA(*new AANoUnwindFunction(F, InfoCache));
|
||||
|
||||
// Walk all instructions to find more attribute opportunities and also
|
||||
// interesting instructions that might be queried by abstract attributes
|
||||
// during their initialization or update.
|
||||
@ -397,10 +462,20 @@ void Attributor::identifyDefaultAbstractAttributes(
|
||||
// to concrete attributes we only cache the ones that are as identified in
|
||||
// the following switch.
|
||||
// Note: There are no concrete attributes now so this is initially empty.
|
||||
//switch (I.getOpcode()) {
|
||||
//default:
|
||||
// break;
|
||||
//}
|
||||
switch (I.getOpcode()) {
|
||||
default:
|
||||
assert((!ImmutableCallSite(&I)) && (!isa<CallBase>(&I)) &&
|
||||
"New call site/base instruction type needs to be known int the "
|
||||
"attributor.");
|
||||
break;
|
||||
case Instruction::Call:
|
||||
case Instruction::CallBr:
|
||||
case Instruction::Invoke:
|
||||
case Instruction::CleanupRet:
|
||||
case Instruction::CatchSwitch:
|
||||
case Instruction::Resume:
|
||||
IsInterestingOpcode = true;
|
||||
}
|
||||
if (IsInterestingOpcode)
|
||||
InstOpcodeMap[I.getOpcode()].push_back(&I);
|
||||
if (I.mayReadOrWriteMemory())
|
||||
|
99
test/Transforms/FunctionAttrs/nounwind.ll
Normal file
99
test/Transforms/FunctionAttrs/nounwind.ll
Normal file
@ -0,0 +1,99 @@
|
||||
; RUN: opt < %s -functionattrs -S | FileCheck %s
|
||||
; RUN: opt < %s -attributor -attributor-disable=false -S | FileCheck %s --check-prefix=ATTRIBUTOR
|
||||
|
||||
; TEST 1
|
||||
; CHECK: Function Attrs: norecurse nounwind readnone
|
||||
; CHECK-NEXT: define i32 @foo1()
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR-NEXT: define i32 @foo1()
|
||||
define i32 @foo1() {
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
; TEST 2
|
||||
; CHECK: Function Attrs: nounwind readnone
|
||||
; CHECK-NEXT: define i32 @scc1_foo()
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR-NEXT: define i32 @scc1_foo()
|
||||
define i32 @scc1_foo() {
|
||||
%1 = call i32 @scc1_bar()
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
|
||||
; TEST 3
|
||||
; CHECK: Function Attrs: nounwind readnone
|
||||
; CHECK-NEXT: define i32 @scc1_bar()
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR-NEXT: define i32 @scc1_bar()
|
||||
define i32 @scc1_bar() {
|
||||
%1 = call i32 @scc1_foo()
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
; CHECK: declare i32 @non_nounwind()
|
||||
declare i32 @non_nounwind()
|
||||
|
||||
; TEST 4
|
||||
; CHECK: define void @call_non_nounwind() {
|
||||
; ATTRIBUTOR: define void @call_non_nounwind() {
|
||||
define void @call_non_nounwind(){
|
||||
tail call i32 @non_nounwind()
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 5 - throw
|
||||
; int maybe_throw(bool canThrow) {
|
||||
; if (canThrow)
|
||||
; throw;
|
||||
; else
|
||||
; return -1;
|
||||
; }
|
||||
|
||||
; CHECK: define i32 @maybe_throw(i1 zeroext)
|
||||
; ATTRIBUTOR: define i32 @maybe_throw(i1 zeroext)
|
||||
define i32 @maybe_throw(i1 zeroext) {
|
||||
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;
|
||||
; }
|
||||
|
||||
; CHECK: define i32 @catch_thing()
|
||||
; ATTRIBUTOR: define i32 @catch_thing()
|
||||
define i32 @catch_thing() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
invoke void @__cxa_rethrow() #1
|
||||
to label %1 unwind label %2
|
||||
|
||||
1: ; preds = %0
|
||||
unreachable
|
||||
|
||||
2: ; preds = %0
|
||||
%3 = landingpad { i8*, i32 }
|
||||
catch i8* null
|
||||
%4 = extractvalue { i8*, i32 } %3, 0
|
||||
%5 = tail call i8* @__cxa_begin_catch(i8* %4) #2
|
||||
tail call void @__cxa_end_catch()
|
||||
ret i32 -1
|
||||
}
|
||||
|
||||
declare i32 @__gxx_personality_v0(...)
|
||||
|
||||
declare i8* @__cxa_begin_catch(i8*)
|
||||
|
||||
declare void @__cxa_end_catch()
|
Loading…
x
Reference in New Issue
Block a user