#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();
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;
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
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_
// 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);
#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"
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();
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();
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);
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;
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:
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 {
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);
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; }
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+public class Main {
+ public static void main(String[] args) {
+ stringEqualsSame();
+ stringArgumentNotNull("Foo");
+ }
+
+ /// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: boolean Main.stringEqualsSame() register (before)
+ /// CHECK: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: Return [<<Const1>>]
+
+ /// CHECK-START: boolean Main.stringEqualsSame() register (before)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ public static boolean stringEqualsSame() {
+ return $inline$callStringEquals("obj", "obj");
+ }
+
+ /// CHECK-START: boolean Main.stringEqualsNull() register (after)
+ /// CHECK: <<Invoke:z\d+>> InvokeStaticOrDirect
+ /// CHECK: Return [<<Invoke>>]
+ public static boolean stringEqualsNull() {
+ String o = (String)myObject;
+ return $inline$callStringEquals(o, o);
+ }
+
+ public static boolean $inline$callStringEquals(String a, String b) {
+ return a.equals(b);
+ }
+
+ /// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
+ /// CHECK: InvokeStaticOrDirect
+ /// CHECK-NOT: test
+ public static boolean stringArgumentNotNull(Object obj) {
+ obj.getClass();
+ return "foo".equals(obj);
+ }
+
+ // Test is very brittle as it depends on the order we emit instructions.
+ /// CHECK-START-X86: boolean Main.stringArgumentIsString() disassembly (after)
+ /// CHECK: InvokeStaticOrDirect
+ /// CHECK: test
+ /// CHECK: jz/eq
+ // Check that we don't try to compare the classes.
+ /// CHECK-NOT: mov
+ /// CHECK: cmp
+ public static boolean stringArgumentIsString() {
+ return "foo".equals(myString);
+ }
+
+ static String myString;
+ static Object myObject;
+}
530-checker-regression-reftype-final \
532-checker-nonnull-arrayset \
534-checker-bce-deoptimization \
+ 536-checker-intrinsic-optimization \
ifeq (mips,$(TARGET_ARCH))
ifneq (,$(filter optimizing,$(COMPILER_TYPES)))