summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dex2oat/linker/image_writer.cc29
-rw-r--r--dex2oat/linker/image_writer.h3
-rw-r--r--libdexfile/dex/modifiers.h2
-rw-r--r--runtime/art_method.h9
-rw-r--r--runtime/class_linker.cc28
-rw-r--r--runtime/interpreter/mterp/riscv64/invoke.S33
-rw-r--r--runtime/nterp_helpers-inl.h57
-rw-r--r--runtime/nterp_helpers.h5
8 files changed, 124 insertions, 42 deletions
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index c4954d3d29..6564fb8136 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -76,6 +76,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
#include "mirror/var_handle.h"
+#include "nterp_helpers-inl.h"
#include "nterp_helpers.h"
#include "oat.h"
#include "oat_file.h"
@@ -3468,6 +3469,7 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
CopyAndFixupReference(copy->GetDeclaringClassAddressWithoutBarrier(),
orig->GetDeclaringClassUnchecked<kWithoutReadBarrier>());
+ ResetNterpFastPathFlags(copy, orig);
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
@@ -3761,5 +3763,32 @@ void ImageWriter::CopyAndFixupPointer(void* object, MemberOffset offset, ValueTy
return CopyAndFixupPointer(object, offset, src_value, target_ptr_size_);
}
+void ImageWriter::ResetNterpFastPathFlags(ArtMethod* copy, ArtMethod* orig) {
+ DCHECK(copy != nullptr);
+ DCHECK(orig != nullptr);
+ if (orig->IsRuntimeMethod() || orig->IsProxyMethod()) {
+ return; // !IsRuntimeMethod() and !IsProxyMethod() for GetShortyView()
+ }
+
+ // Clear old nterp fast path flags.
+ if (copy->HasNterpEntryPointFastPathFlag()) {
+ copy->ClearNterpEntryPointFastPathFlag(); // Flag has other uses, clear it conditionally.
+ }
+ copy->ClearNterpInvokeFastPathFlag();
+
+ // Check if nterp fast paths available on target ISA.
+ std::string_view shorty = orig->GetShortyView(); // Use orig, copy's class not yet ready.
+ uint32_t new_nterp_flags =
+ GetNterpFastPathFlags(shorty, copy->GetAccessFlags(), compiler_options_.GetInstructionSet());
+
+ // Set new nterp fast path flags, if approporiate.
+ if ((new_nterp_flags & kAccNterpEntryPointFastPathFlag) != 0) {
+ copy->SetNterpEntryPointFastPathFlag();
+ }
+ if ((new_nterp_flags & kAccNterpInvokeFastPathFlag) != 0) {
+ copy->SetNterpInvokeFastPathFlag();
+ }
+}
+
} // namespace linker
} // namespace art
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index cdaea8718a..86c0972d37 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -596,6 +596,9 @@ class ImageWriter final {
void CopyAndFixupPointer(void* object, MemberOffset offset, ValueType src_value)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void ResetNterpFastPathFlags(ArtMethod* copy, ArtMethod* orig)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ALWAYS_INLINE
static bool IsStronglyInternedString(ObjPtr<mirror::String> str)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index a17545c52b..1307e0eae5 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -101,7 +101,7 @@ static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (ru
// Whether nterp can take a fast path when entering this method (runtime; non-native)
static constexpr uint32_t kAccNterpEntryPointFastPathFlag = 0x00100000;
// Set by the class linker to mark that a method does not have floating points
-// or longs in its shorty.
+// or longs in its shorty. On RISC-V 64, a method that has only reference args.
static constexpr uint32_t kAccNterpInvokeFastPathFlag = 0x00200000; // method (runtime)
static constexpr uint32_t kAccPublicApi = 0x10000000; // field, method
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d3108da1e5..e5384235fb 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -584,10 +584,19 @@ class ArtMethod final {
AddAccessFlags(kAccNterpEntryPointFastPathFlag);
}
+ void ClearNterpEntryPointFastPathFlag() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!IsNative());
+ ClearAccessFlags(kAccNterpEntryPointFastPathFlag);
+ }
+
void SetNterpInvokeFastPathFlag() REQUIRES_SHARED(Locks::mutator_lock_) {
AddAccessFlags(kAccNterpInvokeFastPathFlag);
}
+ void ClearNterpInvokeFastPathFlag() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClearAccessFlags(kAccNterpInvokeFastPathFlag);
+ }
+
// Returns whether the method is a string constructor. The method must not
// be a class initializer. (Class initializers are called from a different
// context where we do not need to check for string constructors.)
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 003acaea34..838d6c1a80 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -39,7 +39,6 @@
#include "barrier.h"
#include "base/arena_allocator.h"
#include "base/arena_bit_vector.h"
-#include "base/membarrier.h"
#include "base/casts.h"
#include "base/file_utils.h"
#include "base/hash_map.h"
@@ -47,6 +46,7 @@
#include "base/leb128.h"
#include "base/logging.h"
#include "base/mem_map_arena_pool.h"
+#include "base/membarrier.h"
#include "base/metrics/metrics.h"
#include "base/mutex-inl.h"
#include "base/os.h"
@@ -135,6 +135,7 @@
#include "mirror/var_handle.h"
#include "native/dalvik_system_DexFile.h"
#include "nativehelper/scoped_local_ref.h"
+#include "nterp_helpers-inl.h"
#include "nterp_helpers.h"
#include "oat.h"
#include "oat_file-inl.h"
@@ -3997,26 +3998,7 @@ void ClassLinker::LoadMethod(const DexFile& dex_file,
}
}
- // Check for nterp invoke fast-path based on shorty.
- bool all_parameters_are_reference = true;
- bool all_parameters_are_reference_or_int = true;
- for (size_t i = 1; i < shorty.length(); ++i) {
- if (shorty[i] != 'L') {
- all_parameters_are_reference = false;
- if (shorty[i] == 'F' || shorty[i] == 'D' || shorty[i] == 'J') {
- all_parameters_are_reference_or_int = false;
- break;
- }
- }
- }
- if (all_parameters_are_reference_or_int && shorty[0] != 'F' && shorty[0] != 'D') {
- // FIXME(riscv64): This optimization is currently disabled because riscv64 needs
- // to distinguish between zero-extended references and sign-extended integers.
- // We should enable this for references only and fix corresponding nterp fast-paths.
- if (kRuntimeISA != InstructionSet::kRiscv64) {
- access_flags |= kAccNterpInvokeFastPathFlag;
- }
- }
+ access_flags |= GetNterpFastPathFlags(shorty, access_flags, kRuntimeISA);
if (UNLIKELY((access_flags & kAccNative) != 0u)) {
// Check if the native method is annotated with @FastNative or @CriticalNative.
@@ -4039,10 +4021,6 @@ void ClassLinker::LoadMethod(const DexFile& dex_file,
DCHECK_EQ(method.GetCodeItemOffset(), 0u);
dst->SetDataPtrSize(nullptr, image_pointer_size_); // Single implementation not set yet.
} else {
- // Check for nterp entry fast-path based on shorty.
- if (all_parameters_are_reference) {
- access_flags |= kAccNterpEntryPointFastPathFlag;
- }
const dex::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
if (annotations::MethodIsNeverCompile(dex_file, class_def, dex_method_idx)) {
access_flags |= kAccCompileDontBother;
diff --git a/runtime/interpreter/mterp/riscv64/invoke.S b/runtime/interpreter/mterp/riscv64/invoke.S
index 569b750655..0bce65cc2f 100644
--- a/runtime/interpreter/mterp/riscv64/invoke.S
+++ b/runtime/interpreter/mterp/riscv64/invoke.S
@@ -15,9 +15,8 @@
// (0) If the next method's "quick code" is nterp, then set up a fresh nterp frame and perform a
// vreg->vreg transfer. Jump to handler for the next method's first opcode.
// - The following paths leave nterp. -
-// (1) If the next method is guaranteed to avoid floats, doubles, and longs, then the managed ABI is
-// very simple: just place all arguments in the native arg registers. We don't need to know the
-// precise types or widths, just the order matters. Call the quick code.
+// (1) If the next method is guaranteed to be only object refs, then the managed ABI is very simple:
+// just place all arguments in the native arg registers using LWU. Call the quick code.
// (2) If the next method has 0 or 1 argument, then the managed ABI is mildly overloaded by
// pessimistically placing a singleton 32-bit arg in both a0 and fa0; we don't have to know if
// the argument is an int or float. We might be able to avoid the shorty ...
@@ -664,6 +663,7 @@
// Temporaries: z0, z1
%def try_simple_args(v_fedc="", z0="", z1="", arg_start="1", skip="", uniq=""):
lwu $z0, ART_METHOD_ACCESS_FLAGS_OFFSET(a0)
+ // The meaning of nterp-invoke-fast-path-flag for RISC-V diverges from other ISAs.
BRANCH_IF_BIT_CLEAR $z0, $z0, ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG_BIT, $skip
srliw $z0, xINST, 12 // z0 := A
@@ -679,22 +679,22 @@
// A = 5
srliw $z1, xINST, 8 // z1 := A|G
andi $z1, $z1, 0xF // z1 := G
- GET_VREG a5, $z1
+ GET_VREG_OBJECT a5, $z1
.L${uniq}_simple_4:
srliw $z1, $v_fedc, 12 // z1 := F
- GET_VREG a4, $z1
+ GET_VREG_OBJECT a4, $z1
.L${uniq}_simple_3:
srliw $z1, $v_fedc, 8 // z1 := F|E
andi $z1, $z1, 0xF // z1 := E
- GET_VREG a3, $z1
+ GET_VREG_OBJECT a3, $z1
.L${uniq}_simple_2:
srliw $z1, $v_fedc, 4 // z1 := F|E|D
andi $z1, $z1, 0xF // z1 := D
- GET_VREG a2, $z1
+ GET_VREG_OBJECT a2, $z1
.L${uniq}_simple_1:
% if arg_start == "0":
andi $z1, $v_fedc, 0xF // z1 := C
- GET_VREG a1, $z1
+ GET_VREG_OBJECT a1, $z1
// instance: a1 already set to "this"
.L${uniq}_simple_done:
@@ -702,6 +702,7 @@
// Range variant.
%def try_simple_args_range(vC="", z0="", z1="", z2="", z3="", z4="", skip="", arg_start="1", uniq=""):
lwu $z0, ART_METHOD_ACCESS_FLAGS_OFFSET(a0)
+ // The meaning of nterp-invoke-fast-path-flag for RISC-V diverges from other ISAs.
BRANCH_IF_BIT_CLEAR $z0, $z0, ART_METHOD_NTERP_INVOKE_FAST_PATH_FLAG_BIT, $skip
srliw $z0, xINST, 8 // z0 := AA
@@ -734,7 +735,7 @@
// z4 := &OUT[CCCC + 7]
bltz $z2, .L${uniq}_simple_loop_wide
// if AA odd, branch to wide-copy
- lw $z2, ($z3)
+ lwu $z2, ($z3)
sw $z2, ($z4)
addi $z3, $z3, 4
addi $z4, $z4, 4
@@ -750,20 +751,20 @@
// Bottom 7 slots of OUT array never written; first args are passed with a1-a7.
.L${uniq}_simple_7:
- lw a7, 6*4($z1)
+ lwu a7, 6*4($z1)
.L${uniq}_simple_6:
- lw a6, 5*4($z1)
+ lwu a6, 5*4($z1)
.L${uniq}_simple_5:
- lw a5, 4*4($z1)
+ lwu a5, 4*4($z1)
.L${uniq}_simple_4:
- lw a4, 3*4($z1)
+ lwu a4, 3*4($z1)
.L${uniq}_simple_3:
- lw a3, 2*4($z1)
+ lwu a3, 2*4($z1)
.L${uniq}_simple_2:
- lw a2, 1*4($z1)
+ lwu a2, 1*4($z1)
.L${uniq}_simple_1:
% if arg_start == "0": # static:
- lw a1, 0*4($z1)
+ lwu a1, 0*4($z1)
%#:
.L${uniq}_simple_done:
diff --git a/runtime/nterp_helpers-inl.h b/runtime/nterp_helpers-inl.h
new file mode 100644
index 0000000000..84c4f25c18
--- /dev/null
+++ b/runtime/nterp_helpers-inl.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 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_NTERP_HELPERS_INL_H_
+#define ART_RUNTIME_NTERP_HELPERS_INL_H_
+
+#include "nterp_helpers.h"
+
+namespace art {
+
+ALWAYS_INLINE inline uint32_t GetNterpFastPathFlags(std::string_view shorty,
+ uint32_t access_flags,
+ InstructionSet isa) {
+ bool all_parameters_are_reference = true;
+ bool all_parameters_are_reference_or_int = true;
+ for (size_t i = 1; i < shorty.length(); ++i) {
+ if (shorty[i] != 'L') {
+ all_parameters_are_reference = false;
+ if (shorty[i] == 'F' || shorty[i] == 'D' || shorty[i] == 'J') {
+ all_parameters_are_reference_or_int = false;
+ break;
+ }
+ }
+ }
+
+ // Check for nterp entry fast-path based on shorty.
+ uint32_t nterp_flags = 0u;
+ if ((access_flags & kAccNative) == 0u && all_parameters_are_reference) {
+ nterp_flags |= kAccNterpEntryPointFastPathFlag;
+ }
+
+ // Check for nterp invoke fast-path based on shorty.
+ const bool no_float_return = shorty[0] != 'F' && shorty[0] != 'D';
+ if (isa != InstructionSet::kRiscv64 && all_parameters_are_reference_or_int && no_float_return) {
+ nterp_flags |= kAccNterpInvokeFastPathFlag;
+ } else if (isa == InstructionSet::kRiscv64 && all_parameters_are_reference && no_float_return) {
+ nterp_flags |= kAccNterpInvokeFastPathFlag;
+ }
+ return nterp_flags;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_NTERP_HELPERS_INL_H_
diff --git a/runtime/nterp_helpers.h b/runtime/nterp_helpers.h
index 236059b0ed..f79dcf94f3 100644
--- a/runtime/nterp_helpers.h
+++ b/runtime/nterp_helpers.h
@@ -75,6 +75,11 @@ uint32_t NterpGetVRegReference(ArtMethod** frame, uint16_t vreg)
bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa = kRuntimeISA)
REQUIRES_SHARED(Locks::mutator_lock_);
+/**
+ * Returns kAccNterpInvokeFastPathFlag and/or kAccNterpEntryPointFastPathFlag, if appropriate.
+ */
+uint32_t GetNterpFastPathFlags(std::string_view shorty, uint32_t access_flags, InstructionSet isa);
+
} // namespace art
#endif // ART_RUNTIME_NTERP_HELPERS_H_