Add support for intrinsic optimizations.
Change-Id: Ib5a4224022f9360e60c09a19ac8642270a7f3b64
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 3287a0a..973bfbb 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -16,15 +16,16 @@
#include "instruction_simplifier.h"
+#include "intrinsics.h"
#include "mirror/class-inl.h"
#include "scoped_thread_state_change.h"
namespace art {
-class InstructionSimplifierVisitor : public HGraphVisitor {
+class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
public:
InstructionSimplifierVisitor(HGraph* graph, OptimizingCompilerStats* stats)
- : HGraphVisitor(graph),
+ : HGraphDelegateVisitor(graph),
stats_(stats) {}
void Run();
@@ -71,6 +72,7 @@
void VisitXor(HXor* instruction) OVERRIDE;
void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
void VisitFakeString(HFakeString* fake_string) OVERRIDE;
+ void VisitInvoke(HInvoke* invoke) OVERRIDE;
bool CanEnsureNotNullAt(HInstruction* instr, HInstruction* at) const;
@@ -1033,4 +1035,29 @@
instruction->GetBlock()->RemoveInstruction(instruction);
}
+void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
+ if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) {
+ HInstruction* argument = instruction->InputAt(1);
+ HInstruction* receiver = instruction->InputAt(0);
+ if (receiver == argument) {
+ // Because String.equals is an instance call, the receiver is
+ // a null check if we don't know it's null. The argument however, will
+ // be the actual object. So we cannot end up in a situation where both
+ // are equal but could be null.
+ DCHECK(CanEnsureNotNullAt(argument, instruction));
+ instruction->ReplaceWith(GetGraph()->GetIntConstant(1));
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ } else {
+ StringEqualsOptimizations optimizations(instruction);
+ if (CanEnsureNotNullAt(argument, instruction)) {
+ optimizations.SetArgumentNotNull();
+ }
+ ScopedObjectAccess soa(Thread::Current());
+ if (argument->GetReferenceTypeInfo().IsStringClass()) {
+ optimizations.SetArgumentIsString();
+ }
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index d1a17b6..12e79fa 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -116,6 +116,59 @@
DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
};
+#define GENERIC_OPTIMIZATION(name, bit) \
+ public: \
+ void Set##name() { SetBit(k##name); } \
+ bool Get##name() const { return IsBitSet(k##name); } \
+ private: \
+ static constexpr int k##name = bit
+
+class IntrinsicOptimizations : public ValueObject {
+ public:
+ IntrinsicOptimizations(HInvoke* invoke) : value_(invoke->GetIntrinsicOptimizations()) {}
+ IntrinsicOptimizations(const HInvoke& invoke) : value_(invoke.GetIntrinsicOptimizations()) {}
+
+ static constexpr int kNumberOfGenericOptimizations = 2;
+ GENERIC_OPTIMIZATION(DoesNotNeedDexCache, 0);
+ GENERIC_OPTIMIZATION(DoesNotNeedEnvironment, 1);
+
+ protected:
+ bool IsBitSet(uint32_t bit) const {
+ return (*value_ & (1 << bit)) != 0u;
+ }
+
+ void SetBit(uint32_t bit) {
+ *(const_cast<uint32_t*>(value_)) |= (1 << bit);
+ }
+
+ private:
+ const uint32_t *value_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicOptimizations);
+};
+
+#undef GENERIC_OPTIMIZATION
+
+#define INTRINSIC_OPTIMIZATION(name, bit) \
+ public: \
+ void Set##name() { SetBit(k##name); } \
+ bool Get##name() const { return IsBitSet(k##name); } \
+ private: \
+ static constexpr int k##name = bit + kNumberOfGenericOptimizations
+
+class StringEqualsOptimizations : public IntrinsicOptimizations {
+ public:
+ StringEqualsOptimizations(HInvoke* invoke) : IntrinsicOptimizations(invoke) {}
+
+ INTRINSIC_OPTIMIZATION(ArgumentNotNull, 0);
+ INTRINSIC_OPTIMIZATION(ArgumentIsString, 1);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringEqualsOptimizations);
+};
+
+#undef INTRISIC_OPTIMIZATION
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_H_
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 318d3a6..263c375 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1054,17 +1054,22 @@
// Note that the null check must have been done earlier.
DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
- // Check if input is null, return false if it is.
- __ testl(arg, arg);
- __ j(kEqual, &return_false);
+ StringEqualsOptimizations optimizations(invoke);
+ if (!optimizations.GetArgumentNotNull()) {
+ // Check if input is null, return false if it is.
+ __ testl(arg, arg);
+ __ j(kEqual, &return_false);
+ }
// Instanceof check for the argument by comparing class fields.
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
- __ movl(ecx, Address(str, class_offset));
- __ cmpl(ecx, Address(arg, class_offset));
- __ j(kNotEqual, &return_false);
+ if (!optimizations.GetArgumentIsString()) {
+ __ movl(ecx, Address(str, class_offset));
+ __ cmpl(ecx, Address(arg, class_offset));
+ __ j(kNotEqual, &return_false);
+ }
// Reference equality check, return true if same reference.
__ cmpl(str, arg);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 989970f..d35db19 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -21,6 +21,7 @@
#include "base/bit_vector-inl.h"
#include "base/bit_utils.h"
#include "base/stl_util.h"
+#include "intrinsics.h"
#include "mirror/class-inl.h"
#include "scoped_thread_state_change.h"
@@ -1873,6 +1874,35 @@
return false;
}
+void HInvoke::SetIntrinsic(Intrinsics intrinsic,
+ IntrinsicNeedsEnvironmentOrCache needs_env_or_cache) {
+ intrinsic_ = intrinsic;
+ IntrinsicOptimizations opt(this);
+ if (needs_env_or_cache == kNoEnvironmentOrCache) {
+ opt.SetDoesNotNeedDexCache();
+ opt.SetDoesNotNeedEnvironment();
+ }
+}
+
+bool HInvoke::NeedsEnvironment() const {
+ if (!IsIntrinsic()) {
+ return true;
+ }
+ IntrinsicOptimizations opt(*this);
+ return !opt.GetDoesNotNeedEnvironment();
+}
+
+bool HInvokeStaticOrDirect::NeedsDexCache() const {
+ if (IsRecursive() || IsStringInit()) {
+ return false;
+ }
+ if (!IsIntrinsic()) {
+ return true;
+ }
+ IntrinsicOptimizations opt(*this);
+ return !opt.GetDoesNotNeedDexCache();
+}
+
void HInstruction::RemoveEnvironmentUsers() {
for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
HUseListNode<HEnvironment*>* user_node = use_it.Current();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 849f876..b8b9851 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1656,6 +1656,10 @@
return GetTypeHandle()->IsObjectClass();
}
+ bool IsStringClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return IsValid() && GetTypeHandle()->IsStringClass();
+ }
+
bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(IsValid());
return IsArrayClass() && GetTypeHandle()->GetComponentType()->IsObjectClass();
@@ -3034,11 +3038,7 @@
public:
size_t InputCount() const OVERRIDE { return inputs_.size(); }
- // Runtime needs to walk the stack, so Dex -> Dex calls need to
- // know their environment.
- bool NeedsEnvironment() const OVERRIDE {
- return needs_environment_or_cache_ == kNeedsEnvironmentOrCache;
- }
+ bool NeedsEnvironment() const OVERRIDE;
void SetArgumentAt(size_t index, HInstruction* argument) {
SetRawInputAt(index, argument);
@@ -3062,10 +3062,7 @@
return intrinsic_;
}
- void SetIntrinsic(Intrinsics intrinsic, IntrinsicNeedsEnvironmentOrCache needs_env_or_cache) {
- intrinsic_ = intrinsic;
- needs_environment_or_cache_ = needs_env_or_cache;
- }
+ void SetIntrinsic(Intrinsics intrinsic, IntrinsicNeedsEnvironmentOrCache needs_env_or_cache);
bool IsFromInlinedInvoke() const {
return GetEnvironment()->GetParent() != nullptr;
@@ -3073,6 +3070,16 @@
bool CanThrow() const OVERRIDE { return true; }
+ uint32_t* GetIntrinsicOptimizations() {
+ return &intrinsic_optimizations_;
+ }
+
+ const uint32_t* GetIntrinsicOptimizations() const {
+ return &intrinsic_optimizations_;
+ }
+
+ bool IsIntrinsic() const { return intrinsic_ != Intrinsics::kNone; }
+
DECLARE_INSTRUCTION(Invoke);
protected:
@@ -3092,7 +3099,7 @@
dex_method_index_(dex_method_index),
original_invoke_type_(original_invoke_type),
intrinsic_(Intrinsics::kNone),
- needs_environment_or_cache_(kNeedsEnvironmentOrCache) {
+ intrinsic_optimizations_(0) {
}
const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
@@ -3111,7 +3118,9 @@
const uint32_t dex_method_index_;
const InvokeType original_invoke_type_;
Intrinsics intrinsic_;
- IntrinsicNeedsEnvironmentOrCache needs_environment_or_cache_;
+
+ // A magic word holding optimizations for intrinsics. See intrinsics.h.
+ uint32_t intrinsic_optimizations_;
private:
DISALLOW_COPY_AND_ASSIGN(HInvoke);
@@ -3259,10 +3268,7 @@
MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; }
CodePtrLocation GetCodePtrLocation() const { return dispatch_info_.code_ptr_location; }
bool IsRecursive() const { return GetMethodLoadKind() == MethodLoadKind::kRecursive; }
- bool NeedsDexCache() const OVERRIDE {
- if (intrinsic_ != Intrinsics::kNone) { return needs_environment_or_cache_; }
- return !IsRecursive() && !IsStringInit();
- }
+ bool NeedsDexCache() const OVERRIDE;
bool IsStringInit() const { return GetMethodLoadKind() == MethodLoadKind::kStringInit; }
uint32_t GetCurrentMethodInputIndex() const { return GetNumberOfArguments(); }
bool HasMethodAddress() const { return GetMethodLoadKind() == MethodLoadKind::kDirectAddress; }