summaryrefslogtreecommitdiff
path: root/compiler/optimizing/nodes.h
diff options
context:
space:
mode:
author David Brazdil <dbrazdil@google.com> 2015-08-20 17:47:48 +0100
committer David Brazdil <dbrazdil@google.com> 2015-09-14 20:42:58 +0100
commitb022fa1300e6d78639b3b910af0cf85c43df44bb (patch)
tree780c7d6bdee784c2f8248979de348491cfb63b34 /compiler/optimizing/nodes.h
parente481c006e8b055a31d9c7cff27f4145e57e3c113 (diff)
ART: Register allocation and runtime support for try/catch
This patch completes a series of CLs that add support for try/catch in the Optimizing compiler. With it, Optimizing can compile all methods containing try/catch, provided they don't contain catch loops. Future work will focus on improving performance of the generated code. SsaLivenessAnalysis was updated to propagate liveness information of instructions live at catch blocks, and to keep location information on instructions which may be caught by catch phis. RegisterAllocator was extended to spill values used after catch, and to allocate spill slots for catch phis. Catch phis generated for the same vreg share a spill slot as the raw value must be the same. Location builders and slow paths were updated to reflect the fact that throwing an exception may not lead to escaping the method. Instruction code generators are forbidden from using of implicit null checks in try blocks as live registers need to be saved before handing over to the runtime. CodeGenerator emits a stack map for each catch block, storing locations of catch phis. CodeInfo and StackMapStream recognize this new type of stack map and store them separate from other stack maps to avoid dex_pc conflicts. After having found the target catch block to deliver an exception to, QuickExceptionHandler looks up the dex register maps at the throwing instruction and the catch block and copies the values over to their respective locations. The runtime-support approach was selected because it allows for the best performance in the normal control-flow path, since no propagation of catch phi values is necessary until the exception is thrown. In addition, it also greatly simplifies the register allocation phase. ConstantHoisting was removed from LICMTest because it instantiated (now abstract) HConstant and was bogus anyway (constants are always in the entry block). Change-Id: Ie31038ad8e3ee0c13a5bbbbaf5f0b3e532310e4e
Diffstat (limited to 'compiler/optimizing/nodes.h')
-rw-r--r--compiler/optimizing/nodes.h37
1 files changed, 30 insertions, 7 deletions
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 5ec3f22e81..d52a4f7575 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -157,6 +157,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
number_of_in_vregs_(0),
temporaries_vreg_slots_(0),
has_bounds_checks_(false),
+ has_try_catch_(false),
debuggable_(debuggable),
current_instruction_id_(start_instruction_id),
dex_file_(dex_file),
@@ -282,7 +283,6 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
}
uint16_t GetNumberOfVRegs() const {
- DCHECK(!in_ssa_form_);
return number_of_vregs_;
}
@@ -360,8 +360,8 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
return instruction_set_;
}
- // TODO: Remove once the full compilation pipeline is enabled for try/catch.
- bool HasTryCatch() const;
+ bool HasTryCatch() const { return has_try_catch_; }
+ void SetHasTryCatch(bool value) { has_try_catch_ = value; }
private:
void VisitBlockForDominatorTree(HBasicBlock* block,
@@ -433,6 +433,10 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
// Has bounds checks. We can totally skip BCE if it's false.
bool has_bounds_checks_;
+ // Flag whether there are any try/catch blocks in the graph. We will skip
+ // try/catch-related passes if false.
+ bool has_try_catch_;
+
// Indicates whether the graph should be compiled in a way that
// ensures full debuggability. If false, we can apply more
// aggressive optimizations that may limit the level of debugging.
@@ -2187,6 +2191,8 @@ class HConstant : public HExpression<0> {
virtual bool IsZero() const { return false; }
virtual bool IsOne() const { return false; }
+ virtual uint64_t GetValueAsUint64() const = 0;
+
DECLARE_INSTRUCTION(Constant);
private:
@@ -2199,6 +2205,8 @@ class HNullConstant : public HConstant {
return true;
}
+ uint64_t GetValueAsUint64() const OVERRIDE { return 0; }
+
size_t ComputeHashCode() const OVERRIDE { return 0; }
DECLARE_INSTRUCTION(NullConstant);
@@ -2216,6 +2224,8 @@ class HIntConstant : public HConstant {
public:
int32_t GetValue() const { return value_; }
+ uint64_t GetValueAsUint64() const OVERRIDE { return static_cast<uint64_t>(value_); }
+
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
DCHECK(other->IsIntConstant());
return other->AsIntConstant()->value_ == value_;
@@ -2247,6 +2257,8 @@ class HLongConstant : public HConstant {
public:
int64_t GetValue() const { return value_; }
+ uint64_t GetValueAsUint64() const OVERRIDE { return value_; }
+
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
DCHECK(other->IsLongConstant());
return other->AsLongConstant()->value_ == value_;
@@ -2866,10 +2878,13 @@ class HFloatConstant : public HConstant {
public:
float GetValue() const { return value_; }
+ uint64_t GetValueAsUint64() const OVERRIDE {
+ return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_));
+ }
+
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
DCHECK(other->IsFloatConstant());
- return bit_cast<uint32_t, float>(other->AsFloatConstant()->value_) ==
- bit_cast<uint32_t, float>(value_);
+ return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64();
}
size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
@@ -2907,10 +2922,11 @@ class HDoubleConstant : public HConstant {
public:
double GetValue() const { return value_; }
+ uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); }
+
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
DCHECK(other->IsDoubleConstant());
- return bit_cast<uint64_t, double>(other->AsDoubleConstant()->value_) ==
- bit_cast<uint64_t, double>(value_);
+ return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64();
}
size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
@@ -4003,6 +4019,13 @@ class HPhi : public HInstruction {
bool IsDead() const { return !is_live_; }
bool IsLive() const { return is_live_; }
+ bool IsVRegEquivalentOf(HInstruction* other) const {
+ return other != nullptr
+ && other->IsPhi()
+ && other->AsPhi()->GetBlock() == GetBlock()
+ && other->AsPhi()->GetRegNumber() == GetRegNumber();
+ }
+
// Returns the next equivalent phi (starting from the current one) or null if there is none.
// An equivalent phi is a phi having the same dex register and type.
// It assumes that phis with the same dex register are adjacent.