Merge "Fix unquitable apps when debug enabled."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index d59d8f6..bd7f900 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -26,6 +26,8 @@
AbstractMethod \
AllFields \
DexToDexDecompiler \
+ ErroneousA \
+ ErroneousB \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -85,7 +87,7 @@
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
diff --git a/build/art.go b/build/art.go
index ccaa11d..e6e0544 100644
--- a/build/art.go
+++ b/build/art.go
@@ -58,7 +58,7 @@
asflags = append(asflags, "-DART_HEAP_POISONING=1")
}
- if envTrue(ctx, "ART_USE_READ_BARRIER") {
+ if envTrue(ctx, "ART_USE_READ_BARRIER") || ctx.AConfig().ArtUseReadBarrier() {
// Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
// The default is BAKER.
barrierType := envDefault(ctx, "ART_READ_BARRIER_TYPE", "BAKER")
@@ -259,7 +259,7 @@
}
func artLibrary() (blueprint.Module, []interface{}) {
- library, _ := cc.NewLibrary(android.HostAndDeviceSupported, true, true)
+ library, _ := cc.NewLibrary(android.HostAndDeviceSupported)
module, props := library.Init()
props = installCodegenCustomizer(module, props, true)
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index 6e042c3..f4540ff 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -24,10 +24,12 @@
#include <iostream>
#include <string>
-#include "runtime.h"
+#include "android-base/stringprintf.h"
+
+#include "base/logging.h"
#include "base/stringpiece.h"
#include "noop_compiler_callbacks.h"
-#include "base/logging.h"
+#include "runtime.h"
#if !defined(NDEBUG)
#define DBG_LOG LOG(INFO)
@@ -197,7 +199,7 @@
" Example: --boot-image=/system/framework/boot.art\n"
" (specifies /system/framework/<arch>/boot.art as the image file)\n"
"\n";
- usage += StringPrintf( // Optional.
+ usage += android::base::StringPrintf( // Optional.
" --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
" file based on the image location set.\n"
" Example: --instruction-set=x86\n"
@@ -264,8 +266,8 @@
// Check that the boot image location points to a valid file name.
std::string file_name;
if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
- *error_msg = StringPrintf("No corresponding file for location '%s' exists",
- file_name.c_str());
+ *error_msg = android::base::StringPrintf("No corresponding file for location '%s' exists",
+ file_name.c_str());
return false;
}
diff --git a/compiler/Android.bp b/compiler/Android.bp
index db55ea0..46f3358 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -49,6 +49,7 @@
"optimizing/block_builder.cc",
"optimizing/bounds_check_elimination.cc",
"optimizing/builder.cc",
+ "optimizing/cha_guard_optimization.cc",
"optimizing/code_generator.cc",
"optimizing/code_generator_utils.cc",
"optimizing/constant_folding.cc",
@@ -145,6 +146,7 @@
mips64: {
srcs: [
"jni/quick/mips64/calling_convention_mips64.cc",
+ "linker/mips64/relative_patcher_mips64.cc",
"optimizing/code_generator_mips64.cc",
"optimizing/intrinsics_mips64.cc",
"utils/mips64/assembler_mips64.cc",
@@ -381,6 +383,11 @@
"linker/mips/relative_patcher_mips32r6_test.cc",
],
},
+ mips64: {
+ srcs: [
+ "linker/mips64/relative_patcher_mips64_test.cc",
+ ],
+ },
x86: {
srcs: [
"linker/x86/relative_patcher_x86_test.cc",
diff --git a/compiler/compiled_class.h b/compiler/compiled_class.h
index b88d613..06ce946 100644
--- a/compiler/compiled_class.h
+++ b/compiler/compiled_class.h
@@ -28,8 +28,11 @@
mirror::Class::Status GetStatus() const {
return status_;
}
+ void SetStatus(mirror::Class::Status status) {
+ status_ = status;
+ }
private:
- const mirror::Class::Status status_;
+ mirror::Class::Status status_;
};
} // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index cf69f46..d4f6545 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -16,6 +16,8 @@
#include "dex_to_dex_compiler.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
@@ -32,6 +34,8 @@
namespace art {
namespace optimizer {
+using android::base::StringPrintf;
+
// Controls quickening activation.
const bool kEnableQuickening = true;
// Control check-cast elision.
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
index 051125e..bfd485d 100644
--- a/compiler/dex/dex_to_dex_decompiler.cc
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -27,10 +27,13 @@
class DexDecompiler {
public:
- DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info)
+ DexDecompiler(const DexFile::CodeItem& code_item,
+ const ArrayRef<const uint8_t>& quickened_info,
+ bool decompile_return_instruction)
: code_item_(code_item),
quickened_info_ptr_(quickened_info.data()),
- quickened_info_end_(quickened_info.data() + quickened_info.size()) {}
+ quickened_info_end_(quickened_info.data() + quickened_info.size()),
+ decompile_return_instruction_(decompile_return_instruction) {}
bool Decompile();
@@ -87,6 +90,7 @@
const DexFile::CodeItem& code_item_;
const uint8_t* quickened_info_ptr_;
const uint8_t* const quickened_info_end_;
+ const bool decompile_return_instruction_;
DISALLOW_COPY_AND_ASSIGN(DexDecompiler);
};
@@ -102,7 +106,9 @@
switch (inst->Opcode()) {
case Instruction::RETURN_VOID_NO_BARRIER:
- inst->SetOpcode(Instruction::RETURN_VOID);
+ if (decompile_return_instruction_) {
+ inst->SetOpcode(Instruction::RETURN_VOID);
+ }
break;
case Instruction::NOP:
@@ -189,8 +195,12 @@
}
bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
- const ArrayRef<const uint8_t>& quickened_info) {
- DexDecompiler decompiler(code_item, quickened_info);
+ const ArrayRef<const uint8_t>& quickened_info,
+ bool decompile_return_instruction) {
+ if (quickened_info.size() == 0 && !decompile_return_instruction) {
+ return true;
+ }
+ DexDecompiler decompiler(code_item, quickened_info, decompile_return_instruction);
return decompiler.Decompile();
}
diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h
index 5502ca2..b5d5b91 100644
--- a/compiler/dex/dex_to_dex_decompiler.h
+++ b/compiler/dex/dex_to_dex_decompiler.h
@@ -30,7 +30,8 @@
// consistent with DexToDexCompiler, but we should really change it to
// DexFile::CodeItem*.
bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
- const ArrayRef<const uint8_t>& quickened_data);
+ const ArrayRef<const uint8_t>& quickened_data,
+ bool decompile_return_instruction);
} // namespace optimizer
} // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index ea6c7a2..9a8d27c 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -102,7 +102,8 @@
if (compiled_method != nullptr) {
table = compiled_method->GetVmapTable();
}
- optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+ optimizer::ArtDecompileDEX(
+ *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
it.Next();
}
while (it.HasNextVirtualMethod()) {
@@ -113,7 +114,8 @@
if (compiled_method != nullptr) {
table = compiled_method->GetVmapTable();
}
- optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+ optimizer::ArtDecompileDEX(
+ *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
it.Next();
}
DCHECK(!it.HasNext());
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 9d39bf2..00a7d44 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -46,9 +46,7 @@
void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
DCHECK(method_verifier != nullptr);
MethodReference ref = method_verifier->GetMethodReference();
- bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
- std::unique_ptr<const VerifiedMethod> verified_method(
- VerifiedMethod::Create(method_verifier, compile));
+ std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
if (verified_method == nullptr) {
// We'll punt this later.
return;
@@ -84,7 +82,6 @@
// TODO: Investigate why are we doing the work again for this method and try to avoid it.
LOG(WARNING) << "Method processed more than once: " << ref.PrettyMethod();
if (!Runtime::Current()->UseJitCompilation()) {
- DCHECK_EQ(existing->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
DCHECK_EQ(existing->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
}
// Let the unique_ptr delete the new verified method since there was already an existing one
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 1bdace9..cbca333 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -42,25 +42,12 @@
has_runtime_throw_(has_runtime_throw) {
}
-const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier,
- bool compile) {
+const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier) {
+ DCHECK(Runtime::Current()->IsAotCompiler());
std::unique_ptr<VerifiedMethod> verified_method(
new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(),
method_verifier->HasInstructionThatWillThrow()));
- if (compile) {
- // TODO: move this out when DEX-to-DEX supports devirtualization.
- if (method_verifier->HasVirtualOrInterfaceInvokes()) {
- verified_method->GenerateDevirtMap(method_verifier);
- }
-
- // Only need dequicken info for JIT so far.
- if (Runtime::Current()->UseJitCompilation() &&
- !verified_method->GenerateDequickenMap(method_verifier)) {
- return nullptr;
- }
- }
-
if (method_verifier->HasCheckCasts()) {
verified_method->GenerateSafeCastSet(method_verifier);
}
@@ -68,140 +55,10 @@
return verified_method.release();
}
-const MethodReference* VerifiedMethod::GetDevirtTarget(uint32_t dex_pc) const {
- auto it = devirt_map_.find(dex_pc);
- return (it != devirt_map_.end()) ? &it->second : nullptr;
-}
-
-const DexFileReference* VerifiedMethod::GetDequickenIndex(uint32_t dex_pc) const {
- DCHECK(Runtime::Current()->UseJitCompilation());
- auto it = dequicken_map_.find(dex_pc);
- return (it != dequicken_map_.end()) ? &it->second : nullptr;
-}
-
bool VerifiedMethod::IsSafeCast(uint32_t pc) const {
return std::binary_search(safe_cast_set_.begin(), safe_cast_set_.end(), pc);
}
-bool VerifiedMethod::GenerateDequickenMap(verifier::MethodVerifier* method_verifier) {
- if (method_verifier->HasFailures()) {
- return false;
- }
- const DexFile::CodeItem* code_item = method_verifier->CodeItem();
- const uint16_t* insns = code_item->insns_;
- const Instruction* inst = Instruction::At(insns);
- const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
- for (; inst < end; inst = inst->Next()) {
- const bool is_virtual_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK;
- const bool is_range_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK;
- if (is_virtual_quick || is_range_quick) {
- uint32_t dex_pc = inst->GetDexPc(insns);
- verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
- ArtMethod* method =
- method_verifier->GetQuickInvokedMethod(inst, line, is_range_quick, true);
- if (method == nullptr) {
- // It can be null if the line wasn't verified since it was unreachable.
- return false;
- }
- // The verifier must know what the type of the object was or else we would have gotten a
- // failure. Put the dex method index in the dequicken map since we need this to get number of
- // arguments in the compiler.
- dequicken_map_.Put(dex_pc, DexFileReference(method->GetDexFile(),
- method->GetDexMethodIndex()));
- } else if (IsInstructionIGetQuickOrIPutQuick(inst->Opcode())) {
- uint32_t dex_pc = inst->GetDexPc(insns);
- verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
- ArtField* field = method_verifier->GetQuickFieldAccess(inst, line);
- if (field == nullptr) {
- // It can be null if the line wasn't verified since it was unreachable.
- return false;
- }
- // The verifier must know what the type of the field was or else we would have gotten a
- // failure. Put the dex field index in the dequicken map since we need this for lowering
- // in the compiler.
- // TODO: Putting a field index in a method reference is gross.
- dequicken_map_.Put(dex_pc, DexFileReference(field->GetDexFile(), field->GetDexFieldIndex()));
- }
- }
- return true;
-}
-
-void VerifiedMethod::GenerateDevirtMap(verifier::MethodVerifier* method_verifier) {
- // It is risky to rely on reg_types for sharpening in cases of soft
- // verification, we might end up sharpening to a wrong implementation. Just abort.
- if (method_verifier->HasFailures()) {
- return;
- }
-
- const DexFile::CodeItem* code_item = method_verifier->CodeItem();
- const uint16_t* insns = code_item->insns_;
- const Instruction* inst = Instruction::At(insns);
- const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
-
- for (; inst < end; inst = inst->Next()) {
- const bool is_virtual = inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
- inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE;
- const bool is_interface = inst->Opcode() == Instruction::INVOKE_INTERFACE ||
- inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
-
- if (!is_interface && !is_virtual) {
- continue;
- }
- // Get reg type for register holding the reference to the object that will be dispatched upon.
- uint32_t dex_pc = inst->GetDexPc(insns);
- verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
- const bool is_range = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE ||
- inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
- const verifier::RegType&
- reg_type(line->GetRegisterType(method_verifier,
- is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
-
- if (!reg_type.HasClass()) {
- // We will compute devirtualization information only when we know the Class of the reg type.
- continue;
- }
- mirror::Class* reg_class = reg_type.GetClass();
- if (reg_class->IsInterface()) {
- // We can't devirtualize when the known type of the register is an interface.
- continue;
- }
- if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) {
- // We can't devirtualize abstract classes except on arrays of abstract classes.
- continue;
- }
- auto* cl = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = cl->GetImagePointerSize();
- ArtMethod* abstract_method = method_verifier->GetDexCache()->GetResolvedMethod(
- is_range ? inst->VRegB_3rc() : inst->VRegB_35c(), pointer_size);
- if (abstract_method == nullptr) {
- // If the method is not found in the cache this means that it was never found
- // by ResolveMethodAndCheckAccess() called when verifying invoke_*.
- continue;
- }
- // Find the concrete method.
- ArtMethod* concrete_method = nullptr;
- if (is_interface) {
- concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(
- abstract_method, pointer_size);
- }
- if (is_virtual) {
- concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(
- abstract_method, pointer_size);
- }
- if (concrete_method == nullptr || !concrete_method->IsInvokable()) {
- // In cases where concrete_method is not found, or is not invokable, continue to the next
- // invoke.
- continue;
- }
- if (reg_type.IsPreciseReference() || concrete_method->IsFinal() ||
- concrete_method->GetDeclaringClass()->IsFinal()) {
- // If we knew exactly the class being dispatched upon, or if the target method cannot be
- // overridden record the target to be used in the compiler driver.
- devirt_map_.Put(dex_pc, concrete_method->ToMethodReference());
- }
- }
-}
-
void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) {
/*
* Walks over the method code and adds any cast instructions in which
@@ -218,35 +75,32 @@
for (; inst < end; inst = inst->Next()) {
Instruction::Code code = inst->Opcode();
- if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) {
+ if (code == Instruction::CHECK_CAST) {
uint32_t dex_pc = inst->GetDexPc(code_item->insns_);
if (!method_verifier->GetInstructionFlags(dex_pc).IsVisited()) {
// Do not attempt to quicken this instruction, it's unreachable anyway.
continue;
}
const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
- bool is_safe_cast = false;
- if (code == Instruction::CHECK_CAST) {
- const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
- inst->VRegA_21c()));
- const verifier::RegType& cast_type =
- method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
- is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type, method_verifier);
- } else {
- const verifier::RegType& array_type(line->GetRegisterType(method_verifier,
- inst->VRegB_23x()));
- // We only know its safe to assign to an array if the array type is precise. For example,
- // an Object[] can have any type of object stored in it, but it may also be assigned a
- // String[] in which case the stores need to be of Strings.
- if (array_type.IsPreciseReference()) {
- const verifier::RegType& value_type(line->GetRegisterType(method_verifier,
- inst->VRegA_23x()));
- const verifier::RegType& component_type = method_verifier->GetRegTypeCache()
- ->GetComponentType(array_type, method_verifier->GetClassLoader());
- is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type, method_verifier);
+ const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
+ inst->VRegA_21c()));
+ const verifier::RegType& cast_type =
+ method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
+ // Pass null for the method verifier to not record the VerifierDeps dependency
+ // if the types are not assignable.
+ if (cast_type.IsStrictlyAssignableFrom(reg_type, /* method_verifier */ nullptr)) {
+ // The types are assignable, we record that dependency in the VerifierDeps so
+ // that if this changes after OTA, we will re-verify again.
+ // We check if reg_type has a class, as the verifier may have inferred it's
+ // 'null'.
+ if (reg_type.HasClass()) {
+ DCHECK(cast_type.HasClass());
+ verifier::VerifierDeps::MaybeRecordAssignability(method_verifier->GetDexFile(),
+ cast_type.GetClass(),
+ reg_type.GetClass(),
+ /* strict */ true,
+ /* assignable */ true);
}
- }
- if (is_safe_cast) {
// Verify ordering for push_back() to the sorted vector.
DCHECK(safe_cast_set_.empty() || safe_cast_set_.back() < dex_pc);
safe_cast_set_.push_back(dex_pc);
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index ce53417..439e69e 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -39,31 +39,14 @@
// is better for performance (not just memory usage), especially for large sets.
typedef std::vector<uint32_t> SafeCastSet;
- // Devirtualization map type maps dex offset to concrete method reference.
- typedef SafeMap<uint32_t, MethodReference> DevirtualizationMap;
-
- // Devirtualization map type maps dex offset to field / method idx.
- typedef SafeMap<uint32_t, DexFileReference> DequickenMap;
-
- static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier, bool compile)
+ static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier)
REQUIRES_SHARED(Locks::mutator_lock_);
~VerifiedMethod() = default;
- const DevirtualizationMap& GetDevirtMap() const {
- return devirt_map_;
- }
-
const SafeCastSet& GetSafeCastSet() const {
return safe_cast_set_;
}
- // Returns the devirtualization target method, or null if none.
- const MethodReference* GetDevirtTarget(uint32_t dex_pc) const;
-
- // Returns the dequicken field / method for a quick invoke / field get. Returns null if there is
- // no entry for that dex pc.
- const DexFileReference* GetDequickenIndex(uint32_t dex_pc) const;
-
// Returns true if the cast can statically be verified to be redundant
// by using the check-cast elision peephole optimization in the verifier.
bool IsSafeCast(uint32_t pc) const;
@@ -82,38 +65,10 @@
}
private:
- /*
- * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
- * verification). For type-precise determination we have all the data we need, so we just need to
- * encode it in some clever fashion.
- * Stores the data in dex_gc_map_, returns true on success and false on failure.
- */
- bool GenerateGcMap(verifier::MethodVerifier* method_verifier);
-
- // Verify that the GC map associated with method_ is well formed.
- static void VerifyGcMap(verifier::MethodVerifier* method_verifier,
- const std::vector<uint8_t>& data);
-
- // Compute sizes for GC map data.
- static void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier,
- size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
-
- // Generate devirtualizaion map into devirt_map_.
- void GenerateDevirtMap(verifier::MethodVerifier* method_verifier)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Generate dequickening map into dequicken_map_. Returns false if there is an error.
- bool GenerateDequickenMap(verifier::MethodVerifier* method_verifier)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Generate safe case set into safe_cast_set_.
void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
REQUIRES_SHARED(Locks::mutator_lock_);
- DevirtualizationMap devirt_map_;
- // Dequicken map is required for compiling quickened byte codes. The quicken maps from
- // dex PC to dex method index or dex field index based on the instruction.
- DequickenMap dequicken_map_;
SafeCastSet safe_cast_set_;
const uint32_t encountered_error_types_;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e62bdb5..2950266 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -299,7 +299,7 @@
dump_passes_(dump_passes),
timings_logger_(timer),
compiler_context_(nullptr),
- support_boot_image_fixup_(instruction_set != kMips64),
+ support_boot_image_fixup_(true),
dex_files_for_oat_file_(nullptr),
compiled_method_storage_(swap_fd),
profile_compilation_info_(profile_compilation_info),
@@ -423,7 +423,7 @@
// Compile:
// 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
// compilation.
- if (!GetCompilerOptions().VerifyAtRuntime()) {
+ if (GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
Compile(class_loader, dex_files, timings);
}
if (dump_stats_) {
@@ -435,7 +435,8 @@
// In-place unquicken the given `dex_files` based on `quickening_info`.
static void Unquicken(const std::vector<const DexFile*>& dex_files,
- const ArrayRef<const uint8_t>& quickening_info) {
+ const ArrayRef<const uint8_t>& quickening_info,
+ bool decompile_return_instruction) {
const uint8_t* quickening_info_ptr = quickening_info.data();
const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
for (const DexFile* dex_file : dex_files) {
@@ -454,14 +455,14 @@
it.Next();
}
- // Unquicken each method.
while (it.HasNextDirectMethod()) {
const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
if (code_item != nullptr) {
uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
quickening_info_ptr += sizeof(uint32_t);
- optimizer::ArtDecompileDEX(
- *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ optimizer::ArtDecompileDEX(*code_item,
+ ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+ decompile_return_instruction);
quickening_info_ptr += quickening_size;
}
it.Next();
@@ -472,8 +473,9 @@
if (code_item != nullptr) {
uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
quickening_info_ptr += sizeof(uint32_t);
- optimizer::ArtDecompileDEX(
- *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ optimizer::ArtDecompileDEX(*code_item,
+ ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+ decompile_return_instruction);
quickening_info_ptr += quickening_size;
}
it.Next();
@@ -492,7 +494,15 @@
// TODO: we unquicken unconditionnally, as we don't know
// if the boot image has changed. How exactly we'll know is under
// experimentation.
- Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+ if (vdex_file->GetQuickeningInfo().size() != 0) {
+ TimingLogger::ScopedTiming t("Unquicken", timings);
+ // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
+ // optimization does not depend on the boot image (the optimization relies on not
+ // having final fields in a class, which does not change for an app).
+ Unquicken(dex_files,
+ vdex_file->GetQuickeningInfo(),
+ /* decompile_return_instruction */ false);
+ }
Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
}
@@ -504,10 +514,7 @@
const DexFile& dex_file, const DexFile::ClassDef& class_def)
REQUIRES_SHARED(Locks::mutator_lock_) {
auto* const runtime = Runtime::Current();
- if (runtime->UseJitCompilation() || driver.GetCompilerOptions().VerifyAtRuntime()) {
- // Verify at runtime shouldn't dex to dex since we didn't resolve of verify.
- return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
- }
+ DCHECK(driver.GetCompilerOptions().IsAnyMethodCompilationEnabled());
const char* descriptor = dex_file.GetClassDescriptor(class_def);
ClassLinker* class_linker = runtime->GetClassLinker();
mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader);
@@ -948,24 +955,17 @@
LoadImageClasses(timings);
VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
- const bool verification_enabled = compiler_options_->IsVerificationEnabled();
- const bool never_verify = compiler_options_->NeverVerify();
- const bool verify_only_profile = compiler_options_->VerifyOnlyProfile();
-
- // We need to resolve for never_verify since it needs to run dex to dex to add the
- // RETURN_VOID_NO_BARRIER.
- // Let the verifier resolve as needed for the verify_only_profile case.
- if ((never_verify || verification_enabled) && !verify_only_profile) {
+ if (compiler_options_->IsAnyMethodCompilationEnabled()) {
Resolve(class_loader, dex_files, timings);
VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
}
- if (never_verify) {
+ if (compiler_options_->AssumeClassesAreVerified()) {
VLOG(compiler) << "Verify none mode specified, skipping verification.";
SetVerified(class_loader, dex_files, timings);
}
- if (!verification_enabled) {
+ if (!compiler_options_->IsVerificationEnabled()) {
return;
}
@@ -983,8 +983,10 @@
<< "situations. Please check the log.";
}
- InitializeClasses(class_loader, dex_files, timings);
- VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
+ if (compiler_options_->IsAnyMethodCompilationEnabled()) {
+ InitializeClasses(class_loader, dex_files, timings);
+ VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
+ }
UpdateImageClasses(timings);
VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
@@ -1529,90 +1531,6 @@
}
}
-void CompilerDriver::GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
- ArtMethod* method,
- uintptr_t* direct_code,
- uintptr_t* direct_method) {
- // For direct and static methods compute possible direct_code and direct_method values, ie
- // an address for the Method* being invoked and an address of the code for that Method*.
- // For interface calls compute a value for direct_method that is the interface method being
- // invoked, so this can be passed to the out-of-line runtime support code.
- *direct_code = 0;
- *direct_method = 0;
- Runtime* const runtime = Runtime::Current();
- gc::Heap* const heap = runtime->GetHeap();
- auto* cl = runtime->GetClassLinker();
- bool use_dex_cache = GetCompilerOptions().GetCompilePic(); // Off by default
- const bool compiling_boot = heap->IsCompilingBoot();
- // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
- const bool force_relocations = (compiling_boot ||
- GetCompilerOptions().GetIncludePatchInformation());
- // TODO: support patching on all architectures.
- use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
- mirror::Class* declaring_class = method->GetDeclaringClass();
- bool method_code_in_boot = declaring_class->GetClassLoader() == nullptr;
- if (!use_dex_cache) {
- if (!method_code_in_boot) {
- use_dex_cache = true;
- } else if (method->IsStatic() &&
- declaring_class != referrer_class &&
- !declaring_class->IsInitialized()) {
- // Ensure we run the clinit trampoline unless we are invoking a static method in the same
- // class.
- use_dex_cache = true;
- }
- }
- if (runtime->UseJitCompilation()) {
- // If we are the JIT, then don't allow a direct call to the interpreter bridge since this will
- // never be updated even after we compile the method.
- if (cl->IsQuickToInterpreterBridge(
- reinterpret_cast<const void*>(compiler_->GetEntryPointOf(method)))) {
- use_dex_cache = true;
- }
- }
-
- if (!use_dex_cache && force_relocations) {
- bool is_in_image;
- if (GetCompilerOptions().IsBootImage()) {
- is_in_image = IsImageClass(method->GetDeclaringClassDescriptor());
- } else {
- is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 &&
- heap->FindSpaceFromObject(method->GetDeclaringClass(), false)->IsImageSpace() &&
- !cl->IsQuickToInterpreterBridge(
- reinterpret_cast<const void*>(compiler_->GetEntryPointOf(method)));
- }
- if (!is_in_image) {
- // We can only branch directly to Methods that are resolved in the DexCache.
- // Otherwise we won't invoke the resolution trampoline.
- use_dex_cache = true;
- }
- }
-
- if (!use_dex_cache) {
- bool method_in_image = false;
- const std::vector<gc::space::ImageSpace*>& image_spaces = heap->GetBootImageSpaces();
- for (gc::space::ImageSpace* image_space : image_spaces) {
- const auto& method_section = image_space->GetImageHeader().GetMethodsSection();
- if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) {
- method_in_image = true;
- break;
- }
- }
- if (method_in_image || compiling_boot || runtime->UseJitCompilation()) {
- // We know we must be able to get to the method in the image, so use that pointer.
- // In the case where we are the JIT, we can always use direct pointers since we know where
- // the method and its code are / will be. We don't sharpen to interpreter bridge since we
- // check IsQuickToInterpreterBridge above.
- CHECK(!method->IsAbstract());
- *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
- *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
- } else {
- // Set the code and rely on the dex cache for the method.
- *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
- }
- }
-}
-
const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file,
uint32_t method_idx) const {
MethodReference ref(dex_file, method_idx);
@@ -2060,21 +1978,32 @@
std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
- const char* descriptor = dex_file->GetClassDescriptor(class_def);
- cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
- if (cls.Get() == nullptr) {
- CHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
- } else if (set.find(class_def.class_idx_) == set.end()) {
- ObjectLock<mirror::Class> lock(soa.Self(), cls);
- mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
- // Create `VerifiedMethod`s for each methods, the compiler expects one for
- // quickening or compiling.
- // Note that this means:
- // - We're only going to compile methods that did verify.
- // - Quickening will not do checkcast ellision.
- // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
- PopulateVerifiedMethods(*dex_file, i, verification_results_);
+ if (set.find(class_def.class_idx_) == set.end()) {
+ if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+ // Just update the compiled_classes_ map. The compiler doesn't need to resolve
+ // the type.
+ compiled_classes_.Overwrite(
+ ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified));
+ } else {
+ // Resolve the type, so later compilation stages know they don't need to verify
+ // the class.
+ const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
+ if (cls.Get() != nullptr) {
+ ObjectLock<mirror::Class> lock(soa.Self(), cls);
+ mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
+ } else {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ }
+ // Create `VerifiedMethod`s for each methods, the compiler expects one for
+ // quickening or compiling.
+ // Note that this means:
+ // - We're only going to compile methods that did verify.
+ // - Quickening will not do checkcast ellision.
+ // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
+ PopulateVerifiedMethods(*dex_file, i, verification_results_);
+ }
}
}
}
@@ -2675,29 +2604,29 @@
}
void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
+ switch (status) {
+ case mirror::Class::kStatusNotReady:
+ case mirror::Class::kStatusError:
+ case mirror::Class::kStatusRetryVerificationAtRuntime:
+ case mirror::Class::kStatusVerified:
+ case mirror::Class::kStatusInitialized:
+ case mirror::Class::kStatusResolved:
+ break; // Expected states.
+ default:
+ LOG(FATAL) << "Unexpected class status for class "
+ << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
+ << " of " << status;
+ }
+
MutexLock mu(Thread::Current(), compiled_classes_lock_);
auto it = compiled_classes_.find(ref);
- if (it == compiled_classes_.end() || it->second->GetStatus() != status) {
- // An entry doesn't exist or the status is lower than the new status.
- if (it != compiled_classes_.end()) {
- CHECK_GT(status, it->second->GetStatus());
- delete it->second;
- }
- switch (status) {
- case mirror::Class::kStatusNotReady:
- case mirror::Class::kStatusError:
- case mirror::Class::kStatusRetryVerificationAtRuntime:
- case mirror::Class::kStatusVerified:
- case mirror::Class::kStatusInitialized:
- case mirror::Class::kStatusResolved:
- break; // Expected states.
- default:
- LOG(FATAL) << "Unexpected class status for class "
- << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
- << " of " << status;
- }
+ if (it == compiled_classes_.end()) {
CompiledClass* compiled_class = new CompiledClass(status);
compiled_classes_.Overwrite(ref, compiled_class);
+ } else if (status > it->second->GetStatus()) {
+ // Update the status if we now have a greater one. This happens with vdex,
+ // which records a class is verified, but does not resolve it.
+ it->second->SetStatus(status);
}
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index cc50197..2e3b7c8 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -378,13 +378,6 @@
return current_dex_to_dex_methods_;
}
- // Compute constant code and method pointers when possible.
- void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
- ArtMethod* method,
- /* out */ uintptr_t* direct_code,
- /* out */ uintptr_t* direct_method)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
private:
// Return whether the declaring class of `resolved_member` is
// available to `referrer_class` for read or write access using two
@@ -440,9 +433,12 @@
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
+ // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a
+ // single-threaded way.
void Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings);
+ TimingLogger* timings)
+ NO_THREAD_SAFETY_ANALYSIS;
void VerifyDexFile(jobject class_loader,
const DexFile& dex_file,
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 9c62f80..6894cd5 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -109,7 +109,7 @@
return CompilerFilter::IsVerificationEnabled(compiler_filter_);
}
- bool NeverVerify() const {
+ bool AssumeClassesAreVerified() const {
return compiler_filter_ == CompilerFilter::kVerifyNone;
}
@@ -117,6 +117,10 @@
return compiler_filter_ == CompilerFilter::kVerifyProfile;
}
+ bool IsAnyMethodCompilationEnabled() const {
+ return CompilerFilter::IsAnyMethodCompilationEnabled(compiler_filter_);
+ }
+
size_t GetHugeMethodThreshold() const {
return huge_method_threshold_;
}
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 64fd9e7..47b1929 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -16,7 +16,6 @@
#include "dex_compilation_unit.h"
-#include "base/stringprintf.h"
#include "mirror/dex_cache.h"
#include "utils.h"
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 6f48779..9669c4a 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -16,7 +16,6 @@
#include "elf_file.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "common_compiler_test.h"
#include "elf_file.h"
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 9bbe595..b0225a3 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -20,10 +20,14 @@
#include <string>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
+#include "compiler_callbacks.h"
#include "common_compiler_test.h"
#include "debug/method_debug_info.h"
+#include "dex/quick_compiler_callbacks.h"
#include "driver/compiler_options.h"
#include "elf_writer.h"
#include "elf_writer_quick.h"
@@ -74,6 +78,14 @@
const std::string& extra_dex = "",
const std::string& image_class = "");
+ void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+ CommonCompilerTest::SetUpRuntimeOptions(options);
+ callbacks_.reset(new QuickCompilerCallbacks(
+ verification_results_.get(),
+ CompilerCallbacks::CallbackMode::kCompileBootImage));
+ options->push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+ }
+
std::unordered_set<std::string>* GetImageClasses() OVERRIDE {
return new std::unordered_set<std::string>(image_classes_);
}
@@ -134,7 +146,8 @@
// Create a generic tmp file, to be the base of the .art and .oat temporary files.
ScratchFile location;
for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
- std::string cur_location(StringPrintf("%s-%d.art", location.GetFilename().c_str(), i));
+ std::string cur_location =
+ android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i);
image_locations.push_back(ScratchFile(cur_location));
}
}
@@ -241,6 +254,7 @@
driver->GetInstructionSetFeatures(),
&key_value_store,
/* verify */ false, // Dex files may be dex-to-dex-ed, don't verify.
+ /* update_input_vdex */ false,
&cur_opened_dex_files_map,
&cur_opened_dex_files);
ASSERT_TRUE(dex_files_ok);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index b22ca47..9c38445 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1113,11 +1113,15 @@
}
// build an Object[] of the roots needed to restore the runtime
+ int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
auto image_roots(hs.NewHandle(
- ObjectArray<Object>::Alloc(self, object_array_class.Get(), ImageHeader::kImageRootsMax)));
+ ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
- for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+ // image_roots[ImageHeader::kClassLoader] will be set later for app image.
+ static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
+ "Class loader should be the last image root.");
+ for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) {
CHECK(image_roots->Get(i) != nullptr);
}
return image_roots.Get();
@@ -1539,6 +1543,12 @@
}
// Process the work stack in case anything was added by TryAssignBinSlot.
ProcessWorkStack(&work_stack);
+
+ // Store the class loader in the class roots.
+ CHECK_EQ(class_loaders_.size(), 1u);
+ CHECK_EQ(image_roots.size(), 1u);
+ CHECK(*class_loaders_.begin() != nullptr);
+ image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin());
}
// Verify that all objects have assigned image bin slots.
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index c537483..cc7df1c 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -27,6 +27,7 @@
#include <string>
#include <ostream>
+#include "art_method.h"
#include "base/bit_utils.h"
#include "base/dchecked_vector.h"
#include "base/enums.h"
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 9dfb434..148ce4f 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -16,6 +16,8 @@
#include "jit_compiler.h"
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
@@ -81,7 +83,7 @@
va_list ap;
va_start(ap, fmt);
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(FATAL) << error;
va_end(ap);
exit(EXIT_FAILURE);
diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
index a16aaca..4f9a3a0 100644
--- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
@@ -29,10 +29,10 @@
Mips32r6RelativePatcherTest() : RelativePatcherTest(kMips, "mips32r6") {}
protected:
- static const uint8_t UnpatchedPcRelativeRawCode[];
- static const uint32_t LiteralOffset;
- static const uint32_t AnchorOffset;
- static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode;
+ static const uint8_t kUnpatchedPcRelativeRawCode[];
+ static const uint32_t kLiteralOffset;
+ static const uint32_t kAnchorOffset;
+ static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
uint32_t GetMethodOffset(uint32_t method_idx) {
auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
@@ -45,24 +45,25 @@
void TestStringReference(uint32_t string_offset);
};
-const uint8_t Mips32r6RelativePatcherTest::UnpatchedPcRelativeRawCode[] = {
+const uint8_t Mips32r6RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678
};
-const uint32_t Mips32r6RelativePatcherTest::LiteralOffset = 0; // At auipc (where patching starts).
-const uint32_t Mips32r6RelativePatcherTest::AnchorOffset = 0; // At auipc (where PC+0 points).
-const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::UnpatchedPcRelativeCode(
- UnpatchedPcRelativeRawCode);
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffset = 0; // At auipc (where
+ // patching starts).
+const uint32_t Mips32r6RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points).
+const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::kUnpatchedPcRelativeCode(
+ kUnpatchedPcRelativeRawCode);
void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
uint32_t target_offset) {
- AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
Link();
auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
ASSERT_TRUE(result.first);
- uint32_t diff = target_offset - (result.second + AnchorOffset);
+ uint32_t diff = target_offset - (result.second + kAnchorOffset);
if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) {
diff += kDexCacheArrayLwOffset;
}
@@ -79,7 +80,7 @@
uint32_t element_offset) {
dex_cache_arrays_begin_ = dex_cache_arrays_begin;
LinkerPatch patches[] = {
- LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset)
+ LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
dex_cache_arrays_begin_ + element_offset);
@@ -89,7 +90,7 @@
constexpr uint32_t kStringIndex = 1u;
string_index_to_offset_map_.Put(kStringIndex, string_offset);
LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex)
+ LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
}
diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc
index 335ce2e..faeb92a 100644
--- a/compiler/linker/mips/relative_patcher_mips_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips_test.cc
@@ -29,10 +29,10 @@
MipsRelativePatcherTest() : RelativePatcherTest(kMips, "mips32r2") {}
protected:
- static const uint8_t UnpatchedPcRelativeRawCode[];
- static const uint32_t LiteralOffset;
- static const uint32_t AnchorOffset;
- static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode;
+ static const uint8_t kUnpatchedPcRelativeRawCode[];
+ static const uint32_t kLiteralOffset;
+ static const uint32_t kAnchorOffset;
+ static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
uint32_t GetMethodOffset(uint32_t method_idx) {
auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
@@ -45,26 +45,26 @@
void TestStringReference(uint32_t string_offset);
};
-const uint8_t MipsRelativePatcherTest::UnpatchedPcRelativeRawCode[] = {
+const uint8_t MipsRelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
0x00, 0x00, 0x10, 0x04, // nal
0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234
0x78, 0x56, 0x52, 0x36, // ori s2, s2, low(diff); placeholder = 0x5678
0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra
};
-const uint32_t MipsRelativePatcherTest::LiteralOffset = 4; // At lui (where patching starts).
-const uint32_t MipsRelativePatcherTest::AnchorOffset = 8; // At ori (where PC+0 points).
-const ArrayRef<const uint8_t> MipsRelativePatcherTest::UnpatchedPcRelativeCode(
- UnpatchedPcRelativeRawCode);
+const uint32_t MipsRelativePatcherTest::kLiteralOffset = 4; // At lui (where patching starts).
+const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8; // At ori (where PC+0 points).
+const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode(
+ kUnpatchedPcRelativeRawCode);
void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
uint32_t target_offset) {
- AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
Link();
auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
ASSERT_TRUE(result.first);
- uint32_t diff = target_offset - (result.second + AnchorOffset);
+ uint32_t diff = target_offset - (result.second + kAnchorOffset);
if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) {
diff += kDexCacheArrayLwOffset;
}
@@ -82,7 +82,7 @@
uint32_t element_offset) {
dex_cache_arrays_begin_ = dex_cache_arrays_begin;
LinkerPatch patches[] = {
- LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset)
+ LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
dex_cache_arrays_begin_ + element_offset);
@@ -92,7 +92,7 @@
constexpr uint32_t kStringIndex = 1u;
string_index_to_offset_map_.Put(kStringIndex, string_offset);
LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex)
+ LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
}
diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc
new file mode 100644
index 0000000..c479716
--- /dev/null
+++ b/compiler/linker/mips64/relative_patcher_mips64.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 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 "linker/mips64/relative_patcher_mips64.h"
+
+#include "compiled_method.h"
+
+namespace art {
+namespace linker {
+
+uint32_t Mips64RelativePatcher::ReserveSpace(
+ uint32_t offset,
+ const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
+ MethodReference method_ref ATTRIBUTE_UNUSED) {
+ return offset; // No space reserved; no limit on relative call distance.
+}
+
+uint32_t Mips64RelativePatcher::ReserveSpaceEnd(uint32_t offset) {
+ return offset; // No space reserved; no limit on relative call distance.
+}
+
+uint32_t Mips64RelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) {
+ return offset; // No thunks added; no limit on relative call distance.
+}
+
+void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ // Basic sanity checks.
+ DCHECK_GE(code->size(), 8u);
+ DCHECK_LE(literal_offset, code->size() - 8u);
+ // auipc reg, offset_high
+ DCHECK_EQ((*code)[literal_offset + 0], 0x34);
+ DCHECK_EQ((*code)[literal_offset + 1], 0x12);
+ DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
+ DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
+ // jialc reg, offset_low
+ DCHECK_EQ((*code)[literal_offset + 4], 0x78);
+ DCHECK_EQ((*code)[literal_offset + 5], 0x56);
+ DCHECK_EQ(((*code)[literal_offset + 6] & 0xE0), 0x00);
+ DCHECK_EQ((*code)[literal_offset + 7], 0xF8);
+
+ // Apply patch.
+ uint32_t diff = target_offset - patch_offset;
+ // Note that a combination of auipc with an instruction that adds a sign-extended
+ // 16-bit immediate operand (e.g. jialc) provides a PC-relative range of
+ // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
+ // by 32KB.
+ diff += (diff & 0x8000) << 1; // Account for sign extension in jialc.
+
+ // auipc reg, offset_high
+ (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
+ (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
+ // jialc reg, offset_low
+ (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
+ (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
+}
+
+void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ uint32_t anchor_literal_offset = patch.PcInsnOffset();
+ uint32_t literal_offset = patch.LiteralOffset();
+
+ // Basic sanity checks.
+ DCHECK_GE(code->size(), 8u);
+ DCHECK_LE(literal_offset, code->size() - 8u);
+ DCHECK_EQ(literal_offset, anchor_literal_offset);
+ // auipc reg, offset_high
+ DCHECK_EQ((*code)[literal_offset + 0], 0x34);
+ DCHECK_EQ((*code)[literal_offset + 1], 0x12);
+ DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
+ DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
+ // instr reg(s), offset_low
+ DCHECK_EQ((*code)[literal_offset + 4], 0x78);
+ DCHECK_EQ((*code)[literal_offset + 5], 0x56);
+
+ // Apply patch.
+ uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
+ uint32_t diff = target_offset - anchor_offset;
+ // Note that a combination of auipc with an instruction that adds a sign-extended
+ // 16-bit immediate operand (e.g. ld) provides a PC-relative range of
+ // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
+ // by 32KB.
+ diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc.
+
+ // auipc reg, offset_high
+ (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
+ (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
+ // instr reg(s), offset_low
+ (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
+ (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
+}
+
+} // namespace linker
+} // namespace art
diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/compiler/linker/mips64/relative_patcher_mips64.h
new file mode 100644
index 0000000..8ef8ceb
--- /dev/null
+++ b/compiler/linker/mips64/relative_patcher_mips64.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
+#define ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
+
+#include "linker/relative_patcher.h"
+
+namespace art {
+namespace linker {
+
+class Mips64RelativePatcher FINAL : public RelativePatcher {
+ public:
+ explicit Mips64RelativePatcher() {}
+
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
+ MethodReference method_ref) OVERRIDE;
+ uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
+ void PatchPcRelativeReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Mips64RelativePatcher);
+};
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_
diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc
new file mode 100644
index 0000000..c317058
--- /dev/null
+++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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 "linker/relative_patcher_test.h"
+#include "linker/mips64/relative_patcher_mips64.h"
+
+namespace art {
+namespace linker {
+
+class Mips64RelativePatcherTest : public RelativePatcherTest {
+ public:
+ Mips64RelativePatcherTest() : RelativePatcherTest(kMips64, "default") {}
+
+ protected:
+ static const uint8_t kUnpatchedPcRelativeRawCode[];
+ static const uint8_t kUnpatchedPcRelativeCallRawCode[];
+ static const uint32_t kLiteralOffset;
+ static const uint32_t kAnchorOffset;
+ static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
+ static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCallCode;
+
+ uint32_t GetMethodOffset(uint32_t method_idx) {
+ auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
+ CHECK(result.first);
+ return result.second;
+ }
+
+ void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset);
+ void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset);
+ void TestStringReference(uint32_t string_offset);
+};
+
+const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
+ 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
+ 0x78, 0x56, 0x52, 0x66, // daddiu s2, s2, low(diff); placeholder = 0x5678
+};
+const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeCallRawCode[] = {
+ 0x34, 0x12, 0x3E, 0xEC, // auipc at, high(diff); placeholder = 0x1234
+ 0x78, 0x56, 0x01, 0xF8, // jialc at, low(diff); placeholder = 0x5678
+};
+const uint32_t Mips64RelativePatcherTest::kLiteralOffset = 0; // At auipc (where patching starts).
+const uint32_t Mips64RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points).
+const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCode(
+ kUnpatchedPcRelativeRawCode);
+const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCallCode(
+ kUnpatchedPcRelativeCallRawCode);
+
+void Mips64RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
+ uint32_t target_offset) {
+ AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
+ ASSERT_TRUE(result.first);
+
+ uint32_t diff = target_offset - (result.second + kAnchorOffset);
+ diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc.
+
+ const uint8_t expected_code[] = {
+ static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
+ static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x66,
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+}
+
+void Mips64RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin,
+ uint32_t element_offset) {
+ dex_cache_arrays_begin_ = dex_cache_arrays_begin;
+ LinkerPatch patches[] = {
+ LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset)
+ };
+ CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches),
+ dex_cache_arrays_begin_ + element_offset);
+}
+
+TEST_F(Mips64RelativePatcherTest, DexCacheReference) {
+ TestDexCacheReference(/* dex_cache_arrays_begin */ 0x12345678, /* element_offset */ 0x1234);
+}
+
+TEST_F(Mips64RelativePatcherTest, CallOther) {
+ LinkerPatch method1_patches[] = {
+ LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 2u),
+ };
+ AddCompiledMethod(MethodRef(1u),
+ kUnpatchedPcRelativeCallCode,
+ ArrayRef<const LinkerPatch>(method1_patches));
+ LinkerPatch method2_patches[] = {
+ LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 1u),
+ };
+ AddCompiledMethod(MethodRef(2u),
+ kUnpatchedPcRelativeCallCode,
+ ArrayRef<const LinkerPatch>(method2_patches));
+ Link();
+
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t method2_offset = GetMethodOffset(2u);
+ uint32_t diff_after = method2_offset - (method1_offset + kAnchorOffset /* PC adjustment */);
+ diff_after += (diff_after & 0x8000) << 1; // Account for sign extension in jialc.
+ static const uint8_t method1_expected_code[] = {
+ static_cast<uint8_t>(diff_after >> 16), static_cast<uint8_t>(diff_after >> 24), 0x3E, 0xEC,
+ static_cast<uint8_t>(diff_after), static_cast<uint8_t>(diff_after >> 8), 0x01, 0xF8,
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
+ uint32_t diff_before = method1_offset - (method2_offset + kAnchorOffset /* PC adjustment */);
+ diff_before += (diff_before & 0x8000) << 1; // Account for sign extension in jialc.
+ static const uint8_t method2_expected_code[] = {
+ static_cast<uint8_t>(diff_before >> 16), static_cast<uint8_t>(diff_before >> 24), 0x3E, 0xEC,
+ static_cast<uint8_t>(diff_before), static_cast<uint8_t>(diff_before >> 8), 0x01, 0xF8,
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
+}
+
+} // namespace linker
+} // namespace art
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index 7765594..f1538b1 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -25,6 +25,9 @@
#ifdef ART_ENABLE_CODEGEN_mips
#include "linker/mips/relative_patcher_mips.h"
#endif
+#ifdef ART_ENABLE_CODEGEN_mips64
+#include "linker/mips64/relative_patcher_mips64.h"
+#endif
#ifdef ART_ENABLE_CODEGEN_x86
#include "linker/x86/relative_patcher_x86.h"
#endif
@@ -103,6 +106,10 @@
return std::unique_ptr<RelativePatcher>(
new MipsRelativePatcher(features->AsMipsInstructionSetFeatures()));
#endif
+#ifdef ART_ENABLE_CODEGEN_mips64
+ case kMips64:
+ return std::unique_ptr<RelativePatcher>(new Mips64RelativePatcher());
+#endif
default:
return std::unique_ptr<RelativePatcher>(new RelativePatcherNone);
}
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 0a778b0..86d92ff 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -48,7 +50,7 @@
va_list ap;
va_start(ap, fmt);
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(FATAL) << error;
va_end(ap);
UNREACHABLE();
@@ -196,6 +198,7 @@
compiler_driver_->GetInstructionSetFeatures(),
&key_value_store,
verify,
+ /* update_input_vdex */ false,
&opened_dex_files_map,
&opened_dex_files)) {
return false;
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index bebd5f5..a9da09c 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -483,6 +483,7 @@
const InstructionSetFeatures* instruction_set_features,
SafeMap<std::string, std::string>* key_value_store,
bool verify,
+ bool update_input_vdex,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
CHECK(write_state_ == WriteState::kAddingDexFileSources);
@@ -511,15 +512,15 @@
if (kIsVdexEnabled) {
std::unique_ptr<BufferedOutputStream> vdex_out(
MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(vdex_file)));
-
// Write DEX files into VDEX, mmap and open them.
- if (!WriteDexFiles(vdex_out.get(), vdex_file) ||
+ if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) ||
!OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
return false;
}
} else {
+ DCHECK(!update_input_vdex);
// Write DEX files into OAT, mmap and open them.
- if (!WriteDexFiles(oat_rodata, vdex_file) ||
+ if (!WriteDexFiles(oat_rodata, vdex_file, update_input_vdex) ||
!OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
return false;
}
@@ -1468,30 +1469,32 @@
if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
return false;
}
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
- const uint8_t* class_data = dex_file->GetClassData(class_def);
- if (class_data != nullptr) { // ie not an empty class, such as a marker interface
- ClassDataItemIterator it(*dex_file, class_data);
- while (it.HasNextStaticField()) {
- it.Next();
- }
- while (it.HasNextInstanceField()) {
- it.Next();
- }
- size_t class_def_method_index = 0u;
- while (it.HasNextDirectMethod()) {
- if (!visitor->VisitMethod(class_def_method_index, it)) {
- return false;
+ if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data != nullptr) { // ie not an empty class, such as a marker interface
+ ClassDataItemIterator it(*dex_file, class_data);
+ while (it.HasNextStaticField()) {
+ it.Next();
}
- ++class_def_method_index;
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
- return false;
+ while (it.HasNextInstanceField()) {
+ it.Next();
}
- ++class_def_method_index;
- it.Next();
+ size_t class_def_method_index = 0u;
+ while (it.HasNextDirectMethod()) {
+ if (!visitor->VisitMethod(class_def_method_index, it)) {
+ return false;
+ }
+ ++class_def_method_index;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
+ return false;
+ }
+ ++class_def_method_index;
+ it.Next();
+ }
}
}
if (UNLIKELY(!visitor->EndClass())) {
@@ -1548,6 +1551,9 @@
}
size_t OatWriter::InitOatMaps(size_t offset) {
+ if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+ return offset;
+ }
InitMapMethodVisitor visitor(this, offset);
bool success = VisitDexMethods(&visitor);
DCHECK(success);
@@ -1594,6 +1600,9 @@
}
size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
+ if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+ return offset;
+ }
InitCodeMethodVisitor code_visitor(this, offset, vdex_quickening_info_offset_);
bool success = VisitDexMethods(&code_visitor);
DCHECK(success);
@@ -1745,19 +1754,24 @@
return false;
}
- WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset);
- if (!VisitDexMethods(&visitor)) {
- PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
- return false;
+ if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) {
+ WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset);
+ if (!VisitDexMethods(&visitor)) {
+ PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
+ return false;
+ }
+
+ if (!vdex_out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing quickening info."
+ << " File: " << vdex_out->GetLocation();
+ return false;
+ }
+ size_quickening_info_ = visitor.GetNumberOfWrittenBytes();
+ } else {
+ // We know we did not quicken.
+ size_quickening_info_ = 0;
}
- if (!vdex_out->Flush()) {
- PLOG(ERROR) << "Failed to flush stream after writing quickening info."
- << " File: " << vdex_out->GetLocation();
- return false;
- }
-
- size_quickening_info_ = visitor.GetNumberOfWrittenBytes();
vdex_size_ += size_quickening_info_;
return true;
}
@@ -2096,47 +2110,56 @@
return true;
}
-bool OatWriter::WriteDexFiles(OutputStream* out, File* file) {
+bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex) {
TimingLogger::ScopedTiming split("Write Dex files", timings_);
vdex_dex_files_offset_ = vdex_size_;
// Write dex files.
for (OatDexFile& oat_dex_file : oat_dex_files_) {
- if (!WriteDexFile(out, file, &oat_dex_file)) {
+ if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) {
return false;
}
}
- // Close sources.
+ CloseSources();
+ return true;
+}
+
+void OatWriter::CloseSources() {
for (OatDexFile& oat_dex_file : oat_dex_files_) {
oat_dex_file.source_.Clear(); // Get rid of the reference, it's about to be invalidated.
}
zipped_dex_files_.clear();
zip_archives_.clear();
raw_dex_files_.clear();
- return true;
}
-bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) {
+bool OatWriter::WriteDexFile(OutputStream* out,
+ File* file,
+ OatDexFile* oat_dex_file,
+ bool update_input_vdex) {
if (!SeekToDexFile(out, file, oat_dex_file)) {
return false;
}
if (profile_compilation_info_ != nullptr) {
+ DCHECK(!update_input_vdex);
if (!LayoutAndWriteDexFile(out, oat_dex_file)) {
return false;
}
} else if (oat_dex_file->source_.IsZipEntry()) {
+ DCHECK(!update_input_vdex);
if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
return false;
}
} else if (oat_dex_file->source_.IsRawFile()) {
+ DCHECK(!update_input_vdex);
if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) {
return false;
}
} else {
DCHECK(oat_dex_file->source_.IsRawData());
- if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData())) {
+ if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData(), update_input_vdex)) {
return false;
}
}
@@ -2146,6 +2169,7 @@
DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_);
vdex_size_ += oat_dex_file->dex_file_size_;
} else {
+ DCHECK(!update_input_vdex);
DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_);
oat_size_ += oat_dex_file->dex_file_size_;
}
@@ -2216,7 +2240,7 @@
DexLayout dex_layout(options, profile_compilation_info_, nullptr);
dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0);
std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap());
- if (!WriteDexFile(out, oat_dex_file, mem_map->Begin())) {
+ if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) {
return false;
}
// Set the checksum of the new oat dex file to be the original file's checksum.
@@ -2373,22 +2397,27 @@
bool OatWriter::WriteDexFile(OutputStream* out,
OatDexFile* oat_dex_file,
- const uint8_t* dex_file) {
+ const uint8_t* dex_file,
+ bool update_input_vdex) {
// Note: The raw data has already been checked to contain the header
// and all the data that the header specifies as the file size.
DCHECK(dex_file != nullptr);
DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation()));
const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file);
- if (!out->WriteFully(dex_file, header->file_size_)) {
- PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
- << " to " << out->GetLocation();
- return false;
- }
- if (!out->Flush()) {
- PLOG(ERROR) << "Failed to flush stream after writing dex file."
- << " File: " << oat_dex_file->GetLocation();
- return false;
+ if (update_input_vdex) {
+ // The vdex already contains the dex code, no need to write it again.
+ } else {
+ if (!out->WriteFully(dex_file, header->file_size_)) {
+ PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
+ << " to " << out->GetLocation();
+ return false;
+ }
+ if (!out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing dex file."
+ << " File: " << oat_dex_file->GetLocation();
+ return false;
+ }
}
// Update dex file size and resize class offsets in the OatDexFile.
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index da221d6..8d087f4 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -158,12 +158,15 @@
// Supporting data structures are written into the .rodata section of the oat file.
// The `verify` setting dictates whether the dex file verifier should check the dex files.
// This is generally the case, and should only be false for tests.
+ // If `update_input_vdex` is true, then this method won't actually write the dex files,
+ // and the compiler will just re-use the existing vdex file.
bool WriteAndOpenDexFiles(File* vdex_file,
OutputStream* oat_rodata,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
SafeMap<std::string, std::string>* key_value_store,
bool verify,
+ bool update_input_vdex,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
bool WriteQuickeningInfo(OutputStream* vdex_out);
@@ -263,8 +266,13 @@
// with a given DexMethodVisitor.
bool VisitDexMethods(DexMethodVisitor* visitor);
- bool WriteDexFiles(OutputStream* out, File* file);
- bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
+ // If `update_input_vdex` is true, then this method won't actually write the dex files,
+ // and the compiler will just re-use the existing vdex file.
+ bool WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex);
+ bool WriteDexFile(OutputStream* out,
+ File* file,
+ OatDexFile* oat_dex_file,
+ bool update_input_vdex);
bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
bool LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file);
bool WriteDexFile(OutputStream* out,
@@ -275,7 +283,10 @@
File* file,
OatDexFile* oat_dex_file,
File* dex_file);
- bool WriteDexFile(OutputStream* out, OatDexFile* oat_dex_file, const uint8_t* dex_file);
+ bool WriteDexFile(OutputStream* out,
+ OatDexFile* oat_dex_file,
+ const uint8_t* dex_file,
+ bool update_input_vdex);
bool OpenDexFiles(File* file,
bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
@@ -306,6 +317,7 @@
const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
void SetMultiOatRelativePatcherAdjustment();
+ void CloseSources();
enum class WriteState {
kAddingDexFileSources,
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
new file mode 100644
index 0000000..fe42301
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2016 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 "cha_guard_optimization.h"
+
+namespace art {
+
+// Note we can only do CHA guard elimination/motion in a single pass, since
+// if a guard is not removed, another guard might be removed due to
+// the existence of the first guard. The first guard should not be further
+// removed in another pass. For example, due to further optimizations,
+// a receiver of a guard might turn out to be a parameter value, or defined at
+// a different site, which makes the guard removable as a result. However
+// it's not safe to remove the guard in another pass since another guard might
+// have been removed due to the existence of this guard.
+//
+// As a consequence, we decided not to rely on other passes to remove them
+// (such as GVN or instruction simplifier).
+
+class CHAGuardVisitor : HGraphVisitor {
+ public:
+ explicit CHAGuardVisitor(HGraph* graph)
+ : HGraphVisitor(graph),
+ block_has_cha_guard_(GetGraph()->GetBlocks().size(),
+ 0,
+ graph->GetArena()->Adapter(kArenaAllocCHA)) {
+ number_of_guards_to_visit_ = GetGraph()->GetNumberOfCHAGuards();
+ DCHECK_NE(number_of_guards_to_visit_, 0u);
+ // Will recount number of guards during guard optimization.
+ GetGraph()->SetNumberOfCHAGuards(0);
+ }
+
+ void VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) OVERRIDE;
+
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+ private:
+ void RemoveGuard(HShouldDeoptimizeFlag* flag);
+ // Return true if `flag` is removed.
+ bool OptimizeForParameter(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+ // Return true if `flag` is removed.
+ bool OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+ // Return true if `flag` is hoisted.
+ bool HoistGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+
+ // Record if each block has any CHA guard. It's updated during the
+ // reverse post order visit. Use int instead of bool since ArenaVector
+ // does not support bool.
+ ArenaVector<int> block_has_cha_guard_;
+
+ // The iterator that's being used for this visitor. Need it to manually
+ // advance the iterator due to removing/moving more than one instruction.
+ HInstructionIterator* instruction_iterator_;
+
+ // Used to short-circuit the pass when there is no more guards left to visit.
+ uint32_t number_of_guards_to_visit_;
+
+ DISALLOW_COPY_AND_ASSIGN(CHAGuardVisitor);
+};
+
+void CHAGuardVisitor::VisitBasicBlock(HBasicBlock* block) {
+ if (number_of_guards_to_visit_ == 0) {
+ return;
+ }
+ // Skip phis, just iterate through instructions.
+ HInstructionIterator it(block->GetInstructions());
+ instruction_iterator_ = ⁢
+ for (; !it.Done(); it.Advance()) {
+ DCHECK(it.Current()->IsInBlock());
+ it.Current()->Accept(this);
+ }
+}
+
+void CHAGuardVisitor::RemoveGuard(HShouldDeoptimizeFlag* flag) {
+ HBasicBlock* block = flag->GetBlock();
+ HInstruction* compare = flag->GetNext();
+ DCHECK(compare->IsNotEqual());
+ HInstruction* deopt = compare->GetNext();
+ DCHECK(deopt->IsDeoptimize());
+
+ // Advance instruction iterator first before we remove the guard.
+ // We need to do it twice since we remove three instructions and the
+ // visitor is responsible for advancing it once.
+ instruction_iterator_->Advance();
+ instruction_iterator_->Advance();
+ block->RemoveInstruction(deopt);
+ block->RemoveInstruction(compare);
+ block->RemoveInstruction(flag);
+}
+
+bool CHAGuardVisitor::OptimizeForParameter(HShouldDeoptimizeFlag* flag,
+ HInstruction* receiver) {
+ // If some compiled code is invalidated by CHA due to class loading, the
+ // compiled code will not be entered anymore. So the very fact that the
+ // compiled code is invoked guarantees that a parameter receiver conforms
+ // to all the CHA devirtualization assumptions made by the compiled code,
+ // since all parameter receivers pre-exist any (potential) invalidation of
+ // the compiled code.
+ //
+ // TODO: allow more cases such as a phi whose inputs are all parameters.
+ if (receiver->IsParameterValue()) {
+ RemoveGuard(flag);
+ return true;
+ }
+ return false;
+}
+
+bool CHAGuardVisitor::OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag,
+ HInstruction* receiver) {
+ // If there is another guard that dominates the current guard, and
+ // that guard is dominated by receiver's definition, then the current
+ // guard can be eliminated, since receiver must pre-exist that other
+ // guard, and passing that guard guarantees that receiver conforms to
+ // all the CHA devirtualization assumptions.
+ HBasicBlock* dominator = flag->GetBlock();
+ HBasicBlock* receiver_def_block = receiver->GetBlock();
+
+ // Complexity of the following algorithm:
+ // We potentially need to traverse the full dominator chain to receiver_def_block,
+ // plus a (partial) linear search within one block for each guard.
+ // So the worst case for each guard is bounded by the size of the
+ // biggest block plus the depth of the dominating tree.
+
+ while (dominator != receiver_def_block) {
+ if (block_has_cha_guard_[dominator->GetBlockId()] == 1) {
+ RemoveGuard(flag);
+ return true;
+ }
+ dominator = dominator->GetDominator();
+ }
+
+ // At this point dominator is the block where receiver is defined.
+ // We do a linear search within dominator to see if there is a guard after
+ // receiver's definition.
+ HInstruction* instruction;
+ if (dominator == flag->GetBlock()) {
+ // Flag and receiver are defined in the same block. Search backward from
+ // the current guard.
+ instruction = flag->GetPrevious();
+ } else {
+ // Search backward from the last instruction of that dominator.
+ instruction = dominator->GetLastInstruction();
+ }
+ while (instruction != receiver) {
+ if (instruction == nullptr) {
+ // receiver must be defined in this block, we didn't find it
+ // in the instruction list, so it must be a Phi.
+ DCHECK(receiver->IsPhi());
+ break;
+ }
+ if (instruction->IsShouldDeoptimizeFlag()) {
+ RemoveGuard(flag);
+ return true;
+ }
+ instruction = instruction->GetPrevious();
+ }
+ return false;
+}
+
+bool CHAGuardVisitor::HoistGuard(HShouldDeoptimizeFlag* flag,
+ HInstruction* receiver) {
+ // If receiver is loop invariant, we can hoist the guard out of the
+ // loop since passing a guard before entering the loop guarantees that
+ // receiver conforms to all the CHA devirtualization assumptions.
+ // We only hoist guards out of the inner loop since that offers most of the
+ // benefit and it might help remove other guards in the inner loop.
+ HBasicBlock* block = flag->GetBlock();
+ HLoopInformation* loop_info = block->GetLoopInformation();
+ if (loop_info != nullptr &&
+ !loop_info->IsIrreducible() &&
+ loop_info->IsDefinedOutOfTheLoop(receiver)) {
+ HInstruction* compare = flag->GetNext();
+ DCHECK(compare->IsNotEqual());
+ HInstruction* deopt = compare->GetNext();
+ DCHECK(deopt->IsDeoptimize());
+
+ // Advance instruction iterator first before we move the guard.
+ // We need to do it twice since we move three instructions and the
+ // visitor is responsible for advancing it once.
+ instruction_iterator_->Advance();
+ instruction_iterator_->Advance();
+
+ HBasicBlock* pre_header = loop_info->GetPreHeader();
+ flag->MoveBefore(pre_header->GetLastInstruction());
+ compare->MoveBefore(pre_header->GetLastInstruction());
+
+ block->RemoveInstruction(deopt);
+ HInstruction* suspend = loop_info->GetSuspendCheck();
+ // Need a new deoptimize instruction that copies the environment
+ // of the suspend instruction for the loop.
+ HDeoptimize* deoptimize =
+ new (GetGraph()->GetArena()) HDeoptimize(compare, suspend->GetDexPc());
+ pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
+ deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
+ suspend->GetEnvironment(), loop_info->GetHeader());
+ block_has_cha_guard_[pre_header->GetBlockId()] = 1;
+ GetGraph()->IncrementNumberOfCHAGuards();
+ return true;
+ }
+ return false;
+}
+
+void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ number_of_guards_to_visit_--;
+ HInstruction* receiver = flag->InputAt(0);
+ // Don't need the receiver anymore.
+ flag->RemoveInputAt(0);
+ if (receiver->IsNullCheck()) {
+ receiver = receiver->InputAt(0);
+ }
+
+ if (OptimizeForParameter(flag, receiver)) {
+ DCHECK(!flag->IsInBlock());
+ return;
+ }
+ if (OptimizeWithDominatingGuard(flag, receiver)) {
+ DCHECK(!flag->IsInBlock());
+ return;
+ }
+ if (HoistGuard(flag, receiver)) {
+ DCHECK(flag->IsInBlock());
+ return;
+ }
+
+ // Need to keep the CHA guard in place.
+ block_has_cha_guard_[flag->GetBlock()->GetBlockId()] = 1;
+ GetGraph()->IncrementNumberOfCHAGuards();
+}
+
+void CHAGuardOptimization::Run() {
+ if (graph_->GetNumberOfCHAGuards() == 0) {
+ return;
+ }
+ CHAGuardVisitor visitor(graph_);
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ visitor.VisitBasicBlock(block);
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h
new file mode 100644
index 0000000..ba0cdb8
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 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_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+#define ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Optimize CHA guards by removing/moving them.
+ */
+class CHAGuardOptimization : public HOptimization {
+ public:
+ explicit CHAGuardOptimization(HGraph* graph)
+ : HOptimization(graph, kCHAGuardOptimizationPassName) {}
+
+ void Run() OVERRIDE;
+
+ static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CHAGuardOptimization);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 8104613..8a7f6d3 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1201,11 +1201,6 @@
isa_features_(isa_features),
uint32_literals_(std::less<uint32_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- method_patches_(MethodReferenceComparator(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- call_patches_(MethodReferenceComparator(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
boot_image_string_patches_(StringReferenceValueComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -7139,7 +7134,7 @@
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
- HInvokeStaticOrDirect* invoke) {
+ HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
@@ -7151,24 +7146,6 @@
dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
}
- if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
- const DexFile& outer_dex_file = GetGraph()->GetDexFile();
- if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
- // Calls across dex files are more likely to exceed the available BL range,
- // so use absolute patch with fixup if available and kCallArtMethod otherwise.
- HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
- (desired_dispatch_info.method_load_kind ==
- HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
- ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
- : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
- return HInvokeStaticOrDirect::DispatchInfo {
- dispatch_info.method_load_kind,
- code_ptr_location,
- dispatch_info.method_load_data,
- 0u
- };
- }
- }
return dispatch_info;
}
@@ -7199,20 +7176,6 @@
}
void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
- // For better instruction scheduling we load the direct code pointer before the method pointer.
- switch (invoke->GetCodePtrLocation()) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- // LR = code address from literal pool with link-time patch.
- __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR = invoke->GetDirectCodePtr();
- __ LoadImmediate(LR, invoke->GetDirectCodePtr());
- break;
- default:
- break;
- }
-
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
switch (invoke->GetMethodLoadKind()) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
@@ -7228,10 +7191,6 @@
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
__ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress());
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- __ LoadLiteral(temp.AsRegister<Register>(),
- DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
- break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
HArmDexCacheArraysBase* base =
invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
@@ -7270,19 +7229,6 @@
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
__ bl(GetFrameEntryLabel());
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetTargetMethod().dex_method_index);
- __ BindTrackedLabel(&relative_call_patches_.back().label);
- // Arbitrarily branch to the BL itself, override at link time.
- __ bl(&relative_call_patches_.back().label);
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR prepared above for better instruction scheduling.
- // LR()
- __ blx(LR);
- break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// LR = callee_method->entry_point_from_quick_compiled_code_
__ LoadFromOffset(
@@ -7410,9 +7356,6 @@
void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
- method_patches_.size() +
- call_patches_.size() +
- relative_call_patches_.size() +
/* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
boot_image_string_patches_.size() +
/* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
@@ -7420,29 +7363,6 @@
/* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
boot_image_address_patches_.size();
linker_patches->reserve(size);
- for (const auto& entry : method_patches_) {
- const MethodReference& target_method = entry.first;
- Literal* literal = entry.second;
- DCHECK(literal->GetLabel()->IsBound());
- uint32_t literal_offset = literal->GetLabel()->Position();
- linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset,
- target_method.dex_file,
- target_method.dex_method_index));
- }
- for (const auto& entry : call_patches_) {
- const MethodReference& target_method = entry.first;
- Literal* literal = entry.second;
- DCHECK(literal->GetLabel()->IsBound());
- uint32_t literal_offset = literal->GetLabel()->Position();
- linker_patches->push_back(LinkerPatch::CodePatch(literal_offset,
- target_method.dex_file,
- target_method.dex_method_index));
- }
- for (const PatchInfo<Label>& info : relative_call_patches_) {
- uint32_t literal_offset = info.label.Position();
- linker_patches->push_back(
- LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
- }
EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
linker_patches);
for (const auto& entry : boot_image_string_patches_) {
@@ -7494,14 +7414,6 @@
[this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
}
-Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) {
- return DeduplicateMethodLiteral(target_method, &method_patches_);
-}
-
-Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) {
- return DeduplicateMethodLiteral(target_method, &call_patches_);
-}
-
void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 605169d..6435851 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -607,8 +607,6 @@
Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
- Literal* DeduplicateMethodAddressLiteral(MethodReference target_method);
- Literal* DeduplicateMethodCodeLiteral(MethodReference target_method);
PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
uint32_t offset_or_index,
ArenaDeque<PcRelativePatchInfo>* patches);
@@ -627,12 +625,6 @@
// Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
Uint32ToLiteralMap uint32_literals_;
- // Method patch info, map MethodReference to a literal for method address and method code.
- MethodToLiteralMap method_patches_;
- MethodToLiteralMap call_patches_;
- // Relative call patch info.
- // Using ArenaDeque<> which retains element addresses on push/emplace_back().
- ArenaDeque<PatchInfo<Label>> relative_call_patches_;
// PC-relative patch info for each HArmDexCacheArraysBase.
ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
// Deduplication map for boot string literals for kBootImageLinkTimeAddress.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5cff303..5c33fe1 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1147,11 +1147,6 @@
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
uint64_literals_(std::less<uint64_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- method_patches_(MethodReferenceComparator(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- call_patches_(MethodReferenceComparator(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
boot_image_string_patches_(StringReferenceValueComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -1538,8 +1533,17 @@
DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot());
UseScratchRegisterScope temps(GetVIXLAssembler());
- // There is generally less pressure on FP registers.
- FPRegister temp = destination.IsDoubleStackSlot() ? temps.AcquireD() : temps.AcquireS();
+ // Use any scratch register (a core or a floating-point one)
+ // from VIXL scratch register pools as a temporary.
+ //
+ // We used to only use the FP scratch register pool, but in some
+ // rare cases the only register from this pool (D31) would
+ // already be used (e.g. within a ParallelMove instruction, when
+ // a move is blocked by a another move requiring a scratch FP
+ // register, which would reserve D31). To prevent this issue, we
+ // ask for a scratch register of any type (core or FP).
+ CPURegister temp =
+ temps.AcquireCPURegisterOfSize(destination.IsDoubleStackSlot() ? kXRegSize : kWRegSize);
__ Ldr(temp, StackOperandFrom(source));
__ Str(temp, StackOperandFrom(destination));
}
@@ -3971,23 +3975,6 @@
}
void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
- // For better instruction scheduling we load the direct code pointer before the method pointer.
- bool direct_code_loaded = false;
- switch (invoke->GetCodePtrLocation()) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- // LR = code address from literal pool with link-time patch.
- __ Ldr(lr, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
- direct_code_loaded = true;
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR = invoke->GetDirectCodePtr();
- __ Ldr(lr, DeduplicateUint64Literal(invoke->GetDirectCodePtr()));
- direct_code_loaded = true;
- break;
- default:
- break;
- }
-
// Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
switch (invoke->GetMethodLoadKind()) {
@@ -4005,11 +3992,6 @@
// Load method address from literal pool.
__ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress()));
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- // Load method address from literal pool with a link-time patch.
- __ Ldr(XRegisterFrom(temp),
- DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
- break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
// Add ADRP with its PC-relative DexCache access patch.
const DexFile& dex_file = invoke->GetDexFile();
@@ -4051,23 +4033,6 @@
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
__ Bl(&frame_entry_label_);
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: {
- relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetTargetMethod().dex_method_index);
- vixl::aarch64::Label* label = &relative_call_patches_.back().label;
- SingleEmissionCheckScope guard(GetVIXLAssembler());
- __ Bind(label);
- // Branch and link to itself. This will be overriden at link time.
- __ bl(static_cast<int64_t>(0));
- break;
- }
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR prepared above for better instruction scheduling.
- DCHECK(direct_code_loaded);
- // lr()
- __ Blr(lr);
- break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// LR = callee_method->entry_point_from_quick_compiled_code_;
__ Ldr(lr, MemOperand(
@@ -4229,9 +4194,6 @@
void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
- method_patches_.size() +
- call_patches_.size() +
- relative_call_patches_.size() +
pc_relative_dex_cache_patches_.size() +
boot_image_string_patches_.size() +
pc_relative_string_patches_.size() +
@@ -4239,24 +4201,6 @@
pc_relative_type_patches_.size() +
boot_image_address_patches_.size();
linker_patches->reserve(size);
- for (const auto& entry : method_patches_) {
- const MethodReference& target_method = entry.first;
- vixl::aarch64::Literal<uint64_t>* literal = entry.second;
- linker_patches->push_back(LinkerPatch::MethodPatch(literal->GetOffset(),
- target_method.dex_file,
- target_method.dex_method_index));
- }
- for (const auto& entry : call_patches_) {
- const MethodReference& target_method = entry.first;
- vixl::aarch64::Literal<uint64_t>* literal = entry.second;
- linker_patches->push_back(LinkerPatch::CodePatch(literal->GetOffset(),
- target_method.dex_file,
- target_method.dex_method_index));
- }
- for (const PatchInfo<vixl::aarch64::Label>& info : relative_call_patches_) {
- linker_patches->push_back(
- LinkerPatch::RelativeCodePatch(info.label.GetLocation(), &info.dex_file, info.index));
- }
for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) {
linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.GetLocation(),
&info.target_dex_file,
@@ -4314,17 +4258,6 @@
[this]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(/* placeholder */ 0u); });
}
-vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodAddressLiteral(
- MethodReference target_method) {
- return DeduplicateMethodLiteral(target_method, &method_patches_);
-}
-
-vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodCodeLiteral(
- MethodReference target_method) {
- return DeduplicateMethodLiteral(target_method, &call_patches_);
-}
-
-
void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
// Explicit clinit checks triggered by static invokes must have been pruned by
// art::PrepareForRegisterAllocation.
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 85b6f9f..8f33b6b 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -693,8 +693,6 @@
vixl::aarch64::Literal<uint64_t>* DeduplicateUint64Literal(uint64_t value);
vixl::aarch64::Literal<uint64_t>* DeduplicateMethodLiteral(MethodReference target_method,
MethodToLiteralMap* map);
- vixl::aarch64::Literal<uint64_t>* DeduplicateMethodAddressLiteral(MethodReference target_method);
- vixl::aarch64::Literal<uint64_t>* DeduplicateMethodCodeLiteral(MethodReference target_method);
// The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
// and boot image strings/types. The only difference is the interpretation of the
@@ -737,12 +735,6 @@
Uint32ToLiteralMap uint32_literals_;
// Deduplication map for 64-bit literals, used for non-patchable method address or method code.
Uint64ToLiteralMap uint64_literals_;
- // Method patch info, map MethodReference to a literal for method address and method code.
- MethodToLiteralMap method_patches_;
- MethodToLiteralMap call_patches_;
- // Relative call patch info.
- // Using ArenaDeque<> which retains element addresses on push/emplace_back().
- ArenaDeque<PatchInfo<vixl::aarch64::Label>> relative_call_patches_;
// PC-relative DexCache access info.
ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
// Deduplication map for boot string literals for kBootImageLinkTimeAddress.
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 2c6df38..f108595 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -46,8 +46,10 @@
using helpers::InputRegister;
using helpers::InputRegisterAt;
using helpers::InputSRegisterAt;
+using helpers::InputVRegister;
using helpers::InputVRegisterAt;
using helpers::Int32ConstantFrom;
+using helpers::Int64ConstantFrom;
using helpers::LocationFrom;
using helpers::LowRegisterFrom;
using helpers::LowSRegisterFrom;
@@ -56,6 +58,10 @@
using helpers::OutputVRegister;
using helpers::RegisterFrom;
using helpers::SRegisterFrom;
+using helpers::Uint64ConstantFrom;
+
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
using RegisterList = vixl32::RegisterList;
@@ -799,7 +805,7 @@
// as-is.
vixl32::Label done;
__ Cmp(temp1_, ref_reg);
- __ B(eq, &done);
+ __ B(eq, &done, /* far_target */ false);
// Update the the holder's field atomically. This may fail if
// mutator updates before us, but it's OK. This is achieved
@@ -843,19 +849,19 @@
__ Subs(tmp, tmp, expected);
{
- AssemblerAccurateScope aas(arm_codegen->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(arm_codegen->GetVIXLAssembler(),
+ 2 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(ne);
__ clrex(ne);
}
- __ B(ne, &exit_loop);
+ __ B(ne, &exit_loop, /* far_target */ false);
__ Strex(tmp, value, MemOperand(tmp_ptr));
__ Cmp(tmp, 1);
- __ B(eq, &loop_head);
+ __ B(eq, &loop_head, /* far_target */ false);
__ Bind(&exit_loop);
@@ -1237,10 +1243,21 @@
move_resolver_(graph->GetArena(), this),
assembler_(graph->GetArena()),
isa_features_(isa_features),
- relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ uint32_literals_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ boot_image_type_patches_(TypeReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_address_patches_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ jit_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ jit_class_patches_(TypeReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Always save the LR register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(LR));
// Give d14 and d15 as scratch registers to VIXL.
@@ -1261,9 +1278,9 @@
// We are about to use the assembler to place literals directly. Make sure we have enough
// underlying code buffer and we have generated a jump table of the right size, using
// codegen->GetVIXLAssembler()->GetBuffer().Align();
- AssemblerAccurateScope aas(codegen->GetVIXLAssembler(),
- num_entries * sizeof(int32_t),
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
+ num_entries * sizeof(int32_t),
+ CodeBufferCheckScope::kMaximumSize);
// TODO(VIXL): Check that using lower case bind is fine here.
codegen->GetVIXLAssembler()->bind(&table_start_);
for (uint32_t i = 0; i < num_entries; i++) {
@@ -1375,11 +1392,11 @@
if (!skip_overflow_check) {
UseScratchRegisterScope temps(GetVIXLAssembler());
vixl32::Register temp = temps.Acquire();
- __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+ __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(kArm)));
// The load must immediately precede RecordPcInfo.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ ldr(temp, MemOperand(temp));
RecordPcInfo(nullptr, 0);
}
@@ -1637,9 +1654,9 @@
__ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
// Ensure the pc position is recorded immediately after the `blx` instruction.
// blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::k16BitT32InstructionSizeInBytes,
- CodeBufferCheckScope::kExactSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
__ blx(lr);
if (EntrypointRequiresStackMap(entrypoint)) {
RecordPcInfo(instruction, dex_pc, slow_path);
@@ -1792,7 +1809,7 @@
break;
}
if (right.IsConstant()) {
- int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+ int64_t value = Int64ConstantFrom(right);
int32_t val_low = Low32Bits(value);
int32_t val_high = High32Bits(value);
@@ -1877,7 +1894,7 @@
__ B(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
+ DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
if (false_target != nullptr) {
__ B(false_target);
}
@@ -2082,9 +2099,9 @@
__ Cmp(InputRegisterAt(cond, 0),
CodeGenerator::GetInt32ValueOf(right.GetConstant()));
}
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- 3 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ 3 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ ite(ARMCondition(cond->GetCondition()));
__ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
__ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
@@ -2370,9 +2387,9 @@
// Ensure the pc position is recorded immediately after the `ldr` instruction.
{
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
// /* HeapReference<Class> */ temp = receiver->klass_
__ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
codegen_->MaybeRecordImplicitNullCheck(invoke);
@@ -2418,7 +2435,7 @@
{
// Ensure the pc position is recorded immediately after the `blx` instruction.
// blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
+ ExactAssemblyScope aas(GetVIXLAssembler(),
vixl32::k16BitT32InstructionSizeInBytes,
CodeBufferCheckScope::kExactSize);
// LR();
@@ -2479,9 +2496,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- // TODO(VIXL): Consider introducing an InputVRegister()
- // helper function (equivalent to InputRegister()).
- __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
+ __ Vneg(OutputVRegister(neg), InputVRegister(neg));
break;
default:
@@ -2771,8 +2786,8 @@
} else {
DCHECK(in.IsConstant());
DCHECK(in.GetConstant()->IsLongConstant());
- int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
- __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
+ int32_t value = Int32ConstantFrom(in);
+ __ Mov(OutputRegister(conversion), value);
}
break;
@@ -3111,8 +3126,8 @@
// Extra checks to protect caused by the existence of R1_R2.
// The algorithm is wrong if out.hi is either in1.lo or in2.lo:
// (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
- DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
- DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
+ DCHECK(!out_hi.Is(in1_lo));
+ DCHECK(!out_hi.Is(in2_lo));
// input: in1 - 64 bits, in2 - 64 bits
// output: out
@@ -3152,7 +3167,7 @@
vixl32::Register out = OutputRegister(instruction);
vixl32::Register dividend = InputRegisterAt(instruction, 0);
- int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t imm = Int32ConstantFrom(second);
DCHECK(imm == 1 || imm == -1);
if (instruction->IsRem()) {
@@ -3177,7 +3192,7 @@
vixl32::Register out = OutputRegister(instruction);
vixl32::Register dividend = InputRegisterAt(instruction, 0);
vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
- int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t imm = Int32ConstantFrom(second);
uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
int ctz_imm = CTZ(abs_imm);
@@ -3250,7 +3265,7 @@
Location second = instruction->GetLocations()->InAt(1);
DCHECK(second.IsConstant());
- int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t imm = Int32ConstantFrom(second);
if (imm == 0) {
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
@@ -3284,7 +3299,7 @@
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
+ int32_t value = Int32ConstantFrom(div->InputAt(1));
if (value == 1 || value == 0 || value == -1) {
// No temp register required.
} else {
@@ -3397,7 +3412,7 @@
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
+ int32_t value = Int32ConstantFrom(rem->InputAt(1));
if (value == 1 || value == 0 || value == -1) {
// No temp register required.
} else {
@@ -3532,7 +3547,7 @@
__ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
} else {
DCHECK(value.IsConstant()) << value;
- if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
+ if (Int32ConstantFrom(value) == 0) {
__ B(slow_path->GetEntryLabel());
}
}
@@ -3546,7 +3561,7 @@
__ B(eq, slow_path->GetEntryLabel());
} else {
DCHECK(value.IsConstant()) << value;
- if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
+ if (Int64ConstantFrom(value) == 0) {
__ B(slow_path->GetEntryLabel());
}
}
@@ -3622,7 +3637,7 @@
__ And(shift_right, RegisterFrom(rhs), 0x1F);
__ Lsrs(shift_left, RegisterFrom(rhs), 6);
__ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
- __ B(cc, &shift_by_32_plus_shift_right);
+ __ B(cc, &shift_by_32_plus_shift_right, /* far_target */ false);
// out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
// out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
@@ -3756,7 +3771,7 @@
__ Lsr(out_reg, first_reg, out_reg);
}
} else {
- int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t cst = Int32ConstantFrom(second);
uint32_t shift_value = cst & kMaxIntShiftDistance;
if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
__ Mov(out_reg, first_reg);
@@ -3793,9 +3808,9 @@
// If the shift is > 32 bits, override the high part
__ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
{
- AssemblerAccurateScope guard(GetVIXLAssembler(),
- 2 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope guard(GetVIXLAssembler(),
+ 2 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(pl);
__ lsl(pl, o_h, low, temp);
}
@@ -3812,9 +3827,9 @@
// If the shift is > 32 bits, override the low part
__ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
{
- AssemblerAccurateScope guard(GetVIXLAssembler(),
- 2 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope guard(GetVIXLAssembler(),
+ 2 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(pl);
__ asr(pl, o_l, high, temp);
}
@@ -3829,9 +3844,9 @@
__ Orr(o_l, o_l, temp);
__ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
{
- AssemblerAccurateScope guard(GetVIXLAssembler(),
- 2 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope guard(GetVIXLAssembler(),
+ 2 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(pl);
__ lsr(pl, o_l, high, temp);
}
@@ -3841,7 +3856,7 @@
// Register allocator doesn't create partial overlap.
DCHECK(!o_l.Is(high));
DCHECK(!o_h.Is(low));
- int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t cst = Int32ConstantFrom(second);
uint32_t shift_value = cst & kMaxLongShiftDistance;
if (shift_value > 32) {
if (op->IsShl()) {
@@ -3948,9 +3963,9 @@
GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
// blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::k16BitT32InstructionSizeInBytes,
- CodeBufferCheckScope::kExactSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
__ blx(lr);
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
} else {
@@ -4094,8 +4109,8 @@
}
case Primitive::kPrimLong: {
__ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
- __ B(lt, &less);
- __ B(gt, &greater);
+ __ B(lt, &less, /* far_target */ false);
+ __ B(gt, &greater, /* far_target */ false);
// Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
__ Mov(out, 0);
__ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
@@ -4116,8 +4131,8 @@
UNREACHABLE();
}
- __ B(eq, &done);
- __ B(less_cond, &less);
+ __ B(eq, &done, /* far_target */ false);
+ __ B(less_cond, &less, /* far_target */ false);
__ Bind(&greater);
__ Mov(out, 1);
@@ -4192,9 +4207,9 @@
__ Bind(&fail);
{
// Ensure the pc position is recorded immediately after the `ldrexd` instruction.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
// We need a load followed by store. (The address used in a STREX instruction must
// be the same as the address in the most recently executed LDREX instruction.)
__ ldrexd(temp1, temp2, MemOperand(addr));
@@ -4411,7 +4426,7 @@
locations->AddTemp(Location::RequiresRegister());
} else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
- // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
+ // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -4715,9 +4730,9 @@
UseScratchRegisterScope temps(GetVIXLAssembler());
// Ensure the pc position is recorded immediately after the `ldr` instruction.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
RecordPcInfo(instruction, instruction->GetDexPc());
}
@@ -4873,7 +4888,7 @@
object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
// We need a temporary register for the read barrier marking slow
- // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+ // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
// Also need for String compression feature.
if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
|| (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
@@ -4908,13 +4923,13 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
if (index.IsConstant()) {
- int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ int32_t const_index = Int32ConstantFrom(index);
if (maybe_compressed_char_at) {
vixl32::Label uncompressed_load, done;
__ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
- __ B(cs, &uncompressed_load);
+ __ B(cs, &uncompressed_load, /* far_target */ false);
GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
RegisterFrom(out_loc),
obj,
@@ -4942,7 +4957,7 @@
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
- DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+ DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
}
temp = obj;
} else {
@@ -4953,7 +4968,7 @@
__ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
- __ B(cs, &uncompressed_load);
+ __ B(cs, &uncompressed_load, /* far_target */ false);
__ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
__ B(&done);
__ Bind(&uncompressed_load);
@@ -4987,7 +5002,7 @@
vixl32::Register out = OutputRegister(instruction);
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
// TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
// we should use a scope and the assembler to emit the load instruction to guarantee that
@@ -5009,7 +5024,7 @@
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
- DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+ DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
}
temp = obj;
} else {
@@ -5034,7 +5049,7 @@
case Primitive::kPrimLong: {
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5048,7 +5063,7 @@
case Primitive::kPrimFloat: {
vixl32::SRegister out = SRegisterFrom(out_loc);
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->LoadSFromOffset(out, obj, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5061,7 +5076,7 @@
case Primitive::kPrimDouble: {
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5135,7 +5150,7 @@
case Primitive::kPrimChar:
case Primitive::kPrimInt: {
if (index.IsConstant()) {
- int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ int32_t const_index = Int32ConstantFrom(index);
uint32_t full_offset =
data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
StoreOperandType store_type = GetStoreOperandType(value_type);
@@ -5150,7 +5165,7 @@
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
- DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
+ DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
}
temp = array;
} else {
@@ -5171,7 +5186,7 @@
// Just setting null.
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
@@ -5207,7 +5222,7 @@
__ CompareAndBranchIfNonZero(value, &non_zero);
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
@@ -5233,9 +5248,9 @@
{
// Ensure we record the pc position immediately after the `ldr` instruction.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
// /* HeapReference<Class> */ temp1 = array->klass_
__ ldr(temp1, MemOperand(array, class_offset));
codegen_->MaybeRecordImplicitNullCheck(instruction);
@@ -5252,7 +5267,7 @@
if (instruction->StaticTypeOfArrayIsObjectArray()) {
vixl32::Label do_put;
- __ B(eq, &do_put);
+ __ B(eq, &do_put, /* far_target */ false);
// If heap poisoning is enabled, the `temp1` reference has
// not been unpoisoned yet; unpoison it now.
GetAssembler()->MaybeUnpoisonHeapReference(temp1);
@@ -5281,7 +5296,7 @@
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
@@ -5318,7 +5333,7 @@
Location value = locations->InAt(2);
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5333,7 +5348,7 @@
Location value = locations->InAt(2);
DCHECK(value.IsFpuRegister());
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5348,7 +5363,7 @@
Location value = locations->InAt(2);
DCHECK(value.IsFpuRegisterPair());
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5384,9 +5399,9 @@
vixl32::Register obj = InputRegisterAt(instruction, 0);
vixl32::Register out = OutputRegister(instruction);
{
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ ldr(out, MemOperand(obj, offset));
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -5413,7 +5428,7 @@
if (second.IsRegister()) {
__ Add(out, first, RegisterFrom(second));
} else {
- __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue());
+ __ Add(out, first, Int32ConstantFrom(second));
}
}
@@ -5609,7 +5624,7 @@
GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
}
} else if (constant->IsLongConstant()) {
- int64_t value = constant->AsLongConstant()->GetValue();
+ int64_t value = Int64ConstantFrom(source);
if (destination.IsRegisterPair()) {
__ Mov(LowRegisterFrom(destination), Low32Bits(value));
__ Mov(HighRegisterFrom(destination), High32Bits(value));
@@ -5768,17 +5783,15 @@
case HLoadClass::LoadKind::kReferrersClass:
break;
case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
- // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
- return HLoadClass::LoadKind::kDexCacheViaMethod;
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
DCHECK(GetCompilerOptions().GetCompilePic());
break;
case HLoadClass::LoadKind::kBootImageAddress:
- // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
- return HLoadClass::LoadKind::kDexCacheViaMethod;
+ break;
case HLoadClass::LoadKind::kJitTableAddress:
- // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
- return HLoadClass::LoadKind::kDexCacheViaMethod;
+ break;
case HLoadClass::LoadKind::kDexCachePcRelative:
DCHECK(!Runtime::Current()->UseJitCompilation());
// We disable pc-relative load when there is an irreducible loop, as the optimization
@@ -5854,7 +5867,9 @@
break;
}
case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
- TODO_VIXL32(FATAL);
+ DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+ __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+ cls->GetTypeIndex()));
break;
}
case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
@@ -5865,11 +5880,18 @@
break;
}
case HLoadClass::LoadKind::kBootImageAddress: {
- TODO_VIXL32(FATAL);
+ DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
break;
}
case HLoadClass::LoadKind::kJitTableAddress: {
- TODO_VIXL32(FATAL);
+ __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
+ cls->GetTypeIndex(),
+ cls->GetAddress()));
+ // /* GcRoot<mirror::Class> */ out = *out
+ GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
break;
}
case HLoadClass::LoadKind::kDexCachePcRelative: {
@@ -5954,21 +5976,19 @@
HLoadString::LoadKind desired_string_load_kind) {
switch (desired_string_load_kind) {
case HLoadString::LoadKind::kBootImageLinkTimeAddress:
- // TODO(VIXL): Implement missing optimization.
- return HLoadString::LoadKind::kDexCacheViaMethod;
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
DCHECK(GetCompilerOptions().GetCompilePic());
break;
case HLoadString::LoadKind::kBootImageAddress:
- // TODO(VIXL): Implement missing optimization.
- return HLoadString::LoadKind::kDexCacheViaMethod;
+ break;
case HLoadString::LoadKind::kBssEntry:
DCHECK(!Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kJitTableAddress:
DCHECK(Runtime::Current()->UseJitCompilation());
- // TODO(VIXL): Implement missing optimization.
- return HLoadString::LoadKind::kDexCacheViaMethod;
+ break;
case HLoadString::LoadKind::kDexCacheViaMethod:
break;
}
@@ -6010,8 +6030,9 @@
switch (load_kind) {
case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
- TODO_VIXL32(FATAL);
- break;
+ __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
DCHECK(codegen_->GetCompilerOptions().IsBootImage());
@@ -6021,8 +6042,10 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- TODO_VIXL32(FATAL);
- break;
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
+ return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBssEntry: {
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
@@ -6039,8 +6062,11 @@
return;
}
case HLoadString::LoadKind::kJitTableAddress: {
- TODO_VIXL32(FATAL);
- break;
+ __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ // /* GcRoot<mirror::String> */ out = *out
+ GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+ return;
}
default:
break;
@@ -6182,7 +6208,7 @@
kCompilerReadBarrierOption);
__ Cmp(out, cls);
// Classes must be equal for the instanceof to succeed.
- __ B(ne, &zero);
+ __ B(ne, &zero, /* far_target */ false);
__ Mov(out, 1);
__ B(&done);
break;
@@ -6209,7 +6235,7 @@
// If `out` is null, we use it for the result, and jump to `done`.
__ CompareAndBranchIfZero(out, &done, /* far_target */ false);
__ Cmp(out, cls);
- __ B(ne, &loop);
+ __ B(ne, &loop, /* far_target */ false);
__ Mov(out, 1);
if (zero.IsReferenced()) {
__ B(&done);
@@ -6229,7 +6255,7 @@
vixl32::Label loop, success;
__ Bind(&loop);
__ Cmp(out, cls);
- __ B(eq, &success);
+ __ B(eq, &success, /* far_target */ false);
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction,
out_loc,
@@ -6258,7 +6284,7 @@
// Do an exact check.
vixl32::Label exact_check;
__ Cmp(out, cls);
- __ B(eq, &exact_check);
+ __ B(eq, &exact_check, /* far_target */ false);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ out = out->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -6460,7 +6486,7 @@
// Otherwise, compare the classes.
__ Cmp(temp, cls);
- __ B(ne, &loop);
+ __ B(ne, &loop, /* far_target */ false);
break;
}
@@ -6477,7 +6503,7 @@
vixl32::Label loop;
__ Bind(&loop);
__ Cmp(temp, cls);
- __ B(eq, &done);
+ __ B(eq, &done, /* far_target */ false);
// /* HeapReference<Class> */ temp = temp->super_class_
GenerateReferenceLoadOneRegister(instruction,
@@ -6505,7 +6531,7 @@
// Do an exact check.
__ Cmp(temp, cls);
- __ B(eq, &done);
+ __ B(eq, &done, /* far_target */ false);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ temp = temp->component_type_
@@ -6569,7 +6595,7 @@
__ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
// Compare the classes and continue the loop if they do not match.
__ Cmp(cls, RegisterFrom(maybe_temp3_loc));
- __ B(ne, &start_loop);
+ __ B(ne, &start_loop, /* far_target */ false);
break;
}
}
@@ -7202,20 +7228,7 @@
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
- HInvokeStaticOrDirect* invoke) {
- // TODO(VIXL): Implement optimized code paths.
- if (desired_dispatch_info.method_load_kind ==
- HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup ||
- desired_dispatch_info.code_ptr_location ==
- HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup) {
- return {
- HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0u
- };
- }
-
+ HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
@@ -7227,24 +7240,6 @@
dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
}
- if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
- const DexFile& outer_dex_file = GetGraph()->GetDexFile();
- if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
- // Calls across dex files are more likely to exceed the available BL range,
- // so use absolute patch with fixup if available and kCallArtMethod otherwise.
- HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
- (desired_dispatch_info.method_load_kind ==
- HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
- ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
- : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
- return HInvokeStaticOrDirect::DispatchInfo {
- dispatch_info.method_load_kind,
- code_ptr_location,
- dispatch_info.method_load_data,
- 0u
- };
- }
- }
return dispatch_info;
}
@@ -7276,20 +7271,6 @@
void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
HInvokeStaticOrDirect* invoke, Location temp) {
- // For better instruction scheduling we load the direct code pointer before the method pointer.
- switch (invoke->GetCodePtrLocation()) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- // LR = code address from literal pool with link-time patch.
- TODO_VIXL32(FATAL);
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR = invoke->GetDirectCodePtr();
- __ Mov(lr, Operand::From(invoke->GetDirectCodePtr()));
- break;
- default:
- break;
- }
-
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
switch (invoke->GetMethodLoadKind()) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
@@ -7305,9 +7286,6 @@
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
__ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- TODO_VIXL32(FATAL);
- break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
HArmDexCacheArraysBase* base =
invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
@@ -7347,30 +7325,6 @@
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
__ Bl(GetFrameEntryLabel());
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetTargetMethod().dex_method_index);
- {
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
- __ bind(&relative_call_patches_.back().label);
- // Arbitrarily branch to the BL itself, override at link time.
- __ bl(&relative_call_patches_.back().label);
- }
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR prepared above for better instruction scheduling.
- // LR()
- {
- // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::k16BitT32InstructionSizeInBytes,
- CodeBufferCheckScope::kExactSize);
- __ blx(lr);
- }
- break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// LR = callee_method->entry_point_from_quick_compiled_code_
GetAssembler()->LoadFromOffset(
@@ -7380,9 +7334,9 @@
ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
{
// blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::k16BitT32InstructionSizeInBytes,
- CodeBufferCheckScope::kExactSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
// LR()
__ blx(lr);
}
@@ -7406,9 +7360,9 @@
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
{
// Make sure the pc is recorded immediately after the `ldr` instruction.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
// /* HeapReference<Class> */ temp = receiver->klass_
__ ldr(temp, MemOperand(receiver, class_offset));
MaybeRecordImplicitNullCheck(invoke);
@@ -7433,9 +7387,9 @@
// `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee
// that.
// blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- vixl32::k16BitT32InstructionSizeInBytes,
- CodeBufferCheckScope::kExactSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
__ blx(lr);
}
@@ -7460,6 +7414,57 @@
return &patches->back();
}
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageStringLiteral(
+ const DexFile& dex_file,
+ dex::StringIndex string_index) {
+ return boot_image_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() {
+ return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+ });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageTypeLiteral(
+ const DexFile& dex_file,
+ dex::TypeIndex type_index) {
+ return boot_image_type_patches_.GetOrCreate(
+ TypeReference(&dex_file, type_index),
+ [this]() {
+ return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+ });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
+ bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+ Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+ return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateDexCacheAddressLiteral(uint32_t address) {
+ return DeduplicateUint32Literal(address, &uint32_literals_);
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index) {
+ jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+ return jit_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() {
+ return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+ });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ uint64_t address) {
+ jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address);
+ return jit_class_patches_.GetOrCreate(
+ TypeReference(&dex_file, type_index),
+ [this]() {
+ return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+ });
+}
+
template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
const ArenaDeque<PcRelativePatchInfo>& infos,
@@ -7483,18 +7488,24 @@
void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
- relative_call_patches_.size() +
/* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
+ boot_image_string_patches_.size() +
/* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
- /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size();
+ boot_image_type_patches_.size() +
+ /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
+ boot_image_address_patches_.size();
linker_patches->reserve(size);
- for (const PatchInfo<vixl32::Label>& info : relative_call_patches_) {
- uint32_t literal_offset = info.label.GetLocation();
- linker_patches->push_back(
- LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
- }
EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
linker_patches);
+ for (const auto& entry : boot_image_string_patches_) {
+ const StringReference& target_string = entry.first;
+ VIXLUInt32Literal* literal = entry.second;
+ DCHECK(literal->IsBound());
+ uint32_t literal_offset = literal->GetLocation();
+ linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+ target_string.dex_file,
+ target_string.string_index.index_));
+ }
if (!GetCompilerOptions().IsBootImage()) {
EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
linker_patches);
@@ -7502,8 +7513,44 @@
EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
linker_patches);
}
+ for (const auto& entry : boot_image_type_patches_) {
+ const TypeReference& target_type = entry.first;
+ VIXLUInt32Literal* literal = entry.second;
+ DCHECK(literal->IsBound());
+ uint32_t literal_offset = literal->GetLocation();
+ linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+ target_type.dex_file,
+ target_type.type_index.index_));
+ }
EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
linker_patches);
+ for (const auto& entry : boot_image_address_patches_) {
+ DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+ VIXLUInt32Literal* literal = entry.second;
+ DCHECK(literal->IsBound());
+ uint32_t literal_offset = literal->GetLocation();
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
+ uint32_t value,
+ Uint32ToLiteralMap* map) {
+ return map->GetOrCreate(
+ value,
+ [this, value]() {
+ return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value);
+ });
+}
+
+VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateMethodLiteral(
+ MethodReference target_method,
+ MethodToLiteralMap* map) {
+ return map->GetOrCreate(
+ target_method,
+ [this]() {
+ return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
+ });
}
void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
@@ -7699,12 +7746,37 @@
}
}
+static void PatchJitRootUse(uint8_t* code,
+ const uint8_t* roots_data,
+ VIXLUInt32Literal* literal,
+ uint64_t index_in_table) {
+ DCHECK(literal->IsBound());
+ uint32_t literal_offset = literal->GetLocation();
+ uintptr_t address =
+ reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
+ uint8_t* data = code + literal_offset;
+ reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
+}
+
+void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
+ for (const auto& entry : jit_string_patches_) {
+ const auto& it = jit_string_roots_.find(entry.first);
+ DCHECK(it != jit_string_roots_.end());
+ PatchJitRootUse(code, roots_data, entry.second, it->second);
+ }
+ for (const auto& entry : jit_class_patches_) {
+ const auto& it = jit_class_roots_.find(entry.first);
+ DCHECK(it != jit_class_roots_.end());
+ PatchJitRootUse(code, roots_data, entry.second, it->second);
+ }
+}
+
void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
vixl32::Register out) {
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- 3 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(GetVIXLAssembler(),
+ 3 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
// TODO(VIXL): Think about using mov instead of movw.
__ bind(&labels->movw_label);
__ movw(out, /* placeholder */ 0u);
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 5ec3da4..297d63c 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -107,20 +107,20 @@
arraysize(kRuntimeParameterFpuRegistersVIXL);
class LoadClassSlowPathARMVIXL;
-
class CodeGeneratorARMVIXL;
+using VIXLInt32Literal = vixl::aarch32::Literal<int32_t>;
+using VIXLUInt32Literal = vixl::aarch32::Literal<uint32_t>;
+
class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> {
public:
- typedef vixl::aarch32::Literal<int32_t> IntLiteral;
-
explicit JumpTableARMVIXL(HPackedSwitch* switch_instr)
: switch_instr_(switch_instr),
table_start_(),
bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
uint32_t num_entries = switch_instr_->GetNumEntries();
for (uint32_t i = 0; i < num_entries; i++) {
- IntLiteral *lit = new IntLiteral(0);
+ VIXLInt32Literal *lit = new VIXLInt32Literal(0, vixl32::RawLiteral::kManuallyPlaced);
bb_addresses_.emplace_back(lit);
}
}
@@ -133,7 +133,7 @@
private:
HPackedSwitch* const switch_instr_;
vixl::aarch32::Label table_start_;
- ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_;
+ ArenaVector<std::unique_ptr<VIXLInt32Literal>> bb_addresses_;
DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL);
};
@@ -566,8 +566,22 @@
PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
uint32_t element_offset);
+ VIXLUInt32Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index);
+ VIXLUInt32Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+ dex::TypeIndex type_index);
+ VIXLUInt32Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
+ VIXLUInt32Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
+ VIXLUInt32Literal* DeduplicateJitStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index);
+ VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ uint64_t address);
+
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
+ void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
+
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -673,10 +687,19 @@
vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
vixl::aarch32::Register temp);
- using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::aarch32::Literal<uint32_t>*>;
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, VIXLUInt32Literal*>;
using MethodToLiteralMap =
- ArenaSafeMap<MethodReference, vixl::aarch32::Literal<uint32_t>*, MethodReferenceComparator>;
+ ArenaSafeMap<MethodReference, VIXLUInt32Literal*, MethodReferenceComparator>;
+ using StringToLiteralMap = ArenaSafeMap<StringReference,
+ VIXLUInt32Literal*,
+ StringReferenceValueComparator>;
+ using TypeToLiteralMap = ArenaSafeMap<TypeReference,
+ VIXLUInt32Literal*,
+ TypeReferenceValueComparator>;
+ VIXLUInt32Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
+ VIXLUInt32Literal* DeduplicateMethodLiteral(MethodReference target_method,
+ MethodToLiteralMap* map);
PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
uint32_t offset_or_index,
ArenaDeque<PcRelativePatchInfo>* patches);
@@ -697,15 +720,25 @@
ArmVIXLAssembler assembler_;
const ArmInstructionSetFeatures& isa_features_;
- // Relative call patch info.
- // Using ArenaDeque<> which retains element addresses on push/emplace_back().
- ArenaDeque<PatchInfo<vixl::aarch32::Label>> relative_call_patches_;
+ // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+ Uint32ToLiteralMap uint32_literals_;
// PC-relative patch info for each HArmDexCacheArraysBase.
ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+ // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+ StringToLiteralMap boot_image_string_patches_;
// PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+ TypeToLiteralMap boot_image_type_patches_;
// PC-relative type patch info.
ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+ // Deduplication map for patchable boot image addresses.
+ Uint32ToLiteralMap boot_image_address_patches_;
+
+ // Patches for string literals in JIT compiled code.
+ StringToLiteralMap jit_string_patches_;
+ // Patches for class literals in JIT compiled code.
+ TypeToLiteralMap jit_class_patches_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL);
};
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index cae4161..01e0dac 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -458,10 +458,6 @@
isa_features_(isa_features),
uint32_literals_(std::less<uint32_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- method_patches_(MethodReferenceComparator(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- call_patches_(MethodReferenceComparator(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
boot_image_string_patches_(StringReferenceValueComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -757,6 +753,11 @@
if (RequiresCurrentMethod()) {
__ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
}
+
+ if (GetGraph()->HasShouldDeoptimizeFlag()) {
+ // Initialize should deoptimize flag to 0.
+ __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag());
+ }
}
void CodeGeneratorMIPS::GenerateFrameExit() {
@@ -1003,8 +1004,6 @@
void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
- method_patches_.size() +
- call_patches_.size() +
pc_relative_dex_cache_patches_.size() +
pc_relative_string_patches_.size() +
pc_relative_type_patches_.size() +
@@ -1012,24 +1011,6 @@
boot_image_type_patches_.size() +
boot_image_address_patches_.size();
linker_patches->reserve(size);
- for (const auto& entry : method_patches_) {
- const MethodReference& target_method = entry.first;
- Literal* literal = entry.second;
- DCHECK(literal->GetLabel()->IsBound());
- uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
- linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset,
- target_method.dex_file,
- target_method.dex_method_index));
- }
- for (const auto& entry : call_patches_) {
- const MethodReference& target_method = entry.first;
- Literal* literal = entry.second;
- DCHECK(literal->GetLabel()->IsBound());
- uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
- linker_patches->push_back(LinkerPatch::CodePatch(literal_offset,
- target_method.dex_file,
- target_method.dex_method_index));
- }
EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
linker_patches);
if (!GetCompilerOptions().IsBootImage()) {
@@ -1102,14 +1083,6 @@
[this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
}
-Literal* CodeGeneratorMIPS::DeduplicateMethodAddressLiteral(MethodReference target_method) {
- return DeduplicateMethodLiteral(target_method, &method_patches_);
-}
-
-Literal* CodeGeneratorMIPS::DeduplicateMethodCodeLiteral(MethodReference target_method) {
- return DeduplicateMethodLiteral(target_method, &call_patches_);
-}
-
Literal* CodeGeneratorMIPS::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
dex::StringIndex string_index) {
return boot_image_string_patches_.GetOrCreate(
@@ -1160,11 +1133,15 @@
__ SetReorder(reordering);
}
-void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) {
+void CodeGeneratorMIPS::MarkGCCard(Register object,
+ Register value,
+ bool value_can_be_null) {
MipsLabel done;
Register card = AT;
Register temp = TMP;
- __ Beqz(value, &done);
+ if (value_can_be_null) {
+ __ Beqz(value, &done);
+ }
__ LoadFromOffset(kLoadWord,
card,
TR,
@@ -1172,7 +1149,9 @@
__ Srl(temp, object, gc::accounting::CardTable::kCardShift);
__ Addu(temp, card, temp);
__ Sb(card, temp, 0);
- __ Bind(&done);
+ if (value_can_be_null) {
+ __ Bind(&done);
+ }
}
void CodeGeneratorMIPS::SetupBlockedRegisters() const {
@@ -2091,7 +2070,7 @@
__ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
if (needs_write_barrier) {
DCHECK_EQ(value_type, Primitive::kPrimNot);
- codegen_->MarkGCCard(obj, value);
+ codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull());
}
}
} else {
@@ -4689,14 +4668,17 @@
}
}
-void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag(
- HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
- // TODO: to be implemented.
+void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ LocationSummary* locations = new (GetGraph()->GetArena())
+ LocationSummary(flag, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
}
-void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag(
- HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
- // TODO: to be implemented.
+void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ __ LoadFromOffset(kLoadWord,
+ flag->GetLocations()->Out().AsRegister<Register>(),
+ SP,
+ codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
}
void LocationsBuilderMIPS::VisitSelect(HSelect* select) {
@@ -4892,7 +4874,8 @@
void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- uint32_t dex_pc) {
+ uint32_t dex_pc,
+ bool value_can_be_null) {
Primitive::Type type = field_info.GetFieldType();
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsRegister<Register>();
@@ -4987,7 +4970,7 @@
// TODO: memory barriers?
if (CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1))) {
Register src = value_location.AsRegister<Register>();
- codegen_->MarkGCCard(obj, src);
+ codegen_->MarkGCCard(obj, src, value_can_be_null);
}
if (is_volatile) {
@@ -5008,7 +4991,10 @@
}
void InstructionCodeGeneratorMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetDexPc(),
+ instruction->GetValueCanBeNull());
}
void InstructionCodeGeneratorMIPS::GenerateGcRootFieldLoad(
@@ -5149,22 +5135,7 @@
// art::PrepareForRegisterAllocation.
DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
- HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
- HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
- bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
-
- // kDirectAddressWithFixup and kCallDirectWithFixup need no extra input on R6 because
- // R6 has PC-relative addressing.
- bool has_extra_input = !isR6 &&
- ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
- (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup));
-
- if (invoke->HasPcRelativeDexCache()) {
- // kDexCachePcRelative is mutually exclusive with
- // kDirectAddressWithFixup/kCallDirectWithFixup.
- CHECK(!has_extra_input);
- has_extra_input = true;
- }
+ bool has_extra_input = invoke->HasPcRelativeDexCache();
IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
if (intrinsic.TryDispatch(invoke)) {
@@ -5304,9 +5275,7 @@
// is incompatible with it.
bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
bool fallback_load = true;
- bool fallback_call = true;
switch (dispatch_info.method_load_kind) {
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
fallback_load = has_irreducible_loops;
break;
@@ -5314,25 +5283,10 @@
fallback_load = false;
break;
}
- switch (dispatch_info.code_ptr_location) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- fallback_call = has_irreducible_loops;
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement this type.
- break;
- default:
- fallback_call = false;
- break;
- }
if (fallback_load) {
dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
dispatch_info.method_load_data = 0;
}
- if (fallback_call) {
- dispatch_info.code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
- dispatch_info.direct_code_ptr = 0;
- }
return dispatch_info;
}
@@ -5341,31 +5295,10 @@
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
- bool isR6 = isa_features_.IsR6();
- // kDirectAddressWithFixup and kCallDirectWithFixup have no extra input on R6 because
- // R6 has PC-relative addressing.
- bool has_extra_input = invoke->HasPcRelativeDexCache() ||
- (!isR6 &&
- ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
- (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup)));
- Register base_reg = has_extra_input
+ Register base_reg = invoke->HasPcRelativeDexCache()
? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>())
: ZERO;
- // For better instruction scheduling we load the direct code pointer before the method pointer.
- switch (code_ptr_location) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // T9 = invoke->GetDirectCodePtr();
- __ LoadConst32(T9, invoke->GetDirectCodePtr());
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- // T9 = code address from literal pool with link-time patch.
- __ LoadLiteral(T9, base_reg, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
- break;
- default:
- break;
- }
-
switch (method_load_kind) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
// temp = thread->string_init_entrypoint
@@ -5383,11 +5316,6 @@
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
__ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- __ LoadLiteral(temp.AsRegister<Register>(),
- base_reg,
- DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
- break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
HMipsDexCacheArraysBase* base =
invoke->InputAt(invoke->GetSpecialInputIndex())->AsMipsDexCacheArraysBase();
@@ -5430,18 +5358,6 @@
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
__ Bal(&frame_entry_label_);
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- // T9 prepared above for better instruction scheduling.
- // T9()
- __ Jalr(T9);
- __ NopIfNoReordering();
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement this type.
- // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
- LOG(FATAL) << "Unsupported";
- UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// T9 = callee_method->entry_point_from_quick_compiled_code_;
__ LoadFromOffset(kLoadWord,
@@ -5681,11 +5597,7 @@
}
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
- ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod)
- ? LocationSummary::kCallOnMainOnly
- : LocationSummary::kCallOnSlowPath)
- : LocationSummary::kNoCall;
+ LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
HLoadString::LoadKind load_kind = load->GetLoadKind();
switch (load_kind) {
@@ -5735,14 +5647,12 @@
switch (load_kind) {
case HLoadString::LoadKind::kBootImageLinkTimeAddress:
- DCHECK(!kEmitCompilerReadBarrier);
__ LoadLiteral(out,
base_or_current_method_reg,
codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
load->GetStringIndex()));
return; // No dex cache slow path.
case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
- DCHECK(!kEmitCompilerReadBarrier);
DCHECK(codegen_->GetCompilerOptions().IsBootImage());
CodeGeneratorMIPS::PcRelativePatchInfo* info =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
@@ -5750,7 +5660,6 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK(!kEmitCompilerReadBarrier);
DCHECK_NE(load->GetAddress(), 0u);
uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
__ LoadLiteral(out,
@@ -6276,7 +6185,10 @@
}
void InstructionCodeGeneratorMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetDexPc(),
+ instruction->GetValueCanBeNull());
}
void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldGet(
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index f03f29c..7b0812c 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -236,7 +236,10 @@
void HandleBinaryOp(HBinaryOperation* operation);
void HandleCondition(HCondition* instruction);
void HandleShift(HBinaryOperation* operation);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ uint32_t dex_pc,
+ bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
// Generate a GC root reference load:
//
@@ -350,7 +353,7 @@
// Emit linker patches.
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
- void MarkGCCard(Register object, Register value);
+ void MarkGCCard(Register object, Register value, bool value_can_be_null);
// Register allocation.
@@ -474,8 +477,6 @@
Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
- Literal* DeduplicateMethodAddressLiteral(MethodReference target_method);
- Literal* DeduplicateMethodCodeLiteral(MethodReference target_method);
PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
uint32_t offset_or_index,
ArenaDeque<PcRelativePatchInfo>* patches);
@@ -495,9 +496,6 @@
// Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
Uint32ToLiteralMap uint32_literals_;
- // Method patch info, map MethodReference to a literal for method address and method code.
- MethodToLiteralMap method_patches_;
- MethodToLiteralMap call_patches_;
// PC-relative patch info for each HMipsDexCacheArraysBase.
ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
// Deduplication map for boot string literals for kBootImageLinkTimeAddress.
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index b1f9b1d..36690c0 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -18,6 +18,7 @@
#include "art_method.h"
#include "code_generator_utils.h"
+#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
@@ -90,8 +91,6 @@
// Space on the stack is reserved for all arguments.
stack_index_ += Primitive::Is64BitType(type) ? 2 : 1;
- // TODO: review
-
// TODO: shouldn't we use a whole machine word per argument on the stack?
// Implicit 4-byte method pointer (and such) will cause misalignment.
@@ -234,6 +233,7 @@
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
+ HLoadString* load = instruction_->AsLoadString();
const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_;
__ LoadConst32(calling_convention.GetRegisterAt(0), string_index);
mips64_codegen->InvokeRuntime(kQuickResolveString,
@@ -247,6 +247,17 @@
type);
RestoreLiveRegisters(codegen, locations);
+
+ // Store the resolved String to the BSS entry.
+ // TODO: Change art_quick_resolve_string to kSaveEverything and use a temporary for the
+ // .bss entry address in the fast path, so that we can avoid another calculation here.
+ GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+ DCHECK_NE(out, AT);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+ mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Sw(out, AT, /* placeholder */ 0x5678);
+
__ Bc(GetExitLabel());
}
@@ -399,7 +410,20 @@
instruction_visitor_(graph, this),
move_resolver_(graph->GetArena(), this),
assembler_(graph->GetArena()),
- isa_features_(isa_features) {
+ isa_features_(isa_features),
+ uint32_literals_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ uint64_literals_(std::less<uint64_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_type_patches_(TypeReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_address_patches_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Save RA (containing the return address) to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(RA));
}
@@ -510,8 +534,6 @@
RecordPcInfo(nullptr, 0);
}
- // TODO: anything related to T9/GP/GOT/PIC/.so's?
-
if (HasEmptyFrame()) {
return;
}
@@ -562,13 +584,16 @@
"kCurrentMethodStackOffset must fit into int16_t");
__ Sd(kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
}
+
+ if (GetGraph()->HasShouldDeoptimizeFlag()) {
+ // Initialize should_deoptimize flag to 0.
+ __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag());
+ }
}
void CodeGeneratorMIPS64::GenerateFrameExit() {
__ cfi().RememberState();
- // TODO: anything related to T9/GP/GOT/PIC/.so's?
-
if (!HasEmptyFrame()) {
// Deallocate the rest of the frame.
@@ -878,6 +903,136 @@
}
}
+template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches(
+ const ArenaDeque<PcRelativePatchInfo>& infos,
+ ArenaVector<LinkerPatch>* linker_patches) {
+ for (const PcRelativePatchInfo& info : infos) {
+ const DexFile& dex_file = info.target_dex_file;
+ size_t offset_or_index = info.offset_or_index;
+ DCHECK(info.pc_rel_label.IsBound());
+ uint32_t pc_rel_offset = __ GetLabelLocation(&info.pc_rel_label);
+ linker_patches->push_back(Factory(pc_rel_offset, &dex_file, pc_rel_offset, offset_or_index));
+ }
+}
+
+void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
+ DCHECK(linker_patches->empty());
+ size_t size =
+ pc_relative_dex_cache_patches_.size() +
+ pc_relative_string_patches_.size() +
+ pc_relative_type_patches_.size() +
+ boot_image_string_patches_.size() +
+ boot_image_type_patches_.size() +
+ boot_image_address_patches_.size();
+ linker_patches->reserve(size);
+ EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
+ linker_patches);
+ if (!GetCompilerOptions().IsBootImage()) {
+ EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
+ linker_patches);
+ } else {
+ EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
+ linker_patches);
+ }
+ EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
+ linker_patches);
+ for (const auto& entry : boot_image_string_patches_) {
+ const StringReference& target_string = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+ target_string.dex_file,
+ target_string.string_index.index_));
+ }
+ for (const auto& entry : boot_image_type_patches_) {
+ const TypeReference& target_type = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+ target_type.dex_file,
+ target_type.type_index.index_));
+ }
+ for (const auto& entry : boot_image_address_patches_) {
+ DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeStringPatch(
+ const DexFile& dex_file, uint32_t string_index) {
+ return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeTypePatch(
+ const DexFile& dex_file, dex::TypeIndex type_index) {
+ return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeDexCacheArrayPatch(
+ const DexFile& dex_file, uint32_t element_offset) {
+ return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativePatch(
+ const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
+ patches->emplace_back(dex_file, offset_or_index);
+ return &patches->back();
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
+ return map->GetOrCreate(
+ value,
+ [this, value]() { return __ NewLiteral<uint32_t>(value); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateUint64Literal(uint64_t value) {
+ return uint64_literals_.GetOrCreate(
+ value,
+ [this, value]() { return __ NewLiteral<uint64_t>(value); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateMethodLiteral(MethodReference target_method,
+ MethodToLiteralMap* map) {
+ return map->GetOrCreate(
+ target_method,
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index) {
+ return boot_image_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+ dex::TypeIndex type_index) {
+ return boot_image_type_patches_.GetOrCreate(
+ TypeReference(&dex_file, type_index),
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageAddressLiteral(uint64_t address) {
+ bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+ Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+ return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info,
+ GpuRegister out) {
+ __ Bind(&info->pc_rel_label);
+ // Add the high half of a 32-bit offset to PC.
+ __ Auipc(out, /* placeholder */ 0x1234);
+ // The immediately following instruction will add the sign-extended low half of the 32-bit
+ // offset to `out` (e.g. ld, jialc, daddiu).
+}
+
void CodeGeneratorMIPS64::SetupBlockedRegisters() const {
// ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated.
blocked_core_registers_[ZERO] = true;
@@ -901,8 +1056,6 @@
// Reserve T9 for function calls
blocked_core_registers_[T9] = true;
- // TODO: review; anything else?
-
if (GetGraph()->IsDebuggable()) {
// Stubs do not save callee-save floating point registers. If the graph
// is debuggable, we need to deal with these registers differently. For
@@ -946,7 +1099,6 @@
uint32_t dex_pc,
SlowPathCode* slow_path) {
ValidateInvokeRuntime(entrypoint, instruction, slow_path);
- // TODO: anything related to T9/GP/GOT/PIC/.so's?
__ LoadFromOffset(kLoadDoubleword,
T9,
TR,
@@ -1777,9 +1929,6 @@
Primitive::Type type = instruction->InputAt(0)->GetType();
LocationSummary* locations = instruction->GetLocations();
- GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
- Mips64Label true_label;
-
switch (type) {
default:
// Integer case.
@@ -1788,29 +1937,11 @@
case Primitive::kPrimLong:
GenerateIntLongCompare(instruction->GetCondition(), /* is64bit */ true, locations);
return;
-
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- // TODO: don't use branches.
- GenerateFpCompareAndBranch(instruction->GetCondition(),
- instruction->IsGtBias(),
- type,
- locations,
- &true_label);
- break;
+ GenerateFpCompare(instruction->GetCondition(), instruction->IsGtBias(), type, locations);
+ return;
}
-
- // Convert the branches into the result.
- Mips64Label done;
-
- // False case: result = 0.
- __ LoadConst32(dst, 0);
- __ Bc(&done);
-
- // True case: result = 1.
- __ Bind(&true_label);
- __ LoadConst32(dst, 1);
- __ Bind(&done);
}
void InstructionCodeGeneratorMIPS64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
@@ -2225,19 +2356,40 @@
switch (cond) {
case kCondEQ:
case kCondNE:
- if (use_imm && IsUint<16>(rhs_imm)) {
- __ Xori(dst, lhs, rhs_imm);
- } else {
- if (use_imm) {
- rhs_reg = TMP;
- __ LoadConst64(rhs_reg, rhs_imm);
+ if (use_imm && IsInt<16>(-rhs_imm)) {
+ if (rhs_imm == 0) {
+ if (cond == kCondEQ) {
+ __ Sltiu(dst, lhs, 1);
+ } else {
+ __ Sltu(dst, ZERO, lhs);
+ }
+ } else {
+ if (is64bit) {
+ __ Daddiu(dst, lhs, -rhs_imm);
+ } else {
+ __ Addiu(dst, lhs, -rhs_imm);
+ }
+ if (cond == kCondEQ) {
+ __ Sltiu(dst, dst, 1);
+ } else {
+ __ Sltu(dst, ZERO, dst);
+ }
}
- __ Xor(dst, lhs, rhs_reg);
- }
- if (cond == kCondEQ) {
- __ Sltiu(dst, dst, 1);
} else {
- __ Sltu(dst, ZERO, dst);
+ if (use_imm && IsUint<16>(rhs_imm)) {
+ __ Xori(dst, lhs, rhs_imm);
+ } else {
+ if (use_imm) {
+ rhs_reg = TMP;
+ __ LoadConst64(rhs_reg, rhs_imm);
+ }
+ __ Xor(dst, lhs, rhs_reg);
+ }
+ if (cond == kCondEQ) {
+ __ Sltiu(dst, dst, 1);
+ } else {
+ __ Sltu(dst, ZERO, dst);
+ }
}
break;
@@ -2424,6 +2576,121 @@
}
}
+void InstructionCodeGeneratorMIPS64::GenerateFpCompare(IfCondition cond,
+ bool gt_bias,
+ Primitive::Type type,
+ LocationSummary* locations) {
+ GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
+ FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
+ FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
+ if (type == Primitive::kPrimFloat) {
+ switch (cond) {
+ case kCondEQ:
+ __ CmpEqS(FTMP, lhs, rhs);
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondNE:
+ __ CmpEqS(FTMP, lhs, rhs);
+ __ Mfc1(dst, FTMP);
+ __ Addiu(dst, dst, 1);
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ CmpLtS(FTMP, lhs, rhs);
+ } else {
+ __ CmpUltS(FTMP, lhs, rhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ CmpLeS(FTMP, lhs, rhs);
+ } else {
+ __ CmpUleS(FTMP, lhs, rhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ CmpUltS(FTMP, rhs, lhs);
+ } else {
+ __ CmpLtS(FTMP, rhs, lhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ CmpUleS(FTMP, rhs, lhs);
+ } else {
+ __ CmpLeS(FTMP, rhs, lhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+ UNREACHABLE();
+ }
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimDouble);
+ switch (cond) {
+ case kCondEQ:
+ __ CmpEqD(FTMP, lhs, rhs);
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondNE:
+ __ CmpEqD(FTMP, lhs, rhs);
+ __ Mfc1(dst, FTMP);
+ __ Addiu(dst, dst, 1);
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ CmpLtD(FTMP, lhs, rhs);
+ } else {
+ __ CmpUltD(FTMP, lhs, rhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ CmpLeD(FTMP, lhs, rhs);
+ } else {
+ __ CmpUleD(FTMP, lhs, rhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ CmpUltD(FTMP, rhs, lhs);
+ } else {
+ __ CmpLtD(FTMP, rhs, lhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ CmpUleD(FTMP, rhs, lhs);
+ } else {
+ __ CmpLeD(FTMP, rhs, lhs);
+ }
+ __ Mfc1(dst, FTMP);
+ __ Andi(dst, dst, 1);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+ UNREACHABLE();
+ }
+ }
+}
+
void InstructionCodeGeneratorMIPS64::GenerateFpCompareAndBranch(IfCondition cond,
bool gt_bias,
Primitive::Type type,
@@ -2636,14 +2903,17 @@
/* false_target */ nullptr);
}
-void LocationsBuilderMIPS64::VisitShouldDeoptimizeFlag(
- HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
- // TODO: to be implemented.
+void LocationsBuilderMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ LocationSummary* locations = new (GetGraph()->GetArena())
+ LocationSummary(flag, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
}
-void InstructionCodeGeneratorMIPS64::VisitShouldDeoptimizeFlag(
- HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) {
- // TODO: to be implemented.
+void InstructionCodeGeneratorMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ __ LoadFromOffset(kLoadWord,
+ flag->GetLocations()->Out().AsRegister<GpuRegister>(),
+ SP,
+ codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
}
void LocationsBuilderMIPS64::VisitSelect(HSelect* select) {
@@ -2820,6 +3090,31 @@
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad(
+ HInstruction* instruction ATTRIBUTE_UNUSED,
+ Location root,
+ GpuRegister obj,
+ uint32_t offset) {
+ // When handling HLoadClass::LoadKind::kDexCachePcRelative, the caller calls
+ // EmitPcRelativeAddressPlaceholderHigh() and then GenerateGcRootFieldLoad().
+ // The relative patcher expects the two methods to emit the following patchable
+ // sequence of instructions in this case:
+ // auipc reg1, 0x1234 // 0x1234 is a placeholder for offset_high.
+ // lwu reg2, 0x5678(reg1) // 0x5678 is a placeholder for offset_low.
+ // TODO: Adjust GenerateGcRootFieldLoad() and its caller when this method is
+ // extended (e.g. for read barriers) so as not to break the relative patcher.
+ GpuRegister root_reg = root.AsRegister<GpuRegister>();
+ if (kEmitCompilerReadBarrier) {
+ UNIMPLEMENTED(FATAL) << "for read barrier";
+ } else {
+ // Plain GC root load with no read barrier.
+ // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+ __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset);
+ // Note that GC roots are not affected by heap poisoning, thus we
+ // do not have to unpoison `root_reg` here.
+ }
+}
+
void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary::CallKind call_kind =
instruction->IsExactCheck() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath;
@@ -2971,54 +3266,85 @@
}
HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind(
- HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
- // TODO: Implement other kinds.
- return HLoadString::LoadKind::kDexCacheViaMethod;
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ UNIMPLEMENTED(FATAL) << "for read barrier";
+ }
+ bool fallback_load = false;
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kBssEntry:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ case HLoadString::LoadKind::kJitTableAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ // TODO: implement.
+ fallback_load = true;
+ break;
+ }
+ if (fallback_load) {
+ desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod;
+ }
+ return desired_string_load_kind;
}
HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind(
HLoadClass::LoadKind desired_class_load_kind) {
- DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass);
- // TODO: Implement other kinds.
- return HLoadClass::LoadKind::kDexCacheViaMethod;
+ if (kEmitCompilerReadBarrier) {
+ UNIMPLEMENTED(FATAL) << "for read barrier";
+ }
+ bool fallback_load = false;
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ break;
+ case HLoadClass::LoadKind::kJitTableAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ // TODO: implement.
+ fallback_load = true;
+ break;
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ if (fallback_load) {
+ desired_class_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+ }
+ return desired_class_load_kind;
}
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
- switch (desired_dispatch_info.method_load_kind) {
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement these types. For the moment, we fall back to kDexCacheViaMethod.
- return HInvokeStaticOrDirect::DispatchInfo {
- HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0u
- };
- default:
- break;
- }
- switch (desired_dispatch_info.code_ptr_location) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement these types. For the moment, we fall back to kCallArtMethod.
- return HInvokeStaticOrDirect::DispatchInfo {
- desired_dispatch_info.method_load_kind,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- desired_dispatch_info.method_load_data,
- 0u
- };
- default:
- return desired_dispatch_info;
- }
+ // On MIPS64 we support all dispatch types.
+ return desired_dispatch_info;
}
void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// All registers are assumed to be correctly set up per the calling convention.
-
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
- switch (invoke->GetMethodLoadKind()) {
+ HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
+ HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
+
+ switch (method_load_kind) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
// temp = thread->string_init_entrypoint
uint32_t offset =
@@ -3033,14 +3359,18 @@
callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
- __ LoadConst64(temp.AsRegister<GpuRegister>(), invoke->GetMethodAddress());
+ __ LoadLiteral(temp.AsRegister<GpuRegister>(),
+ kLoadDoubleword,
+ DeduplicateUint64Literal(invoke->GetMethodAddress()));
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement these types.
- // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
- LOG(FATAL) << "Unsupported";
- UNREACHABLE();
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
+ uint32_t offset = invoke->GetDexCacheArrayOffset();
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset);
+ EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
+ break;
+ }
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
GpuRegister reg = temp.AsRegister<GpuRegister>();
@@ -3071,23 +3401,10 @@
}
}
- switch (invoke->GetCodePtrLocation()) {
+ switch (code_ptr_location) {
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
- __ Jialc(&frame_entry_label_, T9);
+ __ Balc(&frame_entry_label_);
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR = invoke->GetDirectCodePtr();
- __ LoadConst64(T9, invoke->GetDirectCodePtr());
- // LR()
- __ Jalr(T9);
- __ Nop();
- break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement these types.
- // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
- LOG(FATAL) << "Unsupported";
- UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// T9 = callee_method->entry_point_from_quick_compiled_code_;
__ LoadFromOffset(kLoadDoubleword,
@@ -3157,11 +3474,26 @@
}
void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) {
- InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassLocationSummary(
- cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
- calling_convention.GetReturnLocation(cls->GetType()));
+ if (cls->NeedsAccessCheck()) {
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ calling_convention.GetReturnLocation(Primitive::kPrimNot),
+ /* code_generator_supports_read_barrier */ false);
+ return;
+ }
+
+ LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
@@ -3173,35 +3505,90 @@
return;
}
- GpuRegister out = locations->Out().AsRegister<GpuRegister>();
- GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
- if (cls->IsReferrersClass()) {
- DCHECK(!cls->CanCallRuntime());
- DCHECK(!cls->MustGenerateClinitCheck());
- __ LoadFromOffset(kLoadUnsignedWord, out, current_method,
- ArtMethod::DeclaringClassOffset().Int32Value());
- } else {
- __ LoadFromOffset(kLoadDoubleword, out, current_method,
- ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
- __ LoadFromOffset(
- kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_));
- // TODO: We will need a read barrier here.
- if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
- DCHECK(cls->CanCallRuntime());
- SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
- cls,
- cls,
- cls->GetDexPc(),
- cls->MustGenerateClinitCheck());
- codegen_->AddSlowPath(slow_path);
- if (!cls->IsInDexCache()) {
- __ Beqzc(out, slow_path->GetEntryLabel());
- }
- if (cls->MustGenerateClinitCheck()) {
- GenerateClassInitializationCheck(slow_path, out);
- } else {
- __ Bind(slow_path->GetExitLabel());
- }
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ Location out_loc = locations->Out();
+ GpuRegister out = out_loc.AsRegister<GpuRegister>();
+ GpuRegister current_method_reg = ZERO;
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+ current_method_reg = locations->InAt(0).AsRegister<GpuRegister>();
+ }
+
+ bool generate_null_check = false;
+ switch (load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ DCHECK(!cls->CanCallRuntime());
+ DCHECK(!cls->MustGenerateClinitCheck());
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(cls,
+ out_loc,
+ current_method_reg,
+ ArtMethod::DeclaringClassOffset().Int32Value());
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+ cls->GetTypeIndex()));
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Daddiu(out, AT, /* placeholder */ 0x5678);
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageAddressLiteral(address));
+ break;
+ }
+ case HLoadClass::LoadKind::kJitTableAddress: {
+ LOG(FATAL) << "Unimplemented";
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ uint32_t element_offset = cls->GetDexCacheElementOffset();
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), element_offset);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */
+ GenerateGcRootFieldLoad(cls, out_loc, AT, /* placeholder */ 0x5678);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheViaMethod: {
+ // /* GcRoot<mirror::Class>[] */ out =
+ // current_method.ptr_sized_fields_->dex_cache_resolved_types_
+ __ LoadFromOffset(kLoadDoubleword,
+ out,
+ current_method_reg,
+ ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
+ GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+ generate_null_check = !cls->IsInDexCache();
+ }
+ }
+
+ if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ DCHECK(cls->CanCallRuntime());
+ SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ codegen_->AddSlowPath(slow_path);
+ if (generate_null_check) {
+ __ Beqzc(out, slow_path->GetEntryLabel());
+ }
+ if (cls->MustGenerateClinitCheck()) {
+ GenerateClassInitializationCheck(slow_path, out);
+ } else {
+ __ Bind(slow_path->GetExitLabel());
}
}
}
@@ -3230,20 +3617,68 @@
}
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->NeedsEnvironment()
- ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetOut(calling_convention.GetReturnLocation(load->GetType()));
+ } else {
+ locations->SetOut(Location::RequiresRegister());
+ }
}
void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ LocationSummary* locations = load->GetLocations();
+ Location out_loc = locations->Out();
+ GpuRegister out = out_loc.AsRegister<GpuRegister>();
+
+ switch (load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ return; // No dex cache slow path.
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(codegen_->GetCompilerOptions().IsBootImage());
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Daddiu(out, AT, /* placeholder */ 0x5678);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageAddressLiteral(address));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBssEntry: {
+ DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Lwu(out, AT, /* placeholder */ 0x5678);
+ SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
+ codegen_->AddSlowPath(slow_path);
+ __ Beqzc(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ return;
+ }
+ default:
+ break;
+ }
+
// TODO: Re-add the compiler code to do string dex cache lookup again.
- SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
- codegen_->AddSlowPath(slow_path);
- __ Bc(slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+ DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
+ InvokeRuntimeCallingConvention calling_convention;
+ __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
+ codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+ CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
}
void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) {
@@ -3840,9 +4275,12 @@
break;
case Primitive::kPrimInt:
case Primitive::kPrimLong:
- // Sign-extend 32-bit int into bits 32 through 63 for
- // int-to-long and long-to-int conversions
- __ Sll(dst, src, 0);
+ // Sign-extend 32-bit int into bits 32 through 63 for int-to-long and long-to-int
+ // conversions, except when the input and output registers are the same and we are not
+ // converting longs to shorter types. In these cases, do nothing.
+ if ((input_type == Primitive::kPrimLong) || (dst != src)) {
+ __ Sll(dst, src, 0);
+ }
break;
default:
@@ -4079,27 +4517,20 @@
locations->SetInAt(0, Location::RequiresRegister());
}
-void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
- int32_t lower_bound = switch_instr->GetStartValue();
- int32_t num_entries = switch_instr->GetNumEntries();
- LocationSummary* locations = switch_instr->GetLocations();
- GpuRegister value_reg = locations->InAt(0).AsRegister<GpuRegister>();
- HBasicBlock* default_block = switch_instr->GetDefaultBlock();
-
+void InstructionCodeGeneratorMIPS64::GenPackedSwitchWithCompares(GpuRegister value_reg,
+ int32_t lower_bound,
+ uint32_t num_entries,
+ HBasicBlock* switch_block,
+ HBasicBlock* default_block) {
// Create a set of compare/jumps.
GpuRegister temp_reg = TMP;
- if (IsInt<16>(-lower_bound)) {
- __ Addiu(temp_reg, value_reg, -lower_bound);
- } else {
- __ LoadConst32(AT, -lower_bound);
- __ Addu(temp_reg, value_reg, AT);
- }
+ __ Addiu32(temp_reg, value_reg, -lower_bound);
// Jump to default if index is negative
// Note: We don't check the case that index is positive while value < lower_bound, because in
// this case, index >= num_entries must be true. So that we can save one branch instruction.
__ Bltzc(temp_reg, codegen_->GetLabelOf(default_block));
- const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
+ const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
// Jump to successors[0] if value == lower_bound.
__ Beqzc(temp_reg, codegen_->GetLabelOf(successors[0]));
int32_t last_index = 0;
@@ -4117,11 +4548,66 @@
}
// And the default for any other value.
- if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
+ if (!codegen_->GoesToNextBlock(switch_block, default_block)) {
__ Bc(codegen_->GetLabelOf(default_block));
}
}
+void InstructionCodeGeneratorMIPS64::GenTableBasedPackedSwitch(GpuRegister value_reg,
+ int32_t lower_bound,
+ uint32_t num_entries,
+ HBasicBlock* switch_block,
+ HBasicBlock* default_block) {
+ // Create a jump table.
+ std::vector<Mips64Label*> labels(num_entries);
+ const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
+ for (uint32_t i = 0; i < num_entries; i++) {
+ labels[i] = codegen_->GetLabelOf(successors[i]);
+ }
+ JumpTable* table = __ CreateJumpTable(std::move(labels));
+
+ // Is the value in range?
+ __ Addiu32(TMP, value_reg, -lower_bound);
+ __ LoadConst32(AT, num_entries);
+ __ Bgeuc(TMP, AT, codegen_->GetLabelOf(default_block));
+
+ // We are in the range of the table.
+ // Load the target address from the jump table, indexing by the value.
+ __ LoadLabelAddress(AT, table->GetLabel());
+ __ Sll(TMP, TMP, 2);
+ __ Daddu(TMP, TMP, AT);
+ __ Lw(TMP, TMP, 0);
+ // Compute the absolute target address by adding the table start address
+ // (the table contains offsets to targets relative to its start).
+ __ Daddu(TMP, TMP, AT);
+ // And jump.
+ __ Jr(TMP);
+ __ Nop();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
+ int32_t lower_bound = switch_instr->GetStartValue();
+ uint32_t num_entries = switch_instr->GetNumEntries();
+ LocationSummary* locations = switch_instr->GetLocations();
+ GpuRegister value_reg = locations->InAt(0).AsRegister<GpuRegister>();
+ HBasicBlock* switch_block = switch_instr->GetBlock();
+ HBasicBlock* default_block = switch_instr->GetDefaultBlock();
+
+ if (num_entries > kPackedSwitchJumpTableThreshold) {
+ GenTableBasedPackedSwitch(value_reg,
+ lower_bound,
+ num_entries,
+ switch_block,
+ default_block);
+ } else {
+ GenPackedSwitchWithCompares(value_reg,
+ lower_bound,
+ num_entries,
+ switch_block,
+ default_block);
+ }
+}
+
void LocationsBuilderMIPS64::VisitClassTableGet(HClassTableGet*) {
UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64";
}
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 690eccb..8ac919f 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -22,6 +22,7 @@
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/mips64/assembler_mips64.h"
+#include "utils/type_reference.h"
namespace art {
namespace mips64 {
@@ -216,6 +217,14 @@
Mips64Assembler* GetAssembler() const { return assembler_; }
+ // Compare-and-jump packed switch generates approx. 3 + 2.5 * N 32-bit
+ // instructions for N cases.
+ // Table-based packed switch generates approx. 11 32-bit instructions
+ // and N 32-bit data words for N cases.
+ // At N = 6 they come out as 18 and 17 32-bit words respectively.
+ // We switch to the table-based method starting with 7 cases.
+ static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6;
+
private:
void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg);
void GenerateMemoryBarrier(MemBarrierKind kind);
@@ -227,6 +236,15 @@
const FieldInfo& field_info,
bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ // Generate a GC root reference load:
+ //
+ // root <- *(obj + offset)
+ //
+ // while honoring read barriers (if any).
+ void GenerateGcRootFieldLoad(HInstruction* instruction,
+ Location root,
+ GpuRegister obj,
+ uint32_t offset);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
Mips64Label* true_target,
@@ -240,12 +258,26 @@
bool is64bit,
LocationSummary* locations,
Mips64Label* label);
+ void GenerateFpCompare(IfCondition cond,
+ bool gt_bias,
+ Primitive::Type type,
+ LocationSummary* locations);
void GenerateFpCompareAndBranch(IfCondition cond,
bool gt_bias,
Primitive::Type type,
LocationSummary* locations,
Mips64Label* label);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
+ void GenPackedSwitchWithCompares(GpuRegister value_reg,
+ int32_t lower_bound,
+ uint32_t num_entries,
+ HBasicBlock* switch_block,
+ HBasicBlock* default_block);
+ void GenTableBasedPackedSwitch(GpuRegister value_reg,
+ int32_t lower_bound,
+ uint32_t num_entries,
+ HBasicBlock* switch_block,
+ HBasicBlock* default_block);
Mips64Assembler* const assembler_;
CodeGeneratorMIPS64* const codegen_;
@@ -279,6 +311,9 @@
Mips64Assembler* GetAssembler() OVERRIDE { return &assembler_; }
const Mips64Assembler& GetAssembler() const OVERRIDE { return assembler_; }
+ // Emit linker patches.
+ void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
+
void MarkGCCard(GpuRegister object, GpuRegister value, bool value_can_be_null);
// Register allocation.
@@ -307,6 +342,10 @@
block_labels_ = CommonInitializeLabels<Mips64Label>();
}
+ // We prefer aligned loads and stores (less code), so spill and restore registers in slow paths
+ // at aligned locations.
+ uint32_t GetPreferredSlotsAlignment() const OVERRIDE { return kMips64DoublewordSize; }
+
void Finalize(CodeAllocator* allocator) OVERRIDE;
// Code generation helpers.
@@ -357,7 +396,57 @@
void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE;
void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE;
+ // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays,
+ // boot image strings and method calls. The only difference is the interpretation of
+ // the offset_or_index.
+ struct PcRelativePatchInfo {
+ PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
+ : target_dex_file(dex_file), offset_or_index(off_or_idx) { }
+ PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
+
+ const DexFile& target_dex_file;
+ // Either the dex cache array element offset or the string/type/method index.
+ uint32_t offset_or_index;
+ // Label for the auipc instruction.
+ Mips64Label pc_rel_label;
+ };
+
+ PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+ PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
+ PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset);
+ PcRelativePatchInfo* NewPcRelativeCallPatch(const DexFile& dex_file,
+ uint32_t method_index);
+ Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index);
+ Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index);
+ Literal* DeduplicateBootImageAddressLiteral(uint64_t address);
+
+ void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, GpuRegister out);
+
private:
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
+ using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, Literal*>;
+ using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
+ using BootStringToLiteralMap = ArenaSafeMap<StringReference,
+ Literal*,
+ StringReferenceValueComparator>;
+ using BootTypeToLiteralMap = ArenaSafeMap<TypeReference,
+ Literal*,
+ TypeReferenceValueComparator>;
+
+ Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
+ Literal* DeduplicateUint64Literal(uint64_t value);
+ Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
+
+ PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
+ uint32_t offset_or_index,
+ ArenaDeque<PcRelativePatchInfo>* patches);
+
+ template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+ void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos,
+ ArenaVector<LinkerPatch>* linker_patches);
+
// Labels for each block that will be compiled.
Mips64Label* block_labels_; // Indexed by block id.
Mips64Label frame_entry_label_;
@@ -367,6 +456,24 @@
Mips64Assembler assembler_;
const Mips64InstructionSetFeatures& isa_features_;
+ // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+ Uint32ToLiteralMap uint32_literals_;
+ // Deduplication map for 64-bit literals, used for non-patchable method address or method code
+ // address.
+ Uint64ToLiteralMap uint64_literals_;
+ // PC-relative patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+ // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+ BootStringToLiteralMap boot_image_string_patches_;
+ // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
+ ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+ BootTypeToLiteralMap boot_image_type_patches_;
+ // PC-relative type patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+ // Deduplication map for patchable boot image addresses.
+ Uint32ToLiteralMap boot_image_address_patches_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS64);
};
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8612a67..0abe855 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1006,8 +1006,6 @@
move_resolver_(graph->GetArena(), this),
assembler_(graph->GetArena()),
isa_features_(isa_features),
- method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
@@ -4454,20 +4452,7 @@
HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
}
- switch (dispatch_info.code_ptr_location) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
- // (Though the direct CALL ptr16:32 is available for consideration).
- return HInvokeStaticOrDirect::DispatchInfo {
- dispatch_info.method_load_kind,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- dispatch_info.method_load_data,
- 0u
- };
- default:
- return dispatch_info;
- }
+ return dispatch_info;
}
Register CodeGeneratorX86::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
@@ -4514,12 +4499,6 @@
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
__ movl(temp.AsRegister<Register>(), Immediate(invoke->GetMethodAddress()));
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- __ movl(temp.AsRegister<Register>(), Immediate(/* placeholder */ 0));
- method_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetTargetMethod().dex_method_index);
- __ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn.
- break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke,
temp.AsRegister<Register>());
@@ -4561,19 +4540,6 @@
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
__ call(GetFrameEntryLabel());
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: {
- relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetTargetMethod().dex_method_index);
- Label* label = &relative_call_patches_.back().label;
- __ call(label); // Bind to the patch label, override at link time.
- __ Bind(label); // Bind the label at the end of the "call" insn.
- break;
- }
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // Filtered out by GetSupportedInvokeStaticOrDirectDispatch().
- LOG(FATAL) << "Unsupported";
- UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// (callee_method + offset_of_quick_compiled_code)()
__ call(Address(callee_method.AsRegister<Register>(),
@@ -4664,22 +4630,11 @@
void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
- method_patches_.size() +
- relative_call_patches_.size() +
pc_relative_dex_cache_patches_.size() +
simple_patches_.size() +
string_patches_.size() +
type_patches_.size();
linker_patches->reserve(size);
- for (const PatchInfo<Label>& info : method_patches_) {
- uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
- linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, &info.dex_file, info.index));
- }
- for (const PatchInfo<Label>& info : relative_call_patches_) {
- uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
- linker_patches->push_back(
- LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
- }
EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
linker_patches);
for (const Label& label : simple_patches_) {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index c44da97..1af6850 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -613,9 +613,6 @@
X86Assembler assembler_;
const X86InstructionSetFeatures& isa_features_;
- // Method patch info. Using ArenaDeque<> which retains element addresses on push/emplace_back().
- ArenaDeque<PatchInfo<Label>> method_patches_;
- ArenaDeque<PatchInfo<Label>> relative_call_patches_;
// PC-relative DexCache access info.
ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_;
// Patch locations for patchoat where the linker doesn't do any other work.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 7dfc736..903844f 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -960,19 +960,7 @@
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86_64::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
- switch (desired_dispatch_info.code_ptr_location) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
- return HInvokeStaticOrDirect::DispatchInfo {
- desired_dispatch_info.method_load_kind,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- desired_dispatch_info.method_load_data,
- 0u
- };
- default:
- return desired_dispatch_info;
- }
+ return desired_dispatch_info;
}
Location CodeGeneratorX86_64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
@@ -993,12 +981,6 @@
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
__ movq(temp.AsRegister<CpuRegister>(), Immediate(invoke->GetMethodAddress()));
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- __ movl(temp.AsRegister<CpuRegister>(), Immediate(0)); // Placeholder.
- method_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetTargetMethod().dex_method_index);
- __ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn.
- break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
__ movq(temp.AsRegister<CpuRegister>(),
Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
@@ -1042,19 +1024,6 @@
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
__ call(&frame_entry_label_);
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: {
- relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetTargetMethod().dex_method_index);
- Label* label = &relative_call_patches_.back().label;
- __ call(label); // Bind to the patch label, override at link time.
- __ Bind(label); // Bind the label at the end of the "call" insn.
- break;
- }
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // Filtered out by GetSupportedInvokeStaticOrDirectDispatch().
- LOG(FATAL) << "Unsupported";
- UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// (callee_method + offset_of_quick_compiled_code)()
__ call(Address(callee_method.AsRegister<CpuRegister>(),
@@ -1146,22 +1115,11 @@
void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
- method_patches_.size() +
- relative_call_patches_.size() +
pc_relative_dex_cache_patches_.size() +
simple_patches_.size() +
string_patches_.size() +
type_patches_.size();
linker_patches->reserve(size);
- for (const PatchInfo<Label>& info : method_patches_) {
- uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
- linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, &info.dex_file, info.index));
- }
- for (const PatchInfo<Label>& info : relative_call_patches_) {
- uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
- linker_patches->push_back(
- LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
- }
EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
linker_patches);
for (const Label& label : simple_patches_) {
@@ -1253,8 +1211,6 @@
assembler_(graph->GetArena()),
isa_features_(isa_features),
constant_area_start_(0),
- method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 391a23b..f827e79 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -596,9 +596,6 @@
// Used for fixups to the constant area.
int constant_area_start_;
- // Method patch info. Using ArenaDeque<> which retains element addresses on push/emplace_back().
- ArenaDeque<PatchInfo<Label>> method_patches_;
- ArenaDeque<PatchInfo<Label>> relative_call_patches_;
// PC-relative DexCache access info.
ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_;
// Patch locations for patchoat where the linker doesn't do any other work.
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index eabdbad..21c3ae6 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -122,10 +122,16 @@
if (type == Primitive::kPrimFloat) {
return InputSRegisterAt(instr, input_index);
} else {
+ DCHECK_EQ(type, Primitive::kPrimDouble);
return InputDRegisterAt(instr, input_index);
}
}
+inline vixl::aarch32::VRegister InputVRegister(HInstruction* instr) {
+ DCHECK_EQ(instr->InputCount(), 1u);
+ return InputVRegisterAt(instr, 0);
+}
+
inline vixl::aarch32::Register OutputRegister(HInstruction* instr) {
return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
}
@@ -140,8 +146,7 @@
return InputRegisterAt(instr, 0);
}
-inline int32_t Int32ConstantFrom(Location location) {
- HConstant* instr = location.GetConstant();
+inline int32_t Int32ConstantFrom(HInstruction* instr) {
if (instr->IsIntConstant()) {
return instr->AsIntConstant()->GetValue();
} else if (instr->IsNullConstant()) {
@@ -155,6 +160,10 @@
}
}
+inline int32_t Int32ConstantFrom(Location location) {
+ return Int32ConstantFrom(location.GetConstant());
+}
+
inline int64_t Int64ConstantFrom(Location location) {
HConstant* instr = location.GetConstant();
if (instr->IsIntConstant()) {
@@ -167,6 +176,11 @@
}
}
+inline uint64_t Uint64ConstantFrom(HInstruction* instr) {
+ DCHECK(instr->IsConstant()) << instr->DebugName();
+ return instr->AsConstant()->GetValueAsUint64();
+}
+
inline vixl::aarch32::Operand OperandFrom(Location location, Primitive::Type type) {
if (location.IsRegister()) {
return vixl::aarch32::Operand(RegisterFrom(location, type));
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 188ee3a..34b52a8 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -20,12 +20,15 @@
#include <string>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/arena_containers.h"
#include "base/bit_vector-inl.h"
-#include "base/stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
static bool IsAllowedToJumpToExitBlock(HInstruction* instruction) {
return instruction->IsThrow() || instruction->IsReturn() || instruction->IsReturnVoid();
}
diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc
index d530564..28ee3a5 100644
--- a/compiler/optimizing/graph_test.cc
+++ b/compiler/optimizing/graph_test.cc
@@ -15,7 +15,6 @@
*/
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index c240c67..88473f02 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -73,10 +73,18 @@
}
/**
- * Returns narrowest data type.
+ * Returns result of implicit widening type conversion done in HIR.
*/
-static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) {
- return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2;
+static Primitive::Type ImplicitConversion(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimBoolean:
+ return Primitive::kPrimInt;
+ default:
+ return type;
+ }
}
//
@@ -211,7 +219,7 @@
void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction) {
InductionInfo* info = nullptr;
if (instruction->IsPhi()) {
- info = TransferPhi(loop, instruction, /* input_index */ 0);
+ info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 0);
} else if (instruction->IsAdd()) {
info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)), kAdd);
@@ -224,15 +232,17 @@
info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)));
} else if (instruction->IsShl()) {
- HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
if (mulc != nullptr) {
info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, mulc));
}
+ } else if (instruction->IsSelect()) {
+ info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);
} else if (instruction->IsTypeConversion()) {
- info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
- instruction->AsTypeConversion()->GetInputType(),
- instruction->AsTypeConversion()->GetResultType());
+ info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)),
+ instruction->AsTypeConversion()->GetInputType(),
+ instruction->AsTypeConversion()->GetResultType());
} else if (instruction->IsBoundsCheck()) {
info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through.
}
@@ -265,12 +275,16 @@
return;
}
- // Store interesting cycle.
- AssignCycle(phi->AsPhi());
+ // Store interesting cycle in each loop phi.
+ for (size_t i = 0; i < size; i++) {
+ if (scc_[i]->IsLoopHeaderPhi()) {
+ AssignCycle(scc_[i]->AsPhi());
+ }
+ }
// Singleton is wrap-around induction if all internal links have the same meaning.
if (size == 1) {
- InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
+ InductionInfo* update = TransferPhi(loop, phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
if (update != nullptr) {
AssignInfo(loop, phi, CreateInduction(kWrapAround,
kNop,
@@ -305,10 +319,15 @@
update = SolveOp(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kRem);
} else if (instruction->IsShl()) {
- HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
if (mulc != nullptr) {
update = SolveOp(loop, phi, instruction, instruction->InputAt(0), mulc, kMul);
}
+ } else if (instruction->IsShr() || instruction->IsUShr()) {
+ HInstruction* divc = GetShiftConstant(loop, instruction, initial);
+ if (divc != nullptr) {
+ update = SolveOp(loop, phi, instruction, instruction->InputAt(0), divc, kDiv);
+ }
} else if (instruction->IsXor()) {
update = SolveOp(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kXor);
@@ -316,8 +335,10 @@
update = SolveTest(loop, phi, instruction, 0);
} else if (instruction->IsNotEqual()) {
update = SolveTest(loop, phi, instruction, 1);
+ } else if (instruction->IsSelect()) {
+ update = SolvePhi(instruction, /*input_index*/ 0, /*adjust_input_size*/ 1); // acts like Phi
} else if (instruction->IsTypeConversion()) {
- update = SolveCnv(instruction->AsTypeConversion());
+ update = SolveConversion(loop, phi, instruction->AsTypeConversion());
}
if (update == nullptr) {
return;
@@ -326,7 +347,7 @@
}
// Success if all internal links received the same temporary meaning.
- InductionInfo* induction = SolvePhi(phi, /* input_index */ 1);
+ InductionInfo* induction = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
if (induction != nullptr) {
switch (induction->induction_class) {
case kInvariant:
@@ -385,12 +406,13 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
HInstruction* phi,
- size_t input_index) {
+ size_t input_index,
+ size_t adjust_input_size) {
// Match all phi inputs from input_index onwards exactly.
HInputsRef inputs = phi->GetInputs();
DCHECK_LT(input_index, inputs.size());
InductionInfo* a = LookupInfo(loop, inputs[input_index]);
- for (size_t i = input_index + 1; i < inputs.size(); i++) {
+ for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
InductionInfo* b = LookupInfo(loop, inputs[i]);
if (!InductionEqual(a, b)) {
return nullptr;
@@ -406,18 +428,20 @@
// wrap-around, or periodic can be combined with an invariant to yield a similar result.
// Two linear or two polynomial inputs can be combined too. Other combinations fail.
if (a != nullptr && b != nullptr) {
- type_ = Narrowest(type_, Narrowest(a->type, b->type));
- if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
- return CreateInvariantOp(op, a, b);
+ if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+ return nullptr; // no transfer
+ } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+ return CreateInvariantOp(op, a, b); // direct invariant
} else if ((a->induction_class == kLinear && b->induction_class == kLinear) ||
(a->induction_class == kPolynomial && b->induction_class == kPolynomial)) {
- return CreateInduction(a->induction_class,
- a->operation,
- TransferAddSub(a->op_a, b->op_a, op),
- TransferAddSub(a->op_b, b->op_b, op),
- /*fetch*/ nullptr,
- type_);
+ // Rule induc(a, b) + induc(a', b') -> induc(a + a', b + b').
+ InductionInfo* new_a = TransferAddSub(a->op_a, b->op_a, op);
+ InductionInfo* new_b = TransferAddSub(a->op_b, b->op_b, op);
+ if (new_a != nullptr && new_b != nullptr) {
+ return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+ }
} else if (a->induction_class == kInvariant) {
+ // Rule a + induc(a', b') -> induc(a', a + b') or induc(a + a', a + b').
InductionInfo* new_a = b->op_a;
InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) {
@@ -425,14 +449,19 @@
} else if (op == kSub) { // Negation required.
new_a = TransferNeg(new_a);
}
- return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
+ if (new_a != nullptr && new_b != nullptr) {
+ return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
+ }
} else if (b->induction_class == kInvariant) {
+ // Rule induc(a, b) + b' -> induc(a, b + b') or induc(a + b', b + b').
InductionInfo* new_a = a->op_a;
InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) {
new_a = TransferAddSub(new_a, b, op);
}
- return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+ if (new_a != nullptr && new_b != nullptr) {
+ return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+ }
}
}
return nullptr;
@@ -442,16 +471,17 @@
// Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
// wrap-around, or periodic input yields a similar but negated induction as result.
if (a != nullptr) {
- type_ = Narrowest(type_, a->type);
- if (a->induction_class == kInvariant) {
- return CreateInvariantOp(kNeg, nullptr, a);
+ if (IsNarrowingLinear(a)) {
+ return nullptr; // no transfer
+ } else if (a->induction_class == kInvariant) {
+ return CreateInvariantOp(kNeg, nullptr, a); // direct invariant
} else if (a->induction_class != kGeometric || a->operation == kMul) {
- return CreateInduction(a->induction_class,
- a->operation,
- TransferNeg(a->op_a),
- TransferNeg(a->op_b),
- a->fetch,
- type_);
+ // Rule - induc(a, b) -> induc(-a, -b).
+ InductionInfo* new_a = TransferNeg(a->op_a);
+ InductionInfo* new_b = TransferNeg(a->op_b);
+ if (new_a != nullptr && new_b != nullptr) {
+ return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+ }
}
}
return nullptr;
@@ -463,54 +493,56 @@
// wrap-around, or periodic can be multiplied with an invariant to yield a similar
// but multiplied result. Two non-invariant inputs cannot be multiplied, however.
if (a != nullptr && b != nullptr) {
- type_ = Narrowest(type_, Narrowest(a->type, b->type));
- if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
- return CreateInvariantOp(kMul, a, b);
+ if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+ return nullptr; // no transfer
+ } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+ return CreateInvariantOp(kMul, a, b); // direct invariant
} else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
b->operation == kMul)) {
- return CreateInduction(b->induction_class,
- b->operation,
- TransferMul(a, b->op_a),
- TransferMul(a, b->op_b),
- b->fetch,
- type_);
+ // Rule a * induc(a', b') -> induc(a * a', b * b').
+ InductionInfo* new_a = TransferMul(a, b->op_a);
+ InductionInfo* new_b = TransferMul(a, b->op_b);
+ if (new_a != nullptr && new_b != nullptr) {
+ return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
+ }
} else if (b->induction_class == kInvariant && (a->induction_class != kGeometric ||
a->operation == kMul)) {
- return CreateInduction(a->induction_class,
- a->operation,
- TransferMul(a->op_a, b),
- TransferMul(a->op_b, b),
- a->fetch,
- type_);
- }
- }
- return nullptr;
-}
-
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
- Primitive::Type from,
- Primitive::Type to) {
- if (a != nullptr) {
- // Allow narrowing conversion on linear induction in certain cases.
- if (IsNarrowingIntegralConversion(from, to)) {
- if (a->induction_class == kLinear) {
- if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) {
- return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
- }
+ // Rule induc(a, b) * b' -> induc(a * b', b * b').
+ InductionInfo* new_a = TransferMul(a->op_a, b);
+ InductionInfo* new_b = TransferMul(a->op_b, b);
+ if (new_a != nullptr && new_b != nullptr) {
+ return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
}
}
}
return nullptr;
}
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion(
+ InductionInfo* a,
+ Primitive::Type from,
+ Primitive::Type to) {
+ if (a != nullptr) {
+ // Allow narrowing conversion on linear induction in certain cases:
+ // induction is already at narrow type, or can be made narrower.
+ if (IsNarrowingIntegralConversion(from, to) &&
+ a->induction_class == kLinear &&
+ (a->type == to || IsNarrowingIntegralConversion(a->type, to))) {
+ return CreateInduction(kLinear, kNop, a->op_a, a->op_b, a->fetch, to);
+ }
+ }
+ return nullptr;
+}
+
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HInstruction* phi,
- size_t input_index) {
+ size_t input_index,
+ size_t adjust_input_size) {
// Match all phi inputs from input_index onwards exactly.
HInputsRef inputs = phi->GetInputs();
DCHECK_LT(input_index, inputs.size());
auto ita = cycle_.find(inputs[input_index]);
if (ita != cycle_.end()) {
- for (size_t i = input_index + 1; i < inputs.size(); i++) {
+ for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
auto itb = cycle_.find(inputs[i]);
if (itb == cycle_.end() ||
!HInductionVarAnalysis::InductionEqual(ita->second, itb->second)) {
@@ -527,7 +559,7 @@
HInstruction* entry_phi,
HInstruction* phi) {
// Match all phi inputs.
- InductionInfo* match = SolvePhi(phi, /* input_index */ 0);
+ InductionInfo* match = SolvePhi(phi, /*input_index*/ 0, /*adjust_input_size*/ 0);
if (match != nullptr) {
return match;
}
@@ -542,7 +574,7 @@
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type_);
}
- InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
+ InductionInfo* b = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
if (b != nullptr && b->induction_class == kPeriodic) {
return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type_);
}
@@ -574,17 +606,15 @@
return CreateInvariantOp(op, a, b);
}
}
- } else if (op == kAdd && b->induction_class == kLinear) {
+ } else if (b->induction_class == kLinear && b->type == type_) {
// Solve within a tight cycle that adds a term that is already classified as a linear
// induction for a polynomial induction k = k + i (represented as sum over linear terms).
if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
- return CreateInduction(kPolynomial,
- kNop,
- b,
- initial,
- /*fetch*/ nullptr,
- type_);
+ InductionInfo* new_a = op == kAdd ? b : TransferNeg(b);
+ if (new_a != nullptr) {
+ return CreateInduction(kPolynomial, kNop, new_a, initial, /*fetch*/ nullptr, type_);
+ }
}
}
}
@@ -689,16 +719,29 @@
return nullptr;
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion(
+ HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HTypeConversion* conversion) {
Primitive::Type from = conversion->GetInputType();
Primitive::Type to = conversion->GetResultType();
- // A narrowing conversion is allowed within the cycle of a linear induction, provided that the
- // narrowest encountered type is recorded with the induction to account for the precision loss.
- if (IsNarrowingIntegralConversion(from, to)) {
- auto it = cycle_.find(conversion->GetInput());
- if (it != cycle_.end() && it->second->induction_class == kInvariant) {
- type_ = Narrowest(type_, to);
- return it->second;
+ // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction
+ // with an initial value that fits the type, provided that the narrowest encountered type is
+ // recorded with the induction to account for the precision loss. The narrower induction does
+ // *not* transfer to any wider operations, however, since these may yield out-of-type values
+ if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) {
+ int64_t min = Primitive::MinValueOfIntegralType(to);
+ int64_t max = Primitive::MaxValueOfIntegralType(to);
+ int64_t value = 0;
+ InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+ if (IsNarrowingIntegralConversion(from, to) &&
+ IsAtLeast(initial, &value) && value >= min &&
+ IsAtMost(initial, &value) && value <= max) {
+ auto it = cycle_.find(conversion->GetInput());
+ if (it != cycle_.end() && it->second->induction_class == kInvariant) {
+ type_ = to;
+ return it->second;
+ }
}
}
return nullptr;
@@ -718,7 +761,7 @@
HCondition* condition = if_expr->AsCondition();
InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
- Primitive::Type type = condition->InputAt(0)->GetType();
+ Primitive::Type type = ImplicitConversion(condition->InputAt(0)->GetType());
// Determine if the loop control uses a known sequence on an if-exit (X outside) or on
// an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
if (a == nullptr || b == nullptr) {
@@ -890,8 +933,8 @@
int64_t stride_value,
Primitive::Type type,
IfCondition cmp) {
- const int64_t min = Primitive::MinValueOfIntegralType(type);
- const int64_t max = Primitive::MaxValueOfIntegralType(type);
+ int64_t min = Primitive::MinValueOfIntegralType(type);
+ int64_t max = Primitive::MaxValueOfIntegralType(type);
// Some rules under which it is certain at compile-time that the loop is finite.
int64_t value;
switch (cmp) {
@@ -927,8 +970,6 @@
min++;
}
// Do both bounds fit the range?
- // Note: The `value` is initialized to please valgrind - the compiler can reorder
- // the return value check with the `value` check, b/27651442 .
int64_t value = 0;
return IsAtLeast(lower_expr, &value) && value >= min &&
IsAtMost(lower_expr, &value) && value <= max &&
@@ -1035,16 +1076,27 @@
return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a);
}
}
- return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
+ return new (graph_->GetArena()) InductionInfo(
+ kInvariant, op, a, b, nullptr, ImplicitConversion(b->type));
}
-HInstruction* HInductionVarAnalysis::GetMultConstantForShift(HLoopInformation* loop,
- HInstruction* instruction) {
- // Obtain the constant needed to treat shift as equivalent multiplication. This yields an
- // existing instruction if the constant is already there. Otherwise, this has a side effect
- // on the HIR. The restriction on the shift factor avoids generating a negative constant
- // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization for
- // shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
+HInstruction* HInductionVarAnalysis::GetShiftConstant(HLoopInformation* loop,
+ HInstruction* instruction,
+ InductionInfo* initial) {
+ DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
+ // Shift-rights are only the same as division for non-negative initial inputs.
+ // Otherwise we would round incorrectly.
+ if (initial != nullptr) {
+ int64_t value = -1;
+ if (!IsAtLeast(initial, &value) || value < 0) {
+ return nullptr;
+ }
+ }
+ // Obtain the constant needed to treat shift as equivalent multiplication or division.
+ // This yields an existing instruction if the constant is already there. Otherwise, this
+ // has a side effect on the HIR. The restriction on the shift factor avoids generating a
+ // negative constant (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that
+ // generalization for shift factors outside [0,32) and [0,64) ranges is done earlier.
InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
int64_t value = -1;
if (IsExact(b, &value)) {
@@ -1087,6 +1139,16 @@
return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value);
}
+bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) {
+ return info != nullptr &&
+ info->induction_class == kLinear &&
+ (info->type == Primitive::kPrimByte ||
+ info->type == Primitive::kPrimShort ||
+ info->type == Primitive::kPrimChar ||
+ (info->type == Primitive::kPrimInt && (info->op_a->type == Primitive::kPrimLong ||
+ info->op_b->type == Primitive::kPrimLong)));
+}
+
bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
InductionInfo* info2) {
// Test structural equality only, without accounting for simplifications.
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 4720f2d..39b39cd 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -115,7 +115,7 @@
InductionInfo* op_a;
InductionInfo* op_b;
HInstruction* fetch;
- Primitive::Type type; // precision of induction
+ Primitive::Type type; // precision of operation
};
bool IsVisitedNode(HInstruction* instruction) const {
@@ -160,14 +160,17 @@
InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last);
// Transfer operations.
- InductionInfo* TransferPhi(HLoopInformation* loop, HInstruction* phi, size_t input_index);
+ InductionInfo* TransferPhi(HLoopInformation* loop,
+ HInstruction* phi,
+ size_t input_index,
+ size_t adjust_input_size);
InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
InductionInfo* TransferNeg(InductionInfo* a);
InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
- InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
+ InductionInfo* TransferConversion(InductionInfo* a, Primitive::Type from, Primitive::Type to);
// Solvers.
- InductionInfo* SolvePhi(HInstruction* phi, size_t input_index);
+ InductionInfo* SolvePhi(HInstruction* phi, size_t input_index, size_t adjust_input_size);
InductionInfo* SolvePhiAllInputs(HLoopInformation* loop,
HInstruction* entry_phi,
HInstruction* phi);
@@ -188,7 +191,9 @@
HInstruction* entry_phi,
HInstruction* instruction,
int64_t oppositive_value);
- InductionInfo* SolveCnv(HTypeConversion* conversion);
+ InductionInfo* SolveConversion(HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HTypeConversion* conversion);
// Trip count information.
void VisitControl(HLoopInformation* loop);
@@ -220,7 +225,9 @@
InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
- HInstruction* GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction);
+ HInstruction* GetShiftConstant(HLoopInformation* loop,
+ HInstruction* instruction,
+ InductionInfo* initial);
void AssignCycle(HPhi* phi);
ArenaSet<HInstruction*>* LookupCycle(HPhi* phi);
@@ -230,6 +237,7 @@
bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value);
// Helpers.
+ static bool IsNarrowingLinear(InductionInfo* info);
static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
static std::string FetchToString(HInstruction* fetch);
static std::string InductionToString(InductionInfo* info);
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 2d182f6..82ee93d 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -87,6 +87,7 @@
constant2_ = graph_->GetIntConstant(2);
constant7_ = graph_->GetIntConstant(7);
constant100_ = graph_->GetIntConstant(100);
+ constantm1_ = graph_->GetIntConstant(-1);
float_constant0_ = graph_->GetFloatConstant(0.0f);
return_->AddInstruction(new (&allocator_) HReturnVoid());
exit_->AddInstruction(new (&allocator_) HExit());
@@ -173,6 +174,12 @@
iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
}
+ // Returns true for narrowing linear induction.
+ bool IsNarrowingLinear(HInstruction* instruction) {
+ return HInductionVarAnalysis::IsNarrowingLinear(
+ iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction));
+ }
+
// Performs InductionVarAnalysis (after proper set up).
void PerformInductionVarAnalysis() {
graph_->BuildDominatorTree();
@@ -196,6 +203,7 @@
HInstruction* constant2_;
HInstruction* constant7_;
HInstruction* constant100_;
+ HInstruction* constantm1_;
HInstruction* float_constant0_;
// Loop specifics.
@@ -612,6 +620,45 @@
EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
}
+TEST_F(InductionVarAnalysisTest, FindGeometricShrInduction) {
+ // Setup:
+ // k = 100;
+ // for (int i = 0; i < 100; i++) {
+ // k = k >> 1; // geometric (/ 2)
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant100_);
+
+ HInstruction* shr = InsertInstruction(
+ new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
+ k_header->AddInput(shr);
+ PerformInductionVarAnalysis();
+
+ // Note, only the phi in the cycle is classified.
+ EXPECT_STREQ("geo((100) * 2 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindNotGeometricShrInduction) {
+ // Setup:
+ // k = -1;
+ // for (int i = 0; i < 100; i++) {
+ // k = k >> 1; // initial value is negative
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constantm1_);
+
+ HInstruction* shr = InsertInstruction(
+ new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
+ k_header->AddInput(shr);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
+}
+
TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) {
// Setup:
// k = 100;
@@ -1025,16 +1072,20 @@
// }
BuildLoopNest(1);
HInstruction* conv = InsertInstruction(
- new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
HInstruction* store1 = InsertArrayStore(conv, 0);
HInstruction* store2 = InsertArrayStore(basic_[0], 0);
PerformInductionVarAnalysis();
- // Regular int induction (i) is "transferred" over conversion into byte induction (k).
+ // Regular int induction (i) is transferred over conversion into byte induction (k).
EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+ EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));
+
// Type matters!
EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
@@ -1052,7 +1103,7 @@
// }
BuildLoopNest(1);
HInstruction* conv = InsertInstruction(
- new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
HInstruction* store1 = InsertArrayStore(conv, 0);
HInstruction* add = InsertInstruction(
new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
@@ -1060,11 +1111,86 @@
PerformInductionVarAnalysis();
- // Byte induction (k) is "transferred" over conversion into addition (k + 1).
- // This means only values within byte range can be trusted (even though
- // addition can jump out of the range of course).
+ // Byte induction (k) is detected, but it does not transfer over the addition,
+ // since this may yield out-of-type values.
EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
- EXPECT_STREQ("((1) * i + (1)):PrimByte", GetInductionInfo(store2->InputAt(1), 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(store2->InputAt(1), 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+ EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1))); // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, ByteInduction) {
+ // Setup:
+ // k = -128;
+ // for (int i = 0; i < 100; i++) {
+ // k = k + 1;
+ // k = (byte) k;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(graph_->GetIntConstant(-128));
+
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* conv = InsertInstruction(
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+ k_header->AddInput(conv);
+ PerformInductionVarAnalysis();
+
+ // Byte induction (k) is detected, but it does not transfer over the addition,
+ // since this may yield out-of-type values.
+ EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(k_header));
+ EXPECT_FALSE(IsNarrowingLinear(add)); // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction1) {
+ // Setup:
+ // k = -129; / does not fit!
+ // for (int i = 0; i < 100; i++) {
+ // k = k + 1;
+ // k = (byte) k;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(graph_->GetIntConstant(-129));
+
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* conv = InsertInstruction(
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+ k_header->AddInput(conv);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction2) {
+ // Setup:
+ // k = 0;
+ // for (int i = 0; i < 100; i++) {
+ // k = (byte) k; // conversion not done last!
+ // k = k + 1;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
+
+ HInstruction* conv = InsertInstruction(
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, k_header, kNoDexPc), 0);
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
+ k_header->AddInput(add);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
@@ -1075,12 +1201,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count.
EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
}
@@ -1093,12 +1227,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count undefined.
EXPECT_STREQ("", GetTripCount(0).c_str());
}
@@ -1111,13 +1253,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
- GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count.
EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
}
@@ -1130,13 +1279,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
- GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count undefined.
EXPECT_STREQ("", GetTripCount(0).c_str());
}
@@ -1148,12 +1304,20 @@
BuildLoopNest(1);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count.
EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
}
@@ -1165,12 +1329,20 @@
BuildLoopNest(1);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count undefined.
EXPECT_STREQ("", GetTripCount(0).c_str());
}
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index e665551..d5c4c2f 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -169,8 +169,8 @@
case Primitive::kPrimByte: {
// Constants within range only.
// TODO: maybe some room for improvement, like allowing widening conversions
- const int32_t min = Primitive::MinValueOfIntegralType(type);
- const int32_t max = Primitive::MaxValueOfIntegralType(type);
+ int32_t min = Primitive::MinValueOfIntegralType(type);
+ int32_t max = Primitive::MaxValueOfIntegralType(type);
return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max)
? v
: InductionVarRange::Value();
@@ -551,7 +551,7 @@
int64_t b = 0;
if (IsConstant(info->op_a->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && a >= 0 &&
IsConstant(info->op_a->op_b, kExact, &b) && CanLongValueFitIntoInt(b) && b >= 0) {
- // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known
+ // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for
// maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
Value c = GetVal(info->op_b, trip, in_body, is_min);
if (is_min) {
@@ -629,6 +629,7 @@
}
} else if (instruction->IsTypeConversion()) {
// Since analysis is 32-bit (or narrower), chase beyond widening along the path.
+ // For example, this discovers the length in: for (long i = 0; i < a.length; i++);
if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
@@ -843,7 +844,7 @@
InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
- const int32_t b = v1.b_constant + v2.b_constant;
+ int32_t b = v1.b_constant + v2.b_constant;
if (v1.a_constant == 0) {
return Value(v2.instruction, v2.a_constant, b);
} else if (v2.a_constant == 0) {
@@ -857,7 +858,7 @@
InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
- const int32_t b = v1.b_constant - v2.b_constant;
+ int32_t b = v1.b_constant - v2.b_constant;
if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
return Value(v2.instruction, -v2.a_constant, b);
} else if (v2.a_constant == 0) {
@@ -983,18 +984,21 @@
int64_t a = 0;
int64_t b = 0;
int64_t m = 0;
- if (IsConstant(info->op_a->op_a, kExact, &a) && a >= 0 &&
- IsConstant(info->op_a->op_b, kExact, &b) && b >= 0 &&
+ if (IsConstant(info->op_a->op_a, kExact, &a) &&
+ IsConstant(info->op_a->op_b, kExact, &b) &&
IsConstant(trip->op_a, kExact, &m) && m >= 1) {
- // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known
+ // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known
// maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
- // TODO: generalize
- HInstruction* c_instr = nullptr;
- if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c_instr : nullptr, false, false)) {
+ HInstruction* c = nullptr;
+ if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c : nullptr, false, false)) {
if (graph != nullptr) {
+ Primitive::Type type = info->type;
int64_t sum = a * ((m * (m - 1)) / 2) + b * m;
- *result = Insert(block, new (graph->GetArena()) HAdd(info->type,
- graph->GetIntConstant(sum), c_instr));
+ if (type != Primitive::kPrimLong) {
+ sum = static_cast<int32_t>(sum); // okay to truncate
+ }
+ *result =
+ Insert(block, new (graph->GetArena()) HAdd(type, graph->GetConstant(type, sum), c));
}
return true;
}
@@ -1011,35 +1015,33 @@
DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
// Detect known base and trip count (always taken).
int64_t f = 0;
- int64_t t = 0;
- if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) {
+ int64_t m = 0;
+ if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) {
HInstruction* opa = nullptr;
HInstruction* opb = nullptr;
if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) &&
GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) {
- // Compute f ^ t.
- int64_t fpowt = IntPow(f, t);
+ // Compute f ^ m for known maximum index value m.
+ int64_t fpow = IntPow(f, m);
if (graph != nullptr) {
- DCHECK(info->type == Primitive::kPrimInt); // due to codegen, generalize?
- if (fpowt == 0) {
+ DCHECK(info->operation == HInductionVarAnalysis::kMul ||
+ info->operation == HInductionVarAnalysis::kDiv);
+ Primitive::Type type = info->type;
+ if (fpow == 0) {
// Special case: repeated mul/div always yields zero.
- *result = graph->GetIntConstant(0);
- } else if (info->operation == HInductionVarAnalysis::kMul) {
- // Last value multiplication: a * f ^ t + b.
- HInstruction* mul = Insert(block,
- new (graph->GetArena()) HMul(info->type,
- opa,
- graph->GetIntConstant(fpowt)));
- *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb));
+ *result = graph->GetConstant(type, 0);
} else {
- // Last value multiplication: a * f ^ -t + b.
- DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv);
- HInstruction* div = Insert(block,
- new (graph->GetArena()) HDiv(info->type,
- opa,
- graph->GetIntConstant(fpowt),
- kNoDexPc));
- *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb));
+ // Last value: a * f ^ m + b or a * f ^ -m + b.
+ if (type != Primitive::kPrimLong) {
+ fpow = static_cast<int32_t>(fpow); // okay to truncate
+ }
+ HInstruction* e = nullptr;
+ if (info->operation == HInductionVarAnalysis::kMul) {
+ e = new (graph->GetArena()) HMul(type, opa, graph->GetConstant(type, fpow));
+ } else {
+ e = new (graph->GetArena()) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc);
+ }
+ *result = Insert(block, new (graph->GetArena()) HAdd(type, Insert(block, e), opb));
}
}
return true;
@@ -1060,12 +1062,11 @@
for (; info->induction_class == HInductionVarAnalysis::kWrapAround;
info = info->op_b, ++depth) {}
// Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end.
- // TODO: generalize
- int64_t t = 0;
+ // TODO: generalize, but be careful to adjust the terminal.
+ int64_t m = 0;
if (info->induction_class == HInductionVarAnalysis::kInvariant &&
- IsConstant(trip->op_a, kExact, &t) && t >= depth &&
- GenerateCode(info, nullptr, graph, block, result, false, false)) {
- return true;
+ IsConstant(trip->op_a, kExact, &m) && m >= depth) {
+ return GenerateCode(info, nullptr, graph, block, result, false, false);
}
return false;
}
@@ -1079,43 +1080,49 @@
DCHECK(info != nullptr);
DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic);
// Count period.
- int32_t period = 1;
+ int64_t period = 1;
for (HInductionVarAnalysis::InductionInfo* p = info;
p->induction_class == HInductionVarAnalysis::kPeriodic;
p = p->op_b, ++period) {}
- // Handle periodic(x, y) case for restricted types.
- // TODO: generalize
- if (period != 2 ||
- trip->op_a->type != Primitive::kPrimInt ||
- (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) {
- return false;
+ // Handle any periodic(x, periodic(.., y)) for known maximum index value m.
+ int64_t m = 0;
+ if (IsConstant(trip->op_a, kExact, &m) && m >= 1) {
+ int64_t li = m % period;
+ for (int64_t i = 0; i < li; info = info->op_b, i++) {}
+ if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
+ info = info->op_a;
+ }
+ return GenerateCode(info, nullptr, graph, block, result, false, false);
}
- HInstruction* x_instr = nullptr;
- HInstruction* y_instr = nullptr;
- HInstruction* trip_expr = nullptr;
- if (GenerateCode(info->op_a, nullptr, graph, block, graph ? &x_instr : nullptr, false, false) &&
- GenerateCode(info->op_b, nullptr, graph, block, graph ? &y_instr : nullptr, false, false) &&
- GenerateCode(trip->op_a, nullptr, graph, block, graph ? &trip_expr : nullptr, false, false)) {
- // During actual code generation (graph != nullptr),
- // generate is_even ? x : y select instruction.
+ // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression
+ // directly to obtain the maximum index value t even if taken test is needed.
+ HInstruction* x = nullptr;
+ HInstruction* y = nullptr;
+ HInstruction* t = nullptr;
+ if (period == 2 &&
+ GenerateCode(info->op_a, nullptr, graph, block, graph ? &x : nullptr, false, false) &&
+ GenerateCode(info->op_b, nullptr, graph, block, graph ? &y : nullptr, false, false) &&
+ GenerateCode(trip->op_a, nullptr, graph, block, graph ? &t : nullptr, false, false)) {
+ // During actual code generation (graph != nullptr), generate is_even ? x : y.
if (graph != nullptr) {
- HInstruction* is_even = Insert(block, new (graph->GetArena()) HEqual(
- Insert(block, new (graph->GetArena()) HAnd(
- Primitive::kPrimInt, trip_expr, graph->GetIntConstant(1))),
- graph->GetIntConstant(0), kNoDexPc));
- *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x_instr, y_instr, kNoDexPc));
+ Primitive::Type type = trip->type;
+ HInstruction* msk =
+ Insert(block, new (graph->GetArena()) HAnd(type, t, graph->GetConstant(type, 1)));
+ HInstruction* is_even =
+ Insert(block, new (graph->GetArena()) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc));
+ *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x, y, kNoDexPc));
}
// Guard select with taken test if needed.
if (*needs_taken_test) {
- HInstruction* taken_test = nullptr;
- if (!GenerateCode(
- trip->op_b, nullptr, graph, block, graph ? &taken_test : nullptr, false, false)) {
+ HInstruction* is_taken = nullptr;
+ if (GenerateCode(trip->op_b, nullptr, graph, block, graph ? &is_taken : nullptr, false, false)) {
+ if (graph != nullptr) {
+ *result = Insert(block, new (graph->GetArena()) HSelect(is_taken, *result, x, kNoDexPc));
+ }
+ *needs_taken_test = false; // taken care of
+ } else {
return false;
- } else if (graph != nullptr) {
- *result = Insert(block,
- new (graph->GetArena()) HSelect(taken_test, *result, x_instr, kNoDexPc));
}
- *needs_taken_test = false; // taken care of
}
return true;
}
@@ -1134,13 +1141,8 @@
if (graph != nullptr && result == nullptr) {
return true;
}
- // Verify type safety.
- // TODO: generalize
- Primitive::Type type = Primitive::kPrimInt;
- if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) {
- return false;
- }
// Handle current operation.
+ Primitive::Type type = info->type;
HInstruction* opa = nullptr;
HInstruction* opb = nullptr;
switch (info->induction_class) {
@@ -1214,15 +1216,15 @@
case HInductionVarAnalysis::kTripCountInBodyUnsafe:
if (is_min) {
if (graph != nullptr) {
- *result = graph->GetIntConstant(0);
+ *result = graph->GetConstant(type, 0);
}
return true;
} else if (in_body) {
if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) {
if (graph != nullptr) {
- *result = Insert(block,
- new (graph->GetArena())
- HSub(type, opb, graph->GetIntConstant(1)));
+ *result =
+ Insert(block,
+ new (graph->GetArena()) HSub(type, opb, graph->GetConstant(type, 1)));
}
return true;
}
@@ -1236,26 +1238,31 @@
// Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should
// be restricted to a unit stride to avoid arithmetic wrap-around situations that
// are harder to guard against. For a last value, requesting min/max based on any
- // stride yields right value.
- int64_t stride_value = 0;
- if (IsConstant(info->op_a, kExact, &stride_value)) {
- const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
- if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) &&
- GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
- if (graph != nullptr) {
- HInstruction* oper;
- if (stride_value == 1) {
- oper = new (graph->GetArena()) HAdd(type, opa, opb);
- } else if (stride_value == -1) {
- oper = new (graph->GetArena()) HSub(type, opb, opa);
- } else {
- HInstruction* mul = new (graph->GetArena()) HMul(
- type, graph->GetIntConstant(stride_value), opa);
- oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+ // known stride yields right value. Always avoid any narrowing linear induction or
+ // any type mismatch between the linear induction and the trip count expression.
+ // TODO: careful runtime type conversions could generalize this latter restriction.
+ if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) {
+ int64_t stride_value = 0;
+ if (IsConstant(info->op_a, kExact, &stride_value) &&
+ CanLongValueFitIntoInt(stride_value)) {
+ const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
+ if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) &&
+ GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+ if (graph != nullptr) {
+ HInstruction* oper;
+ if (stride_value == 1) {
+ oper = new (graph->GetArena()) HAdd(type, opa, opb);
+ } else if (stride_value == -1) {
+ oper = new (graph->GetArena()) HSub(type, opb, opa);
+ } else {
+ HInstruction* mul =
+ new (graph->GetArena()) HMul(type, graph->GetConstant(type, stride_value), opa);
+ oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+ }
+ *result = Insert(block, oper);
}
- *result = Insert(block, oper);
+ return true;
}
- return true;
}
}
break;
@@ -1270,7 +1277,7 @@
Value extreme = GetVal(info, trip, in_body, is_min);
if (IsConstantValue(extreme)) {
if (graph != nullptr) {
- *result = graph->GetIntConstant(extreme.b_constant);
+ *result = graph->GetConstant(type, extreme.b_constant);
}
return true;
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index fe4662a..3b83e95 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -344,6 +344,7 @@
if (actual_method != nullptr) {
bool result = TryInlineAndReplace(invoke_instruction,
actual_method,
+ ReferenceTypeInfo::CreateInvalid(),
/* do_rtp */ true,
cha_devirtualize);
if (result && !invoke_instruction->IsInvokeStaticOrDirect()) {
@@ -471,22 +472,21 @@
HInstruction* receiver = invoke_instruction->InputAt(0);
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
-
+ Handle<mirror::Class> handle = handles_->NewHandle(GetMonomorphicType(classes));
if (!TryInlineAndReplace(invoke_instruction,
resolved_method,
+ ReferenceTypeInfo::Create(handle, /* is_exact */ true),
/* do_rtp */ false,
/* cha_devirtualize */ false)) {
return false;
}
// We successfully inlined, now add a guard.
- bool is_referrer =
- (GetMonomorphicType(classes) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
AddTypeGuard(receiver,
cursor,
bb_cursor,
class_index,
- is_referrer,
+ GetMonomorphicType(classes),
invoke_instruction,
/* with_deoptimization */ true);
@@ -506,52 +506,62 @@
uint32_t dex_pc,
HInstruction* cursor,
HBasicBlock* bb_cursor) {
- HInstruction* deopt_flag = new (graph_->GetArena()) HShouldDeoptimizeFlag(dex_pc);
- HInstruction* should_deopt = new (graph_->GetArena()) HNotEqual(
+ HShouldDeoptimizeFlag* deopt_flag = new (graph_->GetArena())
+ HShouldDeoptimizeFlag(graph_->GetArena(), dex_pc);
+ HInstruction* compare = new (graph_->GetArena()) HNotEqual(
deopt_flag, graph_->GetIntConstant(0, dex_pc));
- HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(should_deopt, dex_pc);
+ HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(compare, dex_pc);
if (cursor != nullptr) {
bb_cursor->InsertInstructionAfter(deopt_flag, cursor);
} else {
bb_cursor->InsertInstructionBefore(deopt_flag, bb_cursor->GetFirstInstruction());
}
- bb_cursor->InsertInstructionAfter(should_deopt, deopt_flag);
- bb_cursor->InsertInstructionAfter(deopt, should_deopt);
+ bb_cursor->InsertInstructionAfter(compare, deopt_flag);
+ bb_cursor->InsertInstructionAfter(deopt, compare);
+
+ // Add receiver as input to aid CHA guard optimization later.
+ deopt_flag->AddInput(invoke_instruction->InputAt(0));
+ DCHECK_EQ(deopt_flag->InputCount(), 1u);
deopt->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ outermost_graph_->IncrementNumberOfCHAGuards();
}
HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
HInstruction* cursor,
HBasicBlock* bb_cursor,
dex::TypeIndex class_index,
- bool is_referrer,
+ mirror::Class* klass,
HInstruction* invoke_instruction,
bool with_deoptimization) {
+ ScopedAssertNoThreadSuspension sants("Adding compiler type guard");
+
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
class_linker, receiver, invoke_instruction->GetDexPc());
-
- const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
- // Note that we will just compare the classes, so we don't need Java semantics access checks.
- // Also, the caller of `AddTypeGuard` must have guaranteed that the class is in the dex cache.
- HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
- class_index,
- caller_dex_file,
- is_referrer,
- invoke_instruction->GetDexPc(),
- /* needs_access_check */ false,
- /* is_in_dex_cache */ true,
- /* is_in_boot_image */ false);
-
- HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
- // TODO: Extend reference type propagation to understand the guard.
if (cursor != nullptr) {
bb_cursor->InsertInstructionAfter(receiver_class, cursor);
} else {
bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
}
+
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+ bool is_referrer = (klass == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+ // Note that we will just compare the classes, so we don't need Java semantics access checks.
+ // Note that the type index and the dex file are relative to the method this type guard is
+ // inlined into.
+ HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
+ class_index,
+ caller_dex_file,
+ is_referrer,
+ invoke_instruction->GetDexPc(),
+ /* needs_access_check */ false);
bb_cursor->InsertInstructionAfter(load_class, receiver_class);
+ // Sharpen after adding the instruction, as the sharpening may remove inputs.
+ HSharpening::SharpenClass(load_class, klass, handles_, codegen_, compiler_driver_);
+
+ // TODO: Extend reference type propagation to understand the guard.
+ HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
bb_cursor->InsertInstructionAfter(compare, load_class);
if (with_deoptimization) {
HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
@@ -583,13 +593,13 @@
break;
}
ArtMethod* method = nullptr;
+
+ Handle<mirror::Class> handle = handles_->NewHandle(classes->Get(i));
if (invoke_instruction->IsInvokeInterface()) {
- method = classes->Get(i)->FindVirtualMethodForInterface(
- resolved_method, pointer_size);
+ method = handle->FindVirtualMethodForInterface(resolved_method, pointer_size);
} else {
DCHECK(invoke_instruction->IsInvokeVirtual());
- method = classes->Get(i)->FindVirtualMethodForVirtual(
- resolved_method, pointer_size);
+ method = handle->FindVirtualMethodForVirtual(resolved_method, pointer_size);
}
HInstruction* receiver = invoke_instruction->InputAt(0);
@@ -597,14 +607,16 @@
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
dex::TypeIndex class_index = FindClassIndexIn(
- classes->Get(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
+ handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache());
HInstruction* return_replacement = nullptr;
if (!class_index.IsValid() ||
- !TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+ !TryBuildAndInline(invoke_instruction,
+ method,
+ ReferenceTypeInfo::Create(handle, /* is_exact */ true),
+ &return_replacement)) {
all_targets_inlined = false;
} else {
one_target_inlined = true;
- bool is_referrer = (classes->Get(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
// If we have inlined all targets before, and this receiver is the last seen,
// we deoptimize instead of keeping the original invoke instruction.
@@ -616,8 +628,13 @@
// We do not support HDeoptimize in OSR methods.
deoptimize = false;
}
- HInstruction* compare = AddTypeGuard(
- receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
+ HInstruction* compare = AddTypeGuard(receiver,
+ cursor,
+ bb_cursor,
+ class_index,
+ handle.Get(),
+ invoke_instruction,
+ deoptimize);
if (deoptimize) {
if (return_replacement != nullptr) {
invoke_instruction->ReplaceWith(return_replacement);
@@ -780,7 +797,10 @@
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
HInstruction* return_replacement = nullptr;
- if (!TryBuildAndInline(invoke_instruction, actual_method, &return_replacement)) {
+ if (!TryBuildAndInline(invoke_instruction,
+ actual_method,
+ ReferenceTypeInfo::CreateInvalid(),
+ &return_replacement)) {
return false;
}
@@ -845,13 +865,14 @@
bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
ArtMethod* method,
+ ReferenceTypeInfo receiver_type,
bool do_rtp,
bool cha_devirtualize) {
HInstruction* return_replacement = nullptr;
uint32_t dex_pc = invoke_instruction->GetDexPc();
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- if (!TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+ if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) {
if (invoke_instruction->IsInvokeInterface()) {
// Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always
// better than an invoke-interface because:
@@ -909,6 +930,7 @@
bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
ArtMethod* method,
+ ReferenceTypeInfo receiver_type,
HInstruction** return_replacement) {
if (method->IsProxyMethod()) {
VLOG(compiler) << "Method " << method->PrettyMethod()
@@ -985,7 +1007,8 @@
return false;
}
- if (!TryBuildAndInlineHelper(invoke_instruction, method, same_dex_file, return_replacement)) {
+ if (!TryBuildAndInlineHelper(
+ invoke_instruction, method, receiver_type, same_dex_file, return_replacement)) {
return false;
}
@@ -1182,8 +1205,10 @@
bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
+ ReferenceTypeInfo receiver_type,
bool same_dex_file,
HInstruction** return_replacement) {
+ DCHECK(!(resolved_method->IsStatic() && receiver_type.IsValid()));
ScopedObjectAccess soa(Thread::Current());
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
@@ -1274,12 +1299,13 @@
}
size_t parameter_index = 0;
+ bool run_rtp = false;
for (HInstructionIterator instructions(callee_graph->GetEntryBlock()->GetInstructions());
!instructions.Done();
instructions.Advance()) {
HInstruction* current = instructions.Current();
if (current->IsParameterValue()) {
- HInstruction* argument = invoke_instruction->InputAt(parameter_index++);
+ HInstruction* argument = invoke_instruction->InputAt(parameter_index);
if (argument->IsNullConstant()) {
current->ReplaceWith(callee_graph->GetNullConstant());
} else if (argument->IsIntConstant()) {
@@ -1293,15 +1319,21 @@
current->ReplaceWith(
callee_graph->GetDoubleConstant(argument->AsDoubleConstant()->GetValue()));
} else if (argument->GetType() == Primitive::kPrimNot) {
- current->SetReferenceTypeInfo(argument->GetReferenceTypeInfo());
+ if (!resolved_method->IsStatic() && parameter_index == 0 && receiver_type.IsValid()) {
+ run_rtp = true;
+ current->SetReferenceTypeInfo(receiver_type);
+ } else {
+ current->SetReferenceTypeInfo(argument->GetReferenceTypeInfo());
+ }
current->AsParameterValue()->SetCanBeNull(argument->CanBeNull());
}
+ ++parameter_index;
}
}
// We have replaced formal arguments with actual arguments. If actual types
// are more specific than the declared ones, run RTP again on the inner graph.
- if (ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
+ if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
ReferenceTypePropagation(callee_graph,
dex_compilation_unit.GetDexCache(),
handles_,
@@ -1490,7 +1522,7 @@
ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo();
return (actual_rti.IsExact() && !declared_rti.IsExact()) ||
- declared_rti.IsStrictSupertypeOf(actual_rti);
+ declared_rti.IsStrictSupertypeOf(actual_rti);
}
ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) {
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index ffebd97..4c0b990 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -66,17 +66,20 @@
// a CHA guard needs to be added for the inlining.
bool TryInlineAndReplace(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
+ ReferenceTypeInfo receiver_type,
bool do_rtp,
bool cha_devirtualize)
REQUIRES_SHARED(Locks::mutator_lock_);
bool TryBuildAndInline(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
+ ReferenceTypeInfo receiver_type,
HInstruction** return_replacement)
REQUIRES_SHARED(Locks::mutator_lock_);
bool TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
+ ReferenceTypeInfo receiver_type,
bool same_dex_file,
HInstruction** return_replacement);
@@ -167,7 +170,7 @@
HInstruction* cursor,
HBasicBlock* bb_cursor,
dex::TypeIndex class_index,
- bool is_referrer,
+ mirror::Class* klass,
HInstruction* invoke_instruction,
bool with_deoptimization)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index b97581b..af8e2c8 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -816,8 +816,7 @@
HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- dchecked_integral_cast<uint64_t>(string_init_entry_point),
- 0U
+ dchecked_integral_cast<uint64_t>(string_init_entry_point)
};
MethodReference target_method(dex_file_, method_idx);
HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
@@ -862,8 +861,7 @@
HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0U
+ 0u
};
MethodReference target_method(resolved_method->GetDexFile(),
resolved_method->GetDexMethodIndex());
@@ -937,9 +935,7 @@
outer_dex_file,
IsOutermostCompilingClass(type_index),
dex_pc,
- needs_access_check,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ needs_access_check);
AppendInstruction(load_class);
HInstruction* cls = load_class;
@@ -1029,9 +1025,7 @@
outer_dex_file,
is_outer_class,
dex_pc,
- /*needs_access_check*/ false,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ /*needs_access_check*/ false);
AppendInstruction(load_class);
clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
AppendInstruction(clinit_check);
@@ -1388,9 +1382,7 @@
outer_dex_file,
is_outer_class,
dex_pc,
- /*needs_access_check*/ false,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ /*needs_access_check*/ false);
AppendInstruction(constant);
HInstruction* cls = constant;
@@ -1664,9 +1656,7 @@
dex_file,
IsOutermostCompilingClass(type_index),
dex_pc,
- !can_access,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ !can_access);
AppendInstruction(cls);
TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
@@ -2656,9 +2646,7 @@
*dex_file_,
IsOutermostCompilingClass(type_index),
dex_pc,
- !can_access,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false));
+ !can_access));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index c615df1..439e3b6 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1897,7 +1897,8 @@
static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstruction* user) {
if (user->IsInvokeStaticOrDirect()) {
// Any constructor on StringBuffer is okay.
- return user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
+ return user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
user->InputAt(0) == reference;
} else if (user->IsInvokeVirtual()) {
switch (user->AsInvokeVirtual()->GetIntrinsic()) {
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 433dced..85e84d8 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -47,6 +47,9 @@
using namespace vixl::aarch32; // NOLINT(build/namespaces)
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
+
ArmVIXLAssembler* IntrinsicCodeGeneratorARMVIXL::GetAssembler() {
return codegen_->GetAssembler();
}
@@ -184,7 +187,7 @@
assembler->MaybePoisonHeapReference(tmp);
__ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex));
__ Cmp(src_curr_addr, src_stop_addr);
- __ B(ne, &loop);
+ __ B(ne, &loop, /* far_target */ false);
__ B(GetExitLabel());
}
@@ -467,9 +470,9 @@
__ Cmp(op1, op2);
{
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 3 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 3 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ ite(is_min ? lt : gt);
__ mov(is_min ? lt : gt, out, op1);
@@ -848,7 +851,7 @@
__ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
__ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
__ Cmp(temp_lo, 0);
- __ B(ne, &loop_head);
+ __ B(ne, &loop_head, /* far_target */ false);
} else {
__ Strd(value_lo, value_hi, MemOperand(base, offset));
}
@@ -1050,25 +1053,25 @@
__ Subs(tmp, tmp, expected);
{
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 3 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 3 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ itt(eq);
__ strex(eq, tmp, value, MemOperand(tmp_ptr));
__ cmp(eq, tmp, 1);
}
- __ B(eq, &loop_head);
+ __ B(eq, &loop_head, /* far_target */ false);
__ Dmb(vixl32::ISH);
__ Rsbs(out, tmp, 1);
{
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 2 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(cc);
__ mov(cc, out, 0);
@@ -1185,9 +1188,9 @@
// temp0 = min(len(str), len(arg)).
{
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 2 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(gt);
__ mov(gt, temp0, temp1);
@@ -1207,9 +1210,9 @@
// This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
__ Lsls(temp3, temp3, 31u); // Extract purely the compression flag.
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 2 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(ne);
__ add(ne, temp0, temp0, temp0);
@@ -1235,23 +1238,23 @@
__ Ldr(temp_reg, MemOperand(str, temp1));
__ Ldr(temp2, MemOperand(arg, temp1));
__ Cmp(temp_reg, temp2);
- __ B(ne, &find_char_diff);
+ __ B(ne, &find_char_diff, /* far_target */ false);
__ Add(temp1, temp1, char_size * 2);
__ Ldr(temp_reg, MemOperand(str, temp1));
__ Ldr(temp2, MemOperand(arg, temp1));
__ Cmp(temp_reg, temp2);
- __ B(ne, &find_char_diff_2nd_cmp);
+ __ B(ne, &find_char_diff_2nd_cmp, /* far_target */ false);
__ Add(temp1, temp1, char_size * 2);
// With string compression, we have compared 8 bytes, otherwise 4 chars.
__ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4));
- __ B(hi, &loop);
+ __ B(hi, &loop, /* far_target */ false);
__ B(&end);
__ Bind(&find_char_diff_2nd_cmp);
if (mirror::kUseStringCompression) {
__ Subs(temp0, temp0, 4); // 4 bytes previously compared.
- __ B(ls, &end); // Was the second comparison fully beyond the end?
+ __ B(ls, &end, /* far_target */ false); // Was the second comparison fully beyond the end?
} else {
// Without string compression, we can start treating temp0 as signed
// and rely on the signed comparison below.
@@ -1279,7 +1282,7 @@
// the remaining string data, so just return length diff (out).
// The comparison is unsigned for string compression, otherwise signed.
__ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4)));
- __ B((mirror::kUseStringCompression ? ls : le), &end);
+ __ B((mirror::kUseStringCompression ? ls : le), &end, /* far_target */ false);
// Extract the characters and calculate the difference.
if (mirror::kUseStringCompression) {
@@ -1324,9 +1327,9 @@
__ Mov(temp2, arg);
__ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag.
{
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 3 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 3 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ itt(cs); // Interleave with selection of temp1 and temp2.
__ mov(cs, temp1, arg); // Preserves flags.
__ mov(cs, temp2, str); // Preserves flags.
@@ -1346,9 +1349,9 @@
__ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
__ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
__ Cmp(temp_reg, temp3);
- __ B(ne, &different_compression_diff);
+ __ B(ne, &different_compression_diff, /* far_target */ false);
__ Subs(temp0, temp0, 2);
- __ B(hi, &different_compression_loop);
+ __ B(hi, &different_compression_loop, /* far_target */ false);
__ B(&end);
// Calculate the difference.
@@ -1361,9 +1364,9 @@
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 2 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(cc);
__ rsb(cc, out, out, 0);
}
@@ -1424,7 +1427,7 @@
// Reference equality check, return true if same reference.
__ Cmp(str, arg);
- __ B(eq, &return_true);
+ __ B(eq, &return_true, /* far_target */ false);
if (!optimizations.GetArgumentIsString()) {
// Instanceof check for the argument by comparing class fields.
@@ -1434,7 +1437,7 @@
__ Ldr(temp, MemOperand(str, class_offset));
__ Ldr(temp1, MemOperand(arg, class_offset));
__ Cmp(temp, temp1);
- __ B(ne, &return_false);
+ __ B(ne, &return_false, /* far_target */ false);
}
// Load `count` fields of this and argument strings.
@@ -1443,7 +1446,7 @@
// Check if `count` fields are equal, return false if they're not.
// Also compares the compression style, if differs return false.
__ Cmp(temp, temp1);
- __ B(ne, &return_false);
+ __ B(ne, &return_false, /* far_target */ false);
// Return true if both strings are empty. Even with string compression `count == 0` means empty.
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
@@ -1457,9 +1460,9 @@
// For string compression, calculate the number of bytes to compare (not chars).
// This could in theory exceed INT32_MAX, so treat temp as unsigned.
__ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
- AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 2 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
__ it(cs); // If uncompressed,
__ add(cs, temp, temp, temp); // double the byte count.
}
@@ -1474,10 +1477,10 @@
__ Ldr(temp2, MemOperand(arg, temp1));
__ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
__ Cmp(out, temp2);
- __ B(ne, &return_false);
+ __ B(ne, &return_false, /* far_target */ false);
// With string compression, we have compared 4 bytes, otherwise 2 chars.
__ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
- __ B(hi, &loop);
+ __ B(hi, &loop, /* far_target */ false);
// Return true and exit the function.
// If loop does not result in returning false, we return true.
@@ -1506,7 +1509,7 @@
SlowPathCodeARMVIXL* slow_path = nullptr;
HInstruction* code_point = invoke->InputAt(1);
if (code_point->IsIntConstant()) {
- if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
+ if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) >
std::numeric_limits<uint16_t>::max()) {
// Always needs the slow-path. We could directly dispatch to it, but this case should be
// rare, so for simplicity just put the full slow-path down and branch unconditionally.
@@ -1797,7 +1800,7 @@
} else {
if (!optimizations.GetDestinationIsSource()) {
__ Cmp(src, dest);
- __ B(ne, &conditions_on_positions_validated);
+ __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
}
__ Cmp(RegisterFrom(dest_pos), src_pos_constant);
__ B(gt, intrinsic_slow_path->GetEntryLabel());
@@ -1805,7 +1808,7 @@
} else {
if (!optimizations.GetDestinationIsSource()) {
__ Cmp(src, dest);
- __ B(ne, &conditions_on_positions_validated);
+ __ B(ne, &conditions_on_positions_validated, /* far_target */ false);
}
if (dest_pos.IsConstant()) {
int32_t dest_pos_constant = Int32ConstantFrom(dest_pos);
@@ -1913,7 +1916,7 @@
if (optimizations.GetDestinationIsTypedObjectArray()) {
vixl32::Label do_copy;
- __ B(eq, &do_copy);
+ __ B(eq, &do_copy, /* far_target */ false);
// /* HeapReference<Class> */ temp1 = temp1->component_type_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
@@ -1973,7 +1976,7 @@
if (optimizations.GetDestinationIsTypedObjectArray()) {
vixl32::Label do_copy;
- __ B(eq, &do_copy);
+ __ B(eq, &do_copy, /* far_target */ false);
if (!did_unpoison) {
assembler->MaybeUnpoisonHeapReference(temp1);
}
@@ -2066,7 +2069,7 @@
// Don't enter copy loop if `length == 0`.
__ Cmp(temp1, temp3);
- __ B(eq, &done);
+ __ B(eq, &done, /* far_target */ false);
// /* int32_t */ monitor = src->monitor_
__ Ldr(temp2, MemOperand(src, monitor_offset));
@@ -2119,7 +2122,7 @@
}
__ Cmp(temp1, temp3);
- __ B(ne, &loop);
+ __ B(ne, &loop, /* far_target */ false);
__ Bind(read_barrier_slow_path->GetExitLabel());
__ Bind(&done);
@@ -2139,7 +2142,7 @@
// poison/unpoison.
vixl32::Label loop, done;
__ Cmp(temp1, temp3);
- __ B(eq, &done);
+ __ B(eq, &done, /* far_target */ false);
__ Bind(&loop);
{
@@ -2151,7 +2154,7 @@
}
__ Cmp(temp1, temp3);
- __ B(ne, &loop);
+ __ B(ne, &loop, /* far_target */ false);
__ Bind(&done);
}
@@ -2557,7 +2560,7 @@
__ Subs(num_chr, srcEnd, srcBegin);
// Early out for valid zero-length retrievals.
- __ B(eq, &done);
+ __ B(eq, &done, /* far_target */ false);
// src range to copy.
__ Add(src_ptr, srcObj, value_offset);
@@ -2573,7 +2576,7 @@
__ Ldr(temp, MemOperand(srcObj, count_offset));
__ Tst(temp, 1);
temps.Release(temp);
- __ B(eq, &compressed_string_preloop);
+ __ B(eq, &compressed_string_preloop, /* far_target */ false);
}
__ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1));
@@ -2583,7 +2586,7 @@
temp = temps.Acquire();
// Save repairing the value of num_chr on the < 4 character path.
__ Subs(temp, num_chr, 4);
- __ B(lt, &remainder);
+ __ B(lt, &remainder, /* far_target */ false);
// Keep the result of the earlier subs, we are going to fetch at least 4 characters.
__ Mov(num_chr, temp);
@@ -2598,10 +2601,10 @@
__ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex));
__ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex));
temps.Release(temp);
- __ B(ge, &loop);
+ __ B(ge, &loop, /* far_target */ false);
__ Adds(num_chr, num_chr, 4);
- __ B(eq, &done);
+ __ B(eq, &done, /* far_target */ false);
// Main loop for < 4 character case and remainder handling. Loads and stores one
// 16-bit Java character at a time.
@@ -2611,7 +2614,7 @@
__ Subs(num_chr, num_chr, 1);
__ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
temps.Release(temp);
- __ B(gt, &remainder);
+ __ B(gt, &remainder, /* far_target */ false);
if (mirror::kUseStringCompression) {
__ B(&done);
@@ -2627,7 +2630,7 @@
__ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex));
temps.Release(temp);
__ Subs(num_chr, num_chr, 1);
- __ B(gt, &compressed_string_loop);
+ __ B(gt, &compressed_string_loop, /* far_target */ false);
}
__ Bind(&done);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 9b5d7a0..e9c6615 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1648,7 +1648,8 @@
}
if (type == Primitive::kPrimNot) {
- codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>());
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
+ codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>(), value_can_be_null);
}
}
@@ -1806,7 +1807,8 @@
if (type == Primitive::kPrimNot) {
// Mark card for object assuming new value is stored.
- codegen->MarkGCCard(base, value);
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
+ codegen->MarkGCCard(base, value, value_can_be_null);
}
// do {
@@ -2464,6 +2466,94 @@
__ Bind(&done);
}
+// void java.lang.String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
+void IntrinsicLocationsBuilderMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnMainOnly,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ // We will call memcpy() to do the actual work. Allocate the temporary
+ // registers to use the correct input registers, and output register.
+ // memcpy() uses the normal MIPS calling convention.
+ InvokeRuntimeCallingConvention calling_convention;
+
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+
+ Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt);
+ locations->AddTemp(Location::RegisterLocation(outLocation.AsRegister<Register>()));
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Check assumption that sizeof(Char) is 2 (used in scaling below).
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+ const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar);
+
+ Register srcObj = locations->InAt(0).AsRegister<Register>();
+ Register srcBegin = locations->InAt(1).AsRegister<Register>();
+ Register srcEnd = locations->InAt(2).AsRegister<Register>();
+ Register dstObj = locations->InAt(3).AsRegister<Register>();
+ Register dstBegin = locations->InAt(4).AsRegister<Register>();
+
+ Register dstPtr = locations->GetTemp(0).AsRegister<Register>();
+ DCHECK_EQ(dstPtr, A0);
+ Register srcPtr = locations->GetTemp(1).AsRegister<Register>();
+ DCHECK_EQ(srcPtr, A1);
+ Register numChrs = locations->GetTemp(2).AsRegister<Register>();
+ DCHECK_EQ(numChrs, A2);
+
+ Register dstReturn = locations->GetTemp(3).AsRegister<Register>();
+ DCHECK_EQ(dstReturn, V0);
+
+ MipsLabel done;
+
+ // Location of data in char array buffer.
+ const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+ // Get offset of value field within a string object.
+ const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+
+ __ Beq(srcEnd, srcBegin, &done); // No characters to move.
+
+ // Calculate number of characters to be copied.
+ __ Subu(numChrs, srcEnd, srcBegin);
+
+ // Calculate destination address.
+ __ Addiu(dstPtr, dstObj, data_offset);
+ if (IsR6()) {
+ __ Lsa(dstPtr, dstBegin, dstPtr, char_shift);
+ } else {
+ __ Sll(AT, dstBegin, char_shift);
+ __ Addu(dstPtr, dstPtr, AT);
+ }
+
+ // Calculate source address.
+ __ Addiu(srcPtr, srcObj, value_offset);
+ if (IsR6()) {
+ __ Lsa(srcPtr, srcBegin, srcPtr, char_shift);
+ } else {
+ __ Sll(AT, srcBegin, char_shift);
+ __ Addu(srcPtr, srcPtr, AT);
+ }
+
+ // Calculate number of bytes to copy from number of characters.
+ __ Sll(numChrs, numChrs, char_shift);
+
+ codegen_->InvokeRuntime(kQuickMemcpy, invoke, invoke->GetDexPc(), nullptr);
+
+ __ Bind(&done);
+}
+
// Unimplemented intrinsics.
UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
@@ -2473,7 +2563,6 @@
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 5a99886..3022e97 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1846,6 +1846,84 @@
GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
}
+// void java.lang.String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
+void IntrinsicLocationsBuilderMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnMainOnly,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ // We will call memcpy() to do the actual work. Allocate the temporary
+ // registers to use the correct input registers, and output register.
+ // memcpy() uses the normal MIPS calling conventions.
+ InvokeRuntimeCallingConvention calling_convention;
+
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+
+ Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimLong);
+ locations->AddTemp(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>()));
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ Mips64Assembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Check assumption that sizeof(Char) is 2 (used in scaling below).
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+ const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar);
+
+ GpuRegister srcObj = locations->InAt(0).AsRegister<GpuRegister>();
+ GpuRegister srcBegin = locations->InAt(1).AsRegister<GpuRegister>();
+ GpuRegister srcEnd = locations->InAt(2).AsRegister<GpuRegister>();
+ GpuRegister dstObj = locations->InAt(3).AsRegister<GpuRegister>();
+ GpuRegister dstBegin = locations->InAt(4).AsRegister<GpuRegister>();
+
+ GpuRegister dstPtr = locations->GetTemp(0).AsRegister<GpuRegister>();
+ DCHECK_EQ(dstPtr, A0);
+ GpuRegister srcPtr = locations->GetTemp(1).AsRegister<GpuRegister>();
+ DCHECK_EQ(srcPtr, A1);
+ GpuRegister numChrs = locations->GetTemp(2).AsRegister<GpuRegister>();
+ DCHECK_EQ(numChrs, A2);
+
+ GpuRegister dstReturn = locations->GetTemp(3).AsRegister<GpuRegister>();
+ DCHECK_EQ(dstReturn, V0);
+
+ Mips64Label done;
+
+ // Location of data in char array buffer.
+ const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+ // Get offset of value field within a string object.
+ const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+
+ __ Beqc(srcEnd, srcBegin, &done); // No characters to move.
+
+ // Calculate number of characters to be copied.
+ __ Dsubu(numChrs, srcEnd, srcBegin);
+
+ // Calculate destination address.
+ __ Daddiu(dstPtr, dstObj, data_offset);
+ __ Dlsa(dstPtr, dstBegin, dstPtr, char_shift);
+
+ // Calculate source address.
+ __ Daddiu(srcPtr, srcObj, value_offset);
+ __ Dlsa(srcPtr, srcBegin, srcPtr, char_shift);
+
+ // Calculate number of bytes to copy from number of characters.
+ __ Dsll(numChrs, numChrs, char_shift);
+
+ codegen_->InvokeRuntime(kQuickMemcpy, invoke, invoke->GetDexPc(), nullptr);
+
+ __ Bind(&done);
+}
+
static void GenHighestOneBit(LocationSummary* locations,
Primitive::Type type,
Mips64Assembler* assembler) {
@@ -1925,7 +2003,6 @@
}
UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS64, StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 13e14c5..3831aa6 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -18,7 +18,6 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "code_generator.h"
#include "code_generator_x86.h"
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 925d4f1..a599c2a 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1108,13 +1108,23 @@
return HasEnvironment() ? environment_->Size() : 0;
}
-void HPhi::AddInput(HInstruction* input) {
+void HVariableInputSizeInstruction::AddInput(HInstruction* input) {
DCHECK(input->GetBlock() != nullptr);
inputs_.push_back(HUserRecord<HInstruction*>(input));
input->AddUseAt(this, inputs_.size() - 1);
}
-void HPhi::RemoveInputAt(size_t index) {
+void HVariableInputSizeInstruction::InsertInputAt(size_t index, HInstruction* input) {
+ inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
+ input->AddUseAt(this, index);
+ // Update indexes in use nodes of inputs that have been pushed further back by the insert().
+ for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
+ DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
+ inputs_[i].GetUseNode()->SetIndex(i);
+ }
+}
+
+void HVariableInputSizeInstruction::RemoveInputAt(size_t index) {
RemoveAsUserOfInput(index);
inputs_.erase(inputs_.begin() + index);
// Update indexes in use nodes of inputs that have been pulled forward by the erase().
@@ -1347,7 +1357,9 @@
void HInstruction::MoveBefore(HInstruction* cursor) {
DCHECK(!IsPhi());
DCHECK(!IsControlFlow());
- DCHECK(CanBeMoved());
+ DCHECK(CanBeMoved() ||
+ // HShouldDeoptimizeFlag can only be moved by CHAGuardOptimization.
+ IsShouldDeoptimizeFlag());
DCHECK(!cursor->IsPhi());
next_->previous_ = previous_;
@@ -2386,26 +2398,6 @@
return !opt.GetDoesNotNeedDexCache();
}
-void HInvokeStaticOrDirect::InsertInputAt(size_t index, HInstruction* input) {
- inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
- input->AddUseAt(this, index);
- // Update indexes in use nodes of inputs that have been pushed further back by the insert().
- for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
- DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
- inputs_[i].GetUseNode()->SetIndex(i);
- }
-}
-
-void HInvokeStaticOrDirect::RemoveInputAt(size_t index) {
- RemoveAsUserOfInput(index);
- inputs_.erase(inputs_.begin() + index);
- // Update indexes in use nodes of inputs that have been pulled forward by the erase().
- for (size_t i = index, e = inputs_.size(); i < e; ++i) {
- DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i + 1u);
- inputs_[i].GetUseNode()->SetIndex(i);
- }
-}
-
std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs) {
switch (rhs) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
@@ -2414,8 +2406,6 @@
return os << "recursive";
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
return os << "direct";
- case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- return os << "direct_fixup";
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
return os << "dex_cache_pc_relative";
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod:
@@ -2440,6 +2430,17 @@
}
}
+// Helper for InstructionDataEquals to fetch the mirror Class out
+// from a kJitTableAddress LoadClass kind.
+// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing
+// mirrors, they are stored in a variable size handle scope which is always
+// visited during a pause. Also, the only caller of this helper
+// only uses the mirror for pointer comparison.
+static inline mirror::Class* AsMirrorInternal(uint64_t address)
+ NO_THREAD_SAFETY_ANALYSIS {
+ return reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr();
+}
+
bool HLoadClass::InstructionDataEquals(const HInstruction* other) const {
const HLoadClass* other_load_class = other->AsLoadClass();
// TODO: To allow GVN for HLoadClass from different dex files, we should compare the type
@@ -2448,16 +2449,14 @@
GetPackedFields() != other_load_class->GetPackedFields()) {
return false;
}
- LoadKind load_kind = GetLoadKind();
- if (HasAddress(load_kind)) {
- return GetAddress() == other_load_class->GetAddress();
- } else if (HasTypeReference(load_kind)) {
- return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
- } else {
- DCHECK(HasDexCacheReference(load_kind)) << load_kind;
- // If the type indexes and dex files are the same, dex cache element offsets
- // must also be the same, so we don't need to compare them.
- return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
+ switch (GetLoadKind()) {
+ case LoadKind::kBootImageAddress:
+ return GetAddress() == other_load_class->GetAddress();
+ case LoadKind::kJitTableAddress:
+ return AsMirrorInternal(GetAddress()) == AsMirrorInternal(other_load_class->GetAddress());
+ default:
+ DCHECK(HasTypeReference(GetLoadKind()) || HasDexCacheReference(GetLoadKind()));
+ return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 4a8cfcb..afa17ce 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -330,6 +330,7 @@
invoke_type_(invoke_type),
in_ssa_form_(false),
should_generate_constructor_barrier_(should_generate_constructor_barrier),
+ number_of_cha_guards_(0),
instruction_set_(instruction_set),
cached_null_constant_(nullptr),
cached_int_constants_(std::less<int32_t>(), arena->Adapter(kArenaAllocConstantsMap)),
@@ -551,9 +552,7 @@
}
bool HasShouldDeoptimizeFlag() const {
- // TODO: if all CHA guards can be eliminated, there is no need for the flag
- // even if cha_single_implementation_list_ is not empty.
- return !cha_single_implementation_list_.empty();
+ return number_of_cha_guards_ != 0;
}
bool HasTryCatch() const { return has_try_catch_; }
@@ -572,6 +571,10 @@
ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; }
+ uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; }
+ void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; }
+ void IncrementNumberOfCHAGuards() { number_of_cha_guards_++; }
+
private:
void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited);
@@ -667,6 +670,10 @@
const bool should_generate_constructor_barrier_;
+ // Number of CHA guards in the graph. Used to short-circuit the
+ // CHA guard optimization pass when there is no CHA guard left.
+ uint32_t number_of_cha_guards_;
+
const InstructionSet instruction_set_;
// Cached constants.
@@ -2347,6 +2354,32 @@
DISALLOW_COPY_AND_ASSIGN(HBackwardInstructionIterator);
};
+class HVariableInputSizeInstruction : public HInstruction {
+ public:
+ using HInstruction::GetInputRecords; // Keep the const version visible.
+ ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
+ return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
+ }
+
+ void AddInput(HInstruction* input);
+ void InsertInputAt(size_t index, HInstruction* input);
+ void RemoveInputAt(size_t index);
+
+ protected:
+ HVariableInputSizeInstruction(SideEffects side_effects,
+ uint32_t dex_pc,
+ ArenaAllocator* arena,
+ size_t number_of_inputs,
+ ArenaAllocKind kind)
+ : HInstruction(side_effects, dex_pc),
+ inputs_(number_of_inputs, arena->Adapter(kind)) {}
+
+ ArenaVector<HUserRecord<HInstruction*>> inputs_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction);
+};
+
template<size_t N>
class HTemplateInstruction: public HInstruction {
public:
@@ -2438,15 +2471,19 @@
DISALLOW_COPY_AND_ASSIGN(HReturn);
};
-class HPhi FINAL : public HInstruction {
+class HPhi FINAL : public HVariableInputSizeInstruction {
public:
HPhi(ArenaAllocator* arena,
uint32_t reg_number,
size_t number_of_inputs,
Primitive::Type type,
uint32_t dex_pc = kNoDexPc)
- : HInstruction(SideEffects::None(), dex_pc),
- inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
+ : HVariableInputSizeInstruction(
+ SideEffects::None(),
+ dex_pc,
+ arena,
+ number_of_inputs,
+ kArenaAllocPhiInputs),
reg_number_(reg_number) {
SetPackedField<TypeField>(ToPhiType(type));
DCHECK_NE(GetType(), Primitive::kPrimVoid);
@@ -2464,14 +2501,6 @@
bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
- using HInstruction::GetInputRecords; // Keep the const version visible.
- ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
- return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
- }
-
- void AddInput(HInstruction* input);
- void RemoveInputAt(size_t index);
-
Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
void SetType(Primitive::Type new_type) {
// Make sure that only valid type changes occur. The following are allowed:
@@ -2527,7 +2556,6 @@
static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
- ArenaVector<HUserRecord<HInstruction*>> inputs_;
const uint32_t reg_number_;
DISALLOW_COPY_AND_ASSIGN(HPhi);
@@ -2904,14 +2932,20 @@
// if it's true, starts to do deoptimization.
// It has a 4-byte slot on stack.
// TODO: allocate a register for this flag.
-class HShouldDeoptimizeFlag FINAL : public HExpression<0> {
+class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction {
public:
- // TODO: use SideEffects to aid eliminating some CHA guards.
- explicit HShouldDeoptimizeFlag(uint32_t dex_pc)
- : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
+ // CHA guards are only optimized in a separate pass and it has no side effects
+ // with regard to other passes.
+ HShouldDeoptimizeFlag(ArenaAllocator* arena, uint32_t dex_pc)
+ : HVariableInputSizeInstruction(SideEffects::None(), dex_pc, arena, 0, kArenaAllocCHA) {
}
- // We don't eliminate CHA guards yet.
+ Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimInt; }
+
+ // We do all CHA guard elimination/motion in a single pass, after which there is no
+ // further guard elimination/motion since a guard might have been used for justification
+ // of the elimination of another guard. Therefore, we pretend this guard cannot be moved
+ // to avoid other optimizations trying to move it.
bool CanBeMoved() const OVERRIDE { return false; }
DECLARE_INSTRUCTION(ShouldDeoptimizeFlag);
@@ -3791,15 +3825,10 @@
kCanThrow // Intrinsic may throw exceptions.
};
-class HInvoke : public HInstruction {
+class HInvoke : public HVariableInputSizeInstruction {
public:
bool NeedsEnvironment() const OVERRIDE;
- using HInstruction::GetInputRecords; // Keep the const version visible.
- ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
- return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
- }
-
void SetArgumentAt(size_t index, HInstruction* argument) {
SetRawInputAt(index, argument);
}
@@ -3878,12 +3907,14 @@
uint32_t dex_method_index,
ArtMethod* resolved_method,
InvokeType invoke_type)
- : HInstruction(
- SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays.
+ : HVariableInputSizeInstruction(
+ SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays.
+ dex_pc,
+ arena,
+ number_of_arguments + number_of_other_inputs,
+ kArenaAllocInvokeInputs),
number_of_arguments_(number_of_arguments),
resolved_method_(resolved_method),
- inputs_(number_of_arguments + number_of_other_inputs,
- arena->Adapter(kArenaAllocInvokeInputs)),
dex_method_index_(dex_method_index),
intrinsic_(Intrinsics::kNone),
intrinsic_optimizations_(0) {
@@ -3894,7 +3925,6 @@
uint32_t number_of_arguments_;
ArtMethod* const resolved_method_;
- ArenaVector<HUserRecord<HInstruction*>> inputs_;
const uint32_t dex_method_index_;
Intrinsics intrinsic_;
@@ -3952,12 +3982,6 @@
// Used for app->boot calls with non-relocatable image and for JIT-compiled calls.
kDirectAddress,
- // Use ArtMethod* at an address that will be known at link time, embed the direct
- // address in the code. If the image is relocatable, emit .patch_oat entry.
- // Used for app->boot calls with relocatable image and boot->boot calls, whether
- // the image relocatable or not.
- kDirectAddressWithFixup,
-
// Load from resolved methods array in the dex cache using a PC-relative load.
// Used when we need to use the dex cache, for example for invoke-static that
// may cause class initialization (the entry may point to a resolution method),
@@ -3976,20 +4000,6 @@
// Recursive call, use local PC-relative call instruction.
kCallSelf,
- // Use PC-relative call instruction patched at link time.
- // Used for calls within an oat file, boot->boot or app->app.
- kCallPCRelative,
-
- // Call to a known target address, embed the direct address in code.
- // Used for app->boot call with non-relocatable image and for JIT-compiled calls.
- kCallDirect,
-
- // Call to a target address that will be known at link time, embed the direct
- // address in code. If the image is relocatable, emit .patch_oat entry.
- // Used for app->boot calls with relocatable image and boot->boot calls, whether
- // the image relocatable or not.
- kCallDirectWithFixup,
-
// Use code pointer from the ArtMethod*.
// Used when we don't know the target code. This is also the last-resort-kind used when
// other kinds are unimplemented or impractical (i.e. slow) on a particular architecture.
@@ -4005,7 +4015,6 @@
// - the method address for kDirectAddress
// - the dex cache arrays offset for kDexCachePcRel.
uint64_t method_load_data;
- uint64_t direct_code_ptr;
};
HInvokeStaticOrDirect(ArenaAllocator* arena,
@@ -4115,7 +4124,6 @@
return false;
}
}
- bool HasDirectCodePtr() const { return GetCodePtrLocation() == CodePtrLocation::kCallDirect; }
QuickEntrypointEnum GetStringInitEntryPoint() const {
DCHECK(IsStringInit());
@@ -4132,11 +4140,6 @@
return dispatch_info_.method_load_data;
}
- uint64_t GetDirectCodePtr() const {
- DCHECK(HasDirectCodePtr());
- return dispatch_info_.direct_code_ptr;
- }
-
ClinitCheckRequirement GetClinitCheckRequirement() const {
return GetPackedField<ClinitCheckRequirementField>();
}
@@ -4184,10 +4187,6 @@
DECLARE_INSTRUCTION(InvokeStaticOrDirect);
- protected:
- void InsertInputAt(size_t index, HInstruction* input);
- void RemoveInputAt(size_t index);
-
private:
static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits;
static constexpr size_t kFieldClinitCheckRequirementSize =
@@ -5519,9 +5518,7 @@
const DexFile& dex_file,
bool is_referrers_class,
uint32_t dex_pc,
- bool needs_access_check,
- bool is_in_dex_cache,
- bool is_in_boot_image)
+ bool needs_access_check)
: HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
special_input_(HUserRecord<HInstruction*>(current_method)),
type_index_(type_index),
@@ -5534,8 +5531,8 @@
SetPackedField<LoadKindField>(
is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod);
SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
- SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
- SetPackedFlag<kFlagIsInBootImage>(is_in_boot_image);
+ SetPackedFlag<kFlagIsInDexCache>(false);
+ SetPackedFlag<kFlagIsInBootImage>(false);
SetPackedFlag<kFlagGenerateClInitCheck>(false);
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index ba7012a..4bf5b08 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -54,6 +54,7 @@
#include "base/timing_logger.h"
#include "bounds_check_elimination.h"
#include "builder.h"
+#include "cha_guard_optimization.h"
#include "code_generator.h"
#include "compiled_method.h"
#include "compiler.h"
@@ -517,6 +518,8 @@
return new (arena) SideEffectsAnalysis(graph);
} else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
return new (arena) HLoopOptimization(graph, most_recent_induction);
+ } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
+ return new (arena) CHAGuardOptimization(graph);
#ifdef ART_ENABLE_CODEGEN_arm
} else if (opt_name == arm::DexCacheArrayFixups::kDexCacheArrayFixupsArmPassName) {
return new (arena) arm::DexCacheArrayFixups(graph, codegen, stats);
@@ -779,6 +782,7 @@
InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier$before_codegen");
IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats);
+ CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph);
HOptimization* optimizations1[] = {
intrinsics,
@@ -807,6 +811,7 @@
fold3, // evaluates code generated by dynamic bce
simplify3,
lse,
+ cha_guard,
dce3,
// The codegen has a few assumptions that only the instruction simplifier
// can satisfy. For example, the code generator does not expect to see a
@@ -1193,7 +1198,7 @@
}
uint8_t* stack_map_data = nullptr;
uint8_t* roots_data = nullptr;
- code_cache->ReserveData(
+ uint32_t data_size = code_cache->ReserveData(
self, stack_map_size, number_of_roots, method, &stack_map_data, &roots_data);
if (stack_map_data == nullptr || roots_data == nullptr) {
return false;
@@ -1212,6 +1217,7 @@
codegen->GetFpuSpillMask(),
code_allocator.GetMemory().data(),
code_allocator.GetSize(),
+ data_size,
osr,
roots,
codegen->GetGraph()->HasShouldDeoptimizeFlag(),
diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc
index 82feb95..e321b9e 100644
--- a/compiler/optimizing/pc_relative_fixups_mips.cc
+++ b/compiler/optimizing/pc_relative_fixups_mips.cc
@@ -45,10 +45,6 @@
}
private:
- void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
- HandleInvoke(invoke);
- }
-
void InitializePCRelativeBasePointer() {
// Ensure we only initialize the pointer once.
if (base_ != nullptr) {
@@ -112,38 +108,6 @@
block->ReplaceAndRemoveInstructionWith(switch_insn, mips_switch);
}
- void HandleInvoke(HInvoke* invoke) {
- // If this is an invoke-static/-direct with PC-relative dex cache array
- // addressing, we need the PC-relative address base.
- HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect();
- if (invoke_static_or_direct != nullptr) {
- HInvokeStaticOrDirect::MethodLoadKind method_load_kind =
- invoke_static_or_direct->GetMethodLoadKind();
- HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
- invoke_static_or_direct->GetCodePtrLocation();
-
- bool has_extra_input =
- (method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
- (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup);
-
- // We can't add a pointer to the constant area if we already have a current
- // method pointer. This may arise when sharpening doesn't remove the current
- // method pointer from the invoke.
- if (invoke_static_or_direct->HasCurrentMethodInput()) {
- DCHECK(!invoke_static_or_direct->HasPcRelativeDexCache());
- CHECK(!has_extra_input);
- return;
- }
-
- if (has_extra_input &&
- !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) {
- InitializePCRelativeBasePointer();
- // Add the extra parameter base_.
- invoke_static_or_direct->AddSpecialInput(base_);
- }
- }
- }
-
CodeGeneratorMIPS* codegen_;
// The generated HMipsComputeBaseMethodAddress in the entry block needed as an
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 5891350..c6579dc 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -17,7 +17,8 @@
#ifndef ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_
#define ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "nodes.h"
namespace art {
@@ -108,7 +109,7 @@
: HPrettyPrinter(graph), str_(""), current_block_(nullptr) { }
void PrintInt(int value) OVERRIDE {
- str_ += StringPrintf("%d", value);
+ str_ += android::base::StringPrintf("%d", value);
}
void PrintString(const char* value) OVERRIDE {
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 951cdfb..1af94f3 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -15,7 +15,6 @@
*/
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "dex_file.h"
#include "dex_instruction.h"
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index c191c66..33b3875 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -270,7 +270,7 @@
ScopedObjectAccess soa(Thread::Current());
HInstruction* insert_point = notNullBlock->GetFirstInstruction();
ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create(
- handle_cache_.GetObjectClassHandle(), /* is_exact */ true);
+ handle_cache_.GetObjectClassHandle(), /* is_exact */ false);
if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) {
bound_type = new (graph_->GetArena()) HBoundType(obj);
bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false);
@@ -411,7 +411,9 @@
HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
bound_type = new (graph_->GetArena()) HBoundType(obj);
- bound_type->SetUpperBound(class_rti, /* InstanceOf fails for null. */ false);
+ bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
+ bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
+ /* InstanceOf fails for null. */ false);
instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point);
} else {
// We already have a bound type on the position we would need to insert
@@ -605,15 +607,17 @@
// Narrow the type as much as possible.
HInstruction* obj = instr->InputAt(0);
ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
- if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
- instr->SetReferenceTypeInfo(
- ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true));
+ if (class_rti.IsExact()) {
+ instr->SetReferenceTypeInfo(class_rti);
} else if (obj_rti.IsValid()) {
if (class_rti.IsSupertypeOf(obj_rti)) {
// Object type is more specific.
instr->SetReferenceTypeInfo(obj_rti);
} else {
- // Upper bound is more specific.
+ // Upper bound is more specific, or unrelated to the object's type.
+ // Note that the object might then be exact, and we know the code dominated by this
+ // bound type is dead. To not confuse potential other optimizations, we mark
+ // the bound as non-exact.
instr->SetReferenceTypeInfo(
ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false));
}
@@ -644,8 +648,11 @@
if (class_rti.IsValid()) {
DCHECK(is_first_run_);
+ ScopedObjectAccess soa(Thread::Current());
// This is the first run of RTP and class is resolved.
- bound_type->SetUpperBound(class_rti, /* CheckCast succeeds for nulls. */ true);
+ bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
+ bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
+ /* CheckCast succeeds for nulls. */ true);
} else {
// This is the first run of RTP and class is unresolved. Remove the binding.
// The instruction itself is removed in VisitBoundType so as to not
@@ -795,21 +802,25 @@
}
void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) {
- ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo();
- if (!new_rti.IsValid()) {
+ ReferenceTypeInfo input_rti = instr->InputAt(0)->GetReferenceTypeInfo();
+ if (!input_rti.IsValid()) {
return; // No new info yet.
}
- // Make sure that we don't go over the bounded type.
ReferenceTypeInfo upper_bound_rti = instr->GetUpperBound();
- if (!upper_bound_rti.IsSupertypeOf(new_rti)) {
- // Note that the input might be exact, in which case we know the branch leading
- // to the bound type is dead. We play it safe by not marking the bound type as
- // exact.
- bool is_exact = upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
- new_rti = ReferenceTypeInfo::Create(upper_bound_rti.GetTypeHandle(), is_exact);
+ if (upper_bound_rti.IsExact()) {
+ instr->SetReferenceTypeInfo(upper_bound_rti);
+ } else if (upper_bound_rti.IsSupertypeOf(input_rti)) {
+ // input is more specific.
+ instr->SetReferenceTypeInfo(input_rti);
+ } else {
+ // upper_bound is more specific or unrelated.
+ // Note that the object might then be exact, and we know the code dominated by this
+ // bound type is dead. To not confuse potential other optimizations, we mark
+ // the bound as non-exact.
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(upper_bound_rti.GetTypeHandle(), /* is_exact */ false));
}
- instr->SetReferenceTypeInfo(new_rti);
}
// NullConstant inputs are ignored during merging as they do not provide any useful information.
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index bbbb1a1..9fdeccf 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -54,6 +54,24 @@
}
}
+static bool IsInBootImage(ArtMethod* method) {
+ const std::vector<gc::space::ImageSpace*>& image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ for (gc::space::ImageSpace* image_space : image_spaces) {
+ const auto& method_section = image_space->GetImageHeader().GetMethodsSection();
+ if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) {
+ // Including patch information means the AOT code will be patched, which we don't
+ // support in the compiler, and is anyways moving away b/33192586.
+ return IsInBootImage(method) && !options.GetCompilePic() && !options.GetIncludePatchInformation();
+}
+
void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
if (invoke->IsStringInit()) {
// Not using the dex cache arrays. But we could still try to use a better dispatch...
@@ -61,68 +79,42 @@
return;
}
- HGraph* outer_graph = codegen_->GetGraph();
- ArtMethod* compiling_method = graph_->GetArtMethod();
+ ArtMethod* callee = invoke->GetResolvedMethod();
+ DCHECK(callee != nullptr);
HInvokeStaticOrDirect::MethodLoadKind method_load_kind;
HInvokeStaticOrDirect::CodePtrLocation code_ptr_location;
uint64_t method_load_data = 0u;
- uint64_t direct_code_ptr = 0u;
- if (invoke->GetResolvedMethod() == outer_graph->GetArtMethod()) {
- DCHECK(outer_graph->GetArtMethod() != nullptr);
+ // Note: we never call an ArtMethod through a known code pointer, as
+ // we do not want to keep on invoking it if it gets deoptimized. This
+ // applies to both AOT and JIT.
+ // This also avoids having to find out if the code pointer of an ArtMethod
+ // is the resolution trampoline (for ensuring the class is initialized), or
+ // the interpreter entrypoint. Such code pointers we do not want to call
+ // directly.
+ // Only in the case of a recursive call can we call directly, as we know the
+ // class is initialized already or being initialized, and the call will not
+ // be invoked once the method is deoptimized.
+
+ if (callee == codegen_->GetGraph()->GetArtMethod()) {
+ // Recursive call.
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive;
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf;
+ } else if (Runtime::Current()->UseJitCompilation() ||
+ AOTCanEmbedMethod(callee, codegen_->GetCompilerOptions())) {
+ // JIT or on-device AOT compilation referencing a boot image method.
+ // Use the method address directly.
+ method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
+ method_load_data = reinterpret_cast<uintptr_t>(callee);
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
} else {
- uintptr_t direct_code, direct_method;
- {
- ScopedObjectAccess soa(Thread::Current());
- compiler_driver_->GetCodeAndMethodForDirectCall(
- (compiling_method == nullptr) ? nullptr : compiling_method->GetDeclaringClass(),
- invoke->GetResolvedMethod(),
- &direct_code,
- &direct_method);
- }
- if (direct_method != 0u) { // Should we use a direct pointer to the method?
- // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
- // kDirectAddress would be fine for image methods, we don't support it at the moment.
- DCHECK(!Runtime::Current()->UseJitCompilation());
- if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now?
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
- method_load_data = direct_method;
- } else { // The direct pointer will be known at link time.
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup;
- }
- } else { // Use dex cache.
- if (!Runtime::Current()->UseJitCompilation()) {
- // Use PC-relative access to the dex cache arrays.
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
- DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
- &graph_->GetDexFile());
- method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex());
- } else { // We must go through the ArtMethod's pointer to resolved methods.
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
- }
- }
- if (direct_code != 0u) { // Should we use a direct pointer to the code?
- // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
- // while kCallDirect would be fine for image methods, we don't support it at the moment.
- DCHECK(!Runtime::Current()->UseJitCompilation());
- const DexFile* dex_file_of_callee = invoke->GetTargetMethod().dex_file;
- if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now?
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
- direct_code_ptr = direct_code;
- } else if (ContainsElement(compiler_driver_->GetDexFilesForOatFile(), dex_file_of_callee)) {
- // Use PC-relative calls for invokes within a multi-dex oat file.
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative;
- } else { // The direct pointer will be known at link time.
- // NOTE: This is used for app->boot calls when compiling an app against
- // a relocatable but not yet relocated image.
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup;
- }
- } else { // We must use the code pointer from the ArtMethod.
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
- }
+ // Use PC-relative access to the dex cache arrays.
+ method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
+ DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
+ &graph_->GetDexFile());
+ method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex());
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
}
if (graph_->IsDebuggable()) {
@@ -132,7 +124,7 @@
}
HInvokeStaticOrDirect::DispatchInfo desired_dispatch_info = {
- method_load_kind, code_ptr_location, method_load_data, direct_code_ptr
+ method_load_kind, code_ptr_location, method_load_data
};
HInvokeStaticOrDirect::DispatchInfo dispatch_info =
codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke);
@@ -140,6 +132,25 @@
}
void HSharpening::ProcessLoadClass(HLoadClass* load_class) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ const DexFile& dex_file = load_class->GetDexFile();
+ dex::TypeIndex type_index = load_class->GetTypeIndex();
+ Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
+ ? compilation_unit_.GetDexCache()
+ : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
+ mirror::Class* cls = dex_cache->GetResolvedType(type_index);
+ SharpenClass(load_class, cls, handles_, codegen_, compiler_driver_);
+}
+
+void HSharpening::SharpenClass(HLoadClass* load_class,
+ mirror::Class* klass,
+ VariableSizedHandleScope* handles,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver) {
+ ScopedAssertNoThreadSuspension sants("Sharpening class in compiler");
DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod ||
load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass)
<< load_class->GetLoadKind();
@@ -153,67 +164,60 @@
bool is_in_boot_image = false;
HLoadClass::LoadKind desired_load_kind = static_cast<HLoadClass::LoadKind>(-1);
uint64_t address = 0u; // Class or dex cache element address.
- {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
- ? compilation_unit_.GetDexCache()
- : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
- mirror::Class* klass = dex_cache->GetResolvedType(type_index);
- if (codegen_->GetCompilerOptions().IsBootImage()) {
- // Compiling boot image. Check if the class is a boot image class.
- DCHECK(!runtime->UseJitCompilation());
- if (!compiler_driver_->GetSupportBootImageFixup()) {
- // MIPS64 or compiler_driver_test. Do not sharpen.
- desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
- } else if ((klass != nullptr) && compiler_driver_->IsImageClass(
- dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
- is_in_boot_image = true;
- is_in_dex_cache = true;
- desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
- ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
- : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
- } else {
- // Not a boot image class. We must go through the dex cache.
- DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
- desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
- }
+ Runtime* runtime = Runtime::Current();
+ if (codegen->GetCompilerOptions().IsBootImage()) {
+ // Compiling boot image. Check if the class is a boot image class.
+ DCHECK(!runtime->UseJitCompilation());
+ if (!compiler_driver->GetSupportBootImageFixup()) {
+ // MIPS64 or compiler_driver_test. Do not sharpen.
+ desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+ } else if ((klass != nullptr) && compiler_driver->IsImageClass(
+ dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
+ is_in_boot_image = true;
+ is_in_dex_cache = true;
+ desired_load_kind = codegen->GetCompilerOptions().GetCompilePic()
+ ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
+ : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
} else {
- is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
- if (runtime->UseJitCompilation()) {
- // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
- // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
- is_in_dex_cache = (klass != nullptr);
- if (is_in_boot_image) {
- // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
- desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
- address = reinterpret_cast64<uint64_t>(klass);
- } else if (is_in_dex_cache) {
- desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
- // We store in the address field the location of the stack reference maintained
- // by the handle. We do this now so that the code generation does not need to figure
- // out which class loader to use.
- address = reinterpret_cast<uint64_t>(handles_->NewHandle(klass).GetReference());
- } else {
- // Class not loaded yet. Fallback to the dex cache.
- // TODO(ngeoffray): Generate HDeoptimize instead.
- desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
- }
- } else if (is_in_boot_image && !codegen_->GetCompilerOptions().GetCompilePic()) {
- // AOT app compilation. Check if the class is in the boot image.
+ // Not a boot image class. We must go through the dex cache.
+ DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file));
+ desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
+ }
+ } else {
+ is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
+ if (runtime->UseJitCompilation()) {
+ // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+ // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+ is_in_dex_cache = (klass != nullptr);
+ if (is_in_boot_image) {
+ // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
address = reinterpret_cast64<uint64_t>(klass);
+ } else if (is_in_dex_cache) {
+ desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
+ // We store in the address field the location of the stack reference maintained
+ // by the handle. We do this now so that the code generation does not need to figure
+ // out which class loader to use.
+ address = reinterpret_cast<uint64_t>(handles->NewHandle(klass).GetReference());
} else {
- // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
- // Use PC-relative load from the dex cache if the dex file belongs
- // to the oat file that we're currently compiling.
- desired_load_kind =
- ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_class->GetDexFile())
- ? HLoadClass::LoadKind::kDexCachePcRelative
- : HLoadClass::LoadKind::kDexCacheViaMethod;
+ // Class not loaded yet. This happens when the dex code requesting
+ // this `HLoadClass` hasn't been executed in the interpreter.
+ // Fallback to the dex cache.
+ // TODO(ngeoffray): Generate HDeoptimize instead.
+ desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
}
+ } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) {
+ // AOT app compilation. Check if the class is in the boot image.
+ desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+ address = reinterpret_cast64<uint64_t>(klass);
+ } else {
+ // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
+ // Use PC-relative load from the dex cache if the dex file belongs
+ // to the oat file that we're currently compiling.
+ desired_load_kind =
+ ContainsElement(compiler_driver->GetDexFilesForOatFile(), &load_class->GetDexFile())
+ ? HLoadClass::LoadKind::kDexCachePcRelative
+ : HLoadClass::LoadKind::kDexCacheViaMethod;
}
}
DCHECK_NE(desired_load_kind, static_cast<HLoadClass::LoadKind>(-1));
@@ -239,7 +243,7 @@
load_class->MarkInDexCache();
}
- HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind);
+ HLoadClass::LoadKind load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind);
switch (load_kind) {
case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
@@ -252,7 +256,7 @@
load_class->SetLoadKindWithAddress(load_kind, address);
break;
case HLoadClass::LoadKind::kDexCachePcRelative: {
- PointerSize pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ PointerSize pointer_size = InstructionSetPointerSize(codegen->GetInstructionSet());
DexCacheArraysLayout layout(pointer_size, &dex_file);
size_t element_index = layout.TypeOffset(type_index);
load_class->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index);
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 7418954..ae5ccb3 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -47,6 +47,14 @@
static constexpr const char* kSharpeningPassName = "sharpening";
+ // Used internally but also by the inliner.
+ static void SharpenClass(HLoadClass* load_class,
+ mirror::Class* klass,
+ VariableSizedHandleScope* handles,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
void ProcessLoadClass(HLoadClass* load_class);
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 4297634..f69f417 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
+#include "android-base/stringprintf.h"
+
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "dex_file.h"
#include "dex_instruction.h"
@@ -35,7 +36,7 @@
explicit SsaPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {}
void PrintInt(int value) OVERRIDE {
- str_ += StringPrintf("%d", value);
+ str_ += android::base::StringPrintf("%d", value);
}
void PrintString(const char* value) OVERRIDE {
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index 1614d04..e5eef37 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -23,6 +23,9 @@
using namespace vixl::aarch32; // NOLINT(build/namespaces)
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
+
namespace art {
namespace arm {
@@ -441,7 +444,7 @@
return;
}
Cmp(rn, 0);
- B(eq, label);
+ B(eq, label, is_far_target);
}
void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn,
@@ -452,16 +455,16 @@
return;
}
Cmp(rn, 0);
- B(ne, label);
+ B(ne, label, is_far_target);
}
void ArmVIXLMacroAssembler::B(vixl32::Label* label) {
if (!label->IsBound()) {
// Try to use 16-bit T2 encoding of B instruction.
DCHECK(OutsideITBlock());
- AssemblerAccurateScope ass(this,
- kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope guard(this,
+ k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
b(al, Narrow, label);
AddBranchLabel(label);
return;
@@ -469,13 +472,22 @@
MacroAssembler::B(label);
}
-void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label) {
+void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target) {
+ if (!label->IsBound() && !is_far_target) {
+ // Try to use 16-bit T2 encoding of B instruction.
+ DCHECK(OutsideITBlock());
+ ExactAssemblyScope guard(this,
+ k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ b(cond, Narrow, label);
+ AddBranchLabel(label);
+ return;
+ }
// To further reduce the Bcc encoding size and use 16-bit T1 encoding,
// we can provide a hint to this function: i.e. far_target=false.
// By default this function uses 'EncodingSizeType::Best' which generates 32-bit T3 encoding.
MacroAssembler::B(cond, label);
}
-
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index 17cf106..322f6c4 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -39,6 +39,13 @@
class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler {
public:
+ // Most methods fit in a 1KB code buffer, which results in more optimal alloc/realloc and
+ // fewer system calls than a larger default capacity.
+ static constexpr size_t kDefaultCodeBufferCapacity = 1 * KB;
+
+ ArmVIXLMacroAssembler()
+ : vixl32::MacroAssembler(ArmVIXLMacroAssembler::kDefaultCodeBufferCapacity) {}
+
// The following interfaces can generate CMP+Bcc or Cbz/Cbnz.
// CMP+Bcc are generated by default.
// If a hint is given (is_far_target = false) and rn and label can all fit into Cbz/Cbnz,
@@ -114,7 +121,7 @@
// TODO: Remove when MacroAssembler::Add(FlagsUpdate, Condition, Register, Register, Operand)
// makes the right decision about 16-bit encodings.
void Add(vixl32::Register rd, vixl32::Register rn, const vixl32::Operand& operand) {
- if (rd.Is(rn)) {
+ if (rd.Is(rn) && operand.IsPlainRegister()) {
MacroAssembler::Add(rd, rn, operand);
} else {
MacroAssembler::Add(vixl32::DontCare, rd, rn, operand);
@@ -124,7 +131,10 @@
// These interfaces try to use 16-bit T2 encoding of B instruction.
void B(vixl32::Label* label);
- void B(vixl32::Condition cond, vixl32::Label* label);
+ // For B(label), we always try to use Narrow encoding, because 16-bit T2 encoding supports
+ // jumping within 2KB range. For B(cond, label), because the supported branch range is 256
+ // bytes; we use the far_target hint to try to use 16-bit T1 encoding for short range jumps.
+ void B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target = true);
};
class ArmVIXLAssembler FINAL : public Assembler {
@@ -205,6 +215,15 @@
int32_t value,
vixl32::Condition cond = vixl32::al);
+ template <typename T>
+ vixl::aarch32::Literal<T>* CreateLiteralDestroyedWithPool(T value) {
+ vixl::aarch32::Literal<T>* literal =
+ new vixl::aarch32::Literal<T>(value,
+ vixl32::RawLiteral::kPlacedWhenUsed,
+ vixl32::RawLiteral::kDeletedOnPoolDestruction);
+ return literal;
+ }
+
private:
// VIXL assembler.
ArmVIXLMacroAssembler vixl_masm_;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 30e8f4e..0147a76 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -16,12 +16,15 @@
#include "assembler_thumb2.h"
+#include "android-base/stringprintf.h"
+
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "utils/assembler_test.h"
namespace art {
+using android::base::StringPrintf;
+
class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
arm::Register, arm::SRegister,
uint32_t> {
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 2d026b8..d07c047 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -24,6 +24,9 @@
using namespace vixl::aarch32; // NOLINT(build/namespaces)
namespace vixl32 = vixl::aarch32;
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
+
namespace art {
namespace arm {
@@ -455,16 +458,16 @@
if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
if (!out_reg.Equals(in_reg)) {
- AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- 3 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+ 3 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
___ it(eq, 0xc);
___ mov(eq, out_reg.AsVIXLRegister(), 0);
asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
} else {
- AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- 2 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+ 2 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
___ it(ne, 0x8);
asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
}
@@ -493,9 +496,9 @@
___ Cmp(scratch.AsVIXLRegister(), 0);
if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
- AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- 2 * vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+ 2 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
___ it(ne, 0x8);
asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
} else {
@@ -586,10 +589,12 @@
___ Cmp(scratch.AsVIXLRegister(), 0);
{
- AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- vixl32::kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
- ___ b(ne, Narrow, exception_blocks_.back()->Entry());
+ ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ vixl32::Label* label = exception_blocks_.back()->Entry();
+ ___ b(ne, Narrow, label);
+ ___ AddBranchLabel(label);
}
// TODO: think about using CBNZ here.
}
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index b34e125..5c48759 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -145,7 +145,8 @@
const std::vector<Reg2*> reg2_registers,
std::string (AssemblerTest::*GetName1)(const Reg1&),
std::string (AssemblerTest::*GetName2)(const Reg2&),
- const std::string& fmt) {
+ const std::string& fmt,
+ int bias = 0) {
std::string str;
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
@@ -153,7 +154,7 @@
for (auto reg2 : reg2_registers) {
for (int64_t imm : imms) {
ImmType new_imm = CreateImmediate(imm);
- (assembler_.get()->*f)(*reg1, *reg2, new_imm);
+ (assembler_.get()->*f)(*reg1, *reg2, new_imm + bias);
std::string base = fmt;
std::string reg1_string = (this->*GetName1)(*reg1);
@@ -171,7 +172,7 @@
size_t imm_index = base.find(IMM_TOKEN);
if (imm_index != std::string::npos) {
std::ostringstream sreg;
- sreg << imm;
+ sreg << imm + bias;
std::string imm_string = sreg.str();
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
}
@@ -188,6 +189,67 @@
return str;
}
+ template <typename Reg1, typename Reg2, typename Reg3, typename ImmType>
+ std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, Reg3, ImmType),
+ int imm_bits,
+ const std::vector<Reg1*> reg1_registers,
+ const std::vector<Reg2*> reg2_registers,
+ const std::vector<Reg3*> reg3_registers,
+ std::string (AssemblerTest::*GetName1)(const Reg1&),
+ std::string (AssemblerTest::*GetName2)(const Reg2&),
+ std::string (AssemblerTest::*GetName3)(const Reg3&),
+ std::string fmt,
+ int bias) {
+ std::string str;
+ std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
+
+ for (auto reg1 : reg1_registers) {
+ for (auto reg2 : reg2_registers) {
+ for (auto reg3 : reg3_registers) {
+ for (int64_t imm : imms) {
+ ImmType new_imm = CreateImmediate(imm);
+ (assembler_.get()->*f)(*reg1, *reg2, *reg3, new_imm + bias);
+ std::string base = fmt;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+ base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+ }
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+ base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+ }
+
+ std::string reg3_string = (this->*GetName3)(*reg3);
+ size_t reg3_index;
+ while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
+ base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
+ }
+
+ size_t imm_index = base.find(IMM_TOKEN);
+ if (imm_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << imm + bias;
+ std::string imm_string = sreg.str();
+ base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
+ }
+
+ if (str.size() > 0) {
+ str += "\n";
+ }
+ str += base;
+ }
+ }
+ }
+ }
+ // Add a newline at the end.
+ str += "\n";
+ return str;
+ }
+
template <typename ImmType, typename Reg1, typename Reg2>
std::string RepeatTemplatedImmBitsRegisters(void (Ass::*f)(ImmType, Reg1, Reg2),
const std::vector<Reg1*> reg1_registers,
@@ -245,14 +307,15 @@
int imm_bits,
const std::vector<Reg*> registers,
std::string (AssemblerTest::*GetName)(const RegType&),
- const std::string& fmt) {
+ const std::string& fmt,
+ int bias) {
std::string str;
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
for (auto reg : registers) {
for (int64_t imm : imms) {
ImmType new_imm = CreateImmediate(imm);
- (assembler_.get()->*f)(*reg, new_imm);
+ (assembler_.get()->*f)(*reg, new_imm + bias);
std::string base = fmt;
std::string reg_string = (this->*GetName)(*reg);
@@ -264,7 +327,7 @@
size_t imm_index = base.find(IMM_TOKEN);
if (imm_index != std::string::npos) {
std::ostringstream sreg;
- sreg << imm;
+ sreg << imm + bias;
std::string imm_string = sreg.str();
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
}
@@ -281,36 +344,60 @@
}
template <typename ImmType>
- std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), int imm_bits, const std::string& fmt) {
+ std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType),
+ int imm_bits,
+ const std::string& fmt,
+ int bias = 0) {
return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f,
imm_bits,
GetRegisters(),
GetRegisters(),
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
- fmt);
+ fmt,
+ bias);
}
template <typename ImmType>
- std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, const std::string& fmt) {
+ std::string RepeatRRRIb(void (Ass::*f)(Reg, Reg, Reg, ImmType),
+ int imm_bits,
+ const std::string& fmt,
+ int bias = 0) {
+ return RepeatTemplatedRegistersImmBits<Reg, Reg, Reg, ImmType>(f,
+ imm_bits,
+ GetRegisters(),
+ GetRegisters(),
+ GetRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ fmt,
+ bias);
+ }
+
+ template <typename ImmType>
+ std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt, int bias = 0) {
return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f,
imm_bits,
GetRegisters(),
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
- fmt);
+ fmt,
+ bias);
}
template <typename ImmType>
std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType),
int imm_bits,
- const std::string& fmt) {
+ const std::string& fmt,
+ int bias = 0) {
return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f,
imm_bits,
GetFPRegisters(),
GetRegisters(),
&AssemblerTest::GetFPRegName,
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
- fmt);
+ fmt,
+ bias);
}
std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), const std::string& fmt) {
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 50a1d9f..4e9b619 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1717,6 +1717,11 @@
__ ExceptionPoll(scratch_register, 0);
+ // Push the target out of range of branch emitted by ExceptionPoll.
+ for (int i = 0; i < 64; i++) {
+ __ Store(FrameOffset(2047), scratch_register, 4);
+ }
+
__ DecreaseFrameSize(4096);
__ DecreaseFrameSize(32);
__ RemoveFrame(frame_size, callee_save_regs);
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 69e1d8f..ab4f9e9 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5458,94 +5458,160 @@
};
const char* const VixlJniHelpersResults[] = {
- " 0: e92d 4de0 stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
- " 4: ed2d 8a10 vpush {s16-s31}\n",
- " 8: b089 sub sp, #36 ; 0x24\n",
- " a: 9000 str r0, [sp, #0]\n",
- " c: 9121 str r1, [sp, #132] ; 0x84\n",
- " e: ed8d 0a22 vstr s0, [sp, #136] ; 0x88\n",
- " 12: 9223 str r2, [sp, #140] ; 0x8c\n",
- " 14: 9324 str r3, [sp, #144] ; 0x90\n",
- " 16: b088 sub sp, #32\n",
- " 18: f5ad 5d80 sub.w sp, sp, #4096 ; 0x1000\n",
- " 1c: 9808 ldr r0, [sp, #32]\n",
- " 1e: 981f ldr r0, [sp, #124] ; 0x7c\n",
- " 20: 9821 ldr r0, [sp, #132] ; 0x84\n",
- " 22: 98ff ldr r0, [sp, #1020] ; 0x3fc\n",
- " 24: f8dd 0400 ldr.w r0, [sp, #1024] ; 0x400\n",
- " 28: f8dd cffc ldr.w ip, [sp, #4092] ; 0xffc\n",
- " 2c: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
- " 30: f8dc c000 ldr.w ip, [ip]\n",
- " 34: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
- " 38: f8dc 0080 ldr.w r0, [ip, #128] ; 0x80\n",
- " 3c: 9008 str r0, [sp, #32]\n",
- " 3e: 901f str r0, [sp, #124] ; 0x7c\n",
- " 40: 9021 str r0, [sp, #132] ; 0x84\n",
- " 42: 90ff str r0, [sp, #1020] ; 0x3fc\n",
- " 44: f8cd 0400 str.w r0, [sp, #1024] ; 0x400\n",
- " 48: f8cd cffc str.w ip, [sp, #4092] ; 0xffc\n",
- " 4c: f84d 5d04 str.w r5, [sp, #-4]!\n",
- " 50: f50d 5580 add.w r5, sp, #4096 ; 0x1000\n",
- " 54: f8c5 c004 str.w ip, [r5, #4]\n",
- " 58: f85d 5b04 ldr.w r5, [sp], #4\n",
- " 5c: f04f 0cff mov.w ip, #255 ; 0xff\n",
- " 60: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 64: f06f 4c7f mvn.w ip, #4278190080 ; 0xff000000\n",
- " 68: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 6c: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 70: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 74: 900c str r0, [sp, #48] ; 0x30\n",
- " 76: f8dd c030 ldr.w ip, [sp, #48] ; 0x30\n",
- " 7a: f8cd c034 str.w ip, [sp, #52] ; 0x34\n",
- " 7e: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
- " 82: f8c9 c200 str.w ip, [r9, #512] ; 0x200\n",
- " 86: f8c9 d200 str.w sp, [r9, #512] ; 0x200\n",
- " 8a: f8d0 c030 ldr.w ip, [r0, #48] ; 0x30\n",
- " 8e: 47e0 blx ip\n",
- " 90: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
- " 94: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 98: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
- " 9c: f8cd c02c str.w ip, [sp, #44] ; 0x2c\n",
- " a0: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
- " a4: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " a8: 4648 mov r0, r9\n",
- " aa: f8cd 9030 str.w r9, [sp, #48] ; 0x30\n",
- " ae: 4684 mov ip, r0\n",
- " b0: f1bc 0f00 cmp.w ip, #0\n",
- " b4: bf18 it ne\n",
- " b6: f10d 0c30 addne.w ip, sp, #48 ; 0x30\n",
- " ba: f10d 0c30 add.w ip, sp, #48 ; 0x30\n",
- " be: f1bc 0f00 cmp.w ip, #0\n",
- " c2: bf0c ite eq\n",
- " c4: 2000 moveq r0, #0\n",
- " c6: a80c addne r0, sp, #48 ; 0x30\n",
- " c8: f8dd c040 ldr.w ip, [sp, #64] ; 0x40\n",
- " cc: f1bc 0f00 cmp.w ip, #0\n",
- " d0: bf18 it ne\n",
- " d2: f10d 0c40 addne.w ip, sp, #64 ; 0x40\n",
- " d6: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " da: f1bc 0f00 cmp.w ip, #0\n",
- " de: bf0c ite eq\n",
- " e0: 2000 moveq r0, #0\n",
- " e2: 4668 movne r0, sp\n",
- " e4: f1bc 0f00 cmp.w ip, #0\n",
- " e8: bf0c ite eq\n",
- " ea: 2000 moveq r0, #0\n",
- " ec: f20d 4001 addwne r0, sp, #1025 ; 0x401\n",
- " f0: f1bc 0f00 cmp.w ip, #0\n",
- " f4: bf18 it ne\n",
- " f6: f20d 4c01 addwne ip, sp, #1025 ; 0x401\n",
- " fa: f8d9 c084 ldr.w ip, [r9, #132] ; 0x84\n",
- " fe: f1bc 0f00 cmp.w ip, #0\n",
- " 102: d107 bne.n 114 <VixlJniHelpers+0x114>\n",
- " 104: f50d 5d80 add.w sp, sp, #4096 ; 0x1000\n",
- " 108: b008 add sp, #32\n",
- " 10a: b009 add sp, #36 ; 0x24\n",
- " 10c: ecbd 8a10 vpop {s16-s31}\n",
- " 110: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
- " 114: 4660 mov r0, ip\n",
- " 116: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n",
- " 11a: 47e0 blx ip\n",
+ " 0: e92d 4de0 stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
+ " 4: ed2d 8a10 vpush {s16-s31}\n",
+ " 8: b089 sub sp, #36 ; 0x24\n",
+ " a: 9000 str r0, [sp, #0]\n",
+ " c: 9121 str r1, [sp, #132] ; 0x84\n",
+ " e: ed8d 0a22 vstr s0, [sp, #136] ; 0x88\n",
+ " 12: 9223 str r2, [sp, #140] ; 0x8c\n",
+ " 14: 9324 str r3, [sp, #144] ; 0x90\n",
+ " 16: b088 sub sp, #32\n",
+ " 18: f5ad 5d80 sub.w sp, sp, #4096 ; 0x1000\n",
+ " 1c: 9808 ldr r0, [sp, #32]\n",
+ " 1e: 981f ldr r0, [sp, #124] ; 0x7c\n",
+ " 20: 9821 ldr r0, [sp, #132] ; 0x84\n",
+ " 22: 98ff ldr r0, [sp, #1020] ; 0x3fc\n",
+ " 24: f8dd 0400 ldr.w r0, [sp, #1024] ; 0x400\n",
+ " 28: f8dd cffc ldr.w ip, [sp, #4092] ; 0xffc\n",
+ " 2c: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
+ " 30: f8dc c000 ldr.w ip, [ip]\n",
+ " 34: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
+ " 38: f8dc 0080 ldr.w r0, [ip, #128] ; 0x80\n",
+ " 3c: 9008 str r0, [sp, #32]\n",
+ " 3e: 901f str r0, [sp, #124] ; 0x7c\n",
+ " 40: 9021 str r0, [sp, #132] ; 0x84\n",
+ " 42: 90ff str r0, [sp, #1020] ; 0x3fc\n",
+ " 44: f8cd 0400 str.w r0, [sp, #1024] ; 0x400\n",
+ " 48: f8cd cffc str.w ip, [sp, #4092] ; 0xffc\n",
+ " 4c: f84d 5d04 str.w r5, [sp, #-4]!\n",
+ " 50: f50d 5580 add.w r5, sp, #4096 ; 0x1000\n",
+ " 54: f8c5 c004 str.w ip, [r5, #4]\n",
+ " 58: f85d 5b04 ldr.w r5, [sp], #4\n",
+ " 5c: f04f 0cff mov.w ip, #255 ; 0xff\n",
+ " 60: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 64: f06f 4c7f mvn.w ip, #4278190080 ; 0xff000000\n",
+ " 68: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 6c: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 70: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 74: 900c str r0, [sp, #48] ; 0x30\n",
+ " 76: f8dd c030 ldr.w ip, [sp, #48] ; 0x30\n",
+ " 7a: f8cd c034 str.w ip, [sp, #52] ; 0x34\n",
+ " 7e: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
+ " 82: f8c9 c200 str.w ip, [r9, #512] ; 0x200\n",
+ " 86: f8c9 d200 str.w sp, [r9, #512] ; 0x200\n",
+ " 8a: f8d0 c030 ldr.w ip, [r0, #48] ; 0x30\n",
+ " 8e: 47e0 blx ip\n",
+ " 90: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
+ " 94: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 98: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
+ " 9c: f8cd c02c str.w ip, [sp, #44] ; 0x2c\n",
+ " a0: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
+ " a4: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " a8: 4648 mov r0, r9\n",
+ " aa: f8cd 9030 str.w r9, [sp, #48] ; 0x30\n",
+ " ae: 4684 mov ip, r0\n",
+ " b0: f1bc 0f00 cmp.w ip, #0\n",
+ " b4: bf18 it ne\n",
+ " b6: f10d 0c30 addne.w ip, sp, #48 ; 0x30\n",
+ " ba: f10d 0c30 add.w ip, sp, #48 ; 0x30\n",
+ " be: f1bc 0f00 cmp.w ip, #0\n",
+ " c2: bf0c ite eq\n",
+ " c4: 2000 moveq r0, #0\n",
+ " c6: a80c addne r0, sp, #48 ; 0x30\n",
+ " c8: f8dd c040 ldr.w ip, [sp, #64] ; 0x40\n",
+ " cc: f1bc 0f00 cmp.w ip, #0\n",
+ " d0: bf18 it ne\n",
+ " d2: f10d 0c40 addne.w ip, sp, #64 ; 0x40\n",
+ " d6: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " da: f1bc 0f00 cmp.w ip, #0\n",
+ " de: bf0c ite eq\n",
+ " e0: 2000 moveq r0, #0\n",
+ " e2: 4668 movne r0, sp\n",
+ " e4: f1bc 0f00 cmp.w ip, #0\n",
+ " e8: bf0c ite eq\n",
+ " ea: 2000 moveq r0, #0\n",
+ " ec: f20d 4001 addwne r0, sp, #1025 ; 0x401\n",
+ " f0: f1bc 0f00 cmp.w ip, #0\n",
+ " f4: bf18 it ne\n",
+ " f6: f20d 4c01 addwne ip, sp, #1025 ; 0x401\n",
+ " fa: f8d9 c084 ldr.w ip, [r9, #132] ; 0x84\n",
+ " fe: f1bc 0f00 cmp.w ip, #0\n",
+ " 102: d171 bne.n 1e8 <VixlJniHelpers+0x1e8>\n",
+ " 104: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 108: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 10c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 110: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 114: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 118: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 11c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 120: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 124: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 128: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 12c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 130: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 134: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 138: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 13c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 140: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 144: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 148: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 14c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 150: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 154: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 158: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 15c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 160: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 164: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 168: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 16c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 170: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 174: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 178: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 17c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 180: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 184: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 188: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 18c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 190: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 194: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 198: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 19c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1a0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1a4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1a8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1ac: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1b0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1b4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1b8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1bc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1c0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1c4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1c8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1cc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1d0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1d4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1d8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1dc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1e0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1e4: f000 b802 b.w 1ec <VixlJniHelpers+0x1ec>\n",
+ " 1e8: f000 b818 b.w 21c <VixlJniHelpers+0x21c>\n",
+ " 1ec: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1f0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1f4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1f8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1fc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 200: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 204: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 208: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 20c: f50d 5d80 add.w sp, sp, #4096 ; 0x1000\n",
+ " 210: b008 add sp, #32\n",
+ " 212: b009 add sp, #36 ; 0x24\n",
+ " 214: ecbd 8a10 vpop {s16-s31}\n",
+ " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
+ " 21c: 4660 mov r0, ip\n",
+ " 21e: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n",
+ " 222: 47e0 blx ip\n",
nullptr
};
diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h
index ac54813..c06e9ca 100644
--- a/compiler/utils/dedupe_set-inl.h
+++ b/compiler/utils/dedupe_set-inl.h
@@ -23,10 +23,11 @@
#include <inttypes.h>
#include <unordered_map>
+#include "android-base/stringprintf.h"
+
#include "base/mutex.h"
#include "base/hash_set.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "base/time_utils.h"
namespace art {
@@ -238,13 +239,13 @@
for (HashType shard = 0; shard < kShard; ++shard) {
shards_[shard]->UpdateStats(self, &stats);
}
- return StringPrintf("%zu collisions, %zu max hash collisions, "
- "%zu/%zu probe distance, %" PRIu64 " ns hash time",
- stats.collision_sum,
- stats.collision_max,
- stats.total_probe_distance,
- stats.total_size,
- hash_time_);
+ return android::base::StringPrintf("%zu collisions, %zu max hash collisions, "
+ "%zu/%zu probe distance, %" PRIu64 " ns hash time",
+ stats.collision_sum,
+ stats.collision_max,
+ stats.total_probe_distance,
+ stats.total_size,
+ hash_time_);
}
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 3dcad6a..5e83e82 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -635,6 +635,13 @@
DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04), rd, rd, rt);
}
+void MipsAssembler::Lsa(Register rd, Register rs, Register rt, int saPlusOne) {
+ CHECK(IsR6());
+ CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
+ int sa = saPlusOne - 1;
+ DsFsmInstrRrr(EmitR(0x0, rs, rt, rd, sa, 0x05), rd, rs, rt);
+}
+
void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
}
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 800dc5f..2fca185 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -262,6 +262,7 @@
void Srav(Register rd, Register rt, Register rs);
void Ext(Register rd, Register rt, int pos, int size); // R2+
void Ins(Register rd, Register rt, int pos, int size); // R2+
+ void Lsa(Register rd, Register rs, Register rt, int saPlusOne); // R6
void Lb(Register rt, Register rs, uint16_t imm16);
void Lh(Register rt, Register rs, uint16_t imm16);
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index a52f519..30667ef 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -319,6 +319,14 @@
DriverStr(RepeatRR(&mips::MipsAssembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
}
+TEST_F(AssemblerMIPS32r6Test, Lsa) {
+ DriverStr(RepeatRRRIb(&mips::MipsAssembler::Lsa,
+ 2,
+ "lsa ${reg1}, ${reg2}, ${reg3}, {imm}",
+ 1),
+ "lsa");
+}
+
TEST_F(AssemblerMIPS32r6Test, Seleqz) {
DriverStr(RepeatRRR(&mips::MipsAssembler::Seleqz, "seleqz ${reg1}, ${reg2}, ${reg3}"),
"seleqz");
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 1a21df9..998f2c7 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -35,11 +35,14 @@
for (auto& exception_block : exception_blocks_) {
EmitExceptionPoll(&exception_block);
}
+ ReserveJumpTableSpace();
+ EmitLiterals();
PromoteBranches();
}
void Mips64Assembler::FinalizeInstructions(const MemoryRegion& region) {
EmitBranches();
+ EmitJumpTables();
Assembler::FinalizeInstructions(region);
PatchCFI();
}
@@ -318,6 +321,18 @@
EmitR(0x1f, rs, rt, static_cast<GpuRegister>(pos + size - 33), pos - 32, 0x6);
}
+void Mips64Assembler::Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne) {
+ CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
+ int sa = saPlusOne - 1;
+ EmitR(0x0, rs, rt, rd, sa, 0x05);
+}
+
+void Mips64Assembler::Dlsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne) {
+ CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
+ int sa = saPlusOne - 1;
+ EmitR(0x0, rs, rt, rd, sa, 0x15);
+}
+
void Mips64Assembler::Wsbh(GpuRegister rd, GpuRegister rt) {
EmitRtd(0x1f, rt, rd, 2, 0x20);
}
@@ -450,10 +465,29 @@
EmitI(0x27, rs, rt, imm16);
}
+void Mips64Assembler::Lwpc(GpuRegister rs, uint32_t imm19) {
+ CHECK(IsUint<19>(imm19)) << imm19;
+ EmitI21(0x3B, rs, (0x01 << 19) | imm19);
+}
+
+void Mips64Assembler::Lwupc(GpuRegister rs, uint32_t imm19) {
+ CHECK(IsUint<19>(imm19)) << imm19;
+ EmitI21(0x3B, rs, (0x02 << 19) | imm19);
+}
+
+void Mips64Assembler::Ldpc(GpuRegister rs, uint32_t imm18) {
+ CHECK(IsUint<18>(imm18)) << imm18;
+ EmitI21(0x3B, rs, (0x06 << 18) | imm18);
+}
+
void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) {
EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16);
}
+void Mips64Assembler::Aui(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0xf, rs, rt, imm16);
+}
+
void Mips64Assembler::Dahi(GpuRegister rs, uint16_t imm16) {
EmitI(1, rs, static_cast<GpuRegister>(6), imm16);
}
@@ -548,6 +582,10 @@
EmitI26(0x32, imm26);
}
+void Mips64Assembler::Balc(uint32_t imm26) {
+ EmitI26(0x3A, imm26);
+}
+
void Mips64Assembler::Jic(GpuRegister rt, uint16_t imm16) {
EmitI(0x36, static_cast<GpuRegister>(0), rt, imm16);
}
@@ -1049,6 +1087,20 @@
TemplateLoadConst64(this, rd, value);
}
+void Mips64Assembler::Addiu32(GpuRegister rt, GpuRegister rs, int32_t value) {
+ if (IsInt<16>(value)) {
+ Addiu(rt, rs, value);
+ } else {
+ int16_t high = High16Bits(value);
+ int16_t low = Low16Bits(value);
+ high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
+ Aui(rt, rs, high);
+ if (low != 0) {
+ Addiu(rt, rt, low);
+ }
+ }
+}
+
void Mips64Assembler::Daddiu64(GpuRegister rt, GpuRegister rs, int64_t value, GpuRegister rtmp) {
if (IsInt<16>(value)) {
Daddiu(rt, rs, value);
@@ -1064,19 +1116,37 @@
type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
}
-void Mips64Assembler::Branch::InitializeType(bool is_call) {
+void Mips64Assembler::Branch::InitializeType(Type initial_type) {
OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
- if (is_call) {
- InitShortOrLong(offset_size, kCall, kLongCall);
- } else if (condition_ == kUncond) {
- InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
- } else {
- if (condition_ == kCondEQZ || condition_ == kCondNEZ) {
- // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
- type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch;
- } else {
- InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
- }
+ switch (initial_type) {
+ case kLabel:
+ case kLiteral:
+ case kLiteralUnsigned:
+ case kLiteralLong:
+ CHECK(!IsResolved());
+ type_ = initial_type;
+ break;
+ case kCall:
+ InitShortOrLong(offset_size, kCall, kLongCall);
+ break;
+ case kCondBranch:
+ switch (condition_) {
+ case kUncond:
+ InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
+ break;
+ case kCondEQZ:
+ case kCondNEZ:
+ // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
+ type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch;
+ break;
+ default:
+ InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
+ break;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected branch type " << initial_type;
+ UNREACHABLE();
}
old_type_ = type_;
}
@@ -1109,14 +1179,14 @@
}
}
-Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target)
+Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, bool is_call)
: old_location_(location),
location_(location),
target_(target),
lhs_reg_(ZERO),
rhs_reg_(ZERO),
condition_(kUncond) {
- InitializeType(false);
+ InitializeType(is_call ? kCall : kCondBranch);
}
Mips64Assembler::Branch::Branch(uint32_t location,
@@ -1164,19 +1234,18 @@
// Branch condition is always true, make the branch unconditional.
condition_ = kUncond;
}
- InitializeType(false);
+ InitializeType(kCondBranch);
}
-Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg)
+Mips64Assembler::Branch::Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type)
: old_location_(location),
location_(location),
- target_(target),
- lhs_reg_(indirect_reg),
+ target_(kUnresolved),
+ lhs_reg_(dest_reg),
rhs_reg_(ZERO),
condition_(kUncond) {
- CHECK_NE(indirect_reg, ZERO);
- CHECK_NE(indirect_reg, AT);
- InitializeType(true);
+ CHECK_NE(dest_reg, ZERO);
+ InitializeType(label_or_literal_type);
}
Mips64Assembler::BranchCondition Mips64Assembler::Branch::OppositeCondition(
@@ -1278,11 +1347,23 @@
case kUncondBranch:
case kCondBranch:
case kCall:
+ // Near label.
+ case kLabel:
+ // Near literals.
+ case kLiteral:
+ case kLiteralUnsigned:
+ case kLiteralLong:
return false;
// Long branches.
case kLongUncondBranch:
case kLongCondBranch:
case kLongCall:
+ // Far label.
+ case kFarLabel:
+ // Far literals.
+ case kFarLiteral:
+ case kFarLiteralUnsigned:
+ case kFarLiteralLong:
return true;
}
UNREACHABLE();
@@ -1351,6 +1432,20 @@
case kCall:
type_ = kLongCall;
break;
+ // Near label.
+ case kLabel:
+ type_ = kFarLabel;
+ break;
+ // Near literals.
+ case kLiteral:
+ type_ = kFarLiteral;
+ break;
+ case kLiteralUnsigned:
+ type_ = kFarLiteralUnsigned;
+ break;
+ case kLiteralLong:
+ type_ = kFarLiteralLong;
+ break;
default:
// Note: 'type_' is already long.
break;
@@ -1397,7 +1492,15 @@
uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
// Calculate the byte distance between instructions and also account for
// different PC-relative origins.
- uint32_t offset = target_ - GetOffsetLocation() - branch_info_[type_].pc_org * sizeof(uint32_t);
+ uint32_t offset_location = GetOffsetLocation();
+ if (type_ == kLiteralLong) {
+ // Special case for the ldpc instruction, whose address (PC) is rounded down to
+ // a multiple of 8 before adding the offset.
+ // Note, branch promotion has already taken care of aligning `target_` to an
+ // address that's a multiple of 8.
+ offset_location = RoundDown(offset_location, sizeof(uint64_t));
+ }
+ uint32_t offset = target_ - offset_location - branch_info_[type_].pc_org * sizeof(uint32_t);
// Prepare the offset for encoding into the instruction(s).
offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
return offset;
@@ -1444,7 +1547,7 @@
label->BindTo(bound_pc);
}
-uint32_t Mips64Assembler::GetLabelLocation(Mips64Label* label) const {
+uint32_t Mips64Assembler::GetLabelLocation(const Mips64Label* label) const {
CHECK(label->IsBound());
uint32_t target = label->Position();
if (label->prev_branch_id_plus_one_) {
@@ -1500,7 +1603,7 @@
void Mips64Assembler::Buncond(Mips64Label* label) {
uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
- branches_.emplace_back(buffer_.Size(), target);
+ branches_.emplace_back(buffer_.Size(), target, /* is_call */ false);
FinalizeLabeledBranch(label);
}
@@ -1517,12 +1620,148 @@
FinalizeLabeledBranch(label);
}
-void Mips64Assembler::Call(Mips64Label* label, GpuRegister indirect_reg) {
+void Mips64Assembler::Call(Mips64Label* label) {
uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
- branches_.emplace_back(buffer_.Size(), target, indirect_reg);
+ branches_.emplace_back(buffer_.Size(), target, /* is_call */ true);
FinalizeLabeledBranch(label);
}
+void Mips64Assembler::LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label) {
+ // Label address loads are treated as pseudo branches since they require very similar handling.
+ DCHECK(!label->IsBound());
+ branches_.emplace_back(buffer_.Size(), dest_reg, Branch::kLabel);
+ FinalizeLabeledBranch(label);
+}
+
+Literal* Mips64Assembler::NewLiteral(size_t size, const uint8_t* data) {
+ // We don't support byte and half-word literals.
+ if (size == 4u) {
+ literals_.emplace_back(size, data);
+ return &literals_.back();
+ } else {
+ DCHECK_EQ(size, 8u);
+ long_literals_.emplace_back(size, data);
+ return &long_literals_.back();
+ }
+}
+
+void Mips64Assembler::LoadLiteral(GpuRegister dest_reg,
+ LoadOperandType load_type,
+ Literal* literal) {
+ // Literal loads are treated as pseudo branches since they require very similar handling.
+ Branch::Type literal_type;
+ switch (load_type) {
+ case kLoadWord:
+ DCHECK_EQ(literal->GetSize(), 4u);
+ literal_type = Branch::kLiteral;
+ break;
+ case kLoadUnsignedWord:
+ DCHECK_EQ(literal->GetSize(), 4u);
+ literal_type = Branch::kLiteralUnsigned;
+ break;
+ case kLoadDoubleword:
+ DCHECK_EQ(literal->GetSize(), 8u);
+ literal_type = Branch::kLiteralLong;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected literal load type " << load_type;
+ UNREACHABLE();
+ }
+ Mips64Label* label = literal->GetLabel();
+ DCHECK(!label->IsBound());
+ branches_.emplace_back(buffer_.Size(), dest_reg, literal_type);
+ FinalizeLabeledBranch(label);
+}
+
+JumpTable* Mips64Assembler::CreateJumpTable(std::vector<Mips64Label*>&& labels) {
+ jump_tables_.emplace_back(std::move(labels));
+ JumpTable* table = &jump_tables_.back();
+ DCHECK(!table->GetLabel()->IsBound());
+ return table;
+}
+
+void Mips64Assembler::ReserveJumpTableSpace() {
+ if (!jump_tables_.empty()) {
+ for (JumpTable& table : jump_tables_) {
+ Mips64Label* label = table.GetLabel();
+ Bind(label);
+
+ // Bulk ensure capacity, as this may be large.
+ size_t orig_size = buffer_.Size();
+ size_t required_capacity = orig_size + table.GetSize();
+ if (required_capacity > buffer_.Capacity()) {
+ buffer_.ExtendCapacity(required_capacity);
+ }
+#ifndef NDEBUG
+ buffer_.has_ensured_capacity_ = true;
+#endif
+
+ // Fill the space with dummy data as the data is not final
+ // until the branches have been promoted. And we shouldn't
+ // be moving uninitialized data during branch promotion.
+ for (size_t cnt = table.GetData().size(), i = 0; i < cnt; i++) {
+ buffer_.Emit<uint32_t>(0x1abe1234u);
+ }
+
+#ifndef NDEBUG
+ buffer_.has_ensured_capacity_ = false;
+#endif
+ }
+ }
+}
+
+void Mips64Assembler::EmitJumpTables() {
+ if (!jump_tables_.empty()) {
+ CHECK(!overwriting_);
+ // Switch from appending instructions at the end of the buffer to overwriting
+ // existing instructions (here, jump tables) in the buffer.
+ overwriting_ = true;
+
+ for (JumpTable& table : jump_tables_) {
+ Mips64Label* table_label = table.GetLabel();
+ uint32_t start = GetLabelLocation(table_label);
+ overwrite_location_ = start;
+
+ for (Mips64Label* target : table.GetData()) {
+ CHECK_EQ(buffer_.Load<uint32_t>(overwrite_location_), 0x1abe1234u);
+ // The table will contain target addresses relative to the table start.
+ uint32_t offset = GetLabelLocation(target) - start;
+ Emit(offset);
+ }
+ }
+
+ overwriting_ = false;
+ }
+}
+
+void Mips64Assembler::EmitLiterals() {
+ if (!literals_.empty()) {
+ for (Literal& literal : literals_) {
+ Mips64Label* label = literal.GetLabel();
+ Bind(label);
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ DCHECK_EQ(literal.GetSize(), 4u);
+ for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
+ buffer_.Emit<uint8_t>(literal.GetData()[i]);
+ }
+ }
+ }
+ if (!long_literals_.empty()) {
+ // Reserve 4 bytes for potential alignment. If after the branch promotion the 64-bit
+ // literals don't end up 8-byte-aligned, they will be moved down 4 bytes.
+ Emit(0); // NOP.
+ for (Literal& literal : long_literals_) {
+ Mips64Label* label = literal.GetLabel();
+ Bind(label);
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ DCHECK_EQ(literal.GetSize(), 8u);
+ for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
+ buffer_.Emit<uint8_t>(literal.GetData()[i]);
+ }
+ }
+ }
+}
+
void Mips64Assembler::PromoteBranches() {
// Promote short branches to long as necessary.
bool changed;
@@ -1561,6 +1800,35 @@
end = branch.GetOldLocation();
}
}
+
+ // Align 64-bit literals by moving them down by 4 bytes if needed.
+ // This will reduce the PC-relative distance, which should be safe for both near and far literals.
+ if (!long_literals_.empty()) {
+ uint32_t first_literal_location = GetLabelLocation(long_literals_.front().GetLabel());
+ size_t lit_size = long_literals_.size() * sizeof(uint64_t);
+ size_t buf_size = buffer_.Size();
+ // 64-bit literals must be at the very end of the buffer.
+ CHECK_EQ(first_literal_location + lit_size, buf_size);
+ if (!IsAligned<sizeof(uint64_t)>(first_literal_location)) {
+ buffer_.Move(first_literal_location - sizeof(uint32_t), first_literal_location, lit_size);
+ // The 4 reserved bytes proved useless, reduce the buffer size.
+ buffer_.Resize(buf_size - sizeof(uint32_t));
+ // Reduce target addresses in literal and address loads by 4 bytes in order for correct
+ // offsets from PC to be generated.
+ for (auto& branch : branches_) {
+ uint32_t target = branch.GetTarget();
+ if (target >= first_literal_location) {
+ branch.Resolve(target - sizeof(uint32_t));
+ }
+ }
+ // If after this we ever call GetLabelLocation() to get the location of a 64-bit literal,
+ // we need to adjust the location of the literal's label as well.
+ for (Literal& literal : long_literals_) {
+ // Bound label's position is negative, hence incrementing it instead of decrementing.
+ literal.GetLabel()->position_ += sizeof(uint32_t);
+ }
+ }
+ }
}
// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
@@ -1569,11 +1837,23 @@
{ 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kUncondBranch
{ 2, 0, 1, Mips64Assembler::Branch::kOffset18, 2 }, // kCondBranch
// Exception: kOffset23 for beqzc/bnezc
- { 2, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kCall
+ { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kCall
+ // Near label.
+ { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLabel
+ // Near literals.
+ { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLiteral
+ { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLiteralUnsigned
+ { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 3 }, // kLiteralLong
// Long branches.
{ 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongUncondBranch
{ 3, 1, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCondBranch
- { 3, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCall
+ { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCall
+ // Far label.
+ { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLabel
+ // Far literals.
+ { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteral
+ { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteralUnsigned
+ { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteralLong
};
// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
@@ -1597,8 +1877,26 @@
break;
case Branch::kCall:
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Balc(offset);
+ break;
+
+ // Near label.
+ case Branch::kLabel:
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Addiupc(lhs, offset);
- Jialc(lhs, 0);
+ break;
+ // Near literals.
+ case Branch::kLiteral:
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Lwpc(lhs, offset);
+ break;
+ case Branch::kLiteralUnsigned:
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Lwupc(lhs, offset);
+ break;
+ case Branch::kLiteralLong:
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Ldpc(lhs, offset);
break;
// Long branches.
@@ -1616,11 +1914,37 @@
Jic(AT, Low16Bits(offset));
break;
case Branch::kLongCall:
+ offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Auipc(AT, High16Bits(offset));
+ Jialc(AT, Low16Bits(offset));
+ break;
+
+ // Far label.
+ case Branch::kFarLabel:
offset += (offset & 0x8000) << 1; // Account for sign extension in daddiu.
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
- Auipc(lhs, High16Bits(offset));
- Daddiu(lhs, lhs, Low16Bits(offset));
- Jialc(lhs, 0);
+ Auipc(AT, High16Bits(offset));
+ Daddiu(lhs, AT, Low16Bits(offset));
+ break;
+ // Far literals.
+ case Branch::kFarLiteral:
+ offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Auipc(AT, High16Bits(offset));
+ Lw(lhs, AT, Low16Bits(offset));
+ break;
+ case Branch::kFarLiteralUnsigned:
+ offset += (offset & 0x8000) << 1; // Account for sign extension in lwu.
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Auipc(AT, High16Bits(offset));
+ Lwu(lhs, AT, Low16Bits(offset));
+ break;
+ case Branch::kFarLiteralLong:
+ offset += (offset & 0x8000) << 1; // Account for sign extension in ld.
+ CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
+ Auipc(AT, High16Bits(offset));
+ Ld(lhs, AT, Low16Bits(offset));
break;
}
CHECK_EQ(overwrite_location_, branch->GetEndLocation());
@@ -1631,8 +1955,8 @@
Buncond(label);
}
-void Mips64Assembler::Jialc(Mips64Label* label, GpuRegister indirect_reg) {
- Call(label, indirect_reg);
+void Mips64Assembler::Balc(Mips64Label* label) {
+ Call(label);
}
void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 238cb9d..a0a1db6 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -17,9 +17,11 @@
#ifndef ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
#define ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
+#include <deque>
#include <utility>
#include <vector>
+#include "base/arena_containers.h"
#include "base/enums.h"
#include "base/macros.h"
#include "constants_mips64.h"
@@ -312,6 +314,79 @@
DISALLOW_COPY_AND_ASSIGN(Mips64Label);
};
+// Assembler literal is a value embedded in code, retrieved using a PC-relative load.
+class Literal {
+ public:
+ static constexpr size_t kMaxSize = 8;
+
+ Literal(uint32_t size, const uint8_t* data)
+ : label_(), size_(size) {
+ DCHECK_LE(size, Literal::kMaxSize);
+ memcpy(data_, data, size);
+ }
+
+ template <typename T>
+ T GetValue() const {
+ DCHECK_EQ(size_, sizeof(T));
+ T value;
+ memcpy(&value, data_, sizeof(T));
+ return value;
+ }
+
+ uint32_t GetSize() const {
+ return size_;
+ }
+
+ const uint8_t* GetData() const {
+ return data_;
+ }
+
+ Mips64Label* GetLabel() {
+ return &label_;
+ }
+
+ const Mips64Label* GetLabel() const {
+ return &label_;
+ }
+
+ private:
+ Mips64Label label_;
+ const uint32_t size_;
+ uint8_t data_[kMaxSize];
+
+ DISALLOW_COPY_AND_ASSIGN(Literal);
+};
+
+// Jump table: table of labels emitted after the code and before the literals. Similar to literals.
+class JumpTable {
+ public:
+ explicit JumpTable(std::vector<Mips64Label*>&& labels)
+ : label_(), labels_(std::move(labels)) {
+ }
+
+ size_t GetSize() const {
+ return labels_.size() * sizeof(uint32_t);
+ }
+
+ const std::vector<Mips64Label*>& GetData() const {
+ return labels_;
+ }
+
+ Mips64Label* GetLabel() {
+ return &label_;
+ }
+
+ const Mips64Label* GetLabel() const {
+ return &label_;
+ }
+
+ private:
+ Mips64Label label_;
+ std::vector<Mips64Label*> labels_;
+
+ DISALLOW_COPY_AND_ASSIGN(JumpTable);
+};
+
// Slowpath entered when Thread::Current()->_exception is non-null.
class Mips64ExceptionSlowPath {
public:
@@ -341,6 +416,9 @@
: Assembler(arena),
overwriting_(false),
overwrite_location_(0),
+ literals_(arena->Adapter(kArenaAllocAssembler)),
+ long_literals_(arena->Adapter(kArenaAllocAssembler)),
+ jump_tables_(arena->Adapter(kArenaAllocAssembler)),
last_position_adjustment_(0),
last_old_position_(0),
last_branch_id_(0) {
@@ -386,18 +464,20 @@
void Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt);
void Bitswap(GpuRegister rd, GpuRegister rt);
- void Dbitswap(GpuRegister rd, GpuRegister rt);
+ void Dbitswap(GpuRegister rd, GpuRegister rt); // MIPS64
void Seb(GpuRegister rd, GpuRegister rt);
void Seh(GpuRegister rd, GpuRegister rt);
- void Dsbh(GpuRegister rd, GpuRegister rt);
- void Dshd(GpuRegister rd, GpuRegister rt);
+ void Dsbh(GpuRegister rd, GpuRegister rt); // MIPS64
+ void Dshd(GpuRegister rd, GpuRegister rt); // MIPS64
void Dext(GpuRegister rs, GpuRegister rt, int pos, int size); // MIPS64
void Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64
+ void Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne);
+ void Dlsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne); // MIPS64
void Wsbh(GpuRegister rd, GpuRegister rt);
void Sc(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
- void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
+ void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); // MIPS64
void Ll(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
- void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
+ void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); // MIPS64
void Sll(GpuRegister rd, GpuRegister rt, int shamt);
void Srl(GpuRegister rd, GpuRegister rt, int shamt);
@@ -409,7 +489,7 @@
void Srav(GpuRegister rd, GpuRegister rt, GpuRegister rs);
void Dsll(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64
void Dsrl(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64
- void Drotr(GpuRegister rd, GpuRegister rt, int shamt);
+ void Drotr(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64
void Dsra(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64
void Dsll32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64
void Dsrl32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64
@@ -427,7 +507,11 @@
void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
void Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16); // MIPS64
+ void Lwpc(GpuRegister rs, uint32_t imm19);
+ void Lwupc(GpuRegister rs, uint32_t imm19); // MIPS64
+ void Ldpc(GpuRegister rs, uint32_t imm18); // MIPS64
void Lui(GpuRegister rt, uint16_t imm16);
+ void Aui(GpuRegister rt, GpuRegister rs, uint16_t imm16);
void Dahi(GpuRegister rs, uint16_t imm16); // MIPS64
void Dati(GpuRegister rs, uint16_t imm16); // MIPS64
void Sync(uint32_t stype);
@@ -445,8 +529,8 @@
void Selnez(GpuRegister rd, GpuRegister rs, GpuRegister rt);
void Clz(GpuRegister rd, GpuRegister rs);
void Clo(GpuRegister rd, GpuRegister rs);
- void Dclz(GpuRegister rd, GpuRegister rs);
- void Dclo(GpuRegister rd, GpuRegister rs);
+ void Dclz(GpuRegister rd, GpuRegister rs); // MIPS64
+ void Dclo(GpuRegister rd, GpuRegister rs); // MIPS64
void Jalr(GpuRegister rd, GpuRegister rs);
void Jalr(GpuRegister rs);
@@ -454,6 +538,7 @@
void Auipc(GpuRegister rs, uint16_t imm16);
void Addiupc(GpuRegister rs, uint32_t imm19);
void Bc(uint32_t imm26);
+ void Balc(uint32_t imm26);
void Jic(GpuRegister rt, uint16_t imm16);
void Jialc(GpuRegister rt, uint16_t imm16);
void Bltc(GpuRegister rs, GpuRegister rt, uint16_t imm16);
@@ -566,6 +651,7 @@
// This function is only used for testing purposes.
void RecordLoadConst64Path(int value);
+ void Addiu32(GpuRegister rt, GpuRegister rs, int32_t value);
void Daddiu64(GpuRegister rt, GpuRegister rs, int64_t value, GpuRegister rtmp = AT); // MIPS64
void Bind(Label* label) OVERRIDE {
@@ -605,8 +691,32 @@
UNREACHABLE();
}
+ // Create a new literal with a given value.
+ // NOTE: Force the template parameter to be explicitly specified.
+ template <typename T>
+ Literal* NewLiteral(typename Identity<T>::type value) {
+ static_assert(std::is_integral<T>::value, "T must be an integral type.");
+ return NewLiteral(sizeof(value), reinterpret_cast<const uint8_t*>(&value));
+ }
+
+ // Load label address using PC-relative loads. To be used with data labels in the literal /
+ // jump table area only and not with regular code labels.
+ void LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label);
+
+ // Create a new literal with the given data.
+ Literal* NewLiteral(size_t size, const uint8_t* data);
+
+ // Load literal using PC-relative loads.
+ void LoadLiteral(GpuRegister dest_reg, LoadOperandType load_type, Literal* literal);
+
+ // Create a jump table for the given labels that will be emitted when finalizing.
+ // When the table is emitted, offsets will be relative to the location of the table.
+ // The table location is determined by the location of its label (the label precedes
+ // the table data) and should be loaded using LoadLabelAddress().
+ JumpTable* CreateJumpTable(std::vector<Mips64Label*>&& labels);
+
void Bc(Mips64Label* label);
- void Jialc(Mips64Label* label, GpuRegister indirect_reg);
+ void Balc(Mips64Label* label);
void Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label);
void Bltzc(GpuRegister rt, Mips64Label* label);
void Bgtzc(GpuRegister rt, Mips64Label* label);
@@ -756,12 +866,15 @@
// Returns the (always-)current location of a label (can be used in class CodeGeneratorMIPS64,
// must be used instead of Mips64Label::GetPosition()).
- uint32_t GetLabelLocation(Mips64Label* label) const;
+ uint32_t GetLabelLocation(const Mips64Label* label) const;
// Get the final position of a label after local fixup based on the old position
// recorded before FinalizeCode().
uint32_t GetAdjustedPosition(uint32_t old_position);
+ // Note that PC-relative literal loads are handled as pseudo branches because they need very
+ // similar relocation and may similarly expand in size to accomodate for larger offsets relative
+ // to PC.
enum BranchCondition {
kCondLT,
kCondGE,
@@ -791,10 +904,22 @@
kUncondBranch,
kCondBranch,
kCall,
+ // Near label.
+ kLabel,
+ // Near literals.
+ kLiteral,
+ kLiteralUnsigned,
+ kLiteralLong,
// Long branches.
kLongUncondBranch,
kLongCondBranch,
kLongCall,
+ // Far label.
+ kFarLabel,
+ // Far literals.
+ kFarLiteral,
+ kFarLiteralUnsigned,
+ kFarLiteralLong,
};
// Bit sizes of offsets defined as enums to minimize chance of typos.
@@ -830,16 +955,16 @@
};
static const BranchInfo branch_info_[/* Type */];
- // Unconditional branch.
- Branch(uint32_t location, uint32_t target);
+ // Unconditional branch or call.
+ Branch(uint32_t location, uint32_t target, bool is_call);
// Conditional branch.
Branch(uint32_t location,
uint32_t target,
BranchCondition condition,
GpuRegister lhs_reg,
- GpuRegister rhs_reg = ZERO);
- // Call (branch and link) that stores the target address in a given register (i.e. T9).
- Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg);
+ GpuRegister rhs_reg);
+ // Label address (in literal area) or literal.
+ Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type);
// Some conditional branches with lhs = rhs are effectively NOPs, while some
// others are effectively unconditional. MIPSR6 conditional branches require lhs != rhs.
@@ -923,7 +1048,7 @@
private:
// Completes branch construction by determining and recording its type.
- void InitializeType(bool is_call);
+ void InitializeType(Type initial_type);
// Helper for the above.
void InitShortOrLong(OffsetBits ofs_size, Type short_type, Type long_type);
@@ -932,7 +1057,7 @@
uint32_t target_; // Offset into assembler buffer in bytes.
GpuRegister lhs_reg_; // Left-hand side register in conditional branches or
- // indirect call register.
+ // destination register in literals.
GpuRegister rhs_reg_; // Right-hand side register in conditional branches.
BranchCondition condition_; // Condition for conditional branches.
@@ -957,12 +1082,15 @@
BranchCondition condition,
GpuRegister lhs,
GpuRegister rhs = ZERO);
- void Call(Mips64Label* label, GpuRegister indirect_reg);
+ void Call(Mips64Label* label);
void FinalizeLabeledBranch(Mips64Label* label);
Branch* GetBranch(uint32_t branch_id);
const Branch* GetBranch(uint32_t branch_id) const;
+ void EmitLiterals();
+ void ReserveJumpTableSpace();
+ void EmitJumpTables();
void PromoteBranches();
void EmitBranch(Branch* branch);
void EmitBranches();
@@ -981,6 +1109,14 @@
// The current overwrite location.
uint32_t overwrite_location_;
+ // Use std::deque<> for literal labels to allow insertions at the end
+ // without invalidating pointers and references to existing elements.
+ ArenaDeque<Literal> literals_;
+ ArenaDeque<Literal> long_literals_; // 64-bit literals separated for alignment reasons.
+
+ // Jump table list.
+ ArenaDeque<JumpTable> jump_tables_;
+
// Data for AdjustedPosition(), see the description there.
uint32_t last_position_adjustment_;
uint32_t last_old_position_;
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index ba8f25e..f2cbebb 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -576,83 +576,83 @@
RepeatRRNoDupes(&mips64::Mips64Assembler::Jalr, "jalr ${reg1}, ${reg2}"), "jalr");
}
-TEST_F(AssemblerMIPS64Test, Jialc) {
+TEST_F(AssemblerMIPS64Test, Balc) {
mips64::Mips64Label label1, label2;
- __ Jialc(&label1, mips64::T9);
+ __ Balc(&label1);
constexpr size_t kAdduCount1 = 63;
for (size_t i = 0; i != kAdduCount1; ++i) {
__ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
}
__ Bind(&label1);
- __ Jialc(&label2, mips64::T9);
+ __ Balc(&label2);
constexpr size_t kAdduCount2 = 64;
for (size_t i = 0; i != kAdduCount2; ++i) {
__ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
}
__ Bind(&label2);
- __ Jialc(&label1, mips64::T9);
+ __ Balc(&label1);
std::string expected =
".set noreorder\n"
- "lapc $t9, 1f\n"
- "jialc $t9, 0\n" +
+ "balc 1f\n" +
RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
"1:\n"
- "lapc $t9, 2f\n"
- "jialc $t9, 0\n" +
+ "balc 2f\n" +
RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
"2:\n"
- "lapc $t9, 1b\n"
- "jialc $t9, 0\n";
- DriverStr(expected, "Jialc");
+ "balc 1b\n";
+ DriverStr(expected, "Balc");
}
-TEST_F(AssemblerMIPS64Test, LongJialc) {
+TEST_F(AssemblerMIPS64Test, LongBalc) {
+ constexpr uint32_t kNopCount1 = (1u << 25) + 1;
+ constexpr uint32_t kNopCount2 = (1u << 25) + 1;
+ constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u;
+ ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity);
+ __ GetBuffer()->ExtendCapacity(kRequiredCapacity);
mips64::Mips64Label label1, label2;
- __ Jialc(&label1, mips64::T9);
- constexpr uint32_t kAdduCount1 = (1u << 18) + 1;
- for (uint32_t i = 0; i != kAdduCount1; ++i) {
- __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ __ Balc(&label1);
+ for (uint32_t i = 0; i != kNopCount1; ++i) {
+ __ Nop();
}
__ Bind(&label1);
- __ Jialc(&label2, mips64::T9);
- constexpr uint32_t kAdduCount2 = (1u << 18) + 1;
- for (uint32_t i = 0; i != kAdduCount2; ++i) {
- __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ __ Balc(&label2);
+ for (uint32_t i = 0; i != kNopCount2; ++i) {
+ __ Nop();
}
__ Bind(&label2);
- __ Jialc(&label1, mips64::T9);
+ __ Balc(&label1);
- uint32_t offset_forward1 = 3 + kAdduCount1; // 3: account for auipc, daddiu and jic.
+ uint32_t offset_forward1 = 2 + kNopCount1; // 2: account for auipc and jialc.
offset_forward1 <<= 2;
- offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in daddiu.
+ offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in jialc.
- uint32_t offset_forward2 = 3 + kAdduCount2; // 3: account for auipc, daddiu and jic.
+ uint32_t offset_forward2 = 2 + kNopCount2; // 2: account for auipc and jialc.
offset_forward2 <<= 2;
- offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in daddiu.
+ offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in jialc.
- uint32_t offset_back = -(3 + kAdduCount2); // 3: account for auipc, daddiu and jic.
+ uint32_t offset_back = -(2 + kNopCount2); // 2: account for auipc and jialc.
offset_back <<= 2;
- offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in daddiu.
+ offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jialc.
+ // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs
+ // instead of generating them ourselves in the source code. This saves a few minutes
+ // of test time.
std::ostringstream oss;
oss <<
".set noreorder\n"
- "auipc $t9, 0x" << std::hex << High16Bits(offset_forward1) << "\n"
- "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward1) << "\n"
- "jialc $t9, 0\n" <<
- RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") <<
+ "auipc $at, 0x" << std::hex << High16Bits(offset_forward1) << "\n"
+ "jialc $at, 0x" << std::hex << Low16Bits(offset_forward1) << "\n"
+ ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n"
"1:\n"
- "auipc $t9, 0x" << std::hex << High16Bits(offset_forward2) << "\n"
- "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward2) << "\n"
- "jialc $t9, 0\n" <<
- RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") <<
+ "auipc $at, 0x" << std::hex << High16Bits(offset_forward2) << "\n"
+ "jialc $at, 0x" << std::hex << Low16Bits(offset_forward2) << "\n"
+ ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n"
"2:\n"
- "auipc $t9, 0x" << std::hex << High16Bits(offset_back) << "\n"
- "daddiu $t9, 0x" << std::hex << Low16Bits(offset_back) << "\n"
- "jialc $t9, 0\n";
+ "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n"
+ "jialc $at, 0x" << std::hex << Low16Bits(offset_back) << "\n";
std::string expected = oss.str();
- DriverStr(expected, "LongJialc");
+ DriverStr(expected, "LongBalc");
}
TEST_F(AssemblerMIPS64Test, Bc) {
@@ -827,6 +827,258 @@
// MISC //
//////////
+TEST_F(AssemblerMIPS64Test, Lwpc) {
+ // Lwpc() takes an unsigned 19-bit immediate, while the GNU assembler needs a signed offset,
+ // hence the sign extension from bit 18 with `imm - ((imm & 0x40000) << 1)`.
+ // The GNU assembler also wants the offset to be a multiple of 4, which it will shift right
+ // by 2 positions when encoding, hence `<< 2` to compensate for that shift.
+ // We capture the value of the immediate with `.set imm, {imm}` because the value is needed
+ // twice for the sign extension, but `{imm}` is substituted only once.
+ const char* code = ".set imm, {imm}\nlw ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)";
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwpc, 19, code), "Lwpc");
+}
+
+TEST_F(AssemblerMIPS64Test, Lwupc) {
+ // The comment for the Lwpc test applies here as well.
+ const char* code = ".set imm, {imm}\nlwu ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)";
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwupc, 19, code), "Lwupc");
+}
+
+TEST_F(AssemblerMIPS64Test, Ldpc) {
+ // The comment for the Lwpc test applies here as well.
+ const char* code = ".set imm, {imm}\nld ${reg}, ((imm - ((imm & 0x20000) << 1)) << 3)($pc)";
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Ldpc, 18, code), "Ldpc");
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) {
+ mips64::Mips64Label label;
+ __ LoadLabelAddress(mips64::V0, &label);
+ constexpr uint32_t kAdduCount = 0x3FFDE;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+ __ Bind(&label);
+
+ std::string expected =
+ "lapc $v0, 1f\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "1:\n";
+ DriverStr(expected, "LoadFarthestNearLabelAddress");
+ EXPECT_EQ(__ GetLabelLocation(&label), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLabelAddress) {
+ mips64::Mips64Label label;
+ __ LoadLabelAddress(mips64::V0, &label);
+ constexpr uint32_t kAdduCount = 0x3FFDF;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+ __ Bind(&label);
+
+ std::string expected =
+ "1:\n"
+ "auipc $at, %hi(2f - 1b)\n"
+ "daddiu $v0, $at, %lo(2f - 1b)\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "2:\n";
+ DriverStr(expected, "LoadNearestFarLabelAddress");
+ EXPECT_EQ(__ GetLabelLocation(&label), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteral) {
+ mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+ __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal);
+ constexpr uint32_t kAdduCount = 0x3FFDE;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+
+ std::string expected =
+ "lwpc $v0, 1f\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "1:\n"
+ ".word 0x12345678\n";
+ DriverStr(expected, "LoadFarthestNearLiteral");
+ EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteral) {
+ mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+ __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal);
+ constexpr uint32_t kAdduCount = 0x3FFDF;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+
+ std::string expected =
+ "1:\n"
+ "auipc $at, %hi(2f - 1b)\n"
+ "lw $v0, %lo(2f - 1b)($at)\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "2:\n"
+ ".word 0x12345678\n";
+ DriverStr(expected, "LoadNearestFarLiteral");
+ EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralUnsigned) {
+ mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+ __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal);
+ constexpr uint32_t kAdduCount = 0x3FFDE;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+
+ std::string expected =
+ "lwupc $v0, 1f\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "1:\n"
+ ".word 0x12345678\n";
+ DriverStr(expected, "LoadFarthestNearLiteralUnsigned");
+ EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralUnsigned) {
+ mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678);
+ __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal);
+ constexpr uint32_t kAdduCount = 0x3FFDF;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+
+ std::string expected =
+ "1:\n"
+ "auipc $at, %hi(2f - 1b)\n"
+ "lwu $v0, %lo(2f - 1b)($at)\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "2:\n"
+ ".word 0x12345678\n";
+ DriverStr(expected, "LoadNearestFarLiteralUnsigned");
+ EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralLong) {
+ mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+ __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal);
+ constexpr uint32_t kAdduCount = 0x3FFDD;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+
+ std::string expected =
+ "ldpc $v0, 1f\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "1:\n"
+ ".dword 0x0123456789ABCDEF\n";
+ DriverStr(expected, "LoadFarthestNearLiteralLong");
+ EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralLong) {
+ mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+ __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal);
+ constexpr uint32_t kAdduCount = 0x3FFDE;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+
+ std::string expected =
+ "1:\n"
+ "auipc $at, %hi(2f - 1b)\n"
+ "ld $v0, %lo(2f - 1b)($at)\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "2:\n"
+ ".dword 0x0123456789ABCDEF\n";
+ DriverStr(expected, "LoadNearestFarLiteralLong");
+ EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4);
+}
+
+TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNop) {
+ mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+ mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555));
+ mips64::Literal* literal3 = __ NewLiteral<uint64_t>(UINT64_C(0xAAAAAAAAAAAAAAAA));
+ __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1);
+ __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2);
+ __ LoadLiteral(mips64::A3, mips64::kLoadDoubleword, literal3);
+ __ LoadLabelAddress(mips64::V0, literal1->GetLabel());
+ __ LoadLabelAddress(mips64::V1, literal2->GetLabel());
+ // A nop will be inserted here before the 64-bit literals.
+
+ std::string expected =
+ "ldpc $a1, 1f\n"
+ // The GNU assembler incorrectly requires the ldpc instruction to be located
+ // at an address that's a multiple of 8. TODO: Remove this workaround if/when
+ // the assembler is fixed.
+ // "ldpc $a2, 2f\n"
+ ".word 0xECD80004\n"
+ "ldpc $a3, 3f\n"
+ "lapc $v0, 1f\n"
+ "lapc $v1, 2f\n"
+ "nop\n"
+ "1:\n"
+ ".dword 0x0123456789ABCDEF\n"
+ "2:\n"
+ ".dword 0x5555555555555555\n"
+ "3:\n"
+ ".dword 0xAAAAAAAAAAAAAAAA\n";
+ DriverStr(expected, "LongLiteralAlignmentNop");
+ EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 6 * 4u);
+ EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 8 * 4u);
+ EXPECT_EQ(__ GetLabelLocation(literal3->GetLabel()), 10 * 4u);
+}
+
+TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNoNop) {
+ mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+ mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555));
+ __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1);
+ __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2);
+ __ LoadLabelAddress(mips64::V0, literal1->GetLabel());
+ __ LoadLabelAddress(mips64::V1, literal2->GetLabel());
+
+ std::string expected =
+ "ldpc $a1, 1f\n"
+ // The GNU assembler incorrectly requires the ldpc instruction to be located
+ // at an address that's a multiple of 8. TODO: Remove this workaround if/when
+ // the assembler is fixed.
+ // "ldpc $a2, 2f\n"
+ ".word 0xECD80003\n"
+ "lapc $v0, 1f\n"
+ "lapc $v1, 2f\n"
+ "1:\n"
+ ".dword 0x0123456789ABCDEF\n"
+ "2:\n"
+ ".dword 0x5555555555555555\n";
+ DriverStr(expected, "LongLiteralAlignmentNoNop");
+ EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 4 * 4u);
+ EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 6 * 4u);
+}
+
+TEST_F(AssemblerMIPS64Test, FarLongLiteralAlignmentNop) {
+ mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF));
+ __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal);
+ __ LoadLabelAddress(mips64::V1, literal->GetLabel());
+ constexpr uint32_t kAdduCount = 0x3FFDF;
+ for (uint32_t i = 0; i != kAdduCount; ++i) {
+ __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO);
+ }
+ // A nop will be inserted here before the 64-bit literal.
+
+ std::string expected =
+ "1:\n"
+ "auipc $at, %hi(3f - 1b)\n"
+ "ld $v0, %lo(3f - 1b)($at)\n"
+ "2:\n"
+ "auipc $at, %hi(3f - 2b)\n"
+ "daddiu $v1, $at, %lo(3f - 2b)\n" +
+ RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
+ "nop\n"
+ "3:\n"
+ ".dword 0x0123456789ABCDEF\n";
+ DriverStr(expected, "FarLongLiteralAlignmentNop");
+ EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4);
+}
+
TEST_F(AssemblerMIPS64Test, Bitswap) {
DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
}
@@ -889,6 +1141,22 @@
DriverStr(expected.str(), "Dinsu");
}
+TEST_F(AssemblerMIPS64Test, Lsa) {
+ DriverStr(RepeatRRRIb(&mips64::Mips64Assembler::Lsa,
+ 2,
+ "lsa ${reg1}, ${reg2}, ${reg3}, {imm}",
+ 1),
+ "lsa");
+}
+
+TEST_F(AssemblerMIPS64Test, Dlsa) {
+ DriverStr(RepeatRRRIb(&mips64::Mips64Assembler::Dlsa,
+ 2,
+ "dlsa ${reg1}, ${reg2}, ${reg3}, {imm}",
+ 1),
+ "dlsa");
+}
+
TEST_F(AssemblerMIPS64Test, Wsbh) {
DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh");
}
@@ -1636,9 +1904,9 @@
DriverStr(expected, "StoreFpuToOffset");
}
-///////////////////////
-// Loading Constants //
-///////////////////////
+//////////////////////////////
+// Loading/adding Constants //
+//////////////////////////////
TEST_F(AssemblerMIPS64Test, LoadConst32) {
// IsUint<16>(value)
@@ -1681,6 +1949,31 @@
DriverStr(expected, "LoadConst32");
}
+TEST_F(AssemblerMIPS64Test, Addiu32) {
+ __ Addiu32(mips64::A1, mips64::A2, -0x8000);
+ __ Addiu32(mips64::A1, mips64::A2, +0);
+ __ Addiu32(mips64::A1, mips64::A2, +0x7FFF);
+ __ Addiu32(mips64::A1, mips64::A2, -0x8001);
+ __ Addiu32(mips64::A1, mips64::A2, +0x8000);
+ __ Addiu32(mips64::A1, mips64::A2, -0x10000);
+ __ Addiu32(mips64::A1, mips64::A2, +0x10000);
+ __ Addiu32(mips64::A1, mips64::A2, +0x12345678);
+
+ const char* expected =
+ "addiu $a1, $a2, -0x8000\n"
+ "addiu $a1, $a2, 0\n"
+ "addiu $a1, $a2, 0x7FFF\n"
+ "aui $a1, $a2, 0xFFFF\n"
+ "addiu $a1, $a1, 0x7FFF\n"
+ "aui $a1, $a2, 1\n"
+ "addiu $a1, $a1, -0x8000\n"
+ "aui $a1, $a2, 0xFFFF\n"
+ "aui $a1, $a2, 1\n"
+ "aui $a1, $a2, 0x1234\n"
+ "addiu $a1, $a1, 0x5678\n";
+ DriverStr(expected, "Addiu32");
+}
+
static uint64_t SignExtend16To64(uint16_t n) {
return static_cast<int16_t>(n);
}
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 52765f9..85ae61f 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -508,7 +508,7 @@
/* src */ "LMySSLSocket;",
/* is_strict */ true,
/* is_assignable */ true));
- ASSERT_TRUE(HasAssignable("Ljava/net/Socket;", "LMySSLSocket;", true));
+ ASSERT_TRUE(HasAssignable("Ljava/net/Socket;", "Ljavax/net/ssl/SSLSocket;", true));
}
TEST_F(VerifierDepsTest, Assignable_DestinationInBoot2) {
@@ -516,7 +516,7 @@
/* src */ "LMySimpleTimeZone;",
/* is_strict */ true,
/* is_assignable */ true));
- ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "LMySimpleTimeZone;", true));
+ ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
}
TEST_F(VerifierDepsTest, Assignable_DestinationInBoot3) {
@@ -539,21 +539,9 @@
ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
}
-TEST_F(VerifierDepsTest, Assignable_BothArrays_Erroneous) {
- ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "[[Ljava/util/TimeZone;",
- /* src */ "[[LMyErroneousTimeZone;",
- /* is_strict */ true,
- /* is_assignable */ true));
- // If the component type of an array is erroneous, we record the dependency on
- // the array type.
- ASSERT_FALSE(HasAssignable("[[Ljava/util/TimeZone;", "[[LMyErroneousTimeZone;", true));
- ASSERT_TRUE(HasAssignable("[Ljava/util/TimeZone;", "[LMyErroneousTimeZone;", true));
- ASSERT_FALSE(HasAssignable("Ljava/util/TimeZone;", "LMyErroneousTimeZone;", true));
-}
-
- // We test that VerifierDeps does not try to optimize by storing assignability
- // of the component types. This is due to the fact that the component type may
- // be an erroneous class, even though the array type has resolved status.
+// We test that VerifierDeps does not try to optimize by storing assignability
+// of the component types. This is due to the fact that the component type may
+// be an erroneous class, even though the array type has resolved status.
TEST_F(VerifierDepsTest, Assignable_ArrayToInterface1) {
ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/io/Serializable;",
@@ -584,7 +572,7 @@
/* src */ "LMySSLSocket;",
/* is_strict */ true,
/* is_assignable */ false));
- ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySSLSocket;", false));
+ ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljavax/net/ssl/SSLSocket;", false));
}
TEST_F(VerifierDepsTest, NotAssignable_DestinationInBoot2) {
@@ -592,7 +580,7 @@
/* src */ "LMySimpleTimeZone;",
/* is_strict */ true,
/* is_assignable */ false));
- ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySimpleTimeZone;", false));
+ ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljava/util/SimpleTimeZone;", false));
}
TEST_F(VerifierDepsTest, NotAssignable_BothArrays) {
@@ -608,16 +596,6 @@
ASSERT_TRUE(HasClass("Ljava/lang/Thread;", true, "public"));
}
-TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) {
- ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedReferenceArray"));
- ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public final abstract"));
-}
-
-TEST_F(VerifierDepsTest, ArgumentType_ResolvedPrimitiveArray) {
- ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedPrimitiveArray"));
- ASSERT_TRUE(HasClass("[B", true, "public final abstract"));
-}
-
TEST_F(VerifierDepsTest, ArgumentType_UnresolvedClass) {
ASSERT_TRUE(VerifyMethod("ArgumentType_UnresolvedClass"));
ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
@@ -654,7 +632,7 @@
TEST_F(VerifierDepsTest, MergeTypes_RegisterLines) {
ASSERT_TRUE(VerifyMethod("MergeTypes_RegisterLines"));
- ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySocketTimeoutException;", true));
+ ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljava/net/SocketTimeoutException;", true));
ASSERT_TRUE(HasAssignable(
"Ljava/lang/Exception;", "Ljava/util/concurrent/TimeoutException;", true));
}
@@ -714,11 +692,6 @@
ASSERT_TRUE(HasClass("LUnresolvedClass;", false));
}
-TEST_F(VerifierDepsTest, NewArray_Resolved) {
- ASSERT_TRUE(VerifyMethod("NewArray_Resolved"));
- ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public final abstract"));
-}
-
TEST_F(VerifierDepsTest, NewArray_Unresolved) {
ASSERT_TRUE(VerifyMethod("NewArray_Unresolved"));
ASSERT_TRUE(HasClass("[LUnresolvedClass;", false));
@@ -841,7 +814,7 @@
"public",
"Ljava/io/InterruptedIOException;"));
ASSERT_TRUE(HasAssignable(
- "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true));
+ "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass1) {
@@ -854,7 +827,7 @@
"public",
"Ljava/io/InterruptedIOException;"));
ASSERT_TRUE(HasAssignable(
- "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true));
+ "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass2) {
@@ -866,7 +839,7 @@
"public",
"Ljava/io/InterruptedIOException;"));
ASSERT_TRUE(HasAssignable(
- "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true));
+ "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InstanceField_Unresolved_ReferrerInBoot) {
@@ -995,7 +968,7 @@
"public",
"Ljava/lang/Throwable;"));
// Type dependency on `this` argument.
- ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "LMySocketTimeoutException;", true));
+ ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) {
@@ -1009,7 +982,7 @@
"public",
"Ljava/lang/Throwable;"));
// Type dependency on `this` argument.
- ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "LMySocketTimeoutException;", true));
+ ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true));
}
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass2) {
@@ -1123,7 +1096,7 @@
TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) {
ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable"));
ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public final"));
- ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "LMain;", false));
+ ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false));
ASSERT_TRUE(HasMethod(
"virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;"));
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 5a0f0c6..ece81e3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -33,6 +33,7 @@
#include <sys/utsname.h>
#endif
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set_features.h"
@@ -87,6 +88,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static constexpr size_t kDefaultMinDexFilesForSwap = 2;
static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
@@ -1291,17 +1295,23 @@
DCHECK_EQ(output_vdex_fd_, -1);
std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
- std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
- if (vdex_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename;
- return false;
+ if (vdex_filename == input_vdex_) {
+ update_input_vdex_ = true;
+ std::unique_ptr<File> vdex_file(OS::OpenFileReadWrite(vdex_filename.c_str()));
+ vdex_files_.push_back(std::move(vdex_file));
+ } else {
+ std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
+ if (vdex_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename;
+ return false;
+ }
+ if (fchmod(vdex_file->Fd(), 0644) != 0) {
+ PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename;
+ vdex_file->Erase();
+ return false;
+ }
+ vdex_files_.push_back(std::move(vdex_file));
}
- if (fchmod(vdex_file->Fd(), 0644) != 0) {
- PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename;
- vdex_file->Erase();
- return false;
- }
- vdex_files_.push_back(std::move(vdex_file));
}
} else {
std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, /* check_usage */ true));
@@ -1315,7 +1325,6 @@
}
oat_files_.push_back(std::move(oat_file));
- DCHECK_NE(input_vdex_fd_, output_vdex_fd_);
if (input_vdex_fd_ != -1) {
struct stat s;
int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s));
@@ -1348,8 +1357,13 @@
return false;
}
vdex_file->DisableAutoClose();
- if (vdex_file->SetLength(0) != 0) {
- PLOG(WARNING) << "Truncating vdex file " << vdex_location << " failed.";
+ if (input_vdex_file_ != nullptr && output_vdex_fd_ == input_vdex_fd_) {
+ update_input_vdex_ = true;
+ } else {
+ if (vdex_file->SetLength(0) != 0) {
+ PLOG(ERROR) << "Truncating vdex file " << vdex_location << " failed.";
+ return false;
+ }
}
vdex_files_.push_back(std::move(vdex_file));
@@ -1538,6 +1552,7 @@
instruction_set_features_.get(),
key_value_store_.get(),
verify,
+ update_input_vdex_,
&opened_dex_files_map,
&opened_dex_files)) {
return false;
@@ -2728,6 +2743,9 @@
// See CompilerOptions.force_determinism_.
bool force_determinism_;
+ // Whether the given input vdex is also the output.
+ bool update_input_vdex_ = false;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
};
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b6b62a8..cdb3b9f 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -19,11 +19,15 @@
#include <string>
#include <vector>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "android-base/stringprintf.h"
+
#include "common_runtime_test.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "dex_file-inl.h"
#include "dex2oat_environment_test.h"
#include "jit/offline_profiling_info.h"
@@ -31,9 +35,6 @@
#include "oat_file.h"
#include "utils.h"
-#include <sys/wait.h>
-#include <unistd.h>
-
namespace art {
class Dex2oatTest : public Dex2oatEnvironmentTest {
@@ -217,7 +218,7 @@
std::unique_ptr<ScratchFile> sf;
if (use_fd) {
sf.reset(new ScratchFile());
- copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd()));
+ copy.push_back(android::base::StringPrintf("--swap-fd=%d", sf->GetFd()));
} else {
std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
copy.push_back("--swap-file=" + swap_location);
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 916984c..d5776fa 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -42,7 +42,8 @@
#include <sstream>
#include <vector>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dexdump_cfg.h"
#include "dex_file-inl.h"
#include "dex_file_types.h"
@@ -887,8 +888,10 @@
const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
const Signature signature = pDexFile->GetMethodSignature(pMethodId);
const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
- method = StringPrintf("%s.%s:%s",
- backDescriptor, name, signature.ToString().c_str());
+ method = android::base::StringPrintf("%s.%s:%s",
+ backDescriptor,
+ name,
+ signature.ToString().c_str());
}
if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc
index d28ca28..53dda6a 100644
--- a/dexdump/dexdump_test.cc
+++ b/dexdump/dexdump_test.cc
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "base/stringprintf.h"
#include "common_runtime_test.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/os.h"
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index cfe4837..cac6090 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -30,7 +30,8 @@
#include <sstream>
#include <vector>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_ir_builder.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
@@ -43,6 +44,8 @@
namespace art {
+using android::base::StringPrintf;
+
/*
* Flags for use with createAccessFlagStr().
*/
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 665baa6..46a1c43 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "utils.h"
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
index da1dd7f..1320942 100644
--- a/dexlist/dexlist_test.cc
+++ b/dexlist/dexlist_test.cc
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "base/stringprintf.h"
#include "common_runtime_test.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/gc/heap.h"
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 4787395..1f6b874 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -22,6 +22,8 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "base/bit_utils.h"
+
using android::base::StringPrintf;
namespace art {
@@ -137,6 +139,8 @@
{ kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x11, "clo", "DS" },
{ kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x12, "dclz", "DS" },
{ kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x13, "dclo", "DS" },
+ { kSpecial0Mask | 0x73f, 0x05, "lsa", "DSTj" },
+ { kSpecial0Mask | 0x73f, 0x15, "dlsa", "DSTj" },
// TODO: sdbbp
// SPECIAL2
@@ -154,6 +158,7 @@
{ kSpecial3Mask | 0x3f, (31 << kOpcodeShift), "ext", "TSAZ", },
{ kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 3, "dext", "TSAZ", },
{ kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 4, "ins", "TSAz", },
+ { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 6, "dinsu", "TSFz", },
{ kSpecial3Mask | (0x1f << 21) | (0x1f << 6) | 0x3f,
(31 << kOpcodeShift) | (16 << 6) | 32,
"seb",
@@ -218,8 +223,8 @@
{ kITypeMask, 12 << kOpcodeShift, "andi", "TSi", },
{ kITypeMask, 13 << kOpcodeShift, "ori", "TSi", },
{ kITypeMask, 14 << kOpcodeShift, "xori", "TSi", },
- { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "TI", },
- { kITypeMask, 15 << kOpcodeShift, "aui", "TSI", },
+ { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "Ti", },
+ { kITypeMask, 15 << kOpcodeShift, "aui", "TSi", },
{ kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21), "bc1f", "cB" },
{ kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21) | (1 << 16), "bc1t", "cB" },
@@ -335,6 +340,8 @@
{ kITypeMask | (0x1f << 16), (59u << kOpcodeShift) | (30 << 16), "auipc", "Si" },
{ kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (0 << 19), "addiupc", "Sp" },
{ kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (1 << 19), "lwpc", "So" },
+ { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (2 << 19), "lwupc", "So" },
+ { kITypeMask | (0x7 << 18), (59u << kOpcodeShift) | (6 << 18), "ldpc", "S0" },
{ kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", },
{ kITypeMask | (0x1f << 21), 62u << kOpcodeShift, "jialc", "Ti" },
{ kITypeMask | (1 << 21), (62u << kOpcodeShift) | (1 << 21), "bnezc", "Sb" }, // TODO: de-dup?
@@ -468,6 +475,7 @@
case 'D': args << 'r' << rd; break;
case 'd': args << 'f' << rd; break;
case 'a': args << 'f' << sa; break;
+ case 'F': args << (sa + 32); break; // dinsu position.
case 'f': // Floating point "fmt".
{
size_t fmt = (instruction >> 21) & 0x7; // TODO: other fmts?
@@ -481,12 +489,12 @@
}
continue; // No ", ".
}
- case 'I': // Upper 16-bit immediate.
- args << reinterpret_cast<void*>((instruction & 0xffff) << 16);
- break;
case 'i': // Sign-extended lower 16-bit immediate.
args << static_cast<int16_t>(instruction & 0xffff);
break;
+ case 'j': // sa value for lsa/dlsa.
+ args << (sa + 1);
+ break;
case 'L': // Jump label.
{
// TODO: is this right?
@@ -512,7 +520,7 @@
}
}
break;
- case 'o': // 19-bit offset in lwpc.
+ case 'o': // 19-bit offset in lwpc and lwupc.
{
int32_t offset = (instruction & 0x7ffff) - ((instruction & 0x40000) << 1);
offset <<= 2;
@@ -520,6 +528,15 @@
args << StringPrintf(" ; %+d", offset);
}
break;
+ case '0': // 18-bit offset in ldpc.
+ {
+ int32_t offset = (instruction & 0x3ffff) - ((instruction & 0x20000) << 1);
+ offset <<= 3;
+ uintptr_t ptr = RoundDown(reinterpret_cast<uintptr_t>(instr_ptr), 8);
+ args << FormatInstructionPointer(reinterpret_cast<const uint8_t*>(ptr + offset));
+ args << StringPrintf(" ; %+d", offset);
+ }
+ break;
case 'P': // 26-bit offset in bc and balc.
{
int32_t offset = (instruction & 0x3ffffff) - ((instruction & 0x2000000) << 1);
@@ -541,7 +558,7 @@
case 'T': args << 'r' << rt; break;
case 't': args << 'f' << rt; break;
case 'Z': args << (rd + 1); break; // sz ([d]ext size).
- case 'z': args << (rd - sa + 1); break; // sz ([d]ins size).
+ case 'z': args << (rd - sa + 1); break; // sz ([d]ins, dinsu size).
}
if (*(args_fmt + 1)) {
args << ", ";
@@ -551,17 +568,14 @@
}
}
- // TODO: Simplify this once these sequences are simplified in the compiler.
// Special cases for sequences of:
// pc-relative +/- 2GB branch:
// auipc reg, imm
// jic reg, imm
// pc-relative +/- 2GB branch and link:
// auipc reg, imm
- // daddiu reg, reg, imm
- // jialc reg, 0
- if (((op == 0x36 && rs == 0 && rt != 0) || // jic
- (op == 0x19 && rs == rt && rt != 0)) && // daddiu
+ // jialc reg, imm
+ if (((op == 0x36 || op == 0x3E) && rs == 0 && rt != 0) && // ji[al]c
last_ptr_ && (intptr_t)instr_ptr - (intptr_t)last_ptr_ == 4 &&
(last_instr_ & 0xFC1F0000) == 0xEC1E0000 && // auipc
((last_instr_ >> 21) & 0x1F) == rt) {
@@ -569,9 +583,9 @@
offset -= (offset & 0x8000) << 1;
offset -= 4;
if (op == 0x36) {
- args << " ; b ";
+ args << " ; bc ";
} else {
- args << " ; move r" << rt << ", ";
+ args << " ; balc ";
}
args << FormatInstructionPointer(instr_ptr + (int32_t)offset);
args << StringPrintf(" ; %+d", (int32_t)offset);
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index a374686..f307cbc 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -26,9 +26,10 @@
#include <map>
#include <unordered_set>
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/unix_file/fd_file.h"
-#include "base/stringprintf.h"
#include "gc/space/image_space.h"
#include "gc/heap.h"
#include "mirror/class-inl.h"
@@ -46,6 +47,8 @@
namespace art {
+using android::base::StringPrintf;
+
class ImgDiagDumper {
public:
explicit ImgDiagDumper(std::ostream* os,
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index 9f771ba..3f2afc0 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -20,12 +20,13 @@
#include "common_runtime_test.h"
+#include "android-base/stringprintf.h"
+
#include "runtime/os.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/utils.h"
#include "runtime/gc/space/image_space.h"
#include "runtime/gc/heap.h"
-#include "base/stringprintf.h"
#include <sys/types.h>
#include <unistd.h>
@@ -57,7 +58,7 @@
virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
// Needs to live until CommonRuntimeTest::SetUp finishes, since we pass it a cstring.
- runtime_args_image_ = StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
+ runtime_args_image_ = android::base::StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
options->push_back(std::make_pair(runtime_args_image_, nullptr));
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index e4462d8..148ee88 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -26,6 +26,7 @@
#include <unordered_set>
#include <vector>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set_features.h"
@@ -76,6 +77,8 @@
namespace art {
+using android::base::StringPrintf;
+
const char* image_methods_descriptions_[] = {
"kResolutionMethod",
"kImtConflictMethod",
@@ -89,6 +92,7 @@
const char* image_roots_descriptions_[] = {
"kDexCaches",
"kClassRoots",
+ "kClassLoader",
};
// Map is so that we don't allocate multiple dex files for the same OatDexFile.
@@ -1506,12 +1510,13 @@
os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
static_assert(arraysize(image_roots_descriptions_) ==
static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match");
- for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+ DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax);
+ for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) {
ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
const char* image_root_description = image_roots_descriptions_[i];
mirror::Object* image_root_object = image_header_.GetImageRoot(image_root);
indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
- if (image_root_object->IsObjectArray()) {
+ if (image_root_object != nullptr && image_root_object->IsObjectArray()) {
mirror::ObjectArray<mirror::Object>* image_root_object_array
= image_root_object->AsObjectArray<mirror::Object>();
ScopedIndentation indent2(&vios_);
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index a2eba45..e77d03b 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -22,7 +22,6 @@
#include "common_runtime_test.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/gc/heap.h"
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 62d1ddf..7ae13a5 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -24,6 +24,7 @@
#include <string>
#include <vector>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_field-inl.h"
@@ -31,7 +32,6 @@
#include "base/dumpable.h"
#include "base/scoped_flock.h"
#include "base/stringpiece.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "base/unix_file/random_access_file_utils.h"
#include "elf_utils.h"
@@ -939,7 +939,7 @@
static void UsageErrorV(const char* fmt, va_list ap) {
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(ERROR) << error;
}
diff --git a/profman/profman.cc b/profman/profman.cc
index 0b2d172..e538407 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -25,12 +25,12 @@
#include <string>
#include <vector>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "base/dumpable.h"
#include "base/scoped_flock.h"
#include "base/stringpiece.h"
-#include "base/stringprintf.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "dex_file.h"
@@ -61,7 +61,7 @@
static void UsageErrorV(const char* fmt, va_list ap) {
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(ERROR) << error;
}
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 32ebee2..86019bf 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -40,7 +40,6 @@
"base/scoped_arena_allocator.cc",
"base/scoped_flock.cc",
"base/stringpiece.cc",
- "base/stringprintf.cc",
"base/time_utils.cc",
"base/timing_logger.cc",
"base/unix_file/fd_file.cc",
@@ -508,7 +507,6 @@
"base/histogram_test.cc",
"base/mutex_test.cc",
"base/scoped_flock_test.cc",
- "base/stringprintf_test.cc",
"base/time_utils_test.cc",
"base/timing_logger_test.cc",
"base/transform_array_ref_test.cc",
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index f264b82..181b2ed 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -24,10 +24,10 @@
#include "signal.h"
#include <fstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
+#include "base/logging.h"
#if defined(__arm__)
extern "C" bool artCheckForArmSdivInstruction();
@@ -35,6 +35,8 @@
namespace art {
+using android::base::StringPrintf;
+
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg) {
// Assume all ARM processors are SMP.
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index f7b5a76..52d8b3e 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -19,14 +19,16 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
namespace art {
+using android::base::StringPrintf;
+
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg) {
const bool smp = true; // Conservative default.
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 0b8e531..d489392 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -22,11 +22,14 @@
#include "android-base/properties.h"
#endif
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
#ifdef ART_TARGET_ANDROID
#if defined(__aarch64__)
TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) {
diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc
index 5dfc4b4..5aae93a 100644
--- a/runtime/arch/instruction_set_test.cc
+++ b/runtime/arch/instruction_set_test.cc
@@ -19,7 +19,6 @@
#include <gtest/gtest.h>
#include "base/enums.h"
-#include "base/stringprintf.h"
namespace art {
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index a65c967..5b50573 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -19,14 +19,16 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
namespace art {
+using android::base::StringPrintf;
+
// An enum for the Mips revision.
enum class MipsLevel {
kBase,
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index e564d1e..92c44e8 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -19,13 +19,15 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
+#include "base/logging.h"
namespace art {
+using android::base::StringPrintf;
+
Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
if (variant != "default" && variant != "mips64r6") {
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index cc102ec..c520d63 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -19,14 +19,16 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/x86_64/instruction_set_features_x86_64.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
+#include "base/logging.h"
namespace art {
+using android::base::StringPrintf;
+
// Feature-support arrays.
static constexpr const char* x86_known_variants[] = {
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index ef03bb3..96976d9 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -134,8 +134,7 @@
// NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
// without accessing the DexCache and we don't want to do that in release build.
DCHECK_LT(method_index,
- GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()
- ->GetDexCache()->NumResolvedMethods());
+ GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
ArtMethod* method = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
method_index,
pointer_size);
@@ -154,8 +153,7 @@
// NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
// without accessing the DexCache and we don't want to do that in release build.
DCHECK_LT(method_index,
- GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()
- ->GetDexCache()->NumResolvedMethods());
+ GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr);
mirror::DexCache::SetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
method_index,
@@ -186,8 +184,7 @@
inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index,
PointerSize pointer_size) {
if (kWithCheck) {
- mirror::DexCache* dex_cache =
- GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()->GetDexCache();
+ mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache();
if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) {
ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes());
return nullptr;
@@ -333,7 +330,7 @@
}
inline const DexFile::CodeItem* ArtMethod::GetCodeItem() {
- return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset());
+ return GetDexFile()->GetCodeItem(GetCodeItemOffset());
}
inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) {
@@ -398,11 +395,11 @@
}
inline mirror::DexCache* ArtMethod::GetDexCache() {
- DCHECK(!IsProxyMethod());
- if (UNLIKELY(IsObsolete())) {
- return GetObsoleteDexCache();
- } else {
+ if (LIKELY(!IsObsolete())) {
return GetDeclaringClass()->GetDexCache();
+ } else {
+ DCHECK(!IsProxyMethod());
+ return GetObsoleteDexCache();
}
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 96b6f18..dfc7837 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -18,6 +18,8 @@
#include <cstddef>
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
@@ -46,6 +48,8 @@
namespace art {
+using android::base::StringPrintf;
+
extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
const char*);
extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 3bc6f5d..963a541 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -27,6 +27,7 @@
#include "invoke_type.h"
#include "method_reference.h"
#include "modifiers.h"
+#include "mirror/dex_cache.h"
#include "mirror/object.h"
#include "obj_ptr.h"
#include "read_barrier_option.h"
@@ -220,6 +221,12 @@
return !IsIntrinsic() && (GetAccessFlags() & kAccObsoleteMethod) != 0;
}
+ void SetIsObsolete() {
+ // TODO We should really support redefining intrinsic if possible.
+ DCHECK(!IsIntrinsic());
+ SetAccessFlags(GetAccessFlags() | kAccObsoleteMethod);
+ }
+
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
bool IsNative() {
return (GetAccessFlags<kReadBarrierOption>() & kAccNative) != 0;
@@ -326,6 +333,7 @@
ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+
ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_index,
ArtMethod* new_method,
PointerSize pointer_size)
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5ef1f06..e4972da 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -175,19 +175,19 @@
#define MIRROR_CLASS_IF_TABLE_OFFSET (16 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET,
art::mirror::Class::IfTableOffset().Int32Value())
-#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (56 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
art::mirror::Class::AccessFlagsOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (88 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
art::mirror::Class::ObjectSizeOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (92 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET,
art::mirror::Class::ObjectSizeAllocFastPathOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET,
art::mirror::Class::PrimitiveTypeOffset().Int32Value())
-#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_STATUS_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET,
art::mirror::Class::StatusOffset().Int32Value())
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 5cdf671..61e0aab 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -83,18 +83,19 @@
"GraphChecker ",
"Verifier ",
"CallingConv ",
+ "CHA ",
};
template <bool kCount>
ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl()
- : num_allocations_(0u) {
- std::fill_n(alloc_stats_, arraysize(alloc_stats_), 0u);
+ : num_allocations_(0u),
+ alloc_stats_(kNumArenaAllocKinds, 0u) {
}
template <bool kCount>
void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) {
num_allocations_ = other.num_allocations_;
- std::copy(other.alloc_stats_, other.alloc_stats_ + arraysize(alloc_stats_), alloc_stats_);
+ std::copy_n(other.alloc_stats_.begin(), kNumArenaAllocKinds, alloc_stats_.begin());
}
template <bool kCount>
@@ -111,7 +112,7 @@
template <bool kCount>
size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const {
const size_t init = 0u; // Initial value of the correct type.
- return std::accumulate(alloc_stats_, alloc_stats_ + arraysize(alloc_stats_), init);
+ return std::accumulate(alloc_stats_.begin(), alloc_stats_.end(), init);
}
template <bool kCount>
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 2feb28a..6c764cb 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -21,6 +21,7 @@
#include <stddef.h>
#include "base/bit_utils.h"
+#include "base/dchecked_vector.h"
#include "base/memory_tool.h"
#include "debug_stack.h"
#include "macros.h"
@@ -132,8 +133,7 @@
private:
size_t num_allocations_;
- // TODO: Use std::array<size_t, kNumArenaAllocKinds> from C++11 when we upgrade the STL.
- size_t alloc_stats_[kNumArenaAllocKinds]; // Bytes used by various allocation kinds.
+ dchecked_vector<size_t> alloc_stats_; // Bytes used by various allocation kinds.
static const char* const kAllocNames[];
};
diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc
index de6f423..568a7ae 100644
--- a/runtime/base/file_magic.cc
+++ b/runtime/base/file_magic.cc
@@ -20,13 +20,16 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/unix_file/fd_file.h"
#include "dex_file.h"
-#include "stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
File OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
CHECK(magic != nullptr);
File fd(filename, O_RDONLY, /* check_usage */ false);
diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h
index f24a862..a22efcf 100644
--- a/runtime/base/hash_set.h
+++ b/runtime/base/hash_set.h
@@ -672,6 +672,8 @@
T* data_; // Backing storage.
double min_load_factor_;
double max_load_factor_;
+
+ ART_FRIEND_TEST(InternTableTest, CrossHash);
};
template <class T, class EmptyFn, class HashFn, class Pred, class Alloc>
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 92b7c65..44a84c8 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -21,7 +21,6 @@
#include "mutex.h"
-#include "base/stringprintf.h"
#include "base/value_object.h"
#include "thread.h"
#include "utils.h"
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index ce452cb..9116097 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -19,6 +19,8 @@
#include <errno.h>
#include <sys/time.h>
+#include "android-base/stringprintf.h"
+
#include "atomic.h"
#include "base/logging.h"
#include "base/time_utils.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
static Atomic<Locks::ClientCallback*> safe_to_call_abort_callback(nullptr);
Mutex* Locks::abort_lock_ = nullptr;
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index 0e8031f..d4bb56b 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -19,12 +19,15 @@
#include <sys/file.h>
#include <sys/stat.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
namespace art {
+using android::base::StringPrintf;
+
bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
return Init(filename, O_CREAT | O_RDWR, true, error_msg);
}
diff --git a/runtime/base/stringprintf.cc b/runtime/base/stringprintf.cc
deleted file mode 100644
index 8fd9257..0000000
--- a/runtime/base/stringprintf.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2011 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 "stringprintf.h"
-
-#include <stdio.h>
-
-namespace art {
-
-void StringAppendV(std::string* dst, const char* format, va_list ap) {
- // First try with a small fixed size buffer
- char space[1024];
-
- // It's possible for methods that use a va_list to invalidate
- // the data in it upon use. The fix is to make a copy
- // of the structure before using it and use that copy instead.
- va_list backup_ap;
- va_copy(backup_ap, ap);
- int result = vsnprintf(space, sizeof(space), format, backup_ap);
- va_end(backup_ap);
-
- if (result < static_cast<int>(sizeof(space))) {
- if (result >= 0) {
- // Normal case -- everything fit.
- dst->append(space, result);
- return;
- }
-
- if (result < 0) {
- // Just an error.
- return;
- }
- }
-
- // Increase the buffer size to the size requested by vsnprintf,
- // plus one for the closing \0.
- int length = result+1;
- char* buf = new char[length];
-
- // Restore the va_list before we use it again
- va_copy(backup_ap, ap);
- result = vsnprintf(buf, length, format, backup_ap);
- va_end(backup_ap);
-
- if (result >= 0 && result < length) {
- // It fit
- dst->append(buf, result);
- }
- delete[] buf;
-}
-
-std::string StringPrintf(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string result;
- StringAppendV(&result, fmt, ap);
- va_end(ap);
- return result;
-}
-
-void StringAppendF(std::string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-
-} // namespace art
diff --git a/runtime/base/stringprintf.h b/runtime/base/stringprintf.h
deleted file mode 100644
index 4767a75..0000000
--- a/runtime/base/stringprintf.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 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_BASE_STRINGPRINTF_H_
-#define ART_RUNTIME_BASE_STRINGPRINTF_H_
-
-#include <stdarg.h>
-#include <string>
-
-namespace art {
-
-// Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
- __attribute__((__format__(__printf__, 1, 2)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendF(std::string* dst, const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap);
-
-} // namespace art
-
-#endif // ART_RUNTIME_BASE_STRINGPRINTF_H_
diff --git a/runtime/base/time_utils.cc b/runtime/base/time_utils.cc
index 57f198d..3c09d5a 100644
--- a/runtime/base/time_utils.cc
+++ b/runtime/base/time_utils.cc
@@ -20,8 +20,9 @@
#include "time_utils.h"
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#if defined(__APPLE__)
#include <sys/time.h>
@@ -29,6 +30,8 @@
namespace art {
+using android::base::StringPrintf;
+
std::string PrettyDuration(uint64_t nano_duration, size_t max_fraction_digits) {
if (nano_duration == 0) {
return "0";
diff --git a/runtime/cha.cc b/runtime/cha.cc
index be675a8..d94b091 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -100,7 +100,11 @@
bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = GetMethod();
- if (method == nullptr || method->IsRuntimeMethod() || method->IsNative()) {
+ // Avoid types of methods that do not have an oat quick method header.
+ if (method == nullptr ||
+ method->IsRuntimeMethod() ||
+ method->IsNative() ||
+ method->IsProxyMethod()) {
return true;
}
if (GetCurrentQuickFrame() == nullptr) {
@@ -110,6 +114,7 @@
// Method may have multiple versions of compiled code. Check
// the method header to see if it has should_deoptimize flag.
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ DCHECK(method_header != nullptr);
if (!method_header->HasShouldDeoptimizeFlag()) {
// This compiled version doesn't have should_deoptimize flag. Skip.
return true;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 6c27bc6..1c3328e 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -20,6 +20,8 @@
#include <sys/mman.h>
#include <zlib.h>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
@@ -42,6 +44,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
/*
* ===========================================================================
* JNI function helpers
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 0a65cd1..5fc5f1a 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -25,6 +25,7 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/iftable.h"
+#include "mirror/throwable.h"
#include "mirror/object_array.h"
#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
@@ -72,7 +73,7 @@
// MethodVerifier refuses methods with string_idx out of bounds.
DCHECK_LT(string_idx.index_, declaring_class->GetDexFile().NumStringIds());
ObjPtr<mirror::String> string =
- mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+ mirror::StringDexCachePair::Lookup(declaring_class->GetDexCache()->GetStrings(),
string_idx.index_,
mirror::DexCache::kDexCacheStringCacheSize).Read();
if (UNLIKELY(string == nullptr)) {
@@ -80,26 +81,34 @@
Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
const DexFile& dex_file = *dex_cache->GetDexFile();
string = ResolveString(dex_file, string_idx, dex_cache);
- if (string != nullptr) {
- DCHECK_EQ(dex_cache->GetResolvedString(string_idx), string);
- }
}
return string.Ptr();
}
inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) {
Thread::PoisonObjectPointersIfDebug();
+ if (kIsDebugBuild) {
+ Thread::Current()->AssertNoPendingException();
+ }
ObjPtr<mirror::Class> resolved_type =
referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
if (UNLIKELY(resolved_type == nullptr)) {
- ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- const DexFile& dex_file = *dex_cache->GetDexFile();
- resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
- // Note: We cannot check here to see whether we added the type to the cache. The type
- // might be an erroneous class, which results in it being hidden from us.
+ // There could be an out of bounds exception from GetDexCacheResolvedType, don't call
+ // ResolveType for this case.
+ if (LIKELY(!hs.Self()->IsExceptionPending())) {
+ ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+ // Note: We cannot check here to see whether we added the type to the cache. The type
+ // might be an erroneous class, which results in it being hidden from us.
+ } else {
+ // Make sure its an array out of bounds exception.
+ DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals(
+ "Ljava/lang/ArrayIndexOutOfBoundsException;"));
+ }
}
return resolved_type.Ptr();
}
@@ -180,20 +189,15 @@
return dex_cache->GetResolvedField(field_idx, image_pointer_size_);
}
-inline ArtField* ClassLinker::GetResolvedField(uint32_t field_idx,
- ObjPtr<mirror::Class> field_declaring_class) {
- return GetResolvedField(field_idx, MakeObjPtr(field_declaring_class->GetDexCache()));
-}
-
inline ArtField* ClassLinker::ResolveField(uint32_t field_idx,
ArtMethod* referrer,
bool is_static) {
Thread::PoisonObjectPointersIfDebug();
ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
- ArtField* resolved_field = GetResolvedField(field_idx, declaring_class);
+ ArtField* resolved_field = GetResolvedField(field_idx, referrer->GetDexCache());
if (UNLIKELY(resolved_field == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
const DexFile& dex_file = *dex_cache->GetDexFile();
resolved_field = ResolveField(dex_file, field_idx, dex_cache, class_loader, is_static);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f3a5be2..5b8d4e4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -28,6 +28,8 @@
#include <utility>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/arena_allocator.h"
@@ -106,6 +108,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kSanityCheckObjects = kIsDebugBuild;
static constexpr bool kVerifyArtMethodDeclaringClasses = kIsDebugBuild;
@@ -1401,39 +1405,20 @@
return true;
}
-// Update the class loader and resolved string dex cache array of classes. Should only be used on
-// classes in the image space.
-class UpdateClassLoaderAndResolvedStringsVisitor {
+// Update the class loader. Should only be used on classes in the image space.
+class UpdateClassLoaderVisitor {
public:
- UpdateClassLoaderAndResolvedStringsVisitor(gc::space::ImageSpace* space,
- ObjPtr<mirror::ClassLoader> class_loader,
- bool forward_strings)
+ UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader)
: space_(space),
- class_loader_(class_loader),
- forward_strings_(forward_strings) {}
+ class_loader_(class_loader) {}
bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
- if (forward_strings_) {
- mirror::StringDexCacheType* strings = klass->GetDexCacheStrings();
- if (strings != nullptr) {
- DCHECK(
- space_->GetImageHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
- reinterpret_cast<uint8_t*>(strings) - space_->Begin()))
- << "String dex cache array for " << klass->PrettyClass() << " is not in app image";
- // Dex caches have already been updated, so take the strings pointer from there.
- mirror::StringDexCacheType* new_strings = klass->GetDexCache()->GetStrings();
- DCHECK_NE(strings, new_strings);
- klass->SetDexCacheStrings(new_strings);
- }
- }
- // Finally, update class loader.
klass->SetClassLoader(class_loader_);
return true;
}
gc::space::ImageSpace* const space_;
ObjPtr<mirror::ClassLoader> const class_loader_;
- const bool forward_strings_;
};
static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
@@ -1655,13 +1640,6 @@
Runtime* const runtime = Runtime::Current();
gc::Heap* const heap = runtime->GetHeap();
Thread* const self = Thread::Current();
- StackHandleScope<2> hs(self);
- Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
- hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
- Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
- header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
- const OatFile* oat_file = space->GetOatFile();
- std::unordered_set<mirror::ClassLoader*> image_class_loaders;
// Check that the image is what we are expecting.
if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) {
*error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu",
@@ -1669,6 +1647,22 @@
image_pointer_size_);
return false;
}
+ size_t expected_image_roots = ImageHeader::NumberOfImageRoots(app_image);
+ if (static_cast<size_t>(header.GetImageRoots()->GetLength()) != expected_image_roots) {
+ *error_msg = StringPrintf("Expected %zu image roots but got %d",
+ expected_image_roots,
+ header.GetImageRoots()->GetLength());
+ return false;
+ }
+ StackHandleScope<3> hs(self);
+ Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
+ hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
+ header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
+ static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
+ "Class loader should be the last image root.");
+ MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
+ app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
DCHECK(class_roots.Get() != nullptr);
if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) {
*error_msg = StringPrintf("Expected %d class roots but got %d",
@@ -1683,6 +1677,7 @@
return false;
}
}
+ const OatFile* oat_file = space->GetOatFile();
if (oat_file->GetOatHeader().GetDexFileCount() !=
static_cast<uint32_t>(dex_caches->GetLength())) {
*error_msg = "Dex cache count and dex file count mismatch while trying to initialize from "
@@ -1715,15 +1710,11 @@
// The current dex file field is bogus, overwrite it so that we can get the dex file in the
// loop below.
h_dex_cache->SetDexFile(dex_file.get());
- // Check that each class loader resolved the same way.
- // TODO: Store image class loaders as image roots.
GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes();
for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
ObjPtr<mirror::Class> klass = types[j].Read();
if (klass != nullptr) {
DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
- ObjPtr<mirror::ClassLoader> image_class_loader = klass->GetClassLoader();
- image_class_loaders.insert(image_class_loader.Ptr());
}
}
} else {
@@ -1749,59 +1740,57 @@
// for PathClassLoader does this by looping through the array of dex files. To ensure they
// resolve the same way, simply flatten the hierarchy in the way the resolution order would be,
// and check that the dex file names are the same.
- for (ObjPtr<mirror::ClassLoader> image_class_loader : image_class_loaders) {
- if (IsBootClassLoader(soa, image_class_loader)) {
- // The dex cache can reference types from the boot class loader.
- continue;
+ if (IsBootClassLoader(soa, image_class_loader.Get())) {
+ *error_msg = "Unexpected BootClassLoader in app image";
+ return false;
+ }
+ std::list<mirror::String*> image_dex_file_names;
+ std::string temp_error_msg;
+ if (!FlattenPathClassLoader(image_class_loader.Get(), &image_dex_file_names, &temp_error_msg)) {
+ *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
+ temp_error_msg.c_str());
+ return false;
+ }
+ std::list<mirror::String*> loader_dex_file_names;
+ if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
+ *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
+ temp_error_msg.c_str());
+ return false;
+ }
+ // Add the temporary dex path list elements at the end.
+ auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
+ for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
+ ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
+ if (element != nullptr) {
+ // If we are somewhere in the middle of the array, there may be nulls at the end.
+ loader_dex_file_names.push_back(GetDexPathListElementName(element));
}
- std::list<mirror::String*> image_dex_file_names;
- std::string temp_error_msg;
- if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) {
- *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
- temp_error_msg.c_str());
- return false;
+ }
+ // Ignore the number of image dex files since we are adding those to the class loader anyways.
+ CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
+ static_cast<size_t>(dex_caches->GetLength()));
+ size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
+ // Check that the dex file names match.
+ bool equal = image_count == loader_dex_file_names.size();
+ if (equal) {
+ auto it1 = image_dex_file_names.begin();
+ auto it2 = loader_dex_file_names.begin();
+ for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
+ equal = equal && (*it1)->Equals(*it2);
}
- std::list<mirror::String*> loader_dex_file_names;
- if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
- *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
- temp_error_msg.c_str());
- return false;
+ }
+ if (!equal) {
+ VLOG(image) << "Image dex files " << image_dex_file_names.size();
+ for (ObjPtr<mirror::String> name : image_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
}
- // Add the temporary dex path list elements at the end.
- auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
- for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
- ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
- if (element != nullptr) {
- // If we are somewhere in the middle of the array, there may be nulls at the end.
- loader_dex_file_names.push_back(GetDexPathListElementName(element));
- }
+ VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
+ for (ObjPtr<mirror::String> name : loader_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
}
- // Ignore the number of image dex files since we are adding those to the class loader anyways.
- CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
- static_cast<size_t>(dex_caches->GetLength()));
- size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
- // Check that the dex file names match.
- bool equal = image_count == loader_dex_file_names.size();
- if (equal) {
- auto it1 = image_dex_file_names.begin();
- auto it2 = loader_dex_file_names.begin();
- for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
- equal = equal && (*it1)->Equals(*it2);
- }
- }
- if (!equal) {
- VLOG(image) << "Image dex files " << image_dex_file_names.size();
- for (ObjPtr<mirror::String> name : image_dex_file_names) {
- VLOG(image) << name->ToModifiedUtf8();
- }
- VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
- for (ObjPtr<mirror::String> name : loader_dex_file_names) {
- VLOG(image) << name->ToModifiedUtf8();
- }
- *error_msg = "Rejecting application image due to class loader mismatch";
- // Ignore class loader mismatch for now since these would just use possibly incorrect
- // oat code anyways. The structural class check should be done in the parent.
- }
+ *error_msg = "Rejecting application image due to class loader mismatch";
+ // Ignore class loader mismatch for now since these would just use possibly incorrect
+ // oat code anyways. The structural class check should be done in the parent.
}
}
@@ -1856,10 +1845,8 @@
}
// Update class loader and resolved strings. If added_class_table is false, the resolved
// strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
- UpdateClassLoaderAndResolvedStringsVisitor visitor(space,
- class_loader.Get(),
- forward_dex_cache_arrays);
- for (ClassTable::TableSlot& root : temp_set) {
+ UpdateClassLoaderVisitor visitor(space, class_loader.Get());
+ for (const ClassTable::TableSlot& root : temp_set) {
visitor(root.Read());
}
// forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
@@ -2516,8 +2503,9 @@
// the Java-side could still succeed for racy programs if another thread is actively
// modifying the class loader's path list.
- if (Runtime::Current()->IsAotCompiler()) {
- // Oops, compile-time, can't run actual class-loader code.
+ if (!self->CanCallIntoJava()) {
+ // Oops, we can't call into java so we can't run actual class-loader code.
+ // This is true for e.g. for the compiler (jit or aot).
ObjPtr<mirror::Throwable> pre_allocated =
Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
@@ -2659,6 +2647,8 @@
ObjectLock<mirror::Class> lock(self, klass);
klass->SetClinitThreadId(self->GetTid());
+ // Make sure we have a valid empty iftable even if there are errors.
+ klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());
// Add the newly loaded class to the loaded classes table.
ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash);
@@ -3010,7 +3000,6 @@
klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
klass->SetDexTypeIndex(dex_class_def.class_idx_);
- CHECK(klass->GetDexCacheStrings() != nullptr);
}
void ClassLinker::LoadClass(Thread* self,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9b25303..6ef882a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -327,8 +327,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
- ArtField* GetResolvedField(uint32_t field_idx, ObjPtr<mirror::Class> field_declaring_class)
- REQUIRES_SHARED(Locks::mutator_lock_);
ArtField* GetResolvedField(uint32_t field_idx, ObjPtr<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtField* ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static)
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 685677b..42108d8 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -278,8 +278,6 @@
EXPECT_FALSE(klass->IsArrayClass());
EXPECT_TRUE(klass->GetComponentType() == nullptr);
EXPECT_TRUE(klass->IsInSamePackage(klass.Get()));
- EXPECT_TRUE(klass->GetDexCacheStrings() != nullptr);
- EXPECT_EQ(klass->GetDexCacheStrings(), klass->GetDexCache()->GetStrings());
std::string temp2;
EXPECT_TRUE(mirror::Class::IsInSamePackage(klass->GetDescriptor(&temp),
klass->GetDescriptor(&temp2)));
@@ -590,7 +588,6 @@
addOffset(OFFSETOF_MEMBER(mirror::Class, component_type_), "componentType");
addOffset(OFFSETOF_MEMBER(mirror::Class, copied_methods_offset_), "copiedMethodsOffset");
addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_), "dexCache");
- addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_), "dexCacheStrings");
addOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex");
addOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex");
addOffset(OFFSETOF_MEMBER(mirror::Class, ext_data_), "extData");
@@ -1175,6 +1172,24 @@
EXPECT_TRUE(init->IsInitialized());
}
+TEST_F(ClassLinkerTest, ErroneousClass) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadMultiDex("ErroneousA", "ErroneousB");
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+ hs.Self()->AssertNoPendingException();
+ const char* descriptor = "LErroneous;";
+ ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), descriptor, class_loader);
+ // Erronenous since we are extending final class.
+ hs.Self()->AssertPendingException();
+ EXPECT_TRUE(klass == nullptr);
+ klass = class_linker_->LookupClass(soa.Self(), descriptor, class_loader.Get());
+ EXPECT_FALSE(klass == nullptr);
+ EXPECT_TRUE(klass->IsErroneous());
+ EXPECT_TRUE(klass->GetIfTable() != nullptr);
+}
+
TEST_F(ClassLinkerTest, FinalizableBit) {
ScopedObjectAccess soa(Thread::Current());
mirror::Class* c;
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 8226e60..743fcc8 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -24,11 +24,12 @@
#include <stdlib.h>
#include "../../external/icu/icu4c/source/common/unicode/uvernum.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "base/macros.h"
#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "compiler_callbacks.h"
@@ -65,6 +66,8 @@
namespace art {
+using android::base::StringPrintf;
+
ScratchFile::ScratchFile() {
// ANDROID_DATA needs to be set
CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) <<
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 9f0dbbb..c30272e 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -18,6 +18,7 @@
#include <sstream>
+#include "android-base/stringprintf.h"
#include "ScopedLocalRef.h"
#include "art_field-inl.h"
@@ -37,6 +38,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static void AddReferrerLocation(std::ostream& os, ObjPtr<mirror::Class> referrer)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (referrer != nullptr) {
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
index 6e3e1d8..dc89d32 100644
--- a/runtime/compiler_filter.cc
+++ b/runtime/compiler_filter.cc
@@ -60,6 +60,26 @@
UNREACHABLE();
}
+bool CompilerFilter::IsAnyMethodCompilationEnabled(Filter filter) {
+ switch (filter) {
+ case CompilerFilter::kVerifyNone:
+ case CompilerFilter::kVerifyAtRuntime:
+ case CompilerFilter::kVerifyProfile: return false;
+
+ case CompilerFilter::kInterpretOnly:
+ case CompilerFilter::kSpaceProfile:
+ case CompilerFilter::kSpace:
+ case CompilerFilter::kBalanced:
+ case CompilerFilter::kTime:
+ case CompilerFilter::kSpeedProfile:
+ case CompilerFilter::kSpeed:
+ case CompilerFilter::kLayoutProfile:
+ case CompilerFilter::kEverythingProfile:
+ case CompilerFilter::kEverything: return true;
+ }
+ UNREACHABLE();
+}
+
bool CompilerFilter::IsVerificationEnabled(Filter filter) {
switch (filter) {
case CompilerFilter::kVerifyNone:
diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h
index 781d43a..7eb5f9a 100644
--- a/runtime/compiler_filter.h
+++ b/runtime/compiler_filter.h
@@ -52,6 +52,11 @@
static bool IsBytecodeCompilationEnabled(Filter filter);
// Returns true if an oat file with this compiler filter contains
+ // compiled executable code for bytecode, JNI methods, or quickened dex
+ // bytecode.
+ static bool IsAnyMethodCompilationEnabled(Filter filter);
+
+ // Returns true if an oat file with this compiler filter contains
// compiled executable code for JNI methods.
static bool IsJniCompilationEnabled(Filter filter);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e339666..df4413d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -20,6 +20,8 @@
#include <set>
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
@@ -61,6 +63,8 @@
namespace art {
+using android::base::StringPrintf;
+
// The key identifying the debugger to update instrumentation.
static constexpr const char* kDbgInstrumentationKey = "Debugger";
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index aa8fb38..7d704ad 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -28,10 +28,11 @@
#include <sstream>
#include <type_traits>
+#include "android-base/stringprintf.h"
+
#include "base/enums.h"
#include "base/file_magic.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
#include "dex_file-inl.h"
@@ -45,6 +46,8 @@
namespace art {
+using android::base::StringPrintf;
+
static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong");
static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial");
static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 52b9f11..9504e8b 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -18,6 +18,8 @@
#include <stdlib.h>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "class_linker-inl.h"
@@ -31,6 +33,8 @@
namespace art {
+using android::base::StringPrintf;
+
struct DexFile::AnnotationValue {
JValue value_;
uint8_t type_;
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 07f0fca..a3ab9fa 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -22,7 +22,8 @@
#include <limits>
#include <memory>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_file-inl.h"
#include "experimental_flags.h"
#include "leb128.h"
@@ -32,6 +33,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max();
static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) {
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 9902389..7b8974f 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -21,12 +21,15 @@
#include <iomanip>
#include <sstream>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_file-inl.h"
#include "utils.h"
namespace art {
+using android::base::StringPrintf;
+
const char* const Instruction::kInstructionNames[] = {
#define INSTRUCTION_NAME(o, c, pname, f, i, a, v) pname,
#include "dex_instruction_list.h"
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index ee0f340..59b734f 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -20,11 +20,11 @@
#include <sys/types.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "elf_file_impl.h"
@@ -34,6 +34,8 @@
namespace art {
+using android::base::StringPrintf;
+
template <typename ElfTypes>
ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable,
bool program_header_only,
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index f6eeffc..14c9c21 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -563,7 +563,7 @@
HandleWrapperObjPtr<mirror::Object> h_this(hs2.NewHandleWrapper(this_object));
Handle<mirror::Class> h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass()));
const dex::TypeIndex method_type_idx =
- h_referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
+ referrer->GetDexFile()->GetMethodId(method_idx).class_idx_;
mirror::Class* method_reference_class = class_linker->ResolveType(method_type_idx, referrer);
if (UNLIKELY(method_reference_class == nullptr)) {
// Bad type idx.
@@ -673,8 +673,7 @@
size_t expected_size) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
ArtField* resolved_field =
- referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx,
- kRuntimePointerSize);
+ referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
if (UNLIKELY(resolved_field == nullptr)) {
return nullptr;
}
@@ -733,7 +732,7 @@
}
mirror::Class* referring_class = referrer->GetDeclaringClass();
ArtMethod* resolved_method =
- referring_class->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize);
+ referrer->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize);
if (UNLIKELY(resolved_method == nullptr)) {
return nullptr;
}
@@ -759,9 +758,9 @@
} else if (type == kSuper) {
// TODO This lookup is rather slow.
dex::TypeIndex method_type_idx =
- referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
+ referrer->GetDexFile()->GetMethodId(method_idx).class_idx_;
mirror::Class* method_reference_class =
- referring_class->GetDexCache()->GetResolvedType(method_type_idx);
+ referrer->GetDexCache()->GetResolvedType(method_type_idx);
if (method_reference_class == nullptr) {
// Need to do full type resolution...
return nullptr;
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index f4d0bc7..eb9f039 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -16,8 +16,9 @@
#include "space_bitmap-inl.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
-#include "base/stringprintf.h"
#include "dex_file-inl.h"
#include "mem_map.h"
#include "mirror/object-inl.h"
@@ -28,6 +29,8 @@
namespace gc {
namespace accounting {
+using android::base::StringPrintf;
+
template<size_t kAlignment>
size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) {
const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerIntPtrT;
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 2e4475f..35a251f 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -16,6 +16,13 @@
#include "rosalloc.h"
+#include <map>
+#include <list>
+#include <sstream>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+
#include "base/memory_tool.h"
#include "base/mutex-inl.h"
#include "gc/space/memory_tool_settings.h"
@@ -26,15 +33,12 @@
#include "thread-inl.h"
#include "thread_list.h"
-#include <map>
-#include <list>
-#include <sstream>
-#include <vector>
-
namespace art {
namespace gc {
namespace allocator {
+using android::base::StringPrintf;
+
static constexpr bool kUsePrefetchDuringAllocRun = false;
static constexpr bool kPrefetchNewRunDataByZeroing = false;
static constexpr size_t kPrefetchStride = 64;
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index ed16854..01bcb7d 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -18,6 +18,8 @@
#include "garbage_collector.h"
+#include "android-base/stringprintf.h"
+
#include "base/dumpable.h"
#include "base/histogram-inl.h"
#include "base/logging.h"
@@ -81,7 +83,7 @@
}
void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
- ScopedTrace trace(StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()));
+ ScopedTrace trace(android::base::StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()));
Thread* self = Thread::Current();
uint64_t start_time = NanoTime();
Iteration* current_iteration = GetCurrentIteration();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3ed138c..34d8284 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -21,6 +21,8 @@
#include <unwind.h> // For GC verification.
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "allocation_listener.h"
#include "art_field-inl.h"
#include "base/allocator.h"
@@ -82,6 +84,8 @@
namespace gc {
+using android::base::StringPrintf;
+
static constexpr size_t kCollectorTransitionStressIterations = 0;
static constexpr size_t kCollectorTransitionStressWait = 10 * 1000; // Microseconds
// Minimum amount of remaining bytes before a concurrent GC is triggered.
@@ -3919,7 +3923,7 @@
ScopedObjectAccess soa(env);
env->ThrowNew(WellKnownClasses::java_lang_RuntimeException,
StringPrintf("Attempted to free %zd native bytes with only %zd native bytes "
- "registered as allocated", bytes, expected_size).c_str());
+ "registered as allocated", bytes, expected_size).c_str());
break;
}
} while (!native_bytes_allocated_.CompareExchangeWeakRelaxed(expected_size,
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 2cde7d5..081be96 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -75,11 +75,10 @@
MutexLock mu(self, *Locks::reference_processor_lock_);
while ((!kUseReadBarrier && SlowPathEnabled()) ||
(kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
- mirror::HeapReference<mirror::Object>* const referent_addr =
- reference->GetReferentReferenceAddr();
+ ObjPtr<mirror::Object> referent = reference->GetReferent<kWithoutReadBarrier>();
// If the referent became cleared, return it. Don't need barrier since thread roots can't get
// updated until after we leave the function due to holding the mutator lock.
- if (referent_addr->AsMirrorPtr() == nullptr) {
+ if (referent == nullptr) {
return nullptr;
}
// Try to see if the referent is already marked by using the is_marked_callback. We can return
@@ -91,10 +90,15 @@
// case only black nodes can be safely returned. If the GC is preserving references, the
// mutator could take a white field from a grey or white node and move it somewhere else
// in the heap causing corruption since this field would get swept.
- if (collector_->IsMarkedHeapReference(referent_addr)) {
+ // Use the cached referent instead of calling GetReferent since other threads could call
+ // Reference.clear() after we did the null check resulting in a null pointer being
+ // incorrectly passed to IsMarked. b/33569625
+ ObjPtr<mirror::Object> forwarded_ref = collector_->IsMarked(referent.Ptr());
+ if (forwarded_ref != nullptr) {
+ // Non null means that it is marked.
if (!preserving_references_ ||
(LIKELY(!reference->IsFinalizerReferenceInstance()) && reference->IsUnprocessed())) {
- return referent_addr->AsMirrorPtr();
+ return forwarded_ref;
}
}
}
@@ -265,11 +269,23 @@
}
}
-bool ReferenceProcessor::MakeCircularListIfUnenqueued(
- ObjPtr<mirror::FinalizerReference> reference) {
+void ReferenceProcessor::ClearReferent(ObjPtr<mirror::Reference> ref) {
Thread* self = Thread::Current();
MutexLock mu(self, *Locks::reference_processor_lock_);
- // Wait untul we are done processing reference.
+ // Need to wait until reference processing is done since IsMarkedHeapReference does not have a
+ // CAS. If we do not wait, it can result in the GC un-clearing references due to race conditions.
+ // This also handles the race where the referent gets cleared after a null check but before
+ // IsMarkedHeapReference is called.
+ WaitUntilDoneProcessingReferences(self);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ ref->ClearReferent<true>();
+ } else {
+ ref->ClearReferent<false>();
+ }
+}
+
+void ReferenceProcessor::WaitUntilDoneProcessingReferences(Thread* self) {
+ // Wait until we are done processing reference.
while ((!kUseReadBarrier && SlowPathEnabled()) ||
(kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
@@ -277,6 +293,13 @@
self->CheckEmptyCheckpoint();
condition_.WaitHoldingLocks(self);
}
+}
+
+bool ReferenceProcessor::MakeCircularListIfUnenqueued(
+ ObjPtr<mirror::FinalizerReference> reference) {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::reference_processor_lock_);
+ WaitUntilDoneProcessingReferences(self);
// At this point, since the sentinel of the reference is live, it is guaranteed to not be
// enqueued if we just finished processing references. Otherwise, we may be doing the main GC
// phase. Since we are holding the reference processor lock, it guarantees that reference
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index 759b7e1..b15544d 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -73,6 +73,9 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::reference_processor_lock_,
!Locks::reference_queue_finalizer_references_lock_);
+ void ClearReferent(ObjPtr<mirror::Reference> ref)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::reference_processor_lock_);
private:
bool SlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -84,6 +87,10 @@
// referents.
void StartPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_);
void StopPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_);
+ // Wait until reference processing is done.
+ void WaitUntilDoneProcessingReferences(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::reference_processor_lock_);
// Collector which is clearing references, used by the GetReferent to return referents which are
// already marked.
collector::GarbageCollector* collector_ GUARDED_BY(Locks::reference_processor_lock_);
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 76f3692..e03958d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_method.h"
@@ -45,6 +46,9 @@
namespace gc {
namespace space {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
Atomic<uint32_t> ImageSpace::bitmap_index_(0);
ImageSpace::ImageSpace(const std::string& image_filename,
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index fa941c0..5999548 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -20,9 +20,10 @@
#include <dirent.h>
#include <dlfcn.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "globals.h"
#include "os.h"
@@ -56,7 +57,7 @@
continue;
}
// We only want to delete regular files and symbolic links.
- std::string file = StringPrintf("%s/%s", dir.c_str(), name);
+ std::string file = android::base::StringPrintf("%s/%s", dir.c_str(), name);
if (de->d_type != DT_REG && de->d_type != DT_LNK) {
if (de->d_type == DT_DIR) {
if (recurse) {
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index b1572cc..a186f4c 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -16,6 +16,8 @@
#include "malloc_space.h"
+#include "android-base/stringprintf.h"
+
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
@@ -33,6 +35,8 @@
namespace gc {
namespace space {
+using android::base::StringPrintf;
+
size_t MallocSpace::bitmap_index_ = 0;
MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map,
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index f13ff8c..bebcd71 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -74,8 +74,6 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())))
#define ART_METHOD_DECLARING_CLASS_OFFSET 0
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value())))
-#define DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET 40
-DEFINE_CHECK_EQ(static_cast<int32_t>(DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET), (static_cast<int32_t>(art::mirror::Class:: DexCacheStringsOffset().Int32Value())))
#define STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT 3
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))))
#define STRING_DEX_CACHE_SIZE_MINUS_ONE 1023
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 8cbe491..fe6a6e9 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -37,9 +37,10 @@
#include <set>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
@@ -797,8 +798,9 @@
file->Erase();
}
if (!okay) {
- std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
- filename_.c_str(), strerror(errno)));
+ std::string msg(android::base::StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
+ filename_.c_str(),
+ strerror(errno)));
ThrowRuntimeException("%s", msg.c_str());
LOG(ERROR) << msg;
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 52c9f4e..2ef60c3 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '3', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '4', '\0' }; // mirror::Class update
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index da9976a..6c76f49 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -189,6 +189,7 @@
enum ImageRoot {
kDexCaches,
kClassRoots,
+ kClassLoader, // App image only.
kImageRootsMax,
};
@@ -206,6 +207,10 @@
kSectionCount, // Number of elements in enum.
};
+ static size_t NumberOfImageRoots(bool app_image) {
+ return app_image ? kImageRootsMax : kImageRootsMax - 1u;
+ }
+
ArtMethod* GetImageMethod(ImageMethod index) const;
void SetImageMethod(ImageMethod index, ArtMethod* method);
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 9c634fa..0e66ae9 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -19,6 +19,8 @@
#include "indirect_reference_table.h"
+#include "android-base/stringprintf.h"
+
#include "base/dumpable.h"
#include "gc_root-inl.h"
#include "obj_ptr-inl.h"
@@ -38,15 +40,15 @@
return false;
}
if (UNLIKELY(GetIndirectRefKind(iref) == kHandleScopeOrInvalid)) {
- AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): invalid %s %p",
- GetIndirectRefKindString(kind_),
- iref));
+ AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): invalid %s %p",
+ GetIndirectRefKindString(kind_),
+ iref));
return false;
}
const uint32_t top_index = segment_state_.top_index;
uint32_t idx = ExtractIndex(iref);
if (UNLIKELY(idx >= top_index)) {
- std::string msg = StringPrintf(
+ std::string msg = android::base::StringPrintf(
"JNI ERROR (app bug): accessed stale %s %p (index %d in a table of size %d)",
GetIndirectRefKindString(kind_),
iref,
@@ -56,9 +58,9 @@
return false;
}
if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
- AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): accessed deleted %s %p",
- GetIndirectRefKindString(kind_),
- iref));
+ AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): accessed deleted %s %p",
+ GetIndirectRefKindString(kind_),
+ iref));
return false;
}
if (UNLIKELY(!CheckEntry("use", iref, idx))) {
@@ -73,7 +75,7 @@
uint32_t idx) const {
IndirectRef checkRef = ToIndirectRef(idx);
if (UNLIKELY(checkRef != iref)) {
- std::string msg = StringPrintf(
+ std::string msg = android::base::StringPrintf(
"JNI ERROR (app bug): attempt to %s stale %s %p (should be %p)",
what,
GetIndirectRefKindString(kind_),
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 722b411..bf4cab2 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -16,6 +16,8 @@
#include "indirect_reference_table-inl.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "mirror/object-inl.h"
@@ -23,6 +25,8 @@
namespace art {
+using android::base::StringPrintf;
+
class IndirectReferenceTableTest : public CommonRuntimeTest {};
static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique)
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 870d1ae..4ea1130 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -557,8 +557,10 @@
}
Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentationLevel() const {
- if (interpreter_stubs_installed_) {
+ if (interpreter_stubs_installed_ && interpret_only_) {
return InstrumentationLevel::kInstrumentWithInterpreter;
+ } else if (interpreter_stubs_installed_) {
+ return InstrumentationLevel::kInstrumentWithInterpreterAndJit;
} else if (entry_exit_stubs_installed_) {
return InstrumentationLevel::kInstrumentWithInstrumentationStubs;
} else {
@@ -566,6 +568,14 @@
}
}
+bool Instrumentation::RequiresInstrumentationInstallation(InstrumentationLevel new_level) const {
+ // We need to reinstall instrumentation if we go to a different level or if the current level is
+ // kInstrumentWithInterpreterAndJit since that level does not force all code to always use the
+ // interpreter and so we might have started running optimized code again.
+ return new_level == InstrumentationLevel::kInstrumentWithInterpreterAndJit ||
+ GetCurrentInstrumentationLevel() != new_level;
+}
+
void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desired_level) {
// Store the instrumentation level for this key or remove it.
if (desired_level == InstrumentationLevel::kInstrumentNothing) {
@@ -585,8 +595,7 @@
interpret_only_ = (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) ||
forced_interpret_only_;
- InstrumentationLevel current_level = GetCurrentInstrumentationLevel();
- if (requested_level == current_level) {
+ if (!RequiresInstrumentationInstallation(requested_level)) {
// We're already set.
return;
}
@@ -595,7 +604,7 @@
Locks::mutator_lock_->AssertExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
if (requested_level > InstrumentationLevel::kInstrumentNothing) {
- if (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) {
+ if (requested_level >= InstrumentationLevel::kInstrumentWithInterpreterAndJit) {
interpreter_stubs_installed_ = true;
entry_exit_stubs_installed_ = true;
} else {
@@ -842,7 +851,8 @@
void Instrumentation::DisableDeoptimization(const char* key) {
CHECK_EQ(deoptimization_enabled_, true);
// If we deoptimized everything, undo it.
- if (interpreter_stubs_installed_) {
+ InstrumentationLevel level = GetCurrentInstrumentationLevel();
+ if (level == InstrumentationLevel::kInstrumentWithInterpreter) {
UndeoptimizeEverything(key);
}
// Undeoptimized selected methods.
@@ -869,6 +879,14 @@
return !deoptimization_enabled_ && !interpreter_stubs_installed_;
}
+// TODO we don't check deoptimization_enabled_ because currently there isn't really any support for
+// multiple users of instrumentation. Since this is just a temporary state anyway pending work to
+// ensure that the current_method doesn't get kept across suspend points this should be okay.
+// TODO Remove once b/33630159 is resolved.
+void Instrumentation::ReJitEverything(const char* key) {
+ ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreterAndJit);
+}
+
void Instrumentation::DeoptimizeEverything(const char* key) {
CHECK(deoptimization_enabled_);
ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreter);
@@ -1098,10 +1116,14 @@
Dbg::IsForcedInterpreterNeededForUpcall(self, visitor.caller));
if (deoptimize && Runtime::Current()->IsDeoptimizeable(*return_pc)) {
if (kVerboseInstrumentation) {
- LOG(INFO) << StringPrintf("Deoptimizing %s by returning from %s with result %#" PRIx64 " in ",
- visitor.caller->PrettyMethod().c_str(),
- method->PrettyMethod().c_str(),
- return_value.GetJ()) << *self;
+ LOG(INFO) << "Deoptimizing "
+ << visitor.caller->PrettyMethod()
+ << " by returning from "
+ << method->PrettyMethod()
+ << " with result "
+ << std::hex << return_value.GetJ() << std::dec
+ << " in "
+ << *self;
}
self->PushDeoptimizationContext(return_value,
return_shorty == 'L',
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 1e5fcf2..05c0aaa 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -133,6 +133,9 @@
enum class InstrumentationLevel {
kInstrumentNothing, // execute without instrumentation
kInstrumentWithInstrumentationStubs, // execute with instrumentation entry/exit stubs
+ kInstrumentWithInterpreterAndJit, // execute with interpreter initially and later the JIT
+ // (if it is enabled). This level is special in that it
+ // always requires re-instrumentation.
kInstrumentWithInterpreter // execute with interpreter
};
@@ -163,6 +166,13 @@
}
bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_);
+ // Executes everything with the interpreter/jit (if available).
+ void ReJitEverything(const char* key)
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+ REQUIRES(!Locks::thread_list_lock_,
+ !Locks::classlinker_classes_lock_,
+ !deoptimized_methods_lock_);
+
// Executes everything with interpreter.
void DeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
@@ -432,9 +442,13 @@
return alloc_entrypoints_instrumented_;
}
- private:
InstrumentationLevel GetCurrentInstrumentationLevel() const;
+ private:
+ // Returns true if moving to the given instrumentation level requires the installation of stubs.
+ // False otherwise.
+ bool RequiresInstrumentationInstallation(InstrumentationLevel new_level) const;
+
// Does the job of installing or removing instrumentation code within methods.
// In order to support multiple clients using instrumentation at the same time,
// the caller must pass a unique key (a string) identifying it so we remind which
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 9c05d3c..3e19146 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -319,7 +319,9 @@
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- return static_cast<size_t>(root.Read<kWithoutReadBarrier>()->GetHashCode());
+ // An additional cast to prevent undesired sign extension.
+ return static_cast<size_t>(
+ static_cast<uint32_t>(root.Read<kWithoutReadBarrier>()->GetHashCode()));
}
bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index f661d9f..68454fb 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -163,7 +163,11 @@
NO_THREAD_SAFETY_ANALYSIS;
// Utf8String can be used for lookup.
- std::size_t operator()(const Utf8String& key) const { return key.GetHash(); }
+ std::size_t operator()(const Utf8String& key) const {
+ // A cast to prevent undesired sign extension.
+ return static_cast<uint32_t>(key.GetHash());
+ }
+
bool operator()(const GcRoot<mirror::String>& a, const Utf8String& b) const
NO_THREAD_SAFETY_ANALYSIS;
};
@@ -217,6 +221,8 @@
// We call AddNewTable when we create the zygote to reduce private dirty pages caused by
// modifying the zygote intern table. The back of table is modified when strings are interned.
std::vector<UnorderedSet> tables_;
+
+ ART_FRIEND_TEST(InternTableTest, CrossHash);
};
// Insert if non null, otherwise return null. Must be called holding the mutator lock.
@@ -276,6 +282,7 @@
gc::WeakRootState weak_root_state_ GUARDED_BY(Locks::intern_table_lock_);
friend class Transaction;
+ ART_FRIEND_TEST(InternTableTest, CrossHash);
DISALLOW_COPY_AND_ASSIGN(InternTable);
};
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index b91d946..3991d65 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -16,6 +16,7 @@
#include "intern_table.h"
+#include "base/hash_set.h"
#include "common_runtime_test.h"
#include "mirror/object.h"
#include "handle_scope-inl.h"
@@ -62,6 +63,25 @@
EXPECT_EQ(2U, t.Size());
}
+// Check if table indexes match on 64 and 32 bit machines.
+// This is done by ensuring hash values are the same on every machine and limited to 32-bit wide.
+// Otherwise cross compilation can cause a table to be filled on host using one indexing algorithm
+// and later on a device with different sizeof(size_t) can use another indexing algorithm.
+// Thus the table may provide wrong data.
+TEST_F(InternTableTest, CrossHash) {
+ ScopedObjectAccess soa(Thread::Current());
+ InternTable t;
+
+ // A string that has a negative hash value.
+ GcRoot<mirror::String> str(mirror::String::AllocFromModifiedUtf8(soa.Self(), "00000000"));
+
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+ for (InternTable::Table::UnorderedSet& table : t.strong_interns_.tables_) {
+ // The negative hash value shall be 32-bit wide on every host.
+ ASSERT_TRUE(IsUint<32>(table.hashfn_(str)));
+ }
+}
+
class TestPredicate : public IsMarkedVisitor {
public:
mirror::Object* IsMarked(mirror::Object* s) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 22da07d..a09e71b 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -676,7 +676,7 @@
CHECK(Runtime::Current()->IsActiveTransaction());
// Constructs abort message.
std::string abort_msg;
- StringAppendV(&abort_msg, fmt, args);
+ android::base::StringAppendV(&abort_msg, fmt, args);
// Throws an exception so we can abort the transaction and rollback every change.
Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg);
}
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index c9a5b44..b599949 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -25,6 +25,8 @@
#include <sstream>
#include <atomic>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -249,17 +251,16 @@
}
}
ArtMethod* method = shadow_frame.GetMethod();
- ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
// MethodVerifier refuses methods with string_idx out of bounds.
DCHECK_LT(string_idx.index_ % mirror::DexCache::kDexCacheStringCacheSize,
- declaring_class->GetDexFile().NumStringIds());
+ method->GetDexFile()->NumStringIds());
ObjPtr<mirror::String> string_ptr =
- mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+ mirror::StringDexCachePair::Lookup(method->GetDexCache()->GetStrings(),
string_idx.index_,
mirror::DexCache::kDexCacheStringCacheSize).Read();
if (UNLIKELY(string_ptr == nullptr)) {
StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(*method->GetDexFile(),
string_idx,
dex_cache);
@@ -430,12 +431,12 @@
#define TRACE_LOG std::cerr
std::ostringstream oss;
oss << shadow_frame.GetMethod()->PrettyMethod()
- << StringPrintf("\n0x%x: ", dex_pc)
+ << android::base::StringPrintf("\n0x%x: ", dex_pc)
<< inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\n";
for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
uint32_t raw_value = shadow_frame.GetVReg(i);
ObjPtr<mirror::Object> ref_value = shadow_frame.GetVRegReference(i);
- oss << StringPrintf(" vreg%u=0x%08X", i, raw_value);
+ oss << android::base::StringPrintf(" vreg%u=0x%08X", i, raw_value);
if (ref_value != nullptr) {
if (ref_value->GetClass()->IsStringClass() &&
!ref_value->AsString()->IsValueNull()) {
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 52eacd5..b0d7fb2 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -92,6 +92,16 @@
} \
} while (false)
+#define HANDLE_BACKWARD_BRANCH(offset) \
+ do { \
+ if (IsBackwardBranch(offset)) { \
+ HOTNESS_UPDATE(); \
+ /* Record new dex pc early to have consistent suspend point at loop header. */ \
+ shadow_frame.SetDexPC(inst->GetDexPc(insns)); \
+ self->AllowThreadSuspension(); \
+ } \
+ } while (false)
+
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
@@ -594,55 +604,40 @@
PREAMBLE();
int8_t offset = inst->VRegA_10t(inst_data);
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
break;
}
case Instruction::GOTO_16: {
PREAMBLE();
int16_t offset = inst->VRegA_20t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
break;
}
case Instruction::GOTO_32: {
PREAMBLE();
int32_t offset = inst->VRegA_30t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
break;
}
case Instruction::PACKED_SWITCH: {
PREAMBLE();
int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
break;
}
case Instruction::SPARSE_SWITCH: {
PREAMBLE();
int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
break;
}
@@ -739,11 +734,8 @@
shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -756,11 +748,8 @@
shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -773,11 +762,8 @@
shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -790,11 +776,8 @@
shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -807,11 +790,8 @@
shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -824,11 +804,8 @@
shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -840,11 +817,8 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
int16_t offset = inst->VRegB_21t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -856,11 +830,8 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
int16_t offset = inst->VRegB_21t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -872,11 +843,8 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
int16_t offset = inst->VRegB_21t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -888,11 +856,8 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
int16_t offset = inst->VRegB_21t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -904,11 +869,8 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
int16_t offset = inst->VRegB_21t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
@@ -920,11 +882,8 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
int16_t offset = inst->VRegB_21t();
BRANCH_INSTRUMENTATION(offset);
- if (IsBackwardBranch(offset)) {
- HOTNESS_UPDATE();
- self->AllowThreadSuspension();
- }
inst = inst->RelativeAt(offset);
+ HANDLE_BACKWARD_BRANCH(offset);
} else {
BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index a5b1038..7dd3d3d 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -25,6 +25,7 @@
#include <locale>
#include <unordered_map>
+#include "android-base/stringprintf.h"
#include "ScopedLocalRef.h"
#include "art_method-inl.h"
@@ -56,6 +57,9 @@
namespace art {
namespace interpreter {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static void AbortTransactionOrFail(Thread* self, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index caf705a..f80c43d 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -18,6 +18,8 @@
#include <dlfcn.h>
+#include "android-base/stringprintf.h"
+
#include "art_method.h"
#include "base/dumpable.h"
#include "base/mutex.h"
@@ -42,6 +44,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringAppendV;
+
static constexpr size_t kGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
static constexpr size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index 16e8050..d8869ad 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -20,8 +20,9 @@
#include <sys/un.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "jdwp/jdwp_priv.h"
#include "thread-inl.h"
@@ -53,6 +54,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
struct JdwpAdbState : public JdwpNetStateBase {
public:
explicit JdwpAdbState(JdwpState* state)
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 5574a11..172f52a 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -21,10 +21,11 @@
#include <string.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "debugger.h"
#include "jdwp/jdwp_constants.h"
#include "jdwp/jdwp_expand_buf.h"
@@ -103,6 +104,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
/*
* Stuff to compare against when deciding if a mod matches. Only the
* values for mods valid for the event being evaluated will be filled in.
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0f2d188..964c567 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -20,11 +20,12 @@
#include <memory>
#include <string>
+#include "android-base/stringprintf.h"
+
#include "atomic.h"
#include "base/hex_dump.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "debugger.h"
#include "jdwp/jdwp_constants.h"
#include "jdwp/jdwp_event.h"
@@ -39,6 +40,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
std::string DescribeField(const FieldId& field_id) {
return StringPrintf("%#" PRIx64 " (%s)", field_id, Dbg::GetFieldName(field_id).c_str());
}
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index e3bf3e5..7707ba4 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -20,6 +20,8 @@
#include <time.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "atomic.h"
#include "base/logging.h"
#include "base/time_utils.h"
@@ -31,6 +33,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
static void* StartJdwpThread(void* arg);
/*
diff --git a/runtime/jdwp/jdwp_request.cc b/runtime/jdwp/jdwp_request.cc
index 18f40a1..6af267e 100644
--- a/runtime/jdwp/jdwp_request.cc
+++ b/runtime/jdwp/jdwp_request.cc
@@ -18,7 +18,8 @@
#include <inttypes.h>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "jdwp/jdwp_priv.h"
namespace art {
@@ -100,7 +101,7 @@
ObjectId Request::ReadObjectId(const char* specific_kind) {
ObjectId id = Read8BE();
- VLOG(jdwp) << StringPrintf(" %s id %#" PRIx64, specific_kind, id);
+ VLOG(jdwp) << android::base::StringPrintf(" %s id %#" PRIx64, specific_kind, id);
return id;
}
diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 3be7fd6..97662f0 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -26,8 +26,9 @@
#include <sys/types.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "jdwp/jdwp_priv.h"
namespace art {
@@ -500,7 +501,7 @@
*/
if (IsAwaitingHandshake()) {
if (memcmp(input_buffer_, kMagicHandshake, kMagicHandshakeLen) != 0) {
- LOG(ERROR) << StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_);
+ LOG(ERROR) << android::base::StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_);
goto fail;
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index dac2e60..b7125a8 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -291,7 +291,7 @@
// is not null when we instrument.
thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
- thread_pool_->StartWorkers(Thread::Current());
+ Start();
}
void Jit::DeleteThreadPool() {
@@ -710,14 +710,23 @@
}
}
+void Jit::Stop() {
+ Thread* self = Thread::Current();
+ // TODO(ngeoffray): change API to not require calling WaitForCompilationToFinish twice.
+ WaitForCompilationToFinish(self);
+ GetThreadPool()->StopWorkers(self);
+ WaitForCompilationToFinish(self);
+}
+
+void Jit::Start() {
+ GetThreadPool()->StartWorkers(Thread::Current());
+}
+
ScopedJitSuspend::ScopedJitSuspend() {
jit::Jit* jit = Runtime::Current()->GetJit();
was_on_ = (jit != nullptr) && (jit->GetThreadPool() != nullptr);
if (was_on_) {
- Thread* self = Thread::Current();
- jit->WaitForCompilationToFinish(self);
- jit->GetThreadPool()->StopWorkers(self);
- jit->WaitForCompilationToFinish(self);
+ jit->Stop();
}
}
@@ -725,7 +734,7 @@
if (was_on_) {
DCHECK(Runtime::Current()->GetJit() != nullptr);
DCHECK(Runtime::Current()->GetJit()->GetThreadPool() != nullptr);
- Runtime::Current()->GetJit()->GetThreadPool()->StartWorkers(Thread::Current());
+ Runtime::Current()->GetJit()->Start();
}
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index a230c78..05c3905 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -179,6 +179,12 @@
return thread_pool_.get();
}
+ // Stop the JIT by waiting for all current compilations and enqueued compilations to finish.
+ void Stop();
+
+ // Start JIT threads.
+ void Start();
+
private:
Jit();
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 1b0ad83..6336cdd 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -217,6 +217,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
@@ -230,6 +231,7 @@
fp_spill_mask,
code,
code_size,
+ data_size,
osr,
roots,
has_should_deoptimize_flag,
@@ -246,6 +248,7 @@
fp_spill_mask,
code,
code_size,
+ data_size,
osr,
roots,
has_should_deoptimize_flag,
@@ -513,6 +516,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
@@ -547,6 +551,11 @@
core_spill_mask,
fp_spill_mask,
code_size);
+ DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data);
+ DCHECK_LE(roots_data, stack_map);
+ // Flush data cache, as compiled code references literals in it.
+ FlushDataCache(reinterpret_cast<char*>(roots_data),
+ reinterpret_cast<char*>(roots_data + data_size));
// Flush caches before we remove write permission because on some ARMv8 hardware,
// flushing caches require write permissions.
//
@@ -585,6 +594,9 @@
VLOG(jit) << "JIT discarded jitted code due to invalid single-implementation assumptions.";
return nullptr;
}
+ DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsDebuggable())
+ << "Should not be using cha on debuggable apps/runs!";
+
for (ArtMethod* single_impl : cha_single_implementation_list) {
Runtime::Current()->GetClassHierarchyAnalysis()->AddDependency(
single_impl, method, method_header);
@@ -636,6 +648,69 @@
return CodeCacheSizeLocked();
}
+// This notifies the code cache that the given method has been redefined and that it should remove
+// any cached information it has on the method. All threads must be suspended before calling this
+// method. The compiled code for the method (if there is any) must not be in any threads call stack.
+void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) {
+ MutexLock mu(Thread::Current(), lock_);
+ if (method->IsNative()) {
+ return;
+ }
+ ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize);
+ if (info != nullptr) {
+ auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info);
+ DCHECK(profile != profiling_infos_.end());
+ profiling_infos_.erase(profile);
+ }
+ method->SetProfilingInfo(nullptr);
+ ScopedCodeCacheWrite ccw(code_map_.get());
+ for (auto code_iter = method_code_map_.begin();
+ code_iter != method_code_map_.end();
+ ++code_iter) {
+ if (code_iter->second == method) {
+ FreeCode(code_iter->first);
+ method_code_map_.erase(code_iter);
+ }
+ }
+ auto code_map = osr_code_map_.find(method);
+ if (code_map != osr_code_map_.end()) {
+ osr_code_map_.erase(code_map);
+ }
+}
+
+// This invalidates old_method. Once this function returns one can no longer use old_method to
+// execute code unless it is fixed up. This fixup will happen later in the process of installing a
+// class redefinition.
+// TODO We should add some info to ArtMethod to note that 'old_method' has been invalidated and
+// shouldn't be used since it is no longer logically in the jit code cache.
+// TODO We should add DCHECKS that validate that the JIT is paused when this method is entered.
+void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) {
+ MutexLock mu(Thread::Current(), lock_);
+ // Update ProfilingInfo to the new one and remove it from the old_method.
+ if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) {
+ DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method);
+ ProfilingInfo* info = old_method->GetProfilingInfo(kRuntimePointerSize);
+ old_method->SetProfilingInfo(nullptr);
+ // Since the JIT should be paused and all threads suspended by the time this is called these
+ // checks should always pass.
+ DCHECK(!info->IsInUseByCompiler());
+ new_method->SetProfilingInfo(info);
+ info->method_ = new_method;
+ }
+ // Update method_code_map_ to point to the new method.
+ for (auto& it : method_code_map_) {
+ if (it.second == old_method) {
+ it.second = new_method;
+ }
+ }
+ // Update osr_code_map_ to point to the new method.
+ auto code_map = osr_code_map_.find(old_method);
+ if (code_map != osr_code_map_.end()) {
+ osr_code_map_.Put(new_method, code_map->second);
+ osr_code_map_.erase(old_method);
+ }
+}
+
size_t JitCodeCache::CodeCacheSizeLocked() {
return used_memory_for_code_;
}
@@ -657,12 +732,12 @@
FreeData(reinterpret_cast<uint8_t*>(roots_data));
}
-void JitCodeCache::ReserveData(Thread* self,
- size_t stack_map_size,
- size_t number_of_roots,
- ArtMethod* method,
- uint8_t** stack_map_data,
- uint8_t** roots_data) {
+size_t JitCodeCache::ReserveData(Thread* self,
+ size_t stack_map_size,
+ size_t number_of_roots,
+ ArtMethod* method,
+ uint8_t** stack_map_data,
+ uint8_t** roots_data) {
size_t table_size = ComputeRootTableSize(number_of_roots);
size_t size = RoundUp(stack_map_size + table_size, sizeof(void*));
uint8_t* result = nullptr;
@@ -695,9 +770,11 @@
*roots_data = result;
*stack_map_data = result + table_size;
FillRootTableLength(*roots_data, number_of_roots);
+ return size;
} else {
*roots_data = nullptr;
*stack_map_data = nullptr;
+ return 0;
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 30e2efb..b5e3176 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -75,6 +75,10 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!lock_);
+ void NotifyMethodRedefined(ArtMethod* method)
+ REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!lock_);
+
// Notify to the code cache that the compiler wants to use the
// profiling info of `method` to drive optimizations,
// and therefore ensure the returned profiling info object is not
@@ -106,6 +110,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
@@ -121,12 +126,13 @@
// Allocate a region of data that contain `size` bytes, and potentially space
// for storing `number_of_roots` roots. Returns null if there is no more room.
- void ReserveData(Thread* self,
- size_t size,
- size_t number_of_roots,
- ArtMethod* method,
- uint8_t** stack_map_data,
- uint8_t** roots_data)
+ // Return the number of bytes allocated.
+ size_t ReserveData(Thread* self,
+ size_t size,
+ size_t number_of_roots,
+ ArtMethod* method,
+ uint8_t** stack_map_data,
+ uint8_t** roots_data)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!lock_);
@@ -217,6 +223,11 @@
void DisallowInlineCacheAccess() REQUIRES(!lock_);
void BroadcastForInlineCacheAccess() REQUIRES(!lock_);
+ // Notify the code cache that the method at the pointer 'old_method' is being moved to the pointer
+ // 'new_method' since it is being made obsolete.
+ void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method)
+ REQUIRES(!lock_) REQUIRES(Locks::mutator_lock_);
+
private:
// Take ownership of maps.
JitCodeCache(MemMap* code_map,
@@ -237,6 +248,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 9902bb5..9fbf2e3 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -128,7 +128,9 @@
const uint32_t number_of_inline_caches_;
// Method this profiling info is for.
- ArtMethod* const method_;
+ // Not 'const' as JVMTI introduces obsolete methods that we implement by creating new ArtMethods.
+ // See JitCodeCache::MoveObsoleteMethod.
+ ArtMethod* method_;
// Whether the ArtMethod is currently being compiled. This flag
// is implicitly guarded by the JIT code cache lock.
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 342e0d2..5a3fafa 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -19,6 +19,8 @@
#include <algorithm>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "check_jni.h"
#include "indirect_reference_table.h"
#include "java_vm_ext.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr size_t kMonitorsInitial = 32; // Arbitrary.
static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check.
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index a421c34..4da5e23 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -16,6 +16,8 @@
#include "jni_internal.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "common_compiler_test.h"
#include "indirect_reference_table.h"
@@ -27,6 +29,8 @@
namespace art {
+using android::base::StringPrintf;
+
// TODO: Convert to CommonRuntimeTest. Currently MakeExecutable is used.
class JniInternalTest : public CommonCompilerTest {
protected:
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 6da72e4..19a65bb 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -24,7 +24,8 @@
#include <memory>
#include <sstream>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "base/unix_file/fd_file.h"
#include "os.h"
#include "thread-inl.h"
@@ -42,6 +43,8 @@
namespace art {
+using android::base::StringPrintf;
+
static std::ostream& operator<<(
std::ostream& os,
std::pair<BacktraceMap::const_iterator, BacktraceMap::const_iterator> iters) {
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 3c22d7f..da510ce 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "method_handles.h"
-
#include "method_handles-inl.h"
+
+#include "android-base/stringprintf.h"
+
#include "jvalue.h"
#include "jvalue-inl.h"
#include "reflection.h"
@@ -25,6 +26,8 @@
namespace art {
+using android::base::StringPrintf;
+
namespace {
#define PRIMITIVES_LIST(V) \
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 7d7c1d7..a5db0c0 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -19,10 +19,11 @@
#include "array.h"
+#include "android-base/stringprintf.h"
+
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "class-inl.h"
#include "gc/heap-inl.h"
#include "obj_ptr-inl.h"
@@ -167,9 +168,9 @@
#else
// 32-bit.
if (UNLIKELY(size == 0)) {
- self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
- array_class->PrettyDescriptor().c_str(),
- component_count).c_str());
+ self->ThrowOutOfMemoryError(android::base::StringPrintf("%s of length %d would overflow",
+ array_class->PrettyDescriptor().c_str(),
+ component_count).c_str());
return nullptr;
}
#endif
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 8afa4aa..cc548b9 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "array.h"
+#include "array-inl.h"
#include "class.h"
#include "class-inl.h"
@@ -23,7 +23,6 @@
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "object-inl.h"
-#include "object_array.h"
#include "object_array-inl.h"
#include "handle_scope-inl.h"
#include "thread.h"
@@ -32,6 +31,8 @@
namespace art {
namespace mirror {
+using android::base::StringPrintf;
+
// Create a multi-dimensional array of Objects or primitive types.
//
// We have to generate the names for X[], X[][], X[][][], and so on. The
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 5fdf8f3..2fb8d28 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -937,14 +937,6 @@
}
}
-inline void Class::SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings) {
- SetFieldPtr<false>(DexCacheStringsOffset(), new_dex_cache_strings);
-}
-
-inline StringDexCacheType* Class::GetDexCacheStrings() {
- return GetFieldPtr64<StringDexCacheType*>(DexCacheStringsOffset());
-}
-
template<ReadBarrierOption kReadBarrierOption, class Visitor>
void Class::VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) {
for (ArtField& field : GetSFieldsUnchecked()) {
@@ -1095,12 +1087,6 @@
if (methods != new_methods) {
dest->SetMethodsPtrInternal(new_methods);
}
- // Update dex cache strings.
- StringDexCacheType* strings = GetDexCacheStrings();
- StringDexCacheType* new_strings = visitor(strings);
- if (strings != new_strings) {
- dest->SetDexCacheStrings(new_strings);
- }
// Fix up embedded tables.
if (!IsTemp() && ShouldHaveEmbeddedVTable<kVerifyNone, kReadBarrierOption>()) {
for (int32_t i = 0, count = GetEmbeddedVTableLength(); i < count; ++i) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index a862c97..9964b73 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -16,6 +16,8 @@
#include "class.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "class_ext.h"
@@ -40,6 +42,8 @@
namespace art {
namespace mirror {
+using android::base::StringPrintf;
+
GcRoot<Class> Class::java_lang_Class_;
void Class::SetClassClass(ObjPtr<Class> java_lang_Class) {
@@ -187,7 +191,6 @@
void Class::SetDexCache(ObjPtr<DexCache> new_dex_cache) {
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache);
- SetDexCacheStrings(new_dex_cache != nullptr ? new_dex_cache->GetStrings() : nullptr);
}
void Class::SetClassSize(uint32_t new_class_size) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d7449c8..fb2792a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1247,13 +1247,6 @@
bool GetSlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_);
void SetSlowPath(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_);
- StringDexCacheType* GetDexCacheStrings() REQUIRES_SHARED(Locks::mutator_lock_);
- void SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings)
- REQUIRES_SHARED(Locks::mutator_lock_);
- static MemberOffset DexCacheStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_);
- }
-
// May cause thread suspension due to EqualParameters.
ArtMethod* GetDeclaredConstructor(Thread* self,
Handle<ObjectArray<Class>> args,
@@ -1438,9 +1431,6 @@
// virtual_ methods_ for miranda methods.
HeapReference<PointerArray> vtable_;
- // Short cuts to dex_cache_ member for fast compiled code access.
- uint64_t dex_cache_strings_;
-
// instance fields
//
// These describe the layout of the contents of an Object.
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index ec265e5..6f88cc5 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -19,7 +19,6 @@
#include "array.h"
#include "art_field.h"
-#include "art_method.h"
#include "class.h"
#include "dex_file_types.h"
#include "object.h"
@@ -27,6 +26,7 @@
namespace art {
+class ArtMethod;
struct DexCacheOffsets;
class DexFile;
class ImageWriter;
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 0fdf132..3e04bf6 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -17,12 +17,13 @@
#ifndef ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
#define ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
-#include <string>
-
#include "object_array.h"
+#include <string>
+
+#include "android-base/stringprintf.h"
+
#include "array-inl.h"
-#include "base/stringprintf.h"
#include "gc/heap.h"
#include "mirror/class.h"
#include "obj_ptr-inl.h"
@@ -330,13 +331,15 @@
std::string actualSrcType(mirror::Object::PrettyTypeOf(o));
std::string dstType(PrettyTypeOf());
Thread* self = Thread::Current();
+ std::string msg = android::base::StringPrintf(
+ "source[%d] of type %s cannot be stored in destination array of type %s",
+ src_pos + i,
+ actualSrcType.c_str(),
+ dstType.c_str());
if (throw_exception) {
- self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
- "source[%d] of type %s cannot be stored in destination array of type %s",
- src_pos + i, actualSrcType.c_str(), dstType.c_str());
+ self->ThrowNewException("Ljava/lang/ArrayStoreException;", msg.c_str());
} else {
- LOG(FATAL) << StringPrintf("source[%d] of type %s cannot be stored in destination array of type %s",
- src_pos + i, actualSrcType.c_str(), dstType.c_str());
+ LOG(FATAL) << msg;
}
}
}
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 95516ac..9b8445d 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -16,6 +16,10 @@
#ifndef ART_RUNTIME_MIRROR_STRING_INL_H_
#define ART_RUNTIME_MIRROR_STRING_INL_H_
+#include "string.h"
+
+#include "android-base/stringprintf.h"
+
#include "array.h"
#include "base/bit_utils.h"
#include "class.h"
@@ -24,7 +28,6 @@
#include "globals.h"
#include "intern_table.h"
#include "runtime.h"
-#include "string.h"
#include "thread.h"
#include "utf.h"
#include "utils.h"
@@ -228,9 +231,10 @@
"kObjectAlignment must be at least as big as Java char alignment");
const size_t max_length = RoundDown(max_alloc_length, kObjectAlignment / block_size);
if (UNLIKELY(length > max_length)) {
- self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
- Class::PrettyDescriptor(string_class).c_str(),
- static_cast<int>(length)).c_str());
+ self->ThrowOutOfMemoryError(
+ android::base::StringPrintf("%s of length %d would overflow",
+ Class::PrettyDescriptor(string_class).c_str(),
+ static_cast<int>(length)).c_str());
return nullptr;
}
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index ade4e87..e50409f 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -16,6 +16,8 @@
#include "throwable.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/enums.h"
#include "class-inl.h"
@@ -31,6 +33,8 @@
namespace art {
namespace mirror {
+using android::base::StringPrintf;
+
GcRoot<Class> Throwable::java_lang_Throwable_;
void Throwable::SetDetailMessage(ObjPtr<String> new_detail_message) {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index e7de7e6..222eb5c 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -18,6 +18,8 @@
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/mutex.h"
#include "base/stl_util.h"
@@ -38,6 +40,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr uint64_t kLongWaitMs = 100;
/*
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 1a77072..cd0e55f 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -18,9 +18,10 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "class_linker.h"
#include "common_throws.h"
#include "compiler_filter.h"
@@ -43,6 +44,8 @@
namespace art {
+using android::base::StringPrintf;
+
static bool ConvertJavaArrayToDexFiles(
JNIEnv* env,
jobject arrayObject,
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
index db245aa..981be68 100644
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
@@ -16,6 +16,8 @@
#include "dalvik_system_InMemoryDexClassLoader_DexData.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker.h"
#include "common_throws.h"
#include "dex_file.h"
@@ -29,6 +31,8 @@
namespace art {
+using android::base::StringPrintf;
+
static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
if (end <= start) {
ScopedObjectAccess soa(env);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 3058df4..1af8619 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -27,6 +27,8 @@
#include "toStringArray.h"
#pragma GCC diagnostic pop
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "arch/instruction_set.h"
#include "base/enums.h"
@@ -54,6 +56,8 @@
namespace art {
+using android::base::StringPrintf;
+
static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) {
return Runtime::Current()->GetHeap()->GetTargetHeapUtilization();
}
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a78909b..10fc90b 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -18,6 +18,8 @@
#include <stdlib.h>
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set.h"
#include "debugger.h"
#include "java_vm_ext.h"
@@ -37,6 +39,8 @@
namespace art {
+using android::base::StringPrintf;
+
static void EnableDebugger() {
#if defined(__linux__)
// To let a non-privileged gdbserver attach to this
diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc
index bedca10..c778068 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -33,8 +33,15 @@
return soa.AddLocalReference<jobject>(referent);
}
+static void Reference_clearReferent(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ ObjPtr<mirror::Reference> ref = soa.Decode<mirror::Reference>(javaThis);
+ Runtime::Current()->GetHeap()->GetReferenceProcessor()->ClearReferent(ref);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Reference, getReferent, "!()Ljava/lang/Object;"),
+ NATIVE_METHOD(Reference, clearReferent, "!()V"),
};
void register_java_lang_ref_Reference(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index 73b81a7..ee59c4a 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -16,6 +16,8 @@
#include "java_lang_reflect_Executable.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "dex_file_annotations.h"
#include "handle.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
static jobjectArray Executable_getDeclaredAnnotationsNative(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 6206948..374eeb5 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -16,6 +16,8 @@
#include "java_lang_reflect_Field.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker.h"
#include "class_linker-inl.h"
#include "common_throws.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
template<bool kIsSet>
ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self,
ObjPtr<mirror::Field> field,
diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc
index 16164d2..0bb9e38 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -16,6 +16,8 @@
#include "java_lang_reflect_Parameter.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "common_throws.h"
#include "dex_file-inl.h"
@@ -26,6 +28,8 @@
namespace art {
+using android::base::StringPrintf;
+
static jobject Parameter_getAnnotationNative(JNIEnv* env,
jclass,
jobject javaMethod,
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 5565565..7460d62 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -37,10 +37,11 @@
#include <sys/time.h>
#include <sys/types.h>
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set.h"
#include "base/memory_tool.h"
#include "base/mutex.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "oat_quick_method_header.h"
#include "os.h"
@@ -53,6 +54,8 @@
#if defined(__linux__)
+using android::base::StringPrintf;
+
static constexpr bool kUseAddr2line = !kIsTargetBuild;
ALWAYS_INLINE
diff --git a/runtime/oat.cc b/runtime/oat.cc
index aab0e81..cebe765 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -19,12 +19,15 @@
#include <string.h>
#include <zlib.h>
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set_features.h"
#include "base/bit_utils.h"
-#include "base/stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
constexpr uint8_t OatHeader::kOatMagic[4];
constexpr uint8_t OatHeader::kOatVersion[4];
constexpr const char OatHeader::kTrueValue[];
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index bdf8b0e..0bf7136 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -32,6 +32,8 @@
#include "android/dlext.h"
#endif
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/bit_vector.h"
#include "base/enums.h"
@@ -58,6 +60,8 @@
namespace art {
+using android::base::StringPrintf;
+
// Whether OatFile::Open will try dlopen. Fallback is our own ELF loader.
static constexpr bool kUseDlopen = true;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 7f7b1b5..ee7cf9d 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -23,7 +23,6 @@
#include "android-base/strings.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "compiler_filter.h"
#include "class_linker.h"
#include "gc/heap.h"
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 5641459..33bd0f3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -20,6 +20,8 @@
#include <queue>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/systrace.h"
@@ -38,6 +40,8 @@
namespace art {
+using android::base::StringPrintf;
+
// If true, then we attempt to load the application image if it exists.
static constexpr bool kEnableAppImage = true;
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 1ad3f08..5f97b60 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -700,7 +700,7 @@
jmethodID method,
jint* entry_count_ptr,
jvmtiLineNumberEntry** table_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLineNumberTable(env, method, entry_count_ptr, table_ptr);
}
static jvmtiError GetMethodLocation(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index ffa5ac7..a0a0923 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -130,4 +130,63 @@
return ERR(NONE);
}
+using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
+
+static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
+ LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
+ jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
+ static_cast<jint>(entry.line_) };
+ context->push_back(jvmti_entry);
+ return false; // Collect all, no early exit.
+}
+
+jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr) {
+ if (method == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ DCHECK(!art_method->IsRuntimeMethod());
+
+ const art::DexFile::CodeItem* code_item;
+ const art::DexFile* dex_file;
+ {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+
+ if (art_method->IsProxyMethod()) {
+ return ERR(ABSENT_INFORMATION);
+ }
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+ if (entry_count_ptr == nullptr || table_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ code_item = art_method->GetCodeItem();
+ dex_file = art_method->GetDexFile();
+ DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
+ }
+
+ LineNumberContext context;
+ bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
+ if (!success) {
+ return ERR(ABSENT_INFORMATION);
+ }
+
+ unsigned char* data;
+ jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry);
+ jvmtiError alloc_error = env->Allocate(mem_size, &data);
+ if (alloc_error != ERR(NONE)) {
+ return alloc_error;
+ }
+ *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data);
+ memcpy(*table_ptr, context.data(), mem_size);
+ *entry_count_ptr = static_cast<jint>(context.size());
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index 43f11f9..fb2fbb2 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -52,6 +52,11 @@
static jvmtiError GetMethodModifiers(jvmtiEnv* env,
jmethodID method,
jint* modifiers_ptr);
+
+ static jvmtiError GetLineNumberTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index d0349b9..57cc938 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -33,11 +33,15 @@
#include <limits>
+#include "android-base/stringprintf.h"
+
#include "art_jvmti.h"
#include "base/logging.h"
#include "events-inl.h"
#include "gc/allocation_listener.h"
#include "instrumentation.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "jni_env_ext-inl.h"
#include "jvmti_allocator.h"
#include "mirror/class.h"
@@ -49,13 +53,152 @@
namespace openjdkjvmti {
+using android::base::StringPrintf;
+
+// This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does
+// some basic sanity checks that the obsolete method is sane.
+class ObsoleteMethodStackVisitor : public art::StackVisitor {
+ protected:
+ ObsoleteMethodStackVisitor(
+ art::Thread* thread,
+ art::LinearAlloc* allocator,
+ const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
+ /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
+ /*out*/bool* success,
+ /*out*/std::string* error_msg)
+ : StackVisitor(thread,
+ /*context*/nullptr,
+ StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ allocator_(allocator),
+ obsoleted_methods_(obsoleted_methods),
+ obsolete_maps_(obsolete_maps),
+ success_(success),
+ is_runtime_frame_(false),
+ error_msg_(error_msg) {
+ *success_ = true;
+ }
+
+ ~ObsoleteMethodStackVisitor() OVERRIDE {}
+
+ public:
+ // Returns true if we successfully installed obsolete methods on this thread, filling
+ // obsolete_maps_ with the translations if needed. Returns false and fills error_msg if we fail.
+ // The stack is cleaned up when we fail.
+ static bool UpdateObsoleteFrames(
+ art::Thread* thread,
+ art::LinearAlloc* allocator,
+ const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
+ /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps,
+ /*out*/std::string* error_msg) REQUIRES(art::Locks::mutator_lock_) {
+ bool success = true;
+ ObsoleteMethodStackVisitor visitor(thread,
+ allocator,
+ obsoleted_methods,
+ obsolete_maps,
+ &success,
+ error_msg);
+ visitor.WalkStack();
+ if (!success) {
+ RestoreFrames(thread, *obsolete_maps, error_msg);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ static void RestoreFrames(
+ art::Thread* thread ATTRIBUTE_UNUSED,
+ const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsolete_maps ATTRIBUTE_UNUSED,
+ std::string* error_msg)
+ REQUIRES(art::Locks::mutator_lock_) {
+ LOG(FATAL) << "Restoring stack frames is not yet supported. Error was: " << *error_msg;
+ }
+
+ bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::ArtMethod* old_method = GetMethod();
+ // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
+ // works through runtime methods.
+ bool prev_was_runtime_frame_ = is_runtime_frame_;
+ is_runtime_frame_ = old_method->IsRuntimeMethod();
+ if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
+ // The check below works since when we deoptimize we set shadow frames for all frames until a
+ // native/runtime transition and for those set the return PC to a function that will complete
+ // the deoptimization. This does leave us with the unfortunate side-effect that frames just
+ // below runtime frames cannot be deoptimized at the moment.
+ // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
+ // works through runtime methods.
+ // TODO b/33616143
+ if (!IsShadowFrame() && prev_was_runtime_frame_) {
+ *error_msg_ = StringPrintf("Deoptimization failed due to runtime method in stack.");
+ *success_ = false;
+ return false;
+ }
+ // We cannot ensure that the right dex file is used in inlined frames so we don't support
+ // redefining them.
+ DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition";
+ // TODO We should really support intrinsic obsolete methods.
+ // TODO We should really support redefining intrinsics.
+ // We don't support intrinsics so check for them here.
+ DCHECK(!old_method->IsIntrinsic());
+ art::ArtMethod* new_obsolete_method = nullptr;
+ auto obsolete_method_pair = obsolete_maps_->find(old_method);
+ if (obsolete_method_pair == obsolete_maps_->end()) {
+ // Create a new Obsolete Method and put it in the list.
+ art::Runtime* runtime = art::Runtime::Current();
+ art::ClassLinker* cl = runtime->GetClassLinker();
+ auto ptr_size = cl->GetImagePointerSize();
+ const size_t method_size = art::ArtMethod::Size(ptr_size);
+ auto* method_storage = allocator_->Alloc(GetThread(), method_size);
+ if (method_storage == nullptr) {
+ *success_ = false;
+ *error_msg_ = StringPrintf("Unable to allocate storage for obsolete version of '%s'",
+ old_method->PrettyMethod().c_str());
+ return false;
+ }
+ new_obsolete_method = new (method_storage) art::ArtMethod();
+ new_obsolete_method->CopyFrom(old_method, ptr_size);
+ DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
+ new_obsolete_method->SetIsObsolete();
+ obsolete_maps_->insert({old_method, new_obsolete_method});
+ // Update JIT Data structures to point to the new method.
+ art::jit::Jit* jit = art::Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ // Notify the JIT we are making this obsolete method. It will update the jit's internal
+ // structures to keep track of the new obsolete method.
+ jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method);
+ }
+ } else {
+ new_obsolete_method = obsolete_method_pair->second;
+ }
+ DCHECK(new_obsolete_method != nullptr);
+ SetMethod(new_obsolete_method);
+ }
+ return true;
+ }
+
+ private:
+ // The linear allocator we should use to make new methods.
+ art::LinearAlloc* allocator_;
+ // The set of all methods which could be obsoleted.
+ const std::unordered_set<art::ArtMethod*>& obsoleted_methods_;
+ // A map from the original to the newly allocated obsolete method for frames on this thread. The
+ // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
+ // the redefined classes ClassExt by the caller.
+ std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
+ bool* success_;
+ // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
+ // works through runtime methods.
+ bool is_runtime_frame_;
+ std::string* error_msg_;
+};
+
// Moves dex data to an anonymous, read-only mmap'd region.
std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location,
jint data_len,
unsigned char* dex_data,
std::string* error_msg) {
std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous(
- art::StringPrintf("%s-transformed", original_location.c_str()).c_str(),
+ StringPrintf("%s-transformed", original_location.c_str()).c_str(),
nullptr,
data_len,
PROT_READ|PROT_WRITE,
@@ -72,6 +215,8 @@
return map;
}
+// TODO This should handle doing multiple classes at once so we need to do less cleanup when things
+// go wrong.
jvmtiError Redefiner::RedefineClass(ArtJvmTiEnv* env,
art::Runtime* runtime,
art::Thread* self,
@@ -112,6 +257,9 @@
*error_msg = os.str();
return ERR(INVALID_CLASS_FORMAT);
}
+ // Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we
+ // are going to redefine.
+ art::jit::ScopedJitSuspend suspend_jit;
// Get shared mutator lock.
art::ScopedObjectAccess soa(self);
art::StackHandleScope<1> hs(self);
@@ -246,9 +394,9 @@
}
void Redefiner::RecordFailure(jvmtiError result, const std::string& error_msg) {
- *error_msg_ = art::StringPrintf("Unable to perform redefinition of '%s': %s",
- class_sig_,
- error_msg.c_str());
+ *error_msg_ = StringPrintf("Unable to perform redefinition of '%s': %s",
+ class_sig_,
+ error_msg.c_str());
result_ = result;
}
@@ -292,6 +440,107 @@
return true;
}
+struct CallbackCtx {
+ Redefiner* const r;
+ art::LinearAlloc* allocator;
+ std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
+ std::unordered_set<art::ArtMethod*> obsolete_methods;
+ bool success;
+ std::string* error_msg;
+
+ CallbackCtx(Redefiner* self, art::LinearAlloc* alloc, std::string* error)
+ : r(self), allocator(alloc), success(true), error_msg(error) {}
+};
+
+void DoRestoreObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
+ CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
+ ObsoleteMethodStackVisitor::RestoreFrames(t, data->obsolete_map, data->error_msg);
+}
+
+void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
+ CallbackCtx* data = reinterpret_cast<CallbackCtx*>(vdata);
+ if (data->success) {
+ // Don't do anything if we already failed once.
+ data->success = ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
+ data->allocator,
+ data->obsolete_methods,
+ &data->obsolete_map,
+ data->error_msg);
+ }
+}
+
+// This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
+// updated so they will be run.
+bool Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) {
+ art::ScopedAssertNoThreadSuspension ns("No thread suspension during thread stack walking");
+ art::mirror::ClassExt* ext = art_klass->GetExtData();
+ CHECK(ext->GetObsoleteMethods() != nullptr);
+ CallbackCtx ctx(this, art_klass->GetClassLoader()->GetAllocator(), error_msg_);
+ // Add all the declared methods to the map
+ for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ ctx.obsolete_methods.insert(&m);
+ }
+ for (art::ArtMethod* old_method : ctx.obsolete_methods) {
+ if (old_method->IsIntrinsic()) {
+ *error_msg_ = StringPrintf("Method '%s' is intrinsic and cannot be made obsolete!",
+ old_method->PrettyMethod().c_str());
+ return false;
+ }
+ }
+ {
+ art::MutexLock mu(self_, *art::Locks::thread_list_lock_);
+ art::ThreadList* list = art::Runtime::Current()->GetThreadList();
+ list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
+ if (!ctx.success) {
+ list->ForEach(DoRestoreObsoleteMethodsCallback, static_cast<void*>(&ctx));
+ return false;
+ }
+ }
+ FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
+ return true;
+}
+
+// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
+// figure out their DexCaches.
+void Redefiner::FillObsoleteMethodMap(
+ art::mirror::Class* art_klass,
+ const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) {
+ int32_t index = 0;
+ art::mirror::ClassExt* ext_data = art_klass->GetExtData();
+ art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods();
+ art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches =
+ ext_data->GetObsoleteDexCaches();
+ int32_t num_method_slots = obsolete_methods->GetLength();
+ // Find the first empty index.
+ for (; index < num_method_slots; index++) {
+ if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>(
+ index, art::kRuntimePointerSize) == nullptr) {
+ break;
+ }
+ }
+ // Make sure we have enough space.
+ CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index));
+ CHECK(obsolete_dex_caches->Get(index) == nullptr);
+ // Fill in the map.
+ for (auto& obs : obsoletes) {
+ obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize);
+ obsolete_dex_caches->Set(index, art_klass->GetDexCache());
+ index++;
+ }
+}
+
+// TODO It should be possible to only deoptimize the specific obsolete methods.
+// TODO ReJitEverything can (sort of) fail. In certain cases it will skip deoptimizing some frames.
+// If one of these frames is an obsolete method we have a problem. b/33616143
+// TODO This shouldn't be necessary once we can ensure that the current method is not kept in
+// registers across suspend points.
+// TODO Pending b/33630159
+void Redefiner::EnsureObsoleteMethodsAreDeoptimized() {
+ art::ScopedAssertNoThreadSuspension nts("Deoptimizing everything!");
+ art::instrumentation::Instrumentation* i = runtime_->GetInstrumentation();
+ i->ReJitEverything("libOpenJkdJvmti - Class Redefinition");
+}
+
jvmtiError Redefiner::Run() {
art::StackHandleScope<5> hs(self_);
// TODO We might want to have a global lock (or one based on the class being redefined at least)
@@ -334,6 +583,11 @@
self_->TransitionFromRunnableToSuspended(art::ThreadState::kNative);
runtime_->GetThreadList()->SuspendAll(
"Final installation of redefined Class!", /*long_suspend*/true);
+ // TODO We need to invalidate all breakpoints in the redefined class with the debugger.
+ // TODO We need to deal with any instrumentation/debugger deoptimized_methods_.
+ // TODO We need to update all debugger MethodIDs so they note the method they point to is
+ // obsolete or implement some other well defined semantics.
+ // TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods.
// TODO Might want to move this into a different type.
// Now we reach the part where we must do active cleanup if something fails.
// TODO We should really Retry if this fails instead of simply aborting.
@@ -341,7 +595,8 @@
art::ObjPtr<art::mirror::LongArray> original_dex_file_cookie(nullptr);
if (!UpdateJavaDexFile(java_dex_file.Get(),
new_dex_file_cookie.Get(),
- &original_dex_file_cookie)) {
+ &original_dex_file_cookie) ||
+ !FindAndAllocateObsoleteMethods(art_class.Get())) {
// Release suspendAll
runtime_->GetThreadList()->ResumeAll();
// Get back shared mutator lock as expected for return.
@@ -357,23 +612,25 @@
self_->TransitionFromSuspendedToRunnable();
return result_;
}
- // Update the ClassObjects Keep the old DexCache (and other stuff) around so we can restore
- // functions/fields.
- // Verify the new Class.
- // Failure then undo updates to class
- // Do stack walks and allocate obsolete methods
- // Shrink the obsolete method maps if possible?
- // TODO find appropriate class loader. Allocate new dex files array. Pause all java treads.
- // Replace dex files array. Do stack scan + allocate obsoletes. Remove array if possible.
- // TODO We might want to ensure that all threads are stopped for this!
- // AddDexToClassPath();
- // TODO
- // Release suspendAll
+ // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
+ // pointers to their ArtMethod's stashed in registers that they then use to attempt to hit the
+ // DexCache.
+ // TODO This can fail (leave some methods optimized) near runtime methods (including
+ // quick-to-interpreter transition function).
+ // TODO We probably don't need this at all once we have a way to ensure that the
+ // current_art_method is never stashed in a (physical) register by the JIT and lost to the
+ // stack-walker.
+ EnsureObsoleteMethodsAreDeoptimized();
+ // TODO Verify the new Class.
+ // TODO Failure then undo updates to class
+ // TODO Shrink the obsolete method maps if possible?
+ // TODO find appropriate class loader.
// TODO Put this into a scoped thing.
runtime_->GetThreadList()->ResumeAll();
// Get back shared mutator lock as expected for return.
self_->TransitionFromSuspendedToRunnable();
- // TODO Do this at a more reasonable place.
+ // TODO Do the dex_file_ release at a more reasonable place. This works but it muddles who really
+ // owns the DexFile.
dex_file_.release();
return OK;
}
@@ -392,19 +649,14 @@
}
}
-// Performs updates to class that will allow us to verify it.
-bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+bool Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+ const art::DexFile::ClassDef& class_def) {
art::ClassLinker* linker = runtime_->GetClassLinker();
art::PointerSize image_pointer_size = linker->GetImagePointerSize();
- const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
- *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
- if (class_def == nullptr) {
- RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
- return false;
- }
- const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def->class_idx_);
+ const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def.class_idx_);
const art::DexFile& old_dex_file = mclass->GetDexFile();
+ // Update methods.
for (art::ArtMethod& method : mclass->GetMethods(image_pointer_size)) {
const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(method.GetName());
art::dex::TypeIndex method_return_idx =
@@ -421,25 +673,73 @@
}
const art::DexFile::ProtoId* proto_id = dex_file_->FindProtoId(method_return_idx,
new_type_list);
- CHECK(proto_id != nullptr || old_type_list == nullptr);
// TODO Return false, cleanup.
+ CHECK(proto_id != nullptr || old_type_list == nullptr);
const art::DexFile::MethodId* method_id = dex_file_->FindMethodId(declaring_class_id,
*new_name_id,
*proto_id);
- CHECK(method_id != nullptr);
// TODO Return false, cleanup.
+ CHECK(method_id != nullptr);
uint32_t dex_method_idx = dex_file_->GetIndexForMethodId(*method_id);
method.SetDexMethodIndex(dex_method_idx);
linker->SetEntryPointsToInterpreter(&method);
- method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(*class_def, dex_method_idx));
+ method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size);
method.SetDexCacheResolvedTypes(new_dex_cache->GetResolvedTypes(), image_pointer_size);
+ // Notify the jit that this method is redefined.
+ art::jit::Jit* jit = runtime_->GetJit();
+ if (jit != nullptr) {
+ jit->GetCodeCache()->NotifyMethodRedefined(&method);
+ }
}
+ return true;
+}
+
+bool Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
+ // TODO The IFields & SFields pointers should be combined like the methods_ arrays were.
+ for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) {
+ for (art::ArtField& field : fields_iter) {
+ std::string declaring_class_name;
+ const art::DexFile::TypeId* new_declaring_id =
+ dex_file_->FindTypeId(field.GetDeclaringClass()->GetDescriptor(&declaring_class_name));
+ const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(field.GetName());
+ const art::DexFile::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptor());
+ // TODO Handle error, cleanup.
+ CHECK(new_name_id != nullptr && new_type_id != nullptr && new_declaring_id != nullptr);
+ const art::DexFile::FieldId* new_field_id =
+ dex_file_->FindFieldId(*new_declaring_id, *new_name_id, *new_type_id);
+ CHECK(new_field_id != nullptr);
+ // We only need to update the index since the other data in the ArtField cannot be updated.
+ field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id));
+ }
+ }
+ return true;
+}
+
+// Performs updates to class that will allow us to verify it.
+bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+ const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
+ *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
+ if (class_def == nullptr) {
+ RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
+ return false;
+ }
+ if (!UpdateMethods(mclass, new_dex_cache, *class_def)) {
+ // TODO Investigate appropriate error types.
+ RecordFailure(ERR(INTERNAL), "Unable to update class methods.");
+ return false;
+ }
+ if (!UpdateFields(mclass)) {
+ // TODO Investigate appropriate error types.
+ RecordFailure(ERR(INTERNAL), "Unable to update class fields.");
+ return false;
+ }
+
// Update the class fields.
// Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
// to call GetReturnTypeDescriptor and GetParameterTypeList above).
mclass->SetDexCache(new_dex_cache.Ptr());
- mclass->SetDexCacheStrings(new_dex_cache->GetStrings());
mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_)));
return true;
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index c819acd..9d23ce4 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -64,6 +64,8 @@
namespace openjdkjvmti {
// Class that can redefine a single class's methods.
+// TODO We should really make this be driven by an outside class so we can do multiple classes at
+// the same time and have less required cleanup.
class Redefiner {
public:
// Redefine the given class with the given dex data. Note this function does not take ownership of
@@ -124,6 +126,14 @@
// in the future. For now we will just take the memory hit.
bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
+ // pointers to their ArtMethods stashed in registers that they then use to attempt to hit the
+ // DexCache.
+ void EnsureObsoleteMethodsAreDeoptimized()
+ REQUIRES(art::Locks::mutator_lock_)
+ REQUIRES(!art::Locks::thread_list_lock_,
+ !art::Locks::classlinker_classes_lock_);
+
art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_);
// This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath.
@@ -159,9 +169,24 @@
art::ObjPtr<art::mirror::LongArray> original_cookie)
REQUIRES(art::Locks::mutator_lock_);
+ bool UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ bool UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+ const art::DexFile::ClassDef& class_def)
+ REQUIRES(art::Locks::mutator_lock_);
+
bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
art::ObjPtr<art::mirror::DexCache> new_dex_cache)
REQUIRES(art::Locks::mutator_lock_);
+
+ bool FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ void FillObsoleteMethodMap(art::mirror::Class* art_klass,
+ const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
+ REQUIRES(art::Locks::mutator_lock_);
};
} // namespace openjdkjvmti
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 5b90c6a..8948c71 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -19,7 +19,6 @@
#include <memory>
#include "arch/instruction_set.h"
-#include "base/stringprintf.h"
#include "common_runtime_test.h"
namespace art {
diff --git a/runtime/plugin.cc b/runtime/plugin.cc
index 481b1ca..731967c 100644
--- a/runtime/plugin.cc
+++ b/runtime/plugin.cc
@@ -17,11 +17,15 @@
#include "plugin.h"
#include <dlfcn.h>
-#include "base/stringprintf.h"
+
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
namespace art {
+using android::base::StringPrintf;
+
const char* PLUGIN_INITIALIZATION_FUNCTION_NAME = "ArtPlugin_Initialize";
const char* PLUGIN_DEINITIALIZATION_FUNCTION_NAME = "ArtPlugin_Deinitialize";
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 1c975a4..d8b9dcc 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -16,6 +16,8 @@
#include "reference_table.h"
+#include "android-base/stringprintf.h"
+
#include "base/mutex.h"
#include "indirect_reference_table.h"
#include "mirror/array.h"
@@ -30,6 +32,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max_size)
: name_(name), max_size_(max_size) {
CHECK_LE(initial_size, max_size);
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index d80a9b3..9523e92 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -16,6 +16,8 @@
#include "reference_table.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker.h"
#include "common_runtime_test.h"
#include "handle_scope-inl.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
class ReferenceTableTest : public CommonRuntimeTest {};
static mirror::Object* CreateWeakReference(mirror::Object* referent)
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index 68e7a10..62ce9e9 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -19,7 +19,8 @@
#include "reflection.h"
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "common_throws.h"
#include "jvalue-inl.h"
#include "mirror/object-inl.h"
@@ -103,13 +104,14 @@
}
if (!unbox_for_result) {
- ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s",
- PrettyDescriptor(srcType).c_str(),
- PrettyDescriptor(dstType).c_str()).c_str());
+ ThrowIllegalArgumentException(
+ android::base::StringPrintf("Invalid primitive conversion from %s to %s",
+ PrettyDescriptor(srcType).c_str(),
+ PrettyDescriptor(dstType).c_str()).c_str());
} else {
- ThrowClassCastException(StringPrintf("Couldn't convert result of type %s to %s",
- PrettyDescriptor(srcType).c_str(),
- PrettyDescriptor(dstType).c_str()).c_str());
+ ThrowClassCastException(android::base::StringPrintf("Couldn't convert result of type %s to %s",
+ PrettyDescriptor(srcType).c_str(),
+ PrettyDescriptor(dstType).c_str()).c_str());
}
return false;
}
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 8446b52..4d24501 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -34,6 +34,8 @@
namespace art {
+using android::base::StringPrintf;
+
class ArgArray {
public:
ArgArray(const char* shorty, uint32_t shorty_len)
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 59c5961..2086d70 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1199,6 +1199,8 @@
CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
CHECK(self != nullptr);
+ self->SetCanCallIntoJava(!IsAotCompiler());
+
// Set us to runnable so tools using a runtime can allocate and GC by default
self->TransitionFromSuspendedToRunnable();
@@ -1721,7 +1723,7 @@
if (space->IsImageSpace()) {
auto* image_space = space->AsImageSpace();
const auto& image_header = image_space->GetImageHeader();
- for (size_t i = 0; i < ImageHeader::kImageRootsMax; ++i) {
+ for (int32_t i = 0, size = image_header.GetImageRoots()->GetLength(); i != size; ++i) {
auto* obj = image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i));
if (obj != nullptr) {
auto* after_obj = obj;
diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc
index be97860..0a996a9 100644
--- a/runtime/runtime_android.cc
+++ b/runtime/runtime_android.cc
@@ -21,7 +21,6 @@
#include "base/logging.h"
#include "base/mutex.h"
-#include "base/stringprintf.h"
#include "thread-inl.h"
#include "utils.h"
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 93704a9..b8894d2 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -24,11 +24,12 @@
#include <iostream>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/dumpable.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/mutex.h"
-#include "base/stringprintf.h"
#include "native_stack_dump.h"
#include "thread-inl.h"
#include "thread_list.h"
@@ -36,6 +37,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kUseSigRTTimeout = true;
static constexpr bool kDumpNativeStackOnTimeout = true;
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 792da88..f9efc0b 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -16,6 +16,8 @@
#include "stack.h"
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -39,6 +41,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kDebugStackWalk = false;
mirror::Object* ShadowFrame::GetThisObject() const {
@@ -614,6 +618,17 @@
return result;
}
+void StackVisitor::SetMethod(ArtMethod* method) {
+ DCHECK(GetMethod() != nullptr);
+ if (cur_shadow_frame_ != nullptr) {
+ cur_shadow_frame_->SetMethod(method);
+ } else {
+ DCHECK(cur_quick_frame_ != nullptr);
+ CHECK(!IsInInlinedFrame()) << "We do not support setting inlined method's ArtMethod!";
+ *cur_quick_frame_ = method;
+ }
+}
+
static void AssertPcIsWithinQuickCode(ArtMethod* method, uintptr_t pc)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (method->IsNative() || method->IsRuntimeMethod() || method->IsProxyMethod()) {
diff --git a/runtime/stack.h b/runtime/stack.h
index b1e99e5..9dceb29 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -327,6 +327,12 @@
}
}
+ void SetMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_) {
+ DCHECK(method != nullptr);
+ DCHECK(method_ != nullptr);
+ method_ = method;
+ }
+
ArtMethod* GetMethod() const REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(method_ != nullptr);
return method_;
@@ -610,6 +616,10 @@
ArtMethod* GetMethod() const REQUIRES_SHARED(Locks::mutator_lock_);
+ // Sets this stack frame's method pointer. This requires a full lock of the MutatorLock. This
+ // doesn't work with inlined methods.
+ void SetMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
+
ArtMethod* GetOuterMethod() const {
return *GetCurrentQuickFrame();
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index d79bf36..aff12ff 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -28,6 +28,8 @@
#include <list>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
@@ -88,6 +90,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
extern "C" NO_RETURN void artDeoptimize(Thread* self);
bool Thread::is_started_ = false;
@@ -1802,7 +1807,11 @@
}
}
-Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupted_(false) {
+Thread::Thread(bool daemon)
+ : tls32_(daemon),
+ wait_monitor_(nullptr),
+ interrupted_(false),
+ can_call_into_java_(true) {
wait_mutex_ = new Mutex("a thread wait mutex");
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
diff --git a/runtime/thread.h b/runtime/thread.h
index 31cd0eb..411d85f 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -881,6 +881,15 @@
--tls32_.disable_thread_flip_count;
}
+ // Returns true if the thread is allowed to call into java.
+ bool CanCallIntoJava() const {
+ return can_call_into_java_;
+ }
+
+ void SetCanCallIntoJava(bool can_call_into_java) {
+ can_call_into_java_ = can_call_into_java;
+ }
+
// Activates single step control for debugging. The thread takes the
// ownership of the given SingleStepControl*. It is deleted by a call
// to DeactivateSingleStepControl or upon thread destruction.
@@ -1588,6 +1597,10 @@
// Pending extra checkpoints if checkpoint_function_ is already used.
std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_);
+ // True if the thread is allowed to call back into java (for e.g. during class resolution).
+ // By default this is true.
+ bool can_call_into_java_;
+
friend class Dbg; // For SetStateUnsafe.
friend class gc::collector::SemiSpace; // For getting stack traces.
friend class Runtime; // For CreatePeer.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 664eeb4..bf9eef8 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -25,6 +25,8 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/histogram-inl.h"
#include "base/mutex-inl.h"
#include "base/systrace.h"
@@ -52,6 +54,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5);
static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000; // 30s.
// Use 0 since we want to yield to prevent blocking for an unpredictable amount of time.
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index d9d2ea3..d9179c3 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -21,6 +21,8 @@
#include <sys/time.h>
#include <sys/resource.h>
+#include "android-base/stringprintf.h"
+
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/logging.h"
@@ -31,6 +33,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kMeasureWaitTime = false;
ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name,
@@ -86,6 +90,8 @@
Runtime* runtime = Runtime::Current();
CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false));
worker->thread_ = Thread::Current();
+ // Thread pool workers cannot call into java.
+ worker->thread_->SetCanCallIntoJava(false);
// Do work until its time to shut down.
worker->Run();
runtime->DetachCurrentThread();
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index eaadfe0..7ecfcd1 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -80,6 +80,7 @@
DISALLOW_COPY_AND_ASSIGN(ThreadPoolWorker);
};
+// Note that thread pool workers will set Thread#setCanCallIntoJava to false.
class ThreadPool {
public:
// Returns the number of threads in the thread pool.
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index d21ff77..0bba44c 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -15,12 +15,17 @@
*/
#include "agent.h"
+
+#include "android-base/stringprintf.h"
+
#include "java_vm_ext.h"
#include "runtime.h"
namespace art {
namespace ti {
+using android::base::StringPrintf;
+
const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index 6561756..7408aee 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -20,7 +20,6 @@
#include <dlfcn.h>
#include <jni.h> // for jint, JavaVM* etc declarations
-#include "base/stringprintf.h"
#include "runtime.h"
#include "utils.h"
diff --git a/runtime/trace.cc b/runtime/trace.cc
index f564de4..9d9360e 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -19,6 +19,8 @@
#include <sys/uio.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/casts.h"
#include "base/enums.h"
@@ -46,6 +48,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr size_t TraceActionBits = MinimumBitsToStore(
static_cast<size_t>(kTraceMethodActionMask));
static constexpr uint8_t kOpNewMethod = 1U;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 4732f59..8867743 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <memory>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "base/stl_util.h"
@@ -48,6 +49,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
static const uint8_t kBase64Map[256] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
diff --git a/runtime/utils.h b/runtime/utils.h
index 04e0dde..16ef706 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -284,6 +284,12 @@
__builtin___clear_cache(begin, end);
}
+inline void FlushDataCache(char* begin, char* end) {
+ // Same as FlushInstructionCache for lack of other builtin. __builtin___clear_cache
+ // flushes both caches.
+ __builtin___clear_cache(begin, end);
+}
+
template <typename T>
constexpr PointerSize ConvertToPointerSize(T any) {
if (any == 4 || any == 8) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ebecc85..715b237 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -18,6 +18,8 @@
#include <iostream>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -55,6 +57,8 @@
namespace art {
namespace verifier {
+using android::base::StringPrintf;
+
static constexpr bool kTimeVerifyMethod = !kIsDebugBuild;
static constexpr bool kDebugVerify = false;
// TODO: Add a constant to method_verifier to turn on verbose logging?
@@ -417,6 +421,19 @@
if (method != nullptr) {
if (verifier.HasInstructionThatWillThrow()) {
method->AddAccessFlags(kAccCompileDontBother);
+ if (Runtime::Current()->IsAotCompiler() &&
+ (callbacks != nullptr) && !callbacks->IsBootImage()) {
+ // When compiling apps, make HasInstructionThatWillThrow a soft error to trigger
+ // re-verification at runtime.
+ // The dead code after the throw is not verified and might be invalid. This may cause
+ // the JIT compiler to crash since it assumes that all the code is valid.
+ //
+ // There's a strong assumption that the entire boot image is verified and all its dex
+ // code is valid (even the dead and unverified one). As such this is done only for apps.
+ // (CompilerDriver DCHECKs in VerifyClassVisitor that methods from boot image are
+ // fully verified).
+ result.kind = kSoftFailure;
+ }
}
if ((verifier.encountered_failure_types_ & VerifyError::VERIFY_ERROR_LOCKING) != 0) {
method->AddAccessFlags(kAccMustCountLocks);
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index 10f1be5..aa4a259 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -112,7 +112,8 @@
} else if (lhs.HasClass() && rhs.HasClass()) {
// Test assignability from the Class point-of-view.
bool result = lhs.GetClass()->IsAssignableFrom(rhs.GetClass());
- // Record assignability dependency. The `verifier` is null during unit tests.
+ // Record assignability dependency. The `verifier` is null during unit tests and
+ // VerifiedMethod::GenerateSafeCastSet.
if (verifier != nullptr) {
VerifierDeps::MaybeRecordAssignability(
verifier->GetDexFile(), lhs.GetClass(), rhs.GetClass(), strict, result);
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index ab23773..52f7e34 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -16,6 +16,8 @@
#include "reg_type-inl.h"
+#include "android-base/stringprintf.h"
+
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
#include "base/casts.h"
@@ -35,6 +37,8 @@
namespace art {
namespace verifier {
+using android::base::StringPrintf;
+
const UndefinedType* UndefinedType::instance_ = nullptr;
const ConflictType* ConflictType::instance_ = nullptr;
const BooleanType* BooleanType::instance_ = nullptr;
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index a6088aa..383d890 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -16,7 +16,8 @@
#include "register_line.h"
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_instruction-inl.h"
#include "method_verifier-inl.h"
#include "register_line-inl.h"
@@ -25,6 +26,8 @@
namespace art {
namespace verifier {
+using android::base::StringPrintf;
+
bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const {
if (kIsDebugBuild && this_initialized_) {
// Ensure that there is no UninitializedThisReference type anymore if this_initialized_ is true.
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index f9bff23..5f94a1b 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -249,11 +249,12 @@
if (dex_cache == nullptr) {
// This is a synthesized class, in this case always an array. They are not
// defined in the compiled DEX files and therefore are part of the classpath.
- // We could avoid recording dependencies on arrays with component types in
- // the compiled DEX files but we choose to record them anyway so as to
- // record the access flags VM sets for array classes.
+ // We do not record dependencies on arrays with component types in
+ // the compiled DEX files, as the only thing that might change is their
+ // access flags. If we were to change these flags in a breaking way, we would
+ // need to enforce full verification again anyways by updating the vdex version.
DCHECK(klass->IsArrayClass()) << klass->PrettyDescriptor();
- return true;
+ return false;
}
const DexFile* dex_file = dex_cache->GetDexFile();
@@ -345,8 +346,21 @@
// merely on no issues with linking (valid access flags, superclass and
// implemented interfaces). If the class at any point reached the IsResolved
// status, the requirement holds. This is guaranteed by RegTypeCache::ResolveClass.
- DCHECK(destination != nullptr && !destination->IsPrimitive());
- DCHECK(source != nullptr && !source->IsPrimitive());
+ DCHECK(destination != nullptr);
+ DCHECK(source != nullptr);
+
+ if (destination->IsPrimitive() || source->IsPrimitive()) {
+ // Primitive types are trivially non-assignable to anything else.
+ // We do not need to record trivial assignability, as it will
+ // not change across releases.
+ return;
+ }
+
+ if (source->IsObjectClass() && !is_assignable) {
+ // j.l.Object is trivially non-assignable to other types, don't
+ // record it.
+ return;
+ }
if (destination == source ||
destination->IsObjectClass() ||
@@ -389,6 +403,21 @@
return;
}
+ if (!IsInClassPath(source) && !source->IsInterface() && !destination->IsInterface()) {
+ // Find the super class at the classpath boundary. Only that class
+ // can change the assignability.
+ // TODO: also chase the boundary for interfaces.
+ do {
+ source = source->GetSuperClass();
+ } while (!IsInClassPath(source));
+
+ // If that class is the actual destination, no need to record it.
+ if (source == destination) {
+ return;
+ }
+ }
+
+
// Get string IDs for both descriptors and store in the appropriate set.
dex::StringIndex destination_id = GetClassDescriptorStringId(dex_file, destination);
dex::StringIndex source_id = GetClassDescriptorStringId(dex_file, source);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3549586..009170c 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -20,6 +20,8 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "jni_internal.h"
@@ -189,7 +191,7 @@
static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const char* boxed_name) {
ScopedLocalRef<jclass> boxed_class(env, env->FindClass(boxed_name));
return CacheMethod(env, boxed_class.get(), true, "valueOf",
- StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
+ android::base::StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
}
#define STRING_INIT_LIST(V) \
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index d96fb42..cd79bb6 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -23,7 +23,6 @@
#include <unistd.h>
#include <vector>
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
namespace art {
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 113b35f..3b237f4 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -25,11 +25,11 @@
#include "jni.h"
+#include "android-base/stringprintf.h"
#include <backtrace/Backtrace.h>
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "oat_file.h"
@@ -91,7 +91,7 @@
static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
printf("Secondary pid is %d\n", pid);
- PrintFileToLog(StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR);
+ PrintFileToLog(android::base::StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR);
if (sig_quit_on_fail) {
int res = kill(pid, SIGQUIT);
diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt
index 0a03ecb..a1c5fa8 100644
--- a/test/141-class-unload/expected.txt
+++ b/test/141-class-unload/expected.txt
@@ -20,5 +20,6 @@
JNI_OnLoad called
class null false test
JNI_OnUnload called
+JNI_OnLoad called
Number of loaded unload-ex maps 0
Too small false
diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc
index bbbb0a6..9b7e171 100644
--- a/test/141-class-unload/jni_unload.cc
+++ b/test/141-class-unload/jni_unload.cc
@@ -32,5 +32,19 @@
}
}
+extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->Stop();
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->Start();
+ }
+}
+
} // namespace
} // namespace art
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 2a6e944..595c70d 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -55,11 +55,15 @@
}
private static void testOatFilesUnloaded(int pid) throws Exception {
+ System.loadLibrary(nativeLibraryName);
+ // Stop the JIT to ensure its threads and work queue are not keeping classes
+ // artifically alive.
+ stopJit();
+ Runtime.getRuntime().gc();
+ System.runFinalization();
BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
String line;
int count = 0;
- Runtime.getRuntime().gc();
- System.runFinalization();
while ((line = reader.readLine()) != null) {
if (line.contains("@141-class-unload-ex.jar")) {
System.out.println(line);
@@ -67,6 +71,7 @@
}
}
System.out.println("Number of loaded unload-ex maps " + count);
+ startJit();
}
private static void stressTest(Constructor<?> constructor) throws Exception {
@@ -229,4 +234,7 @@
private static int getPid() throws Exception {
return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
}
+
+ public static native void stopJit();
+ public static native void startJit();
}
diff --git a/test/153-reference-stress/expected.txt b/test/153-reference-stress/expected.txt
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/test/153-reference-stress/expected.txt
@@ -0,0 +1 @@
+PASS
diff --git a/test/153-reference-stress/info.txt b/test/153-reference-stress/info.txt
new file mode 100644
index 0000000..6bc0040
--- /dev/null
+++ b/test/153-reference-stress/info.txt
@@ -0,0 +1 @@
+Tests java.lang.ref.Reference.get() and GC running in parallel.
diff --git a/test/153-reference-stress/src/Main.java b/test/153-reference-stress/src/Main.java
new file mode 100644
index 0000000..fc6f9cc
--- /dev/null
+++ b/test/153-reference-stress/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.ref.WeakReference;
+
+public class Main {
+ static final int numWeakReferences = 16 * 1024;
+ static WeakReference[] weakReferences = new WeakReference[numWeakReferences];
+ static volatile boolean done = false;
+ static Object keepAlive;
+
+ public static void main(String[] args) throws Exception {
+ // Try to call Reference.get repeatedly while the GC is running.
+ Thread gcThread = new GcThread();
+ Thread[] readerThread = new ReaderThread[4];
+ for (int i = 0; i < readerThread.length; ++i) {
+ readerThread[i] = new ReaderThread();
+ }
+ gcThread.start();
+ for (int i = 0; i < readerThread.length; ++i) {
+ readerThread[i].start();
+ }
+ gcThread.join();
+ for (int i = 0; i < readerThread.length; ++i) {
+ readerThread[i].join();
+ }
+ System.out.println("PASS");
+ }
+
+ static class GcThread extends Thread {
+ GcThread() {
+ Object temp = new Object();
+ for (int j = 0; j < weakReferences.length; ++j) {
+ weakReferences[j] = new WeakReference(temp);
+ }
+ }
+ public void run() {
+ for (int i = 0; i < 1000; ++i) {
+ Object o = new Object();
+ for (int j = 0; j < weakReferences.length; ++j) {
+ weakReferences[j] = new WeakReference(o);
+ }
+ }
+ done = true;
+ }
+ }
+
+ static class ReaderThread extends Thread {
+ public void run() {
+ while (!done) {
+ for (int j = 0; j < weakReferences.length; ++j) {
+ keepAlive = weakReferences[j].get();
+ }
+ for (int j = 0; j < weakReferences.length; ++j) {
+ weakReferences[j].clear();
+ }
+ }
+ }
+ }
+}
diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java
index 209786a..dfc4a5f 100644
--- a/test/530-checker-loops3/src/Main.java
+++ b/test/530-checker-loops3/src/Main.java
@@ -235,6 +235,59 @@
}
}
+ /// CHECK-START: void Main.shortBound1(int[], short) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-NOT: Deoptimize
+ //
+ /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void shortBound1(int[] array, short s) {
+ // Lower precision bound will appear in deopt arithmetic
+ // and follows normal implicit widening conversion.
+ for (int i = 0; i < s; i++) {
+ array[i] = 222;
+ }
+ }
+
+ /// CHECK-START: void Main.shortBound2(int[], short) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-NOT: Deoptimize
+ //
+ /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void shortBound2(int[] array, short s) {
+ // Lower precision bound will appear in deopt arithmetic
+ // and follows normal implicit widening conversion.
+ for (int i = 0; s > i; i++) {
+ array[i] = 444;
+ }
+ }
+
+ /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (after)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ public static void narrowingFromLong(int[] array, int n) {
+ // Parallel induction in long precision that is narrowed provides type
+ // conversion challenges for BCE in deopt arithmetic when combined
+ // with the int loop induction. Therefore, currently skipped.
+ long l = 0;
+ for (int i = 0; i < n; i++, l++) {
+ array[(int)l] = 888;
+ }
+ }
+
//
// Verifier.
//
@@ -316,6 +369,38 @@
} catch (ArrayIndexOutOfBoundsException e) {
}
+ shortBound1(a, (short)a.length);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(222, a[i]);
+ }
+ shortBound2(a, (short)a.length);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(444, a[i]);
+ }
+
+ try {
+ shortBound1(a, (short)(a.length + 1));
+ throw new Error("Should throw AIOOBE");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(222, a[i]);
+ }
+
+ try {
+ shortBound2(a, (short)(a.length + 1));
+ throw new Error("Should throw AIOOBE");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(444, a[i]);
+ }
+
+ narrowingFromLong(a, a.length);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(888, a[i]);
+ }
+
System.out.println("passed");
}
diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java
index 7d3d7d9..91af1f4 100644
--- a/test/530-checker-loops4/src/Main.java
+++ b/test/530-checker-loops4/src/Main.java
@@ -96,7 +96,29 @@
/// CHECK-NOT: Phi
public static int geo4(int a) {
for (int i = 0; i < 10; i++) {
- a %= 7;
+ a %= 7; // a wrap-around induction
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo5() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Shr loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo5() loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Int1:i\d+>> IntConstant 2147483647 loop:none
+ /// CHECK-DAG: <<Int2:i\d+>> IntConstant 1024 loop:none
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<Int1>>,<<Int2>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Div>>,<<Zero>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo5() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo5() {
+ int a = 0x7fffffff;
+ for (int i = 0; i < 10; i++) {
+ a >>= 1;
}
return a;
}
@@ -186,7 +208,7 @@
int r = 0;
for (int i = 0; i < 100; i++) { // a converges to 0
r += x[a];
- a %= 5;
+ a %= 5; // a wrap-around induction
}
return r;
}
@@ -305,6 +327,8 @@
expectEquals(i % 7, geo4(i));
}
+ expectEquals(0x1fffff, geo5());
+
expectEquals(34, geo1BCE());
expectEquals(36, geo2BCE());
expectEquals(131, geo3BCE());
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 9e475ab..fe6ff13 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -55,6 +55,9 @@
/// CHECK-NOT: MipsDexCacheArraysBase
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+ /// CHECK-START-MIPS64: int Main.testSimple(int) sharpening (after)
+ /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+
/// CHECK-START-X86: int Main.testSimple(int) sharpening (after)
/// CHECK-NOT: X86ComputeBaseMethodAddress
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
@@ -96,6 +99,10 @@
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+ /// CHECK-START-MIPS64: int Main.testDiamond(boolean, int) sharpening (after)
+ /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+ /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+
/// CHECK-START-X86: int Main.testDiamond(boolean, int) sharpening (after)
/// CHECK-NOT: X86ComputeBaseMethodAddress
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
@@ -274,6 +281,11 @@
// TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
/// CHECK: LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}}
+ /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getBootImageString() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}}
+
public static String $noinline$getBootImageString() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
@@ -303,6 +315,9 @@
/// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after)
/// CHECK: LoadString load_kind:BssEntry
+ /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after)
+ /// CHECK: LoadString load_kind:BssEntry
+
public static String $noinline$getNonBootImageString() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
@@ -338,6 +353,11 @@
// TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
/// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+ /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+
public static Class<?> $noinline$getStringClass() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
@@ -375,6 +395,9 @@
/// CHECK-DAG: MipsDexCacheArraysBase
/// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other
+ /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
+ /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other
+
public static Class<?> $noinline$getOtherClass() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
diff --git a/test/616-cha-regression-proxy-method/expected.txt b/test/616-cha-regression-proxy-method/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-regression-proxy-method/info.txt b/test/616-cha-regression-proxy-method/info.txt
new file mode 100644
index 0000000..386a07f
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/info.txt
@@ -0,0 +1 @@
+Regression test for Class Hierarchy Analysis (CHA) on visiting proxy method frame.
diff --git a/test/616-cha-regression-proxy-method/src/Main.java b/test/616-cha-regression-proxy-method/src/Main.java
new file mode 100644
index 0000000..19c92be
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/src/Main.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+class Main1 {
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
+ }
+
+ void printError(String msg) {
+ System.out.println(msg);
+ }
+}
+
+class Main2 extends Main1 {
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
+ }
+}
+
+class Proxied implements Runnable {
+ public void run() {
+ synchronized(Main.class) {
+ Main.sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+}
+
+class MyInvocationHandler implements InvocationHandler {
+ private final Proxied proxied;
+
+ public MyInvocationHandler(Proxied proxied) {
+ this.proxied = proxied;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return method.invoke(proxied, args);
+ }
+}
+
+public class Main {
+ static Main1 sMain1;
+ static Main1 sMain2;
+ static volatile boolean sOtherThreadStarted;
+
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Dummy.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride() {
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Dummy.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Dummy.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
+ }
+
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ // Also create a proxy method such that a proxy method's frame is visited
+ // during stack walking.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ // Create another thread that calls a proxy method.
+ new Thread() {
+ public void run() {
+ Runnable proxy = (Runnable)Proxy.newProxyInstance(
+ Proxied.class.getClassLoader(),
+ new Class[] { Runnable.class },
+ new MyInvocationHandler(new Proxied()));
+ proxy.run();
+ }
+ }.start();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride();
+ }
+
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+}
+
+// Put createMain2() in another class to avoid class loading due to verifier.
+class Dummy {
+ static Main1 createMain2() {
+ return new Main2();
+ }
+}
diff --git a/test/Android.arm_vixl.mk b/test/616-cha/run
similarity index 72%
rename from test/Android.arm_vixl.mk
rename to test/616-cha/run
index c89eb4a..9c64c7d 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/616-cha/run
@@ -1,3 +1,4 @@
+#!/bin/bash
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -5,16 +6,13 @@
# 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
+# 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.
-#
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
- 562-checker-no-intermediate \
- 624-checker-stringops \
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index 787318d..b617944 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -179,7 +179,7 @@
}
}
- // Test scanerios under which CHA-based devirtualization happens,
+ // Test scenarios under which CHA-based devirtualization happens,
// and class loading that overrides a method can invalidate compiled code.
// Also test pure non-overriding case, which is more for checking generated
// code form.
@@ -206,11 +206,6 @@
// sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
sMain1 = new Main1();
- // Loop enough to get testOverride() JITed.
- for (int i=0; i<100; i++) {
- testOverride(false, false, false);
- }
-
ensureJitCompiled(Main.class, "testOverride");
testOverride(false, false, true);
@@ -244,7 +239,7 @@
private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
-// Do it in another class to avoid class loading due to verifier.
+// Put createMain2() in another class to avoid class loading due to verifier.
class Dummy {
static Main1 createMain2() {
return new Main2();
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 87a69b2..ad3ff44 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -248,6 +248,33 @@
return closed; // only needs last value
}
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Select loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Select
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 81 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormInductionTrivialIf() {
+ int closed = 11;
+ for (int i = 0; i < 10; i++) {
+ // Trivial if becomes trivial select at HIR level.
+ // Make sure this is still recognized as induction.
+ if (i < 5) {
+ closed += 7;
+ } else {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last value
+ }
+
/// CHECK-START: int Main.closedFormNested() loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
@@ -438,6 +465,20 @@
return i;
}
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort1(short s) {
+ int i = 0;
+ for (i = 0; i < s; i++) { }
+ return i;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort2(short s) {
+ int i = 0;
+ for (i = 0; s > i; i++) { }
+ return i;
+ }
+
/// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
@@ -666,6 +707,75 @@
return x;
}
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 2 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat10() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 10; i++) {
+ float tmp = t; t = r; r = s; s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant -1 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat11() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 11; i++) {
+ float tmp = t; t = r; r = s; s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 4.5 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat12() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 12; i++) {
+ float tmp = t; t = r; r = s; s = tmp;
+ }
+ return r;
+ }
+
private static int exceptionExitBeforeAdd() {
int k = 0;
try {
@@ -732,6 +842,7 @@
expectEquals(12395, closedFormInductionUp());
expectEquals(12295, closedFormInductionInAndDown(12345));
+ expectEquals(81, closedFormInductionTrivialIf());
expectEquals(10 * 10, closedFormNested());
expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
for (int n = -4; n < 10; n++) {
@@ -751,6 +862,8 @@
for (int n = -4; n < 4; n++) {
int tc = (n <= 0) ? 0 : n;
expectEquals(tc, mainIndexReturnedN(n));
+ expectEquals(tc, mainIndexShort1((short) n));
+ expectEquals(tc, mainIndexShort2((short) n));
expectEquals(tc & 1, periodicReturnedN(n));
expectEquals((tc * (tc + 1)) / 2, getSumN(n));
}
@@ -775,6 +888,10 @@
expectEquals(!even, periodicBoolIdiom3N(false, n));
}
+ expectEquals( 2.0f, periodicFloat10());
+ expectEquals(-1.0f, periodicFloat11());
+ expectEquals( 4.5f, periodicFloat12());
+
expectEquals(100, exceptionExitBeforeAdd());
expectEquals(100, exceptionExitAfterAdd());
a = null;
@@ -787,6 +904,12 @@
System.out.println("passed");
}
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
private static void expectEquals(int expected, int result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index ce5bda1..7cc0b8b 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -82,6 +82,88 @@
return 0;
}
+ // Regression test for b/33774618: transfer operations involving
+ // narrowing linear induction should be done correctly.
+ //
+ /// CHECK-START: int Main.transferNarrowWrap() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.transferNarrowWrap() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ static int transferNarrowWrap() {
+ short x = 0;
+ int w = 10;
+ int v = 3;
+ for (int i = 0; i < 10; i++) {
+ v = w + 1; // transfer on wrap-around
+ w = x; // wrap-around
+ x += 2; // narrowing linear
+ }
+ return v;
+ }
+
+ // Regression test for b/33774618: transfer operations involving
+ // narrowing linear induction should be done correctly
+ // (currently rejected, could be improved).
+ //
+ /// CHECK-START: int Main.polynomialShort() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.polynomialShort() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ static int polynomialShort() {
+ int x = 0;
+ for (short i = 0; i < 10; i++) {
+ x = x - i; // polynomial on narrowing linear
+ }
+ return x;
+ }
+
+ // Regression test for b/33774618: transfer operations involving
+ // narrowing linear induction should be done correctly
+ // (currently rejected, could be improved).
+ //
+ /// CHECK-START: int Main.polynomialIntFromLong() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.polynomialIntFromLong() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ static int polynomialIntFromLong() {
+ int x = 0;
+ for (long i = 0; i < 10; i++) {
+ x = x - (int) i; // polynomial on narrowing linear
+ }
+ return x;
+ }
+
+ /// CHECK-START: int Main.polynomialInt() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.polynomialInt() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.polynomialInt() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant -45 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int polynomialInt() {
+ int x = 0;
+ for (int i = 0; i < 10; i++) {
+ x = x - i;
+ }
+ return x;
+ }
+
public static void main(String[] args) {
expectEquals(10, earlyExitFirst(-1));
for (int i = 0; i <= 10; i++) {
@@ -98,6 +180,11 @@
expectEquals(2, earlyExitNested());
+ expectEquals(17, transferNarrowWrap());
+ expectEquals(-45, polynomialShort());
+ expectEquals(-45, polynomialIntFromLong());
+ expectEquals(-45, polynomialInt());
+
System.out.println("passed");
}
diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java
index d965e3f..75b782e 100644
--- a/test/624-checker-stringops/src/Main.java
+++ b/test/624-checker-stringops/src/Main.java
@@ -258,6 +258,20 @@
return b.length();
}
+ // Regression b/33656359: StringBuffer x is passed to constructor of String
+ // (this caused old code to crash due to missing nullptr check).
+ //
+ /// CHECK-START: void Main.doesNothing() instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
+ //
+ /// CHECK-START: void Main.doesNothing() instruction_simplifier (after)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
+ static void doesNothing() {
+ StringBuffer x = new StringBuffer();
+ String y = new String(x);
+ x.toString();
+ }
+
public static void main(String[] args) {
expectEquals(1865, liveIndexOf());
expectEquals(29, deadIndexOf());
@@ -281,6 +295,8 @@
expectEquals(0, bufferDeadLoop());
expectEquals(0, builderDeadLoop());
+ doesNothing();
+
System.out.println("passed");
}
diff --git a/test/626-checker-arm64-scratch-register/expected.txt b/test/626-checker-arm64-scratch-register/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/626-checker-arm64-scratch-register/info.txt b/test/626-checker-arm64-scratch-register/info.txt
new file mode 100644
index 0000000..8472131
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/info.txt
@@ -0,0 +1,2 @@
+Regression test checking that the ARM64 scratch register pool is not
+exhausted during moves between stack slots (b/32545705).
diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java
new file mode 100644
index 0000000..aa211be
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/src/Main.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 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 {
+
+ boolean b00;
+ boolean b01;
+ boolean b02;
+ boolean b03;
+ boolean b04;
+ boolean b05;
+ boolean b06;
+ boolean b07;
+ boolean b08;
+ boolean b09;
+ boolean b10;
+ boolean b11;
+ boolean b12;
+ boolean b13;
+ boolean b14;
+ boolean b15;
+ boolean b16;
+ boolean b17;
+ boolean b18;
+ boolean b19;
+ boolean b20;
+ boolean b21;
+ boolean b22;
+ boolean b23;
+ boolean b24;
+ boolean b25;
+ boolean b26;
+ boolean b27;
+ boolean b28;
+ boolean b29;
+ boolean b30;
+ boolean b31;
+ boolean b32;
+ boolean b33;
+ boolean b34;
+ boolean b35;
+ boolean b36;
+
+ boolean conditionA;
+ boolean conditionB;
+ boolean conditionC;
+
+ /// CHECK-START-ARM64: void Main.test() register (after)
+ /// CHECK: begin_block
+ /// CHECK: name "B0"
+ /// CHECK: <<This:l\d+>> ParameterValue
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+ /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB
+ /// CHECK: If [<<CondB>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: name "<<ElseBlock>>"
+ /// CHECK: ParallelMove moves:[#100->d17,32(sp)->d1,36(sp)->d2,d17->d3,d3->d4,d4->d5,d5->d6,d6->d7,d7->d18,d18->d19,d19->d20,d20->d21,d21->d22,d22->d23,d23->d10,d10->d11,d11->d12,24(sp)->d13,28(sp)->d14,d14->16(sp),d12->20(sp),d13->24(sp),d1->28(sp),d2->32(sp),16(sp)->36(sp),20(sp)->40(sp)]
+ /// CHECK: end_block
+
+ /// CHECK-START-ARM64: void Main.test() disassembly (after)
+ /// CHECK: begin_block
+ /// CHECK: name "B0"
+ /// CHECK: <<This:l\d+>> ParameterValue
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+ /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB
+ /// CHECK: If [<<CondB>>]
+ /// CHECK: end_block
+ /// CHECK: begin_block
+ /// CHECK: name "<<ElseBlock>>"
+ /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
+ /// CHECK: fmov d31, d2
+ /// CHECK: ldr s2, [sp, #36]
+ /// CHECK: ldr w16, [sp, #16]
+ /// CHECK: str w16, [sp, #36]
+ /// CHECK: str s14, [sp, #16]
+ /// CHECK: ldr s14, [sp, #28]
+ /// CHECK: str s1, [sp, #28]
+ /// CHECK: ldr s1, [sp, #32]
+ /// CHECK: str s31, [sp, #32]
+ /// CHECK: ldr w16, [sp, #20]
+ /// CHECK: str w16, [sp, #40]
+ /// CHECK: str s12, [sp, #20]
+ /// CHECK: fmov d12, d11
+ /// CHECK: fmov d11, d10
+ /// CHECK: fmov d10, d23
+ /// CHECK: fmov d23, d22
+ /// CHECK: fmov d22, d21
+ /// CHECK: fmov d21, d20
+ /// CHECK: fmov d20, d19
+ /// CHECK: fmov d19, d18
+ /// CHECK: fmov d18, d7
+ /// CHECK: fmov d7, d6
+ /// CHECK: fmov d6, d5
+ /// CHECK: fmov d5, d4
+ /// CHECK: fmov d4, d3
+ /// CHECK: fmov d3, d17
+ /// CHECK: fmov d17, d13
+ /// CHECK: ldr s13, [sp, #24]
+ /// CHECK: str s17, [sp, #24]
+ /// CHECK: ldr s17, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
+ /// CHECK: end_block
+
+ public void test() {
+ String r = "";
+
+ // For the purpose of this regression test, the order of
+ // definition of these float variable matters. Likewise with the
+ // order of the instructions where these variables are used below.
+ // Reordering these lines make make the original (b/32545705)
+ // issue vanish.
+ float f17 = b17 ? 0.0f : 1.0f;
+ float f16 = b16 ? 0.0f : 1.0f;
+ float f18 = b18 ? 0.0f : 1.0f;
+ float f19 = b19 ? 0.0f : 1.0f;
+ float f20 = b20 ? 0.0f : 1.0f;
+ float f21 = b21 ? 0.0f : 1.0f;
+ float f15 = b15 ? 0.0f : 1.0f;
+ float f00 = b00 ? 0.0f : 1.0f;
+ float f22 = b22 ? 0.0f : 1.0f;
+ float f23 = b23 ? 0.0f : 1.0f;
+ float f24 = b24 ? 0.0f : 1.0f;
+ float f25 = b25 ? 0.0f : 1.0f;
+ float f26 = b26 ? 0.0f : 1.0f;
+ float f27 = b27 ? 0.0f : 1.0f;
+ float f29 = b29 ? 0.0f : 1.0f;
+ float f28 = b28 ? 0.0f : 1.0f;
+ float f01 = b01 ? 0.0f : 1.0f;
+ float f02 = b02 ? 0.0f : 1.0f;
+ float f03 = b03 ? 0.0f : 1.0f;
+ float f04 = b04 ? 0.0f : 1.0f;
+ float f05 = b05 ? 0.0f : 1.0f;
+ float f07 = b07 ? 0.0f : 1.0f;
+ float f06 = b06 ? 0.0f : 1.0f;
+ float f30 = b30 ? 0.0f : 1.0f;
+ float f31 = b31 ? 0.0f : 1.0f;
+ float f32 = b32 ? 0.0f : 1.0f;
+ float f33 = b33 ? 0.0f : 1.0f;
+ float f34 = b34 ? 0.0f : 1.0f;
+ float f36 = b36 ? 0.0f : 1.0f;
+ float f35 = b35 ? 0.0f : 1.0f;
+ float f08 = b08 ? 0.0f : 1.0f;
+ float f09 = b09 ? 0.0f : 1.0f;
+ float f10 = b10 ? 0.0f : 1.0f;
+ float f11 = b11 ? 0.0f : 1.0f;
+ float f12 = b12 ? 0.0f : 1.0f;
+ float f14 = b14 ? 0.0f : 1.0f;
+ float f13 = b13 ? 0.0f : 1.0f;
+
+ if (conditionA) {
+ f16 /= 1000.0f;
+ f17 /= 1000.0f;
+ f18 /= 1000.0f;
+ f19 /= 1000.0f;
+ f20 /= 1000.0f;
+ f21 /= 1000.0f;
+ f15 /= 1000.0f;
+ f08 /= 1000.0f;
+ f09 /= 1000.0f;
+ f10 /= 1000.0f;
+ f11 /= 1000.0f;
+ f12 /= 1000.0f;
+ f30 /= 1000.0f;
+ f31 /= 1000.0f;
+ f32 /= 1000.0f;
+ f33 /= 1000.0f;
+ f34 /= 1000.0f;
+ f01 /= 1000.0f;
+ f02 /= 1000.0f;
+ f03 /= 1000.0f;
+ f04 /= 1000.0f;
+ f05 /= 1000.0f;
+ f23 /= 1000.0f;
+ f24 /= 1000.0f;
+ f25 /= 1000.0f;
+ f26 /= 1000.0f;
+ f27 /= 1000.0f;
+ f22 /= 1000.0f;
+ f00 /= 1000.0f;
+ f14 /= 1000.0f;
+ f13 /= 1000.0f;
+ f36 /= 1000.0f;
+ f35 /= 1000.0f;
+ f07 /= 1000.0f;
+ f06 /= 1000.0f;
+ f29 /= 1000.0f;
+ f28 /= 1000.0f;
+ }
+ // The parallel move that used to exhaust the ARM64 parallel move
+ // resolver's scratch register pool (provided by VIXL) was in the
+ // "else" branch of the following condition generated by ART's
+ // compiler.
+ if (conditionB) {
+ f16 /= 100.0f;
+ f17 /= 100.0f;
+ f18 /= 100.0f;
+ f19 /= 100.0f;
+ f20 /= 100.0f;
+ f21 /= 100.0f;
+ f15 /= 100.0f;
+ f08 /= 100.0f;
+ f09 /= 100.0f;
+ f10 /= 100.0f;
+ f11 /= 100.0f;
+ f12 /= 100.0f;
+ f30 /= 100.0f;
+ f31 /= 100.0f;
+ f32 /= 100.0f;
+ f33 /= 100.0f;
+ f34 /= 100.0f;
+ f01 /= 100.0f;
+ f02 /= 100.0f;
+ f03 /= 100.0f;
+ f04 /= 100.0f;
+ f05 /= 100.0f;
+ f23 /= 100.0f;
+ f24 /= 100.0f;
+ f25 /= 100.0f;
+ f26 /= 100.0f;
+ f27 /= 100.0f;
+ f22 /= 100.0f;
+ f00 /= 100.0f;
+ f14 /= 100.0f;
+ f13 /= 100.0f;
+ f36 /= 100.0f;
+ f35 /= 100.0f;
+ f07 /= 100.0f;
+ f06 /= 100.0f;
+ f29 /= 100.0f;
+ f28 /= 100.0f;
+ }
+ if (conditionC) {
+ f16 /= 12.0f;
+ f17 /= 12.0f;
+ f18 /= 12.0f;
+ f19 /= 12.0f;
+ f20 /= 12.0f;
+ f21 /= 12.0f;
+ f15 /= 12.0f;
+ f08 /= 12.0f;
+ f09 /= 12.0f;
+ f10 /= 12.0f;
+ f11 /= 12.0f;
+ f12 /= 12.0f;
+ f30 /= 12.0f;
+ f31 /= 12.0f;
+ f32 /= 12.0f;
+ f33 /= 12.0f;
+ f34 /= 12.0f;
+ f01 /= 12.0f;
+ f02 /= 12.0f;
+ f03 /= 12.0f;
+ f04 /= 12.0f;
+ f05 /= 12.0f;
+ f23 /= 12.0f;
+ f24 /= 12.0f;
+ f25 /= 12.0f;
+ f26 /= 12.0f;
+ f27 /= 12.0f;
+ f22 /= 12.0f;
+ f00 /= 12.0f;
+ f14 /= 12.0f;
+ f13 /= 12.0f;
+ f36 /= 12.0f;
+ f35 /= 12.0f;
+ f07 /= 12.0f;
+ f06 /= 12.0f;
+ f29 /= 12.0f;
+ f28 /= 12.0f;
+ }
+ float s = 0.0f;
+ s = ((float) Math.round(100.0f * s)) / 100.0f;
+ String res = s + r;
+ }
+
+ public static void main(String[] args) {
+ Main main = new Main();
+ main.test();
+ System.out.println("passed");
+ }
+}
diff --git a/test/628-vdex/run b/test/628-vdex/run
index f1b0a95..4cbcea3 100644
--- a/test/628-vdex/run
+++ b/test/628-vdex/run
@@ -14,4 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-exec ${RUN} --vdex "${@}"
+exec ${RUN} -Xcompiler-option --compiler-filter=verify-profile --vdex "${@}"
diff --git a/test/630-safecast-array/expected.txt b/test/630-safecast-array/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/630-safecast-array/expected.txt
diff --git a/test/630-safecast-array/info.txt b/test/630-safecast-array/info.txt
new file mode 100644
index 0000000..e105167
--- /dev/null
+++ b/test/630-safecast-array/info.txt
@@ -0,0 +1,3 @@
+Regression test for vdex, which used to crash in AddAssignability
+called by the dex2dex compiler, not anticipating arrays of primitive
+type.
diff --git a/test/630-safecast-array/smali/Main.smali b/test/630-safecast-array/smali/Main.smali
new file mode 100644
index 0000000..a50f37c
--- /dev/null
+++ b/test/630-safecast-array/smali/Main.smali
@@ -0,0 +1,33 @@
+# Copyright 2016 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.
+
+.class LMain;
+.super Ljava/lang/Object;
+
+.method public static main([Ljava/lang/String;)V
+.registers 1
+ return-void
+.end method
+
+.method public static testPrimitiveDestination([Ljava/lang/String;)V
+.registers 1
+ check-cast p0, [B
+ return-void
+.end method
+
+.method public static testPrimitiveSource([B)V
+.registers 1
+ check-cast p0, [Ljava/lang/String;
+ return-void
+.end method
diff --git a/test/706-jit-skip-compilation/expected.txt b/test/706-jit-skip-compilation/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/706-jit-skip-compilation/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/706-jit-skip-compilation/info.txt b/test/706-jit-skip-compilation/info.txt
new file mode 100644
index 0000000..e9ef86b
--- /dev/null
+++ b/test/706-jit-skip-compilation/info.txt
@@ -0,0 +1,4 @@
+Regression test for the JIT crashing when compiling a method with invalid
+dead dex code. For not compilable methods we don't gather samples and we don't
+trigger JIT compilation. However kAccDontBotherCompile is not persisted in the
+oat file and so we may end up compiling a method which we shouldn't.
diff --git a/test/Android.arm_vixl.mk b/test/706-jit-skip-compilation/run
similarity index 69%
copy from test/Android.arm_vixl.mk
copy to test/706-jit-skip-compilation/run
index c89eb4a..6c5720a 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/706-jit-skip-compilation/run
@@ -1,3 +1,4 @@
+#!/bin/bash
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -5,16 +6,14 @@
# 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
+# 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.
-#
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
- 562-checker-no-intermediate \
- 624-checker-stringops \
+# Run without the app image, otherwise the verification results will be cached
+# in the ArtMethod of the image and the test will be skewed.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/706-jit-skip-compilation/smali/errclass.smali b/test/706-jit-skip-compilation/smali/errclass.smali
new file mode 100644
index 0000000..410504c
--- /dev/null
+++ b/test/706-jit-skip-compilation/smali/errclass.smali
@@ -0,0 +1,34 @@
+# Copyright (C) 2016 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.
+
+
+.class public LErrClass;
+
+.super Ljava/lang/Object;
+
+.method public static errMethod()J
+ .registers 8
+ const/4 v0, 0x0
+ const/4 v3, 0x0
+ aget v1, v0, v3 # v0 is null, this will alays throw and the invalid code
+ # below will not be verified.
+ move v3, v4
+ move-wide/from16 v6, v2 # should trigger a verification error if verified as
+ # v3 is a single register but used as a pair here.
+ return v6
+.end method
+
+# Add a field to work around demerger bug b/18051191.
+# Failure to verify dex file '...': Offset(552) should be zero when size is zero for field-ids.
+.field private a:I
diff --git a/test/706-jit-skip-compilation/src/Main.java b/test/706-jit-skip-compilation/src/Main.java
new file mode 100644
index 0000000..aa84724
--- /dev/null
+++ b/test/706-jit-skip-compilation/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Class<?> c = Class.forName("ErrClass");
+ Method m = c.getMethod("errMethod");
+
+ // Print the counter before invokes. The golden file expects this to be 0.
+ int hotnessCounter = getHotnessCounter(c, "errMethod");
+ if (hotnessCounter != 0) {
+ throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter);
+ }
+
+ // Loop enough to make sure the interpreter reports invocations count.
+ long result = 0;
+ for (int i = 0; i < 10000; i++) {
+ try {
+ result += (Long)m.invoke(null);
+ hotnessCounter = getHotnessCounter(c, "errMethod");
+ if (hotnessCounter != 0) {
+ throw new RuntimeException(
+ "Unexpected hotnessCounter: " + hotnessCounter);
+ }
+
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof NullPointerException)) {
+ throw e;
+ }
+ }
+ }
+
+ // Not compilable methods should not increase their hotness counter.
+ if (hotnessCounter != 0) {
+ throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter);
+ }
+ }
+
+ public static native int getHotnessCounter(Class cls, String method_name);
+}
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index 77c77ca..f8c97ce 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -3,206 +3,206 @@
###################
From top
---------
- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 38
- main ([Ljava/lang/String;)V 6
+ getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 38 34
+ main ([Ljava/lang/String;)V 6 24
---------
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 42
- main ([Ljava/lang/String;)V 6
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 42 35
+ main ([Ljava/lang/String;)V 6 24
---------
- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
+ getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
---------
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
From bottom
---------
- main ([Ljava/lang/String;)V 6
+ main ([Ljava/lang/String;)V 6 24
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 65
- main ([Ljava/lang/String;)V 6
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 65 41
+ main ([Ljava/lang/String;)V 6 24
---------
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
################################
### Other thread (suspended) ###
################################
From top
---------
- wait ()V -1
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
---------
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
---------
- wait ()V -1
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
From bottom
---------
- run ()V 4
+ run ()V 4 54
---------
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
###########################
### Other thread (live) ###
###########################
From top
---------
- printOrWait (IILMain$ControlData;)V 44
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ printOrWait (IILMain$ControlData;)V 44 164
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
---------
- printOrWait (IILMain$ControlData;)V 44
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
+ printOrWait (IILMain$ControlData;)V 44 164
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
---------
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
From bottom
---------
- run ()V 4
+ run ()V 4 88
---------
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index b5b5678..57f6a92 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -20,9 +20,10 @@
#include <memory>
#include <stdio.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "jni.h"
#include "openjdkjvmti/jvmti.h"
#include "ScopedLocalRef.h"
@@ -32,6 +33,25 @@
namespace art {
namespace Test911GetStackTrace {
+using android::base::StringPrintf;
+
+static jint FindLineNumber(jint line_number_count,
+ jvmtiLineNumberEntry* line_number_table,
+ jlocation location) {
+ if (line_number_table == nullptr) {
+ return -2;
+ }
+
+ jint line_number = -1;
+ for (jint i = 0; i != line_number_count; ++i) {
+ if (line_number_table[i].start_location > location) {
+ return line_number;
+ }
+ line_number = line_number_table[i].line_number;
+ }
+ return line_number;
+}
+
extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) {
std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
@@ -61,6 +81,26 @@
}
}
+ jint line_number_count;
+ jvmtiLineNumberEntry* line_number_table;
+ {
+ jvmtiError line_result = jvmti_env->GetLineNumberTable(frames[method_index].method,
+ &line_number_count,
+ &line_number_table);
+ if (line_result != JVMTI_ERROR_NONE) {
+ // Accept absent info and native method errors.
+ if (line_result != JVMTI_ERROR_ABSENT_INFORMATION &&
+ line_result != JVMTI_ERROR_NATIVE_METHOD) {
+ char* err;
+ jvmti_env->GetErrorName(line_result, &err);
+ printf("Failure running GetLineNumberTable: %s\n", err);
+ return nullptr;
+ }
+ line_number_table = nullptr;
+ line_number_count = 0;
+ }
+ }
+
auto inner_callback = [&](jint component_index) -> jstring {
switch (component_index) {
case 0:
@@ -69,11 +109,17 @@
return (sig == nullptr) ? nullptr : env->NewStringUTF(sig);
case 2:
return env->NewStringUTF(StringPrintf("%" PRId64, frames[method_index].location).c_str());
+ case 3: {
+ jint line_number = FindLineNumber(line_number_count,
+ line_number_table,
+ frames[method_index].location);
+ return env->NewStringUTF(StringPrintf("%d", line_number).c_str());
+ }
}
LOG(FATAL) << "Unreachable";
UNREACHABLE();
};
- jobjectArray inner_array = CreateObjectArray(env, 3, "java/lang/String", inner_callback);
+ jobjectArray inner_array = CreateObjectArray(env, 4, "java/lang/String", inner_callback);
if (name != nullptr) {
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
@@ -84,6 +130,9 @@
if (gen != nullptr) {
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
}
+ if (line_number_table != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(line_number_table));
+ }
return inner_array;
};
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index e5fa53f..7522a65 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -5,12 +5,12 @@
root@root --(thread)--> 3000@0 [size=132, length=-1]
0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -22,12 +22,12 @@
6@1000 --(class)--> 1000@0 [size=123, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -46,12 +46,12 @@
root@root --(thread)--> 1@1000 [size=16, length=-1]
root@root --(thread)--> 3000@0 [size=132, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -63,12 +63,12 @@
6@1000 --(class)--> 1000@0 [size=123, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 7b00fcd..49ab7dd 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -22,9 +22,10 @@
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "jit/jit.h"
#include "jni.h"
#include "native_stack_dump.h"
@@ -39,6 +40,8 @@
namespace art {
namespace Test913Heaps {
+using android::base::StringPrintf;
+
extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env ATTRIBUTE_UNUSED,
jclass klass ATTRIBUTE_UNUSED) {
jvmtiError ret = jvmti_env->ForceGarbageCollection();
@@ -180,7 +183,7 @@
if (*tag_ptr >= 1000) {
// This is a class or interface, the size of which will be dependent on the architecture.
// Do not print the size, but detect known values and "normalize" for the golden file.
- if ((sizeof(void*) == 4 && size == 180) || (sizeof(void*) == 8 && size == 232)) {
+ if ((sizeof(void*) == 4 && size == 172) || (sizeof(void*) == 8 && size == 224)) {
adapted_size = 123;
}
}
diff --git a/test/Android.arm_vixl.mk b/test/914-hello-obsolescence/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/914-hello-obsolescence/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/914-hello-obsolescence/build
@@ -1,5 +1,6 @@
+#!/bin/bash
#
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 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.
@@ -12,9 +13,5 @@
# 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.
-#
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
- 562-checker-no-intermediate \
- 624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/914-hello-obsolescence/expected.txt b/test/914-hello-obsolescence/expected.txt
new file mode 100644
index 0000000..83efda1
--- /dev/null
+++ b/test/914-hello-obsolescence/expected.txt
@@ -0,0 +1,9 @@
+hello
+Not doing anything here
+goodbye
+hello
+transforming calling function
+goodbye
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
diff --git a/test/914-hello-obsolescence/info.txt b/test/914-hello-obsolescence/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/914-hello-obsolescence/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/914-hello-obsolescence/run b/test/914-hello-obsolescence/run
new file mode 100755
index 0000000..b2f0b04
--- /dev/null
+++ b/test/914-hello-obsolescence/run
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+ if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+ else
+ other_args=""
+ fi
+fi
+
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=914-hello-obsolescence,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ --android-runtime-option -Xfully-deoptable \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/914-hello-obsolescence/src/Main.java b/test/914-hello-obsolescence/src/Main.java
new file mode 100644
index 0000000..46266ef
--- /dev/null
+++ b/test/914-hello-obsolescence/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.util.Base64;
+
+public class Main {
+ // class Transform {
+ // public void sayHi(Runnable r) {
+ // System.out.println("Hello - Transformed");
+ // r.run();
+ // System.out.println("Goodbye - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+ "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
+ "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
+ "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
+ "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
+ "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
+ "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
+ "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
+ "AQAPAAAAAgAQ");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
+ "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
+ "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
+ "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+ "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
+ "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
+ "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
+ "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+ "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+ "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+ "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
+ "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
+ "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
+ "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
+ "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[1]);
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ t.sayHi(() -> {
+ System.out.println("transforming calling function");
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ });
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+}
diff --git a/test/914-hello-obsolescence/src/Transform.java b/test/914-hello-obsolescence/src/Transform.java
new file mode 100644
index 0000000..8cda6cd
--- /dev/null
+++ b/test/914-hello-obsolescence/src/Transform.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class Transform {
+ public void sayHi(Runnable r) {
+ // Use lower 'h' to make sure the string will have a different string id
+ // than the transformation (the transformation code is the same except
+ // the actual printed String, which was making the test inacurately passing
+ // in JIT mode when loading the string from the dex cache, as the string ids
+ // of the two different strings were the same).
+ // We know the string ids will be different because lexicographically:
+ // "Hello" < "LTransform;" < "hello".
+ System.out.println("hello");
+ r.run();
+ System.out.println("goodbye");
+ }
+}
diff --git a/test/Android.arm_vixl.mk b/test/915-obsolete-2/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/915-obsolete-2/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/915-obsolete-2/build
@@ -1,5 +1,6 @@
+#!/bin/bash
#
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 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.
@@ -12,9 +13,5 @@
# 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.
-#
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
- 562-checker-no-intermediate \
- 624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/915-obsolete-2/expected.txt b/test/915-obsolete-2/expected.txt
new file mode 100644
index 0000000..04aff3a
--- /dev/null
+++ b/test/915-obsolete-2/expected.txt
@@ -0,0 +1,21 @@
+Pre Start private method call
+hello - private
+Post Start private method call
+Not doing anything here
+Pre Finish private method call
+goodbye - private
+Post Finish private method call
+Pre Start private method call
+hello - private
+Post Start private method call
+transforming calling function
+Pre Finish private method call
+Goodbye - private - Transformed
+Post Finish private method call
+Pre Start private method call - Transformed
+Hello - private - Transformed
+Post Start private method call - Transformed
+Not doing anything here
+Pre Finish private method call - Transformed
+Goodbye - private - Transformed
+Post Finish private method call - Transformed
diff --git a/test/915-obsolete-2/info.txt b/test/915-obsolete-2/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/915-obsolete-2/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/915-obsolete-2/run b/test/915-obsolete-2/run
new file mode 100755
index 0000000..bfe227f
--- /dev/null
+++ b/test/915-obsolete-2/run
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+ if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+ else
+ other_args=""
+ fi
+fi
+
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=915-obsolete-2,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ --android-runtime-option -Xfully-deoptable \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/915-obsolete-2/src/Main.java b/test/915-obsolete-2/src/Main.java
new file mode 100644
index 0000000..bbeb726
--- /dev/null
+++ b/test/915-obsolete-2/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.util.Base64;
+
+public class Main {
+ // class Transform {
+ // private void Start() {
+ // System.out.println("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish() {
+ // System.out.println("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r) {
+ // System.out.println("Pre Start private method call - Transformed");
+ // Start();
+ // System.out.println("Post Start private method call - Transformed");
+ // r.run();
+ // System.out.println("Pre Finish private method call - Transformed");
+ // Finish();
+ // System.out.println("Post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAMgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
+ "JwcAKAcAKQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
+ "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAO" +
+ "VHJhbnNmb3JtLmphdmEMAA8AEAcAKgwAKwAsAQAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3Jt" +
+ "ZWQHAC0MAC4ALwEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQBACtQcmUgU3RhcnQg" +
+ "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAATABABACxQb3N0IFN0YXJ0IHByaXZh" +
+ "dGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcAMAwAMQAQAQAsUHJlIEZpbmlzaCBwcml2YXRl" +
+ "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABQAEAEALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0" +
+ "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAEACVRyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QBABBq" +
+ "YXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9Q" +
+ "cmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABJqYXZhL2xhbmcv" +
+ "UnVubmFibGUBAANydW4AIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAAB" +
+ "ABIAAAAGAAEAAAABAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAA" +
+ "AAMACAAEAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAcACAAI" +
+ "AAEAFQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQq" +
+ "twALsgACEgy2AASxAAAAAQASAAAAIgAIAAAACwAIAAwADAANABQADgAaAA8AIgAQACYAEQAuABIA" +
+ "AQAXAAAAAgAY");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCM0QYTJmX+NsZXkImojgSkJtXyuew3oaXcBAAAcAAAAHhWNBIAAAAAAAAAADwEAAAX" +
+ "AAAAcAAAAAcAAADMAAAAAwAAAOgAAAABAAAADAEAAAcAAAAUAQAAAQAAAEwBAABwAwAAbAEAAD4C" +
+ "AABGAgAATgIAAG8CAACOAgAAmwIAALICAADGAgAA3AIAAPACAAAEAwAAMwMAAGEDAACPAwAAvAMA" +
+ "AMMDAADTAwAA1gMAANoDAADuAwAA8wMAAPwDAAABBAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA" +
+ "EAAAABAAAAAGAAAAAAAAABEAAAAGAAAAMAIAABEAAAAGAAAAOAIAAAUAAQATAAAAAAAAAAAAAAAA" +
+ "AAAAAQAAAAAAAAAOAAAAAAABABYAAAABAAIAFAAAAAIAAAAAAAAAAwAAABUAAAAAAAAAAAAAAAIA" +
+ "AAAAAAAADwAAAAAAAAAmBAAAAAAAAAEAAQABAAAACAQAAAQAAABwEAUAAAAOAAMAAQACAAAADQQA" +
+ "AAkAAABiAAAAGwECAAAAbiAEABAADgAAAAMAAQACAAAAEwQAAAkAAABiAAAAGwEDAAAAbiAEABAA" +
+ "DgAAAAQAAgACAAAAGQQAACoAAABiAAAAGwENAAAAbiAEABAAcBACAAIAYgAAABsBCwAAAG4gBAAQ" +
+ "AHIQBgADAGIAAAAbAQwAAABuIAQAEABwEAEAAgBiAAAAGwEKAAAAbiAEABAADgABAAAAAwAAAAEA" +
+ "AAAEAAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVs" +
+ "bG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07ABVMamF2YS9pby9QcmludFN0" +
+ "cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xh" +
+ "bmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
+ "ZCBjYWxsIC0gVHJhbnNmb3JtZWQALFBvc3QgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
+ "YW5zZm9ybWVkACxQcmUgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAr" +
+ "UHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAFU3RhcnQADlRyYW5z" +
+ "Zm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00LjEzAANvdXQAB3ByaW50bG4AA3J1bgAF" +
+ "c2F5SGkAAQAHDgAHAAcOhwADAAcOhwALAQAHDoc8hzyHPIcAAAADAQCAgATsAgEChAMBAqgDAwHM" +
+ "Aw0AAAAAAAAAAQAAAAAAAAABAAAAFwAAAHAAAAACAAAABwAAAMwAAAADAAAAAwAAAOgAAAAEAAAA" +
+ "AQAAAAwBAAAFAAAABwAAABQBAAAGAAAAAQAAAEwBAAABIAAABAAAAGwBAAABEAAAAgAAADACAAAC" +
+ "IAAAFwAAAD4CAAADIAAABAAAAAgEAAAAIAAAAQAAACYEAAAAEAAAAQAAADwEAAA=");
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[1]);
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ t.sayHi(() -> {
+ System.out.println("transforming calling function");
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ });
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+}
diff --git a/test/915-obsolete-2/src/Transform.java b/test/915-obsolete-2/src/Transform.java
new file mode 100644
index 0000000..e914e29
--- /dev/null
+++ b/test/915-obsolete-2/src/Transform.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class Transform {
+ private void Start() {
+ System.out.println("hello - private");
+ }
+
+ private void Finish() {
+ System.out.println("goodbye - private");
+ }
+
+ public void sayHi(Runnable r) {
+ System.out.println("Pre Start private method call");
+ Start();
+ System.out.println("Post Start private method call");
+ r.run();
+ System.out.println("Pre Finish private method call");
+ Finish();
+ System.out.println("Post Finish private method call");
+ }
+}
diff --git a/test/Android.arm_vixl.mk b/test/916-obsolete-jit/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/916-obsolete-jit/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/916-obsolete-jit/build
@@ -1,5 +1,6 @@
+#!/bin/bash
#
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 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.
@@ -12,9 +13,5 @@
# 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.
-#
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
- 562-checker-no-intermediate \
- 624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/916-obsolete-jit/expected.txt b/test/916-obsolete-jit/expected.txt
new file mode 100644
index 0000000..4caefc6
--- /dev/null
+++ b/test/916-obsolete-jit/expected.txt
@@ -0,0 +1,21 @@
+Pre Start private method call
+hello - private
+Post Start private method call
+Not doing anything here
+Pre Finish private method call
+goodbye - private
+Post Finish private method call
+Pre Start private method call
+hello - private
+Post Start private method call
+transforming calling function
+Pre Finish private method call
+Goodbye - private - Transformed
+Post Finish private method call
+pre Start private method call - Transformed
+Hello - private - Transformed
+post Start private method call - Transformed
+Not doing anything here
+pre Finish private method call - Transformed
+Goodbye - private - Transformed
+post Finish private method call - Transformed
diff --git a/test/916-obsolete-jit/info.txt b/test/916-obsolete-jit/info.txt
new file mode 100644
index 0000000..c8b892c
--- /dev/null
+++ b/test/916-obsolete-jit/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/916-obsolete-jit/run b/test/916-obsolete-jit/run
new file mode 100755
index 0000000..25c2c07
--- /dev/null
+++ b/test/916-obsolete-jit/run
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jit"* ]]; then
+ other_args=""
+else
+ other_args="--jit"
+fi
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+ if [[ "$@" != *"--debuggable"* ]]; then
+ other_args="$other_args -Xcompiler-option --debuggable "
+ fi
+fi
+
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=915-obsolete-2,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ --android-runtime-option -Xfully-deoptable \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java
new file mode 100644
index 0000000..74eb003
--- /dev/null
+++ b/test/916-obsolete-jit/src/Main.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.util.function.Consumer;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+
+ // import java.util.function.Consumer;
+ //
+ // class Transform {
+ // private void Start(Consumer<String> reporter) {
+ // reporter.accept("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish(Consumer<String> reporter) {
+ // reporter.accept("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r, Consumer<String> reporter) {
+ // reporter.accept("pre Start private method call - Transformed");
+ // Start(reporter);
+ // reporter.accept("post Start private method call - Transformed");
+ // r.run();
+ // reporter.accept("pre Finish private method call - Transformed");
+ // Finish(reporter);
+ // reporter.accept("post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAMAoADQAcCAAdCwAeAB8IACAIACEKAAwAIggAIwsAJAAlCAAmCgAMACcIACgHACkH" +
+ "ACoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFU3RhcnQBACAoTGph" +
+ "dmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjspVgEACVNpZ25hdHVyZQEANChMamF2YS91dGlsL2Z1" +
+ "bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47KVYBAAZGaW5pc2gBAAVzYXlIaQEA" +
+ "NChMamF2YS9sYW5nL1J1bm5hYmxlO0xqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAEgo" +
+ "TGphdmEvbGFuZy9SdW5uYWJsZTtMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xh" +
+ "bmcvU3RyaW5nOz47KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAA4ADwEAHUhlbGxv" +
+ "IC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkBwArDAAsAC0BAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRy" +
+ "YW5zZm9ybWVkAQArcHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwA" +
+ "EgATAQAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHAC4MAC8A" +
+ "DwEALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAWABMBAC1w" +
+ "b3N0IEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQBAAlUcmFuc2Zvcm0B" +
+ "ABBqYXZhL2xhbmcvT2JqZWN0AQAbamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAQAGYWNjZXB0" +
+ "AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAADAAN" +
+ "AAAAAAAEAAAADgAPAAEAEAAAAB0AAQABAAAABSq3AAGxAAAAAQARAAAABgABAAAAEwACABIAEwAC" +
+ "ABAAAAAlAAIAAgAAAAkrEgK5AAMCALEAAAABABEAAAAKAAIAAAAVAAgAFgAUAAAAAgAVAAIAFgAT" +
+ "AAIAEAAAACUAAgACAAAACSsSBLkAAwIAsQAAAAEAEQAAAAoAAgAAABkACAAaABQAAAACABUAAQAX" +
+ "ABgAAgAQAAAAZQACAAMAAAAxLBIFuQADAgAqLLcABiwSB7kAAwIAK7kACAEALBIJuQADAgAqLLcA" +
+ "CiwSC7kAAwIAsQAAAAEAEQAAACIACAAAAB0ACAAeAA0AHwAVACAAGwAhACMAIgAoACMAMAAkABQA" +
+ "AAACABkAAQAaAAAAAgAb");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBc8wr9PcHqnOR61m+0kimXTSddVMToJPuYBQAAcAAAAHhWNBIAAAAAAAAAAOAEAAAc" +
+ "AAAAcAAAAAYAAADgAAAABAAAAPgAAAAAAAAAAAAAAAcAAAAoAQAAAQAAAGABAAAYBAAAgAEAAHoC" +
+ "AAB9AgAAgAIAAIgCAACOAgAAlgIAALcCAADWAgAA4wIAAAIDAAAWAwAALAMAAEADAABeAwAAfQMA" +
+ "AIQDAACUAwAAlwMAAJsDAACgAwAAqAMAALwDAADrAwAAGQQAAEcEAAB0BAAAeQQAAIAEAAAHAAAA" +
+ "CAAAAAkAAAAKAAAADQAAABAAAAAQAAAABQAAAAAAAAARAAAABQAAAGQCAAASAAAABQAAAGwCAAAR" +
+ "AAAABQAAAHQCAAAAAAAAAgAAAAAAAwAEAAAAAAADAA4AAAAAAAIAGgAAAAIAAAACAAAAAwAAABkA" +
+ "AAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAPAAAAPAIAAMoEAAAAAAAAAQAAAKgEAAABAAAAuAQA" +
+ "AAEAAQABAAAAhwQAAAQAAABwEAQAAAAOAAMAAgACAAAAjAQAAAcAAAAbAAUAAAByIAYAAgAOAAAA" +
+ "AwACAAIAAACTBAAABwAAABsABgAAAHIgBgACAA4AAAAEAAMAAgAAAJoEAAAiAAAAGwAYAAAAciAG" +
+ "AAMAcCACADEAGwAWAAAAciAGAAMAchAFAAIAGwAXAAAAciAGAAMAcCABADEAGwAVAAAAciAGAAMA" +
+ "DgAAAAAAAAAAAAMAAAAAAAAAAQAAAIABAAACAAAAgAEAAAMAAACIAQAAAQAAAAIAAAACAAAAAwAE" +
+ "AAEAAAAEAAEoAAE8AAY8aW5pdD4ABD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
+ "cmFuc2Zvcm1lZAAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1M" +
+ "ZGFsdmlrL2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9s" +
+ "YW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0Nv" +
+ "bnN1bWVyAB1MamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5q" +
+ "YXZhAAFWAAJWTAADVkxMAAZhY2NlcHQAEmVtaXR0ZXI6IGphY2stNC4xOQAtcG9zdCBGaW5pc2gg" +
+ "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0" +
+ "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0g" +
+ "VHJhbnNmb3JtZWQAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQA" +
+ "A3J1bgAFc2F5SGkABXZhbHVlABMABw4AGQEABw5pABUBAAcOaQAdAgAABw5pPGk8aTxpAAIBARsc" +
+ "BRcAFwwXARcLFwMCAQEbHAYXABcKFwwXARcLFwMAAAMBAICABJADAQKoAwECyAMDAegDDwAAAAAA" +
+ "AAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAGAAAA4AAAAAMAAAAEAAAA+AAAAAUAAAAHAAAAKAEA" +
+ "AAYAAAABAAAAYAEAAAMQAAACAAAAgAEAAAEgAAAEAAAAkAEAAAYgAAABAAAAPAIAAAEQAAADAAAA" +
+ "ZAIAAAIgAAAcAAAAegIAAAMgAAAEAAAAhwQAAAQgAAACAAAAqAQAAAAgAAABAAAAygQAAAAQAAAB" +
+ "AAAA4AQAAA==");
+
+ // A class that we can use to keep track of the output of this test.
+ private static class TestWatcher implements Consumer<String> {
+ private StringBuilder sb;
+ public TestWatcher() {
+ sb = new StringBuilder();
+ }
+
+ @Override
+ public void accept(String s) {
+ sb.append(s);
+ sb.append('\n');
+ }
+
+ public String getOutput() {
+ return sb.toString();
+ }
+
+ public void clear() {
+ sb = new StringBuilder();
+ }
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[1]);
+ doTest(new Transform(), new TestWatcher());
+ }
+
+ // TODO Workaround to (1) inability to ensure that current_method is not put into a register by
+ // the JIT and/or (2) inability to deoptimize frames near runtime functions.
+ // TODO Fix one/both of these issues.
+ public static void doCall(Runnable r) {
+ r.run();
+ }
+
+ private static boolean interpreting = true;
+ private static boolean retry = false;
+
+ public static void doTest(Transform t, TestWatcher w) {
+ // Get the methods that need to be optimized.
+ Method say_hi_method;
+ Method do_call_method;
+ // Figure out if we can even JIT at all.
+ final boolean has_jit = hasJit();
+ try {
+ say_hi_method = Transform.class.getDeclaredMethod(
+ "sayHi", Runnable.class, Consumer.class);
+ do_call_method = Main.class.getDeclaredMethod("doCall", Runnable.class);
+ } catch (Exception e) {
+ System.out.println("Unable to find methods!");
+ e.printStackTrace();
+ return;
+ }
+ // Makes sure the stack is the way we want it for the test and does the redefinition. It will
+ // set the retry boolean to true if we need to go around again due to a bad stack.
+ Runnable do_redefinition = () -> {
+ if (has_jit &&
+ (Main.isInterpretedFunction(say_hi_method, true) ||
+ Main.isInterpretedFunction(do_call_method, false))) {
+ // Try again. We are not running the right jitted methods/cannot redefine them now.
+ retry = true;
+ } else {
+ // Actually do the redefinition. The stack looks good.
+ retry = false;
+ w.accept("transforming calling function");
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ }
+ };
+ // This does nothing.
+ Runnable noop = () -> {};
+ // This just prints something out to show we are running the Runnable.
+ Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+ // This checks to see if we have jitted the methods we are testing.
+ Runnable check_interpreting = () -> {
+ // TODO remove the second check when we remove the doCall function. We need to check that
+ // both of these functions aren't being interpreted because if sayHi is the test doesn't do
+ // anything and if doCall is then there will be a runtime call right above the sayHi
+ // function preventing sayHi from being deoptimized.
+ interpreting = has_jit && (Main.isInterpretedFunction(say_hi_method, true) ||
+ Main.isInterpretedFunction(do_call_method, false));
+ };
+ do {
+ w.clear();
+ // Wait for the methods to be jitted
+ long j = 0;
+ do {
+ for (int i = 0; i < 10000; i++) {
+ t.sayHi(noop, w);
+ j++;
+ // Clear so that we won't OOM if we go around a few times.
+ w.clear();
+ }
+ t.sayHi(check_interpreting, w);
+ if (j >= 1000000) {
+ System.out.println("FAIL: Could not make sayHi be Jitted!");
+ return;
+ }
+ j++;
+ } while(interpreting);
+ // Clear output. Now we try for real.
+ w.clear();
+ // Try and redefine.
+ t.sayHi(say_nothing, w);
+ t.sayHi(do_redefinition, w);
+ t.sayHi(say_nothing, w);
+ } while (retry);
+ // Print output of last run.
+ System.out.print(w.getOutput());
+ }
+
+ private static native boolean hasJit();
+
+ private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+}
diff --git a/test/916-obsolete-jit/src/Transform.java b/test/916-obsolete-jit/src/Transform.java
new file mode 100644
index 0000000..f4dcf09
--- /dev/null
+++ b/test/916-obsolete-jit/src/Transform.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.util.function.Consumer;
+
+class Transform {
+ private void Start(Consumer<String> reporter) {
+ reporter.accept("hello - private");
+ }
+
+ private void Finish(Consumer<String> reporter) {
+ reporter.accept("goodbye - private");
+ }
+
+ public void sayHi(Runnable r, Consumer<String> reporter) {
+ reporter.accept("Pre Start private method call");
+ Start(reporter);
+ reporter.accept("Post Start private method call");
+ // TODO Revisit with b/33616143
+ // TODO Uncomment this once either b/33630159 or b/33616143 are resolved.
+ // r.run();
+ // TODO This doCall function is a very temporary fix until we get either deoptimization near
+ // runtime frames working, forcing current method to be always read from the stack or both
+ // working.
+ Main.doCall(r);
+ reporter.accept("Pre Finish private method call");
+ Finish(reporter);
+ reporter.accept("Post Finish private method call");
+ }
+}
diff --git a/test/Android.arm_vixl.mk b/test/917-fields-transformation/build
old mode 100644
new mode 100755
similarity index 71%
copy from test/Android.arm_vixl.mk
copy to test/917-fields-transformation/build
index c89eb4a..898e2e5
--- a/test/Android.arm_vixl.mk
+++ b/test/917-fields-transformation/build
@@ -1,5 +1,6 @@
+#!/bin/bash
#
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright 2016 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.
@@ -12,9 +13,5 @@
# 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.
-#
-# Known broken tests for the ARM VIXL backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
- 562-checker-no-intermediate \
- 624-checker-stringops \
+./default-build "$@" --experimental agents
diff --git a/test/917-fields-transformation/expected.txt b/test/917-fields-transformation/expected.txt
new file mode 100644
index 0000000..bcdd201
--- /dev/null
+++ b/test/917-fields-transformation/expected.txt
@@ -0,0 +1,12 @@
+Result is Hello
+take1 is Hello
+take2 is Goodbye
+Result is start
+take1 is start
+take2 is end
+Result is Goodbye
+take1 is Hello
+take2 is Goodbye
+Result is end
+take1 is start
+take2 is end
diff --git a/test/917-fields-transformation/info.txt b/test/917-fields-transformation/info.txt
new file mode 100644
index 0000000..4cd1bd9
--- /dev/null
+++ b/test/917-fields-transformation/info.txt
@@ -0,0 +1 @@
+Tests field access after class redefinition support in the jvmti plugin.
diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run
new file mode 100755
index 0000000..a434b63
--- /dev/null
+++ b/test/917-fields-transformation/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+ if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+ else
+ other_args=""
+ fi
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=917-fields-transformation,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ --android-runtime-option -Xfully-deoptable \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
new file mode 100644
index 0000000..5378bb7
--- /dev/null
+++ b/test/917-fields-transformation/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.util.Base64;
+public class Main {
+
+ // base64 encoded class/dex file for
+ // class Transform {
+ // public String take1;
+ // public String take2;
+ //
+ // public Transform(String a, String b) {
+ // take1 = a;
+ // take2 = b;
+ // }
+ //
+ // public String getResult() {
+ // return take2;
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAFwoABQARCQAEABIJAAQAEwcAFAcAFQEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
+ "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
+ "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
+ "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkAFgwABgAHDAAIAAcBAAlU" +
+ "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQADKClWACAABAAFAAAAAgABAAYABwAAAAEACAAH" +
+ "AAAAAgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAU" +
+ "AAQAFQAJABYADgAXAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAAGgAB" +
+ "AA8AAAACABA=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAGUTBb4jIABRlaI9rejdk7RCfyqR2kmNSkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAM" +
+ "AAAAcAAAAAQAAACgAAAAAwAAALAAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAACIAQAAHAEAAFwB" +
+ "AABkAQAAZwEAAHQBAACIAQAAnAEAAKwBAACvAQAAtAEAAMgBAADTAQAA2gEAAAIAAAADAAAABAAA" +
+ "AAYAAAABAAAAAgAAAAAAAAAGAAAAAwAAAAAAAAAHAAAAAwAAAFQBAAAAAAIACgAAAAAAAgALAAAA" +
+ "AAACAAAAAAAAAAAACQAAAAEAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAUAAAAAAAAA8AEAAAAAAAAD" +
+ "AAMAAQAAAOEBAAAIAAAAcBACAAAAWwEAAFsCAQAOAAIAAQAAAAAA6wEAAAMAAABUEAEAEQAAAAIA" +
+ "AAACAAIABjxpbml0PgABTAALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" +
+ "bGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgADVkxMABJlbWl0dGVyOiBqYWNrLTQuMTkA" +
+ "CWdldFJlc3VsdAAFdGFrZTEABXRha2UyABQCAAAHDjwtLQAaAAcOAAACAQEAAQEBAIGABJwCAQG8" +
+ "AgAADQAAAAAAAAABAAAAAAAAAAEAAAAMAAAAcAAAAAIAAAAEAAAAoAAAAAMAAAADAAAAsAAAAAQA" +
+ "AAACAAAA1AAAAAUAAAADAAAA5AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" +
+ "AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA==");
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[1]);
+ doTest(new Transform("Hello", "Goodbye"),
+ new Transform("start", "end"));
+ }
+
+ private static void printTransform(Transform t) {
+ System.out.println("Result is " + t.getResult());
+ System.out.println("take1 is " + t.take1);
+ System.out.println("take2 is " + t.take2);
+ }
+ public static void doTest(Transform t1, Transform t2) {
+ printTransform(t1);
+ printTransform(t2);
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ printTransform(t1);
+ printTransform(t2);
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_file,
+ byte[] dex_file);
+}
diff --git a/runtime/base/stringprintf_test.cc b/test/917-fields-transformation/src/Transform.java
similarity index 62%
rename from runtime/base/stringprintf_test.cc
rename to test/917-fields-transformation/src/Transform.java
index 0bfde33..6fe6223 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/917-fields-transformation/src/Transform.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-#include "stringprintf.h"
+class Transform {
+ public String take1;
+ public String take2;
-#include "gtest/gtest.h"
+ public Transform(String take1, String take2) {
+ this.take1 = take1;
+ this.take2 = take2;
+ }
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
- EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
+ public String getResult() {
+ return take1;
+ }
}
-
-} // namespace art
diff --git a/test/Android.bp b/test/Android.bp
index 2625f56..5a2c902 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -243,6 +243,9 @@
name: "libtiagent-defaults",
defaults: ["libartagent-defaults"],
srcs: [
+ // This is to get the IsInterpreted native method.
+ "common/stack_inspect.cc",
+ "common/runtime_state.cc",
"ti-agent/common_load.cc",
"ti-agent/common_helper.cc",
"901-hello-ti-agent/basics.cc",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index b515130..ec1f6ba 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -226,6 +226,12 @@
$(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_')
endef # name-to-var
+# Disable 153-reference-stress temporarily until a fix arrives. b/33389022.
+# Disable 080-oom-fragmentation due to flakes. b/33795328
+ART_TEST_RUN_TEST_SKIP += \
+ 153-reference-stress \
+ 080-oom-fragmentation
+
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
$(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES))
@@ -280,6 +286,10 @@
911-get-stack-trace \
912-classes \
913-heaps \
+ 914-hello-obsolescence \
+ 915-obsolete-2 \
+ 916-obsolete-jit \
+ 917-fields-transformation \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -492,6 +502,24 @@
$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
endif
+TEST_ART_BROKEN_TRACING_RUN_TESTS :=
+
+# These tests expect JIT compilation, which is suppressed when tracing.
+TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := \
+ 604-hot-static-interface \
+ 612-jit-dex-cache \
+ 613-inlining-dex-cache \
+ 616-cha \
+ 626-set-resolved-string \
+
+ifneq (,$(filter trace stream,$(TRACE_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ jit,$(RELOCATE_TYPES),trace stream,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+ $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS :=
+
# Known broken tests for the interpreter.
# CFI unwinding expects managed frames.
# 629 requires compilation.
@@ -514,15 +542,22 @@
# Test 906 iterates the heap filtering with different options. No instances should be created
# between those runs to be able to have precise checks.
# Test 902 hits races with the JIT compiler. b/32821077
-# Test 626-const-class-linking can deadlock with JIT. b/33567581
# Test 629 requires compilation.
+# Test 914, 915, 917, & 918 are very sensitive to the exact state of the stack,
+# including the jit-inserted runtime frames. This causes them to be somewhat
+# flaky as JIT tests. This should be fixed once b/33630159 or b/33616143 are
+# resolved but until then just disable them. Test 916 already checks this
+# feature for JIT use cases in a way that is resilient to the jit frames.
TEST_ART_BROKEN_JIT_RUN_TESTS := \
137-cfi \
- 626-const-class-linking \
629-vdex-speed \
902-hello-transformation \
904-object-allocation \
906-iterate-heap \
+ 914-hello-obsolescence \
+ 915-obsolete-2 \
+ 917-fields-transformation \
+ 918-obsolete-fields \
ifneq (,$(filter jit,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -546,26 +581,6 @@
$(TEST_ART_BROKEN_OPTIMIZING_GRAPH_COLOR),$(ALL_ADDRESS_SIZES))
endif
-# Known broken tests for the ARM VIXL backend.
-# Android.arm_vixl.mk defines TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS.
-include $(LOCAL_PATH)/Android.arm_vixl.mk
-
-ifdef ART_USE_VIXL_ARM_BACKEND
- ifeq (arm,$(filter arm,$(TARGET_ARCH) $(TARGET_2ND_ARCH)))
- ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES)))
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
- $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
- $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
- $(TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS),32)
- endif
- endif
- # TODO(VIXL): These two tests currently fail, but adding them to `ART_TEST_KNOWN_BROKEN` breaks
- # `export ART_USE_VIXL_ARM_BACKEND=true && mma -j6 test-art-target-gtest dist`
- #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-dex2oat_test32
- #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-image_test32
-endif
-
-
# Known broken tests for the mips32 optimizing compiler backend.
TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \
@@ -674,15 +689,6 @@
endif
endif
-# Tests disabled for GSS.
-TEST_ART_BROKEN_GSS_RUN_TESTS := 080-oom-fragmentation
-ifeq ($(ART_DEFAULT_GC_TYPE),GSS)
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \
- $(PREBUILD_TYPES),$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \
- $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
- $(TEST_ART_BROKEN_GSS_RUN_TESTS),$(ALL_ADDRESS_SIZES))
-endif
-
TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
diff --git a/runtime/base/stringprintf_test.cc b/test/ErroneousA/ErroneousA.java
similarity index 61%
copy from runtime/base/stringprintf_test.cc
copy to test/ErroneousA/ErroneousA.java
index 0bfde33..49da544 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/ErroneousA/ErroneousA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,16 +14,4 @@
* limitations under the License.
*/
-#include "stringprintf.h"
-
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
- EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
-}
-
-} // namespace art
+final class FinalSuper {}
diff --git a/runtime/base/stringprintf_test.cc b/test/ErroneousB/ErroneousB.java
similarity index 61%
copy from runtime/base/stringprintf_test.cc
copy to test/ErroneousB/ErroneousB.java
index 0bfde33..6c2902a 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/ErroneousB/ErroneousB.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-#include "stringprintf.h"
+// Only final in first dex.
+class FinalSuper {}
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
- EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
-}
-
-} // namespace art
+class Erroneous extends FinalSuper {}
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 285f3aa..7451cf9 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -19,6 +19,7 @@
#include "base/enums.h"
#include "base/logging.h"
#include "dex_file-inl.h"
+#include "instrumentation.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "mirror/class-inl.h"
@@ -30,6 +31,16 @@
namespace art {
+// public static native boolean hasJit();
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass) {
+ Runtime* runtime = Runtime::Current();
+ return runtime != nullptr
+ && runtime->GetJit() != nullptr
+ && runtime->GetInstrumentation()->GetCurrentInstrumentationLevel() !=
+ instrumentation::Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter;
+}
+
// public static native boolean hasOatFile();
extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasOatFile(JNIEnv* env, jclass cls) {
@@ -188,4 +199,28 @@
return method->HasSingleImplementation();
}
+extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env,
+ jclass,
+ jclass cls,
+ jstring method_name) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit == nullptr) {
+ // The hotness counter is valid only under JIT.
+ // If we don't JIT return 0 to match test expectations.
+ return 0;
+ }
+
+ ArtMethod* method = nullptr;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
+ method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
+ chars.c_str(), kRuntimePointerSize);
+ }
+
+ return method->GetCounter();
+}
+
} // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index 4df2d47..df7fa20 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -18,6 +18,7 @@
#include "base/logging.h"
#include "dex_file-inl.h"
+#include "jni_internal.h"
#include "mirror/class-inl.h"
#include "nth_caller_visitor.h"
#include "oat_file.h"
@@ -52,6 +53,89 @@
return IsInterpreted(env, klass, 1);
}
+// public static native boolean isInterpreted(int depth);
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpretedAt(JNIEnv* env,
+ jclass klass,
+ jint depth) {
+ return IsInterpreted(env, klass, depth);
+}
+
+
+// public static native boolean isInterpretedFunction(String smali);
+
+// TODO Remove 'allow_runtime_frames' option once we have deoptimization through runtime frames.
+struct MethodIsInterpretedVisitor : public StackVisitor {
+ public:
+ MethodIsInterpretedVisitor(Thread* thread, ArtMethod* goal, bool require_deoptable)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ goal_(goal),
+ method_is_interpreted_(true),
+ method_found_(false),
+ prev_was_runtime_(true),
+ require_deoptable_(require_deoptable) {}
+
+ virtual bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (goal_ == GetMethod()) {
+ method_is_interpreted_ = (require_deoptable_ && prev_was_runtime_) || IsShadowFrame();
+ method_found_ = true;
+ return false;
+ }
+ prev_was_runtime_ = GetMethod()->IsRuntimeMethod();
+ return true;
+ }
+
+ bool IsInterpreted() {
+ return method_is_interpreted_;
+ }
+
+ bool IsFound() {
+ return method_found_;
+ }
+
+ private:
+ const ArtMethod* goal_;
+ bool method_is_interpreted_;
+ bool method_found_;
+ bool prev_was_runtime_;
+ bool require_deoptable_;
+};
+
+// TODO Remove 'require_deoptimizable' option once we have deoptimization through runtime frames.
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpretedFunction(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method, jboolean require_deoptimizable) {
+ // Return false if this seems to not be an ART runtime.
+ if (Runtime::Current() == nullptr) {
+ return JNI_FALSE;
+ }
+ if (method == nullptr) {
+ env->ThrowNew(env->FindClass("java/lang/NullPointerException"), "method is null!");
+ return JNI_FALSE;
+ }
+ jmethodID id = env->FromReflectedMethod(method);
+ if (id == nullptr) {
+ env->ThrowNew(env->FindClass("java/lang/Error"), "Unable to interpret method argument!");
+ return JNI_FALSE;
+ }
+ bool result;
+ bool found;
+ {
+ ScopedObjectAccess soa(env);
+ ArtMethod* goal = jni::DecodeArtMethod(id);
+ MethodIsInterpretedVisitor v(soa.Self(), goal, require_deoptimizable);
+ v.WalkStack();
+ bool enters_interpreter = Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(
+ goal->GetEntryPointFromQuickCompiledCode());
+ result = (v.IsInterpreted() || enters_interpreter);
+ found = v.IsFound();
+ }
+ if (!found) {
+ env->ThrowNew(env->FindClass("java/lang/Error"), "Unable to find given method in stack!");
+ return JNI_FALSE;
+ }
+ return result;
+}
+
// public static native void assertIsInterpreted();
extern "C" JNIEXPORT void JNICALL Java_Main_assertIsInterpreted(JNIEnv* env, jclass klass) {
diff --git a/test/etc/default-build b/test/etc/default-build
index 51ae175..e9e3886 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -69,6 +69,7 @@
# Setup experimental flag mappings in a bash associative array.
declare -A JACK_EXPERIMENTAL_ARGS
+JACK_EXPERIMENTAL_ARGS["agents"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1"
@@ -76,12 +77,14 @@
declare -A SMALI_EXPERIMENTAL_ARGS
SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"
SMALI_EXPERIMENTAL_ARGS["method-handles"]="--api-level 26"
+SMALI_EXPERIMENTAL_ARGS["agents"]="--api-level 26"
declare -A JAVAC_EXPERIMENTAL_ARGS
JAVAC_EXPERIMENTAL_ARGS["default-methods"]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS["lambdas"]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS["method-handles"]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.7 -target 1.7"
+JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8"
while true; do
if [ "x$1" = "x--dx-option" ]; then
@@ -126,6 +129,16 @@
# Be sure to get any default arguments if not doing any experiments.
EXPERIMENTAL="${EXPERIMENTAL} ${DEFAULT_EXPERIMENT}"
+if [ "${JACK_SERVER}" = "false" ]; then
+ # Run in single-threaded mode for the continuous buildbot.
+ JACK_ARGS="${JACK_ARGS} -D sched.runner=single-threaded"
+else
+ # Run with 4 threads to reduce memory footprint and thread contention.
+ JACK_ARGS="${JACK_ARGS} -D sched.runner=multi-threaded"
+ JACK_ARGS="${JACK_ARGS} -D sched.runner.thread.kind=fixed"
+ JACK_ARGS="${JACK_ARGS} -D sched.runner.thread.fixed.count=4"
+fi
+
# Add args from the experimental mappings.
for experiment in ${EXPERIMENTAL}; do
JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}"
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index f0abb44..566f7ba 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -59,6 +59,7 @@
EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
DRY_RUN="n" # if y prepare to run the test but don't run it.
TEST_VDEX="n"
+APP_IMAGE="y"
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -132,6 +133,9 @@
elif [ "x$1" = "x--prebuild" ]; then
PREBUILD="y"
shift
+ elif [ "x$1" = "x--no-app-image" ]; then
+ APP_IMAGE="n"
+ shift
elif [ "x$1" = "x--strip-dex" ]; then
STRIP_DEX="y"
shift
@@ -369,8 +373,8 @@
if [ "$JIT" = "y" ]; then
INT_OPTS="-Xusejit:true"
if [ "$VERIFY" = "y" ] ; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-at-runtime"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-at-runtime"
+ INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=interpret-only"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
else
INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-none"
COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
@@ -453,11 +457,14 @@
mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
strip_cmdline="true"
-# Pick a base that will force the app image to get relocated.
-app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art"
if [ "$PREBUILD" = "y" ]; then
mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA"
+ if [ "$APP_IMAGE" = "y" ]; then
+ # Pick a base that will force the app image to get relocated.
+ app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art"
+ fi
+
dex2oat_cmdline="$INVOKE_WITH $ANDROID_ROOT/bin/dex2oatd \
$COMPILE_FLAGS \
--boot-image=${BOOT_IMAGE} \
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 3e2b168..ebf1e46 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -18,8 +18,11 @@
#include <stdio.h>
+#include "art_method.h"
#include "jni.h"
#include "openjdkjvmti/jvmti.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
#include "ti-agent/common_load.h"
#include "utils.h"
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 2795cbc..79c17d7 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -66,6 +66,10 @@
{ "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr },
{ "912-classes", Test912Classes::OnLoad, nullptr },
{ "913-heaps", Test913Heaps::OnLoad, nullptr },
+ { "914-hello-obsolescence", common_redefine::OnLoad, nullptr },
+ { "915-obsolete-2", common_redefine::OnLoad, nullptr },
+ { "916-obsolete-jit", common_redefine::OnLoad, nullptr },
+ { "917-fields-transformation", common_redefine::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_dexcache.def
index 4b9d481..abb5e1e 100644
--- a/tools/cpp-define-generator/offset_dexcache.def
+++ b/tools/cpp-define-generator/offset_dexcache.def
@@ -38,7 +38,6 @@
DEFINE_ART_METHOD_OFFSET_SIZED(JNI, EntryPointFromJni)
DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE, EntryPointFromQuickCompiledCode)
DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS, DeclaringClass)
-DEFINE_DECLARING_CLASS_OFFSET(DEX_CACHE_STRINGS, DexCacheStrings)
#undef DEFINE_ART_METHOD_OFFSET
#undef DEFINE_ART_METHOD_OFFSET_32
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index fd8415d..b5f856f 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -43,10 +43,11 @@
BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT)
-def GetExecutionModeRunner(device, mode):
+def GetExecutionModeRunner(use_dx, device, mode):
"""Returns a runner for the given execution mode.
Args:
+ use_dx: boolean, if True use dx rather than jack
device: string, target device serial number (or None)
mode: string, execution mode
Returns:
@@ -57,13 +58,13 @@
if mode == 'ri':
return TestRunnerRIOnHost()
if mode == 'hint':
- return TestRunnerArtIntOnHost()
+ return TestRunnerArtIntOnHost(use_dx)
if mode == 'hopt':
- return TestRunnerArtOptOnHost()
+ return TestRunnerArtOptOnHost(use_dx)
if mode == 'tint':
- return TestRunnerArtIntOnTarget(device)
+ return TestRunnerArtIntOnTarget(use_dx, device)
if mode == 'topt':
- return TestRunnerArtOptOnTarget(device)
+ return TestRunnerArtOptOnTarget(use_dx, device)
raise FatalError('Unknown execution mode')
@@ -113,6 +114,33 @@
"""
+class TestRunnerWithHostCompilation(TestRunner):
+ """Abstract test runner that supports compilation on host."""
+
+ def __init__(self, use_dx):
+ """Constructor for the runner with host compilation.
+
+ Args:
+ use_dx: boolean, if True use dx rather than jack
+ """
+ self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
+ 'Test.java']
+ self._use_dx = use_dx
+
+ def CompileOnHost(self):
+ if self._use_dx:
+ if RunCommand(['javac', 'Test.java'],
+ out=None, err=None, timeout=30) == RetCode.SUCCESS:
+ retc = RunCommand(['dx', '--dex', '--output=classes.dex'] + glob('*.class'),
+ out=None, err='dxerr.txt', timeout=30)
+ else:
+ retc = RetCode.NOTCOMPILED
+ else:
+ retc = RunCommand(['jack'] + self._jack_args,
+ out=None, err='jackerr.txt', timeout=30)
+ return retc
+
+
class TestRunnerRIOnHost(TestRunner):
"""Concrete test runner of the reference implementation on host."""
@@ -136,25 +164,24 @@
return None
-class TestRunnerArtOnHost(TestRunner):
+class TestRunnerArtOnHost(TestRunnerWithHostCompilation):
"""Abstract test runner of Art on host."""
- def __init__(self, extra_args=None):
+ def __init__(self, use_dx, extra_args=None):
"""Constructor for the Art on host tester.
Args:
+ use_dx: boolean, if True use dx rather than jack
extra_args: list of strings, extra arguments for dalvikvm
"""
+ super().__init__(use_dx)
self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex']
if extra_args is not None:
self._art_cmd += extra_args
self._art_cmd.append('Test')
- self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
- 'Test.java']
def CompileAndRunTest(self):
- if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt',
- timeout=30) == RetCode.SUCCESS:
+ if self.CompileOnHost() == RetCode.SUCCESS:
retc = RunCommand(self._art_cmd, self.output_file, 'arterr.txt')
else:
retc = RetCode.NOTCOMPILED
@@ -164,9 +191,13 @@
class TestRunnerArtIntOnHost(TestRunnerArtOnHost):
"""Concrete test runner of interpreter mode Art on host."""
- def __init__(self):
- """Constructor."""
- super().__init__(['-Xint'])
+ def __init__(self, use_dx):
+ """Constructor for the Art on host tester (interpreter).
+
+ Args:
+ use_dx: boolean, if True use dx rather than jack
+ """
+ super().__init__(use_dx, ['-Xint'])
@property
def description(self):
@@ -183,9 +214,13 @@
class TestRunnerArtOptOnHost(TestRunnerArtOnHost):
"""Concrete test runner of optimizing compiler mode Art on host."""
- def __init__(self):
- """Constructor."""
- super().__init__(None)
+ def __init__(self, use_dx):
+ """Constructor for the Art on host tester (optimizing).
+
+ Args:
+ use_dx: boolean, if True use dx rather than jack
+ """
+ super().__init__(use_dx, None)
@property
def description(self):
@@ -201,28 +236,27 @@
return ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
-class TestRunnerArtOnTarget(TestRunner):
+class TestRunnerArtOnTarget(TestRunnerWithHostCompilation):
"""Abstract test runner of Art on target."""
- def __init__(self, device, extra_args=None):
+ def __init__(self, use_dx, device, extra_args=None):
"""Constructor for the Art on target tester.
Args:
+ use_dx: boolean, if True use dx rather than jack
device: string, target device serial number (or None)
extra_args: list of strings, extra arguments for dalvikvm
"""
+ super().__init__(use_dx)
self._test_env = DeviceTestEnv('jfuzz_', specific_device=device)
self._dalvik_cmd = ['dalvikvm']
if extra_args is not None:
self._dalvik_cmd += extra_args
self._device = device
- self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
- 'Test.java']
self._device_classpath = None
def CompileAndRunTest(self):
- if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt',
- timeout=30) == RetCode.SUCCESS:
+ if self.CompileOnHost() == RetCode.SUCCESS:
self._device_classpath = self._test_env.PushClasspath('classes.dex')
cmd = self._dalvik_cmd + ['-cp', self._device_classpath, 'Test']
(output, retc) = self._test_env.RunCommand(
@@ -247,13 +281,14 @@
class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget):
"""Concrete test runner of interpreter mode Art on target."""
- def __init__(self, device):
- """Constructor.
+ def __init__(self, use_dx, device):
+ """Constructor for the Art on target tester (interpreter).
Args:
+ use_dx: boolean, if True use dx rather than jack
device: string, target device serial number (or None)
"""
- super().__init__(device, ['-Xint'])
+ super().__init__(use_dx, device, ['-Xint'])
@property
def description(self):
@@ -270,13 +305,14 @@
class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget):
"""Concrete test runner of optimizing compiler mode Art on target."""
- def __init__(self, device):
- """Constructor.
+ def __init__(self, use_dx, device):
+ """Constructor for the Art on target tester (optimizing).
Args:
+ use_dx: boolean, if True use dx rather than jack
device: string, target device serial number (or None)
"""
- super().__init__(device, None)
+ super().__init__(use_dx, device, None)
@property
def description(self):
@@ -306,7 +342,7 @@
"""Tester that runs JFuzz many times and report divergences."""
def __init__(self, num_tests, device, mode1, mode2, jfuzz_args,
- report_script, true_divergence_only):
+ report_script, true_divergence_only, use_dx):
"""Constructor for the tester.
Args:
@@ -317,14 +353,16 @@
jfuzz_args: list of strings, additional arguments for jfuzz
report_script: string, path to script called for each divergence
true_divergence_only: boolean, if True don't bisect timeout divergences
+ use_dx: boolean, if True use dx rather than jack
"""
self._num_tests = num_tests
self._device = device
- self._runner1 = GetExecutionModeRunner(device, mode1)
- self._runner2 = GetExecutionModeRunner(device, mode2)
+ self._runner1 = GetExecutionModeRunner(use_dx, device, mode1)
+ self._runner2 = GetExecutionModeRunner(use_dx, device, mode2)
self._jfuzz_args = jfuzz_args
self._report_script = report_script
self._true_divergence_only = true_divergence_only
+ self._use_dx = use_dx
self._save_dir = None
self._results_dir = None
self._jfuzz_dir = None
@@ -367,6 +405,7 @@
print('Directory :', self._results_dir)
print('Exec-mode1:', self._runner1.description)
print('Exec-mode2:', self._runner2.description)
+ print('Compiler :', 'dx' if self._use_dx else 'jack')
print()
self.ShowStats()
for self._test in range(1, self._num_tests + 1):
@@ -414,14 +453,16 @@
retc2: int, normalized return code of second runner
"""
if retc1 == retc2:
- # Non-divergent in return code.
+ # No divergence in return code.
if retc1 == RetCode.SUCCESS:
# Both compilations and runs were successful, inspect generated output.
runner1_out = self._runner1.output_file
runner2_out = self._runner2.output_file
if not filecmp.cmp(runner1_out, runner2_out, shallow=False):
+ # Divergence in output.
self.ReportDivergence(retc1, retc2, is_output_divergence=True)
else:
+ # No divergence in output.
self._num_success += 1
elif retc1 == RetCode.TIMEOUT:
self._num_timed_out += 1
@@ -429,8 +470,12 @@
self._num_not_compiled += 1
else:
self._num_not_run += 1
+ elif self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2):
+ # When only true divergences are requested, any divergence in return
+ # code where one is a time out is treated as a regular time out.
+ self._num_timed_out += 1
else:
- # Divergent in return code.
+ # Divergence in return code.
self.ReportDivergence(retc1, retc2, is_output_divergence=False)
def GetCurrentDivergenceDir(self):
@@ -450,13 +495,12 @@
os.mkdir(ddir)
for f in glob('*.txt') + ['Test.java']:
shutil.copy(f, ddir)
- if not (self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2)):
- # Maybe run bisection bug search.
- if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
- self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
- # Call reporting script.
- if self._report_script:
- self.RunReportScript(retc1, retc2, is_output_divergence)
+ # Maybe run bisection bug search.
+ if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
+ self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
+ # Call reporting script.
+ if self._report_script:
+ self.RunReportScript(retc1, retc2, is_output_divergence)
def RunReportScript(self, retc1, retc2, is_output_divergence):
"""Runs report script."""
@@ -546,18 +590,21 @@
parser.add_argument('--mode2', default='hopt',
help='execution mode 2 (default: hopt)')
parser.add_argument('--report_script', help='script called for each'
- 'divergence')
+ ' divergence')
parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args',
action='append', help='argument for jfuzz')
parser.add_argument('--true_divergence', default=False, action='store_true',
help='don\'t bisect timeout divergences')
+ parser.add_argument('--use_dx', default=False, action='store_true',
+ help='use old-style dx (rather than jack)')
args = parser.parse_args()
if args.mode1 == args.mode2:
raise FatalError('Identical execution modes given')
# Run the JFuzz tester.
- with JFuzzTester(args.num_tests, args.device, args.mode1, args.mode2,
+ with JFuzzTester(args.num_tests,
+ args.device, args.mode1, args.mode2,
args.jfuzz_args, args.report_script,
- args.true_divergence) as fuzzer:
+ args.true_divergence, args.use_dx) as fuzzer:
fuzzer.Run()
if __name__ == '__main__':
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 53fe8fe..dcef8c0 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -169,7 +169,8 @@
description: "Lack of IPv6 on some buildbot slaves",
result: EXEC_FAILED,
names: ["libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6",
- "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6"],
+ "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6",
+ "libcore.io.OsTest#test_recvfrom_EmptyPacket"],
bug: 25178637
},
{