Interpreter intrinsics
First of several intrinsic CLs. This one provides the basic
mechanism plus a handful of the most simple examples. Note that
Intrinsic support is limited to Mterp both to keep the switch
interpreter as a pure reference, and to avoid mixing debugging
and intrinsics.
Bug: 30933338
Test: ART_TEST_INTERPRETER m test-art-host
Test: Note: existing 082-inline-execute, 123-inline-execute2
Test: cover intrinsics enabled by this CL.
Change-Id: I9d79e23a84bf00cadb54e1db52d8ed193bbf8887
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 9958814..d075c58 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -99,6 +99,7 @@
"intern_table.cc",
"interpreter/interpreter.cc",
"interpreter/interpreter_common.cc",
+ "interpreter/interpreter_intrinsics.cc",
"interpreter/interpreter_switch_impl.cc",
"interpreter/unstarted_runtime.cc",
"java_vm_ext.cc",
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 6b22af9..2589ad0 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_
#include "interpreter.h"
+#include "interpreter_intrinsics.h"
#include <math.h>
@@ -104,13 +105,58 @@
void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count)
REQUIRES_SHARED(Locks::mutator_lock_);
-// Invokes the given method. This is part of the invocation support and is used by DoInvoke and
-// DoInvokeVirtualQuick functions.
+// Invokes the given method. This is part of the invocation support and is used by DoInvoke,
+// DoFastInvoke and DoInvokeVirtualQuick functions.
// Returns true on success, otherwise throws an exception and returns false.
template<bool is_range, bool do_assignability_check>
bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
const Instruction* inst, uint16_t inst_data, JValue* result);
+// Handles streamlined non-range invoke static, direct and virtual instructions originating in
+// mterp. Access checks and instrumentation other than jit profiling are not supported, but does
+// support interpreter intrinsics if applicable.
+// Returns true on success, otherwise throws an exception and returns false.
+template<InvokeType type>
+static inline bool DoFastInvoke(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) {
+ const uint32_t method_idx = inst->VRegB_35c();
+ const uint32_t vregC = inst->VRegC_35c();
+ ObjPtr<mirror::Object> receiver = (type == kStatic)
+ ? nullptr
+ : shadow_frame.GetVRegReference(vregC);
+ ArtMethod* sf_method = shadow_frame.GetMethod();
+ ArtMethod* const called_method = FindMethodFromCode<type, false>(
+ method_idx, &receiver, sf_method, self);
+ // The shadow frame should already be pushed, so we don't need to update it.
+ if (UNLIKELY(called_method == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return false;
+ } else if (UNLIKELY(!called_method->IsInvokable())) {
+ called_method->ThrowInvocationTimeError();
+ result->SetJ(0);
+ return false;
+ } else {
+ if (called_method->IsIntrinsic()) {
+ if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data,
+ shadow_frame.GetResultRegister())) {
+ return !self->IsExceptionPending();
+ }
+ }
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ if (type == kVirtual) {
+ jit->InvokeVirtualOrInterface(receiver, sf_method, shadow_frame.GetDexPC(), called_method);
+ }
+ jit->AddSamples(self, sf_method, 1, /*with_backedges*/false);
+ }
+ return DoCall<false, false>(called_method, self, shadow_frame, inst, inst_data, result);
+ }
+}
+
// Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
// Returns true on success, otherwise throws an exception and returns false.
template<InvokeType type, bool is_range, bool do_access_check>
@@ -495,8 +541,9 @@
// Explicitly instantiate all DoInvoke functions.
#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \
- template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoInvoke<_type, _is_range, _do_check>(Thread* self, ShadowFrame& shadow_frame, \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvoke<_type, _is_range, _do_check>(Thread* self, \
+ ShadowFrame& shadow_frame, \
const Instruction* inst, uint16_t inst_data, \
JValue* result)
@@ -514,6 +561,19 @@
#undef EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL
+// Explicitly instantiate all DoFastInvoke functions.
+#define EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(_type) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoFastInvoke<_type>(Thread* self, \
+ ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data, \
+ JValue* result)
+
+EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kStatic); // invoke-static
+EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kDirect); // invoke-direct
+EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kVirtual); // invoke-virtual
+#undef EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL
+
// Explicitly instantiate all DoInvokeVirtualQuick functions.
#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
new file mode 100644
index 0000000..5e901cd
--- /dev/null
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "interpreter/interpreter_common.h"
+#include "interpreter/interpreter_intrinsics.h"
+
+namespace art {
+namespace interpreter {
+
+#define BINARY_SIMPLE_INTRINSIC(name, op, get, set, offset) \
+static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result_register) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
+ inst->GetVarArgs(arg, inst_data); \
+ result_register->set(op(shadow_frame->get(arg[0]), shadow_frame->get(arg[offset]))); \
+ return true; \
+}
+
+#define UNARY_SIMPLE_INTRINSIC(name, op, get, set) \
+static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result_register) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
+ inst->GetVarArgs(arg, inst_data); \
+ result_register->set(op(shadow_frame->get(arg[0]))); \
+ return true; \
+}
+
+// java.lang.Math.min(II)I
+BINARY_SIMPLE_INTRINSIC(MterpMathMinIntInt, std::min, GetVReg, SetI, 1);
+// java.lang.Math.min(JJ)J
+BINARY_SIMPLE_INTRINSIC(MterpMathMinLongLong, std::min, GetVRegLong, SetJ, 2);
+// java.lang.Math.max(II)I
+BINARY_SIMPLE_INTRINSIC(MterpMathMaxIntInt, std::max, GetVReg, SetI, 1);
+// java.lang.Math.max(JJ)J
+BINARY_SIMPLE_INTRINSIC(MterpMathMaxLongLong, std::max, GetVRegLong, SetJ, 2);
+// java.lang.Math.abs(I)I
+UNARY_SIMPLE_INTRINSIC(MterpMathAbsInt, std::abs, GetVReg, SetI);
+// java.lang.Math.abs(J)J
+UNARY_SIMPLE_INTRINSIC(MterpMathAbsLong, std::abs, GetVRegLong, SetJ);
+// java.lang.Math.abs(F)F
+UNARY_SIMPLE_INTRINSIC(MterpMathAbsFloat, 0x7fffffff&, GetVReg, SetI);
+// java.lang.Math.abs(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathAbsDouble, INT64_C(0x7fffffffffffffff)&, GetVRegLong, SetJ);
+// java.lang.Math.sqrt(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathSqrt, std::sqrt, GetVRegDouble, SetD);
+// java.lang.Math.ceil(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathCeil, std::ceil, GetVRegDouble, SetD);
+// java.lang.Math.floor(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathFloor, std::floor, GetVRegDouble, SetD);
+// java.lang.Math.sin(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathSin, std::sin, GetVRegDouble, SetD);
+// java.lang.Math.cos(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathCos, std::cos, GetVRegDouble, SetD);
+// java.lang.Math.tan(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathTan, std::tan, GetVRegDouble, SetD);
+// java.lang.Math.asin(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathAsin, std::asin, GetVRegDouble, SetD);
+// java.lang.Math.acos(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathAcos, std::acos, GetVRegDouble, SetD);
+// java.lang.Math.atan(D)D
+UNARY_SIMPLE_INTRINSIC(MterpMathAtan, std::atan, GetVRegDouble, SetD);
+
+#define INTRINSIC_CASE(name) \
+ case Intrinsics::k##name: \
+ res = Mterp##name(shadow_frame, inst, inst_data, result_register); \
+ break;
+
+bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
+ ArtMethod* const called_method,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result_register)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Intrinsics intrinsic = static_cast<Intrinsics>(called_method->GetIntrinsic());
+ bool res = false; // Assume failure
+ switch (intrinsic) {
+ INTRINSIC_CASE(MathMinIntInt)
+ INTRINSIC_CASE(MathMinLongLong)
+ INTRINSIC_CASE(MathMaxIntInt)
+ INTRINSIC_CASE(MathMaxLongLong)
+ INTRINSIC_CASE(MathAbsInt)
+ INTRINSIC_CASE(MathAbsLong)
+ INTRINSIC_CASE(MathAbsFloat)
+ INTRINSIC_CASE(MathAbsDouble)
+ INTRINSIC_CASE(MathSqrt)
+ INTRINSIC_CASE(MathCeil)
+ INTRINSIC_CASE(MathFloor)
+ INTRINSIC_CASE(MathSin)
+ INTRINSIC_CASE(MathCos)
+ INTRINSIC_CASE(MathTan)
+ INTRINSIC_CASE(MathAsin)
+ INTRINSIC_CASE(MathAcos)
+ INTRINSIC_CASE(MathAtan)
+ default:
+ res = false; // Punt
+ break;
+ }
+ return res;
+}
+
+} // namespace interpreter
+} // namespace art
diff --git a/runtime/interpreter/interpreter_intrinsics.h b/runtime/interpreter/interpreter_intrinsics.h
new file mode 100644
index 0000000..ae45679
--- /dev/null
+++ b/runtime/interpreter/interpreter_intrinsics.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
+#define ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
+
+#include "compiler/intrinsics_enum.h"
+#include "dex_instruction.h"
+
+namespace art {
+namespace interpreter {
+
+// Invokes to methods identified as intrinics are routed here. If there is
+// no interpreter implementation, return false and a normal invoke will proceed.
+bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
+ ArtMethod* const called_method,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result_register);
+
+} // namespace interpreter
+} // namespace art
+
+#endif // ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 8bf094e..a53040c 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -18,6 +18,7 @@
* Mterp entry point and support functions.
*/
#include "interpreter/interpreter_common.h"
+#include "interpreter/interpreter_intrinsics.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "mterp.h"
#include "debugger.h"
@@ -157,7 +158,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
JValue* result_register = shadow_frame->GetResultRegister();
const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kVirtual, false, false>(
+ return DoFastInvoke<kVirtual>(
self, *shadow_frame, inst, inst_data, result_register);
}
@@ -190,7 +191,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
JValue* result_register = shadow_frame->GetResultRegister();
const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kDirect, false, false>(
+ return DoFastInvoke<kDirect>(
self, *shadow_frame, inst, inst_data, result_register);
}
@@ -201,7 +202,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
JValue* result_register = shadow_frame->GetResultRegister();
const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kStatic, false, false>(
+ return DoFastInvoke<kStatic>(
self, *shadow_frame, inst, inst_data, result_register);
}
@@ -267,6 +268,18 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
JValue* result_register = shadow_frame->GetResultRegister();
const Instruction* inst = Instruction::At(dex_pc_ptr);
+ const uint32_t vregC = inst->VRegC_35c();
+ const uint32_t vtable_idx = inst->VRegB_35c();
+ ObjPtr<mirror::Object> const receiver = shadow_frame->GetVRegReference(vregC);
+ if (receiver != nullptr) {
+ ArtMethod* const called_method = receiver->GetClass()->GetEmbeddedVTableEntry(
+ vtable_idx, kRuntimePointerSize);
+ if ((called_method != nullptr) && called_method->IsIntrinsic()) {
+ if (MterpHandleIntrinsic(shadow_frame, called_method, inst, inst_data, result_register)) {
+ return !self->IsExceptionPending();
+ }
+ }
+ }
return DoInvokeVirtualQuick<false>(
self, *shadow_frame, inst, inst_data, result_register);
}