summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/Android.bp1
-rw-r--r--compiler/dex/dex_to_dex_compiler.h2
-rw-r--r--compiler/dex/inline_method_analyser.cc8
-rw-r--r--compiler/driver/compiler_driver.cc122
-rw-r--r--compiler/driver/compiler_driver.h9
-rw-r--r--compiler/driver/compiler_options.cc1
-rw-r--r--compiler/driver/compiler_options.h8
-rw-r--r--compiler/driver/compiler_options_map-inl.h6
-rw-r--r--compiler/driver/compiler_options_map.def1
-rw-r--r--compiler/optimizing/code_generator.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc94
-rw-r--r--compiler/optimizing/code_generator_arm64.h2
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc147
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.h3
-rw-r--r--compiler/optimizing/code_generator_mips.cc104
-rw-r--r--compiler/optimizing/code_generator_mips.h1
-rw-r--r--compiler/optimizing/code_generator_mips64.cc104
-rw-r--r--compiler/optimizing/code_generator_mips64.h1
-rw-r--r--compiler/optimizing/code_generator_x86.cc74
-rw-r--r--compiler/optimizing/code_generator_x86.h1
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc83
-rw-r--r--compiler/optimizing/code_generator_x86_64.h1
-rw-r--r--compiler/optimizing/graph_checker.cc92
-rw-r--r--compiler/optimizing/graph_checker.h6
-rw-r--r--compiler/optimizing/graph_visualizer.cc45
-rw-r--r--compiler/optimizing/inliner.cc9
-rw-r--r--compiler/optimizing/inliner.h2
-rw-r--r--compiler/optimizing/instruction_builder.cc107
-rw-r--r--compiler/optimizing/instruction_builder.h7
-rw-r--r--compiler/optimizing/instruction_simplifier.cc43
-rw-r--r--compiler/optimizing/intrinsics.cc2
-rw-r--r--compiler/optimizing/load_store_elimination.cc378
-rw-r--r--compiler/optimizing/nodes.cc2
-rw-r--r--compiler/optimizing/nodes.h213
-rw-r--r--compiler/optimizing/optimizing_compiler.cc14
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h1
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.cc14
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.h2
-rw-r--r--compiler/optimizing/reference_type_propagation.cc35
-rw-r--r--compiler/optimizing/sharpening.cc69
-rw-r--r--compiler/optimizing/sharpening.h27
-rw-r--r--compiler/utils/x86/assembler_x86.cc24
-rw-r--r--compiler/utils/x86/assembler_x86.h6
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc4
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc23
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h6
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc5
47 files changed, 523 insertions, 1388 deletions
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 2e60e7d658..453965947d 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -184,6 +184,7 @@ art_cc_defaults {
},
generated_sources: ["art_compiler_operator_srcs"],
shared_libs: [
+ "libdexfile",
"libbase",
"libcutils", // for atrace.
"liblzma",
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index abd048167c..2105a9ded4 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -23,8 +23,8 @@
#include "base/bit_vector.h"
#include "dex/dex_file.h"
+#include "dex/invoke_type.h"
#include "handle.h"
-#include "invoke_type.h"
#include "method_reference.h"
#include "quicken_info.h"
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index ce67b85b99..dc044c1210 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -142,7 +142,7 @@ ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_dir
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
if (kIsDebugBuild) {
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
DCHECK_EQ(invoke_direct->VRegC_35c(),
accessor.RegistersSize() - accessor.InsSize());
}
@@ -324,9 +324,9 @@ bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item,
return false;
}
if (target_method->GetDeclaringClass()->IsObjectClass()) {
- DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID);
+ DCHECK_EQ(target_method->DexInstructionData().begin()->Opcode(), Instruction::RETURN_VOID);
} else {
- CodeItemDataAccessor target_code_item(target_method);
+ CodeItemDataAccessor target_code_item(target_method->DexInstructionData());
if (!target_code_item.HasCodeItem()) {
return false; // Native constructor?
}
@@ -430,7 +430,7 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
- CodeItemDataAccessor code_item(method);
+ CodeItemDataAccessor code_item(method->DexInstructionData());
if (!code_item.HasCodeItem()) {
// Native or abstract.
return false;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 70cbb01569..c617f54f55 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -384,6 +384,12 @@ static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel
Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file, const DexFile::ClassDef& class_def)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ // When the dex file is uncompressed in the APK, we do not generate a copy in the .vdex
+ // file. As a result, dex2oat will map the dex file read-only, and we only need to check
+ // that to know if we can do quickening.
+ if (dex_file.GetContainer() != nullptr && dex_file.GetContainer()->IsReadOnly()) {
+ return optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile;
+ }
auto* const runtime = Runtime::Current();
DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled());
const char* descriptor = dex_file.GetClassDescriptor(class_def);
@@ -674,8 +680,7 @@ void CompilerDriver::Resolve(jobject class_loader,
// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a
// stable order.
-static void ResolveConstStrings(ClassLinker* class_linker,
- Handle<mirror::DexCache> dex_cache,
+static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache,
const DexFile& dex_file,
const DexFile::CodeItem* code_item)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -684,6 +689,7 @@ static void ResolveConstStrings(ClassLinker* class_linker,
return;
}
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {
switch (inst->Opcode()) {
case Instruction::CONST_STRING:
@@ -731,105 +737,22 @@ static void ResolveConstStrings(CompilerDriver* driver,
dex_file->StringByTypeIdx(class_def.class_idx_));
if (!compilation_enabled) {
// Compilation is skipped, do not resolve const-string in code of this class.
- // FIXME: Make sure that inlining honors this. b/26687569
+ // TODO: Make sure that inlining honors this.
continue;
}
// Direct and virtual methods.
+ int64_t previous_method_idx = -1;
while (it.HasNextMethod()) {
- ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem());
- it.Next();
- }
- DCHECK(!it.HasNext());
- }
- }
-}
-
-// Initialize type check bit strings for check-cast and instance-of in the code. Done to have
-// deterministic allocation behavior. Right now this is single-threaded for simplicity.
-// TODO: Collect the relevant type indices in parallel, then process them sequentially in a
-// stable order.
-
-static void InitializeTypeCheckBitstrings(CompilerDriver* driver,
- ClassLinker* class_linker,
- Handle<mirror::DexCache> dex_cache,
- const DexFile& dex_file,
- const DexFile::CodeItem* code_item)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (code_item == nullptr) {
- // Abstract or native method.
- return;
- }
-
- for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {
- switch (inst->Opcode()) {
- case Instruction::CHECK_CAST:
- case Instruction::INSTANCE_OF: {
- dex::TypeIndex type_index(
- (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c());
- const char* descriptor = dex_file.StringByTypeIdx(type_index);
- // We currently do not use the bitstring type check for array or final (including
- // primitive) classes. We may reconsider this in future if it's deemed to be beneficial.
- // And we cannot use it for classes outside the boot image as we do not know the runtime
- // value of their bitstring when compiling (it may not even get assigned at runtime).
- if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) {
- ObjPtr<mirror::Class> klass =
- class_linker->LookupResolvedType(type_index,
- dex_cache.Get(),
- /* class_loader */ nullptr);
- CHECK(klass != nullptr) << descriptor << " should have been previously resolved.";
- // Now assign the bitstring if the class is not final. Keep this in sync with sharpening.
- if (!klass->IsFinal()) {
- MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
- SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass);
- }
+ uint32_t method_idx = it.GetMemberIndex();
+ if (method_idx == previous_method_idx) {
+ // smali can create dex files with two encoded_methods sharing the same method_idx
+ // http://code.google.com/p/smali/issues/detail?id=119
+ it.Next();
+ continue;
}
- break;
- }
-
- default:
- break;
- }
- }
-}
-
-static void InitializeTypeCheckBitstrings(CompilerDriver* driver,
- const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
-
- for (const DexFile* dex_file : dex_files) {
- dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file));
- TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings);
-
- size_t class_def_count = dex_file->NumClassDefs();
- for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
- 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) {
- // empty class, probably a marker interface
- continue;
- }
-
- ClassDataItemIterator it(*dex_file, class_data);
- it.SkipAllFields();
-
- bool compilation_enabled = driver->IsClassToCompile(
- dex_file->StringByTypeIdx(class_def.class_idx_));
- if (!compilation_enabled) {
- // Compilation is skipped, do not look for type checks in code of this class.
- // FIXME: Make sure that inlining honors this. b/26687569
- continue;
- }
-
- // Direct and virtual methods.
- while (it.HasNextMethod()) {
- InitializeTypeCheckBitstrings(
- driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem());
+ previous_method_idx = method_idx;
+ ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem());
it.Next();
}
DCHECK(!it.HasNext());
@@ -931,13 +854,6 @@ void CompilerDriver::PreCompile(jobject class_loader,
UpdateImageClasses(timings);
VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
-
- if (GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) {
- // Initialize type check bit string used by check-cast and instanceof.
- // Do this now to have a deterministic image.
- // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final.
- InitializeTypeCheckBitstrings(this, dex_files, timings);
- }
}
bool CompilerDriver::IsImageClass(const char* descriptor) const {
@@ -1016,7 +932,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
if (method->GetCodeItem() == nullptr) {
return; // native or abstract method
}
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
if (accessor.TriesSize() == 0) {
return; // nothing to process
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 4b5916d572..b51e0debbb 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -77,9 +77,6 @@ class VdexFile;
class VerificationResults;
class VerifiedMethod;
-// Compile-time flag to enable/disable bitstring type checks.
-static constexpr bool kUseBitstringTypeCheck = true;
-
enum EntryPointCallingConvention {
// ABI of invocations to a method's interpreter entry point.
kInterpreterAbi,
@@ -110,13 +107,13 @@ class CompilerDriver {
~CompilerDriver();
- // Set dex files that will be stored in the oat file after being compiled.
+ // Set dex files associated with the oat file being compiled.
void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files);
// Set dex files classpath.
void SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files);
- // Get dex file that will be stored in the oat file after being compiled.
+ // Get dex files associated with the the oat file being compiled.
ArrayRef<const DexFile* const> GetDexFilesForOatFile() const {
return ArrayRef<const DexFile* const>(dex_files_for_oat_file_);
}
@@ -533,7 +530,7 @@ class CompilerDriver {
bool support_boot_image_fixup_;
- // List of dex files that will be stored in the oat file.
+ // List of dex files associates with the oat file.
std::vector<const DexFile*> dex_files_for_oat_file_;
CompiledMethodStorage compiled_method_storage_;
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 1780b1d7ed..2d82d79c4a 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -60,6 +60,7 @@ CompilerOptions::CompilerOptions()
dump_cfg_append_(false),
force_determinism_(false),
deduplicate_code_(true),
+ count_hotness_in_compiled_code_(false),
register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault),
passes_to_run_(nullptr) {
}
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 3f660293d2..18b0913430 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -274,6 +274,10 @@ class CompilerOptions FINAL {
return dump_stats_;
}
+ bool CountHotnessInCompiledCode() const {
+ return count_hotness_in_compiled_code_;
+ }
+
private:
bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
@@ -336,6 +340,10 @@ class CompilerOptions FINAL {
// Whether code should be deduplicated.
bool deduplicate_code_;
+ // Whether compiled code should increment the hotness count of ArtMethod. Note that the increments
+ // won't be atomic for performance reasons, so we accept races, just like in interpreter.
+ bool count_hotness_in_compiled_code_;
+
RegisterAllocator::Strategy register_allocation_strategy_;
// If not null, specifies optimization passes which will be run instead of defaults.
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index f97ab08600..3b18db09fc 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -77,6 +77,9 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string
}
map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_);
options->deduplicate_code_ = map.GetOrDefault(Base::DeduplicateCode);
+ if (map.Exists(Base::CountHotnessInCompiledCode)) {
+ options->count_hotness_in_compiled_code_ = true;
+ }
if (map.Exists(Base::DumpTimings)) {
options->dump_timings_ = true;
@@ -137,6 +140,9 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) {
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(Map::DeduplicateCode)
+ .Define({"--count-hotness-in-compiled-code"})
+ .IntoKey(Map::CountHotnessInCompiledCode)
+
.Define({"--dump-timings"})
.IntoKey(Map::DumpTimings)
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index 2c56fd7974..acddae7299 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -58,6 +58,7 @@ COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend)
COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy)
COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods)
COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true)
+COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode)
COMPILER_OPTIONS_KEY (Unit, DumpTimings)
COMPILER_OPTIONS_KEY (Unit, DumpStats)
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 2dafbf7f6d..3c5a37f958 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -438,8 +438,6 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
case TypeCheckKind::kArrayCheck:
case TypeCheckKind::kUnresolvedCheck:
return false;
- case TypeCheckKind::kBitstringCheck:
- return true;
}
LOG(FATAL) << "Unreachable";
UNREACHABLE();
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b47a5cf3c4..1380596ab2 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1488,6 +1488,14 @@ void CodeGeneratorARM64::GenerateFrameEntry() {
MacroAssembler* masm = GetVIXLAssembler();
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(masm);
+ Register temp = temps.AcquireX();
+ __ Ldrh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp, temp, 1);
+ __ Strh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ }
+
bool do_overflow_check =
FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm64) || !IsLeafMethod();
if (do_overflow_check) {
@@ -2112,26 +2120,6 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod
__ Bind(slow_path->GetExitLabel());
}
-void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare(
- HTypeCheckInstruction* check, vixl::aarch64::Register temp) {
- uint32_t path_to_root = check->GetBitstringPathToRoot();
- uint32_t mask = check->GetBitstringMask();
- DCHECK(IsPowerOfTwo(mask + 1));
- size_t mask_bits = WhichPowerOf2(mask + 1);
-
- if (mask_bits == 16u) {
- // Load only the bitstring part of the status word.
- __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset()));
- } else {
- // /* uint32_t */ temp = temp->status_
- __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset()));
- // Extract the bitstring bits.
- __ Ubfx(temp, temp, 0, mask_bits);
- }
- // Compare the bitstring bits to `path_to_root`.
- __ Cmp(temp, path_to_root);
-}
-
void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {
BarrierType type = BarrierAll;
@@ -3521,6 +3509,15 @@ void InstructionCodeGeneratorARM64::HandleGoto(HInstruction* got, HBasicBlock* s
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp1 = temps.AcquireX();
+ Register temp2 = temps.AcquireX();
+ __ Ldr(temp1, MemOperand(sp, 0));
+ __ Ldrh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp2, temp2, 1);
+ __ Strh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value()));
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
@@ -3860,8 +3857,6 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
@@ -3870,13 +3865,7 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
// The "out" register is used as a temporary, so it overlaps with the inputs.
// Note that TypeCheckSlowPathARM64 uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -3889,9 +3878,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = InputRegisterAt(instruction, 0);
- Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
- ? Register()
- : InputRegisterAt(instruction, 1);
+ Register cls = InputRegisterAt(instruction, 1);
Location out_loc = locations->Out();
Register out = OutputRegister(instruction);
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -4077,23 +4064,6 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
}
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- out_loc,
- obj_loc,
- class_offset,
- maybe_temp_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, out);
- __ Cset(out, eq);
- if (zero.IsLinked()) {
- __ B(&done);
- }
- break;
- }
}
if (zero.IsLinked()) {
@@ -4116,13 +4086,7 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
// Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64.
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -4132,9 +4096,7 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = InputRegisterAt(instruction, 0);
- Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
- ? Register()
- : InputRegisterAt(instruction, 1);
+ Register cls = InputRegisterAt(instruction, 1);
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
DCHECK_GE(num_temps, 1u);
DCHECK_LE(num_temps, 3u);
@@ -4315,20 +4277,6 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
__ B(ne, &start_loop);
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- temp_loc,
- obj_loc,
- class_offset,
- maybe_temp2_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, temp);
- __ B(ne, type_check_slow_path->GetEntryLabel());
- break;
- }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index cc369de983..f92c94fda7 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -264,8 +264,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {
private:
void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
vixl::aarch64::Register class_reg);
- void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
- vixl::aarch64::Register temp);
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void HandleBinaryOp(HBinaryOperation* instr);
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 504c6479cc..18e7d1cc46 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -2485,17 +2485,21 @@ void CodeGeneratorARMVIXL::GenerateFrameEntry() {
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp, temp, 1);
+ __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ }
+
if (HasEmptyFrame()) {
return;
}
if (!skip_overflow_check) {
- // Using r4 instead of IP saves 2 bytes. Start by asserting that r4 is available here.
- for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
- DCHECK(!reg.Is(r4));
- }
- DCHECK(!kCoreCalleeSaves.Includes(r4));
- vixl32::Register temp = r4;
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
__ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
// The load must immediately precede RecordPcInfo.
ExactAssemblyScope aas(GetVIXLAssembler(),
@@ -2790,6 +2794,16 @@ void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock*
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ __ Push(vixl32::Register(kMethodRegister));
+ GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
+ __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp, temp, 1);
+ __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Pop(vixl32::Register(kMethodRegister));
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
@@ -7195,67 +7209,6 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
__ Bind(slow_path->GetExitLabel());
}
-void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
- HTypeCheckInstruction* check,
- vixl32::Register temp,
- vixl32::FlagsUpdate flags_update) {
- uint32_t path_to_root = check->GetBitstringPathToRoot();
- uint32_t mask = check->GetBitstringMask();
- DCHECK(IsPowerOfTwo(mask + 1));
- size_t mask_bits = WhichPowerOf2(mask + 1);
-
- // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
- // the Z flag for BNE. This is indicated by the `flags_update` parameter.
- if (mask_bits == 16u) {
- // Load only the bitstring part of the status word.
- __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
- // Check if the bitstring bits are equal to `path_to_root`.
- if (flags_update == SetFlags) {
- __ Cmp(temp, path_to_root);
- } else {
- __ Sub(temp, temp, path_to_root);
- }
- } else {
- // /* uint32_t */ temp = temp->status_
- __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
- if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
- // Compare the bitstring bits using SUB.
- __ Sub(temp, temp, path_to_root);
- // Shift out bits that do not contribute to the comparison.
- __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
- } else if (IsUint<16>(path_to_root)) {
- if (temp.IsLow()) {
- // Note: Optimized for size but contains one more dependent instruction than necessary.
- // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
- // macro assembler would use the high reg IP for the constant by default.
- // Compare the bitstring bits using SUB.
- __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2
- __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3
- // Shift out bits that do not contribute to the comparison.
- __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
- } else {
- // Extract the bitstring bits.
- __ Ubfx(temp, temp, 0, mask_bits);
- // Check if the bitstring bits are equal to `path_to_root`.
- if (flags_update == SetFlags) {
- __ Cmp(temp, path_to_root);
- } else {
- __ Sub(temp, temp, path_to_root);
- }
- }
- } else {
- // Shift out bits that do not contribute to the comparison.
- __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
- // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
- if (flags_update == SetFlags) {
- __ Cmp(temp, path_to_root << (32u - mask_bits));
- } else {
- __ Sub(temp, temp, path_to_root << (32u - mask_bits));
- }
- }
- }
-}
-
HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) {
switch (desired_string_load_kind) {
@@ -7447,8 +7400,6 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
@@ -7457,13 +7408,7 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
// The "out" register is used as a temporary, so it overlaps with the inputs.
// Note that TypeCheckSlowPathARM uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -7478,9 +7423,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction)
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
vixl32::Register obj = InputRegisterAt(instruction, 0);
- vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
- ? vixl32::Register()
- : InputRegisterAt(instruction, 1);
+ vixl32::Register cls = InputRegisterAt(instruction, 1);
Location out_loc = locations->Out();
vixl32::Register out = OutputRegister(instruction);
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -7720,26 +7663,6 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction)
__ B(slow_path->GetEntryLabel());
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- out_loc,
- obj_loc,
- class_offset,
- maybe_temp_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
- // If `out` is a low reg and we would have another low reg temp, we could
- // optimize this as RSBS+ADC, see GenerateConditionWithZero().
- //
- // Also, in some cases when `out` is a low reg and we're loading a constant to IP
- // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
- // would be the same and we would have fewer direct data dependencies.
- codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR
- break;
- }
}
if (done.IsReferenced()) {
@@ -7757,13 +7680,7 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -7772,9 +7689,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
vixl32::Register obj = InputRegisterAt(instruction, 0);
- vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
- ? vixl32::Register()
- : InputRegisterAt(instruction, 1);
+ vixl32::Register cls = InputRegisterAt(instruction, 1);
Location temp_loc = locations->GetTemp(0);
vixl32::Register temp = RegisterFrom(temp_loc);
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -7959,20 +7874,6 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
__ B(ne, &start_loop, /* far_target */ false);
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- temp_loc,
- obj_loc,
- class_offset,
- maybe_temp2_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
- __ B(ne, type_check_slow_path->GetEntryLabel());
- break;
- }
}
if (done.IsReferenced()) {
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index bd815f45b3..38570bb0fe 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -322,9 +322,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator {
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path,
vixl32::Register class_reg);
- void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
- vixl::aarch32::Register temp,
- vixl::aarch32::FlagsUpdate flags_update);
void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 2ed0ab79a1..51fb4dacfd 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1276,6 +1276,10 @@ static dwarf::Reg DWARFReg(Register reg) {
void CodeGeneratorMIPS::GenerateFrameEntry() {
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ LOG(WARNING) << "Unimplemented hotness update in mips backend";
+ }
+
bool do_overflow_check =
FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips) || !IsLeafMethod();
@@ -1929,34 +1933,6 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode
__ Bind(slow_path->GetExitLabel());
}
-void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
- Register temp) {
- uint32_t path_to_root = check->GetBitstringPathToRoot();
- uint32_t mask = check->GetBitstringMask();
- DCHECK(IsPowerOfTwo(mask + 1));
- size_t mask_bits = WhichPowerOf2(mask + 1);
-
- if (mask_bits == 16u) {
- // Load only the bitstring part of the status word.
- __ LoadFromOffset(
- kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value());
- // Compare the bitstring bits using XOR.
- __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
- } else {
- // /* uint32_t */ temp = temp->status_
- __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value());
- // Compare the bitstring bits using XOR.
- if (IsUint<16>(path_to_root)) {
- __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
- } else {
- __ LoadConst32(TMP, path_to_root);
- __ Xor(temp, temp, TMP);
- }
- // Shift out bits that do not contribute to the comparison.
- __ Sll(temp, temp, 32 - mask_bits);
- }
-}
-
void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
__ Sync(0); // Only stype 0 is supported.
}
@@ -3317,20 +3293,12 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -3339,7 +3307,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = obj_loc.AsRegister<Register>();
- Location cls = locations->InAt(1);
+ Register cls = locations->InAt(1).AsRegister<Register>();
Location temp_loc = locations->GetTemp(0);
Register temp = temp_loc.AsRegister<Register>();
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -3389,7 +3357,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
kWithoutReadBarrier);
// Jump to slow path for throwing the exception or doing a
// more involved array check.
- __ Bne(temp, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
+ __ Bne(temp, cls, slow_path->GetEntryLabel());
break;
}
@@ -3415,7 +3383,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
// exception.
__ Beqz(temp, slow_path->GetEntryLabel());
// Otherwise, compare the classes.
- __ Bne(temp, cls.AsRegister<Register>(), &loop);
+ __ Bne(temp, cls, &loop);
break;
}
@@ -3430,7 +3398,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
// Walk over the class hierarchy to find a match.
MipsLabel loop;
__ Bind(&loop);
- __ Beq(temp, cls.AsRegister<Register>(), &done);
+ __ Beq(temp, cls, &done);
// /* HeapReference<Class> */ temp = temp->super_class_
GenerateReferenceLoadOneRegister(instruction,
temp_loc,
@@ -3453,7 +3421,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
maybe_temp2_loc,
kWithoutReadBarrier);
// Do an exact check.
- __ Beq(temp, cls.AsRegister<Register>(), &done);
+ __ Beq(temp, cls, &done);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ temp = temp->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -3512,21 +3480,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
// Go to next interface.
__ Addiu(TMP, TMP, -2);
// Compare the classes and continue the loop if they do not match.
- __ Bne(AT, cls.AsRegister<Register>(), &loop);
- break;
- }
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- temp_loc,
- obj_loc,
- class_offset,
- maybe_temp2_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, temp);
- __ Bnez(temp, slow_path->GetEntryLabel());
+ __ Bne(AT, cls, &loop);
break;
}
}
@@ -7257,8 +7211,6 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
@@ -7267,13 +7219,7 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
// The output does overlap inputs.
// Note that TypeCheckSlowPathMIPS uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -7285,7 +7231,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = obj_loc.AsRegister<Register>();
- Location cls = locations->InAt(1);
+ Register cls = locations->InAt(1).AsRegister<Register>();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -7315,7 +7261,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
maybe_temp_loc,
kCompilerReadBarrierOption);
// Classes must be equal for the instanceof to succeed.
- __ Xor(out, out, cls.AsRegister<Register>());
+ __ Xor(out, out, cls);
__ Sltiu(out, out, 1);
break;
}
@@ -7340,7 +7286,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
kCompilerReadBarrierOption);
// If `out` is null, we use it for the result, and jump to `done`.
__ Beqz(out, &done);
- __ Bne(out, cls.AsRegister<Register>(), &loop);
+ __ Bne(out, cls, &loop);
__ LoadConst32(out, 1);
break;
}
@@ -7356,7 +7302,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
// Walk over the class hierarchy to find a match.
MipsLabel loop, success;
__ Bind(&loop);
- __ Beq(out, cls.AsRegister<Register>(), &success);
+ __ Beq(out, cls, &success);
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction,
out_loc,
@@ -7381,7 +7327,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
kCompilerReadBarrierOption);
// Do an exact check.
MipsLabel success;
- __ Beq(out, cls.AsRegister<Register>(), &success);
+ __ Beq(out, cls, &success);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ out = out->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -7413,7 +7359,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
instruction, /* is_fatal */ false);
codegen_->AddSlowPath(slow_path);
- __ Bne(out, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
+ __ Bne(out, cls, slow_path->GetEntryLabel());
__ LoadConst32(out, 1);
break;
}
@@ -7445,20 +7391,6 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
__ B(slow_path->GetEntryLabel());
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- out_loc,
- obj_loc,
- class_offset,
- maybe_temp_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, out);
- __ Sltiu(out, out, 1);
- break;
- }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index ffeb3b00a7..32b3e4221f 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -237,7 +237,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator {
private:
void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg);
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
- void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);
void HandleBinaryOp(HBinaryOperation* operation);
void HandleCondition(HCondition* instruction);
void HandleShift(HBinaryOperation* operation);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 3ae8a30754..480b9178d2 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1079,6 +1079,10 @@ static dwarf::Reg DWARFReg(FpuRegister reg) {
void CodeGeneratorMIPS64::GenerateFrameEntry() {
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ LOG(WARNING) << "Unimplemented hotness update in mips64 backend";
+ }
+
bool do_overflow_check =
FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips64) || !IsLeafMethod();
@@ -1775,34 +1779,6 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo
__ Bind(slow_path->GetExitLabel());
}
-void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
- GpuRegister temp) {
- uint32_t path_to_root = check->GetBitstringPathToRoot();
- uint32_t mask = check->GetBitstringMask();
- DCHECK(IsPowerOfTwo(mask + 1));
- size_t mask_bits = WhichPowerOf2(mask + 1);
-
- if (mask_bits == 16u) {
- // Load only the bitstring part of the status word.
- __ LoadFromOffset(
- kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value());
- // Compare the bitstring bits using XOR.
- __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
- } else {
- // /* uint32_t */ temp = temp->status_
- __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value());
- // Compare the bitstring bits using XOR.
- if (IsUint<16>(path_to_root)) {
- __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
- } else {
- __ LoadConst32(TMP, path_to_root);
- __ Xor(temp, temp, TMP);
- }
- // Shift out bits that do not contribute to the comparison.
- __ Sll(temp, temp, 32 - mask_bits);
- }
-}
-
void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
__ Sync(0); // only stype 0 is supported
}
@@ -2872,20 +2848,12 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -2894,7 +2862,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
- Location cls = locations->InAt(1);
+ GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
Location temp_loc = locations->GetTemp(0);
GpuRegister temp = temp_loc.AsRegister<GpuRegister>();
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -2944,7 +2912,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
kWithoutReadBarrier);
// Jump to slow path for throwing the exception or doing a
// more involved array check.
- __ Bnec(temp, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
+ __ Bnec(temp, cls, slow_path->GetEntryLabel());
break;
}
@@ -2970,7 +2938,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
// exception.
__ Beqzc(temp, slow_path->GetEntryLabel());
// Otherwise, compare the classes.
- __ Bnec(temp, cls.AsRegister<GpuRegister>(), &loop);
+ __ Bnec(temp, cls, &loop);
break;
}
@@ -2985,7 +2953,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
// Walk over the class hierarchy to find a match.
Mips64Label loop;
__ Bind(&loop);
- __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);
+ __ Beqc(temp, cls, &done);
// /* HeapReference<Class> */ temp = temp->super_class_
GenerateReferenceLoadOneRegister(instruction,
temp_loc,
@@ -3008,7 +2976,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
maybe_temp2_loc,
kWithoutReadBarrier);
// Do an exact check.
- __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);
+ __ Beqc(temp, cls, &done);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ temp = temp->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -3067,21 +3035,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
__ Daddiu(temp, temp, 2 * kHeapReferenceSize);
__ Addiu(TMP, TMP, -2);
// Compare the classes and continue the loop if they do not match.
- __ Bnec(AT, cls.AsRegister<GpuRegister>(), &loop);
- break;
- }
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- temp_loc,
- obj_loc,
- class_offset,
- maybe_temp2_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, temp);
- __ Bnezc(temp, slow_path->GetEntryLabel());
+ __ Bnec(AT, cls, &loop);
break;
}
}
@@ -5574,8 +5528,6 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
@@ -5584,13 +5536,7 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
+ locations->SetInAt(1, Location::RequiresRegister());
// The output does overlap inputs.
// Note that TypeCheckSlowPathMIPS64 uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -5602,7 +5548,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
- Location cls = locations->InAt(1);
+ GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
Location out_loc = locations->Out();
GpuRegister out = out_loc.AsRegister<GpuRegister>();
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -5632,7 +5578,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
maybe_temp_loc,
kCompilerReadBarrierOption);
// Classes must be equal for the instanceof to succeed.
- __ Xor(out, out, cls.AsRegister<GpuRegister>());
+ __ Xor(out, out, cls);
__ Sltiu(out, out, 1);
break;
}
@@ -5657,7 +5603,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
kCompilerReadBarrierOption);
// If `out` is null, we use it for the result, and jump to `done`.
__ Beqzc(out, &done);
- __ Bnec(out, cls.AsRegister<GpuRegister>(), &loop);
+ __ Bnec(out, cls, &loop);
__ LoadConst32(out, 1);
break;
}
@@ -5673,7 +5619,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
// Walk over the class hierarchy to find a match.
Mips64Label loop, success;
__ Bind(&loop);
- __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);
+ __ Beqc(out, cls, &success);
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction,
out_loc,
@@ -5698,7 +5644,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
kCompilerReadBarrierOption);
// Do an exact check.
Mips64Label success;
- __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);
+ __ Beqc(out, cls, &success);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ out = out->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -5730,7 +5676,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(
instruction, /* is_fatal */ false);
codegen_->AddSlowPath(slow_path);
- __ Bnec(out, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
+ __ Bnec(out, cls, slow_path->GetEntryLabel());
__ LoadConst32(out, 1);
break;
}
@@ -5762,20 +5708,6 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
__ Bc(slow_path->GetEntryLabel());
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- out_loc,
- obj_loc,
- class_offset,
- maybe_temp_loc,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, out);
- __ Sltiu(out, out, 1);
- break;
- }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 87d5a9c15a..d479410f07 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -233,7 +233,6 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator {
private:
void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg);
- void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp);
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
void HandleBinaryOp(HBinaryOperation* operation);
void HandleCondition(HCondition* instruction);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e85f9001b1..c52c7ff7f1 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1061,6 +1061,11 @@ void CodeGeneratorX86::GenerateFrameEntry() {
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ addw(Address(kMethodRegisterArgument, ArtMethod::HotnessCountOffset().Int32Value()),
+ Immediate(1));
+ }
+
if (!skip_overflow_check) {
size_t reserved_bytes = GetStackOverflowReservedBytes(InstructionSet::kX86);
__ testl(EAX, Address(ESP, -static_cast<int32_t>(reserved_bytes)));
@@ -1357,6 +1362,12 @@ void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* suc
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ pushl(EAX);
+ __ movl(EAX, Address(ESP, kX86WordSize));
+ __ addw(Address(EAX, ArtMethod::HotnessCountOffset().Int32Value()), Immediate(1));
+ __ popl(EAX);
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
@@ -6234,27 +6245,6 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck(
// No need for memory fence, thanks to the X86 memory model.
}
-void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
- Register temp) {
- uint32_t path_to_root = check->GetBitstringPathToRoot();
- uint32_t mask = check->GetBitstringMask();
- DCHECK(IsPowerOfTwo(mask + 1));
- size_t mask_bits = WhichPowerOf2(mask + 1);
-
- if ((false) && mask_bits == 16u) {
- // FIXME: cmpw() erroneously emits the constant as 32 bits instead of 16 bits. b/71853552
- // Compare the bitstring in memory.
- __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root));
- } else {
- // /* uint32_t */ temp = temp->status_
- __ movl(temp, Address(temp, mirror::Class::StatusOffset()));
- // Compare the bitstring bits using SUB.
- __ subl(temp, Immediate(path_to_root));
- // Shift out bits that do not contribute to the comparison.
- __ shll(temp, Immediate(32u - mask_bits));
- }
-}
-
HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) {
switch (desired_string_load_kind) {
@@ -6447,8 +6437,6 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
@@ -6457,13 +6445,7 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::Any());
- }
+ locations->SetInAt(1, Location::Any());
// Note that TypeCheckSlowPathX86 uses this "out" register too.
locations->SetOut(Location::RequiresRegister());
// When read barriers are enabled, we need a temporary register for some cases.
@@ -6684,21 +6666,6 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) {
}
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- out_loc,
- obj_loc,
- class_offset,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, out);
- __ j(kNotEqual, &zero);
- __ movl(out, Immediate(1));
- __ jmp(&done);
- break;
- }
}
if (zero.IsLinked()) {
@@ -6725,10 +6692,6 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) {
// Require a register for the interface check since there is a loop that compares the class to
// a memory address.
locations->SetInAt(1, Location::RequiresRegister());
- } else if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
} else {
locations->SetInAt(1, Location::Any());
}
@@ -6948,19 +6911,6 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {
__ MaybeUnpoisonHeapReference(cls.AsRegister<Register>());
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- temp_loc,
- obj_loc,
- class_offset,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, temp);
- __ j(kNotEqual, type_check_slow_path->GetEntryLabel());
- break;
- }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 2d14d4cdc2..0082853184 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -211,7 +211,6 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator {
// the suspend call.
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg);
- void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);
void HandleBitwiseOperation(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 9f8b1bb038..ee5918de71 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1268,6 +1268,12 @@ void CodeGeneratorX86_64::GenerateFrameEntry() {
&& !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ addw(Address(CpuRegister(kMethodRegisterArgument),
+ ArtMethod::HotnessCountOffset().Int32Value()),
+ Immediate(1));
+ }
+
if (!skip_overflow_check) {
size_t reserved_bytes = GetStackOverflowReservedBytes(InstructionSet::kX86_64);
__ testq(CpuRegister(RAX), Address(CpuRegister(RSP), -static_cast<int32_t>(reserved_bytes)));
@@ -1459,6 +1465,11 @@ void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock*
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), 0));
+ __ addw(Address(CpuRegister(TMP), ArtMethod::HotnessCountOffset().Int32Value()),
+ Immediate(1));
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
@@ -5440,27 +5451,6 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck(
// No need for memory fence, thanks to the x86-64 memory model.
}
-void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
- CpuRegister temp) {
- uint32_t path_to_root = check->GetBitstringPathToRoot();
- uint32_t mask = check->GetBitstringMask();
- DCHECK(IsPowerOfTwo(mask + 1));
- size_t mask_bits = WhichPowerOf2(mask + 1);
-
- if ((false) && mask_bits == 16u) {
- // FIXME: cmpw() erroneously emits the constant as 32 bits instead of 16 bits. b/71853552
- // Compare the bitstring in memory.
- __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root));
- } else {
- // /* uint32_t */ temp = temp->status_
- __ movl(temp, Address(temp, mirror::Class::StatusOffset()));
- // Compare the bitstring bits using SUB.
- __ subl(temp, Immediate(path_to_root));
- // Shift out bits that do not contribute to the comparison.
- __ shll(temp, Immediate(32u - mask_bits));
- }
-}
-
HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
HLoadClass::LoadKind desired_class_load_kind) {
switch (desired_class_load_kind) {
@@ -5833,8 +5823,6 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
- case TypeCheckKind::kBitstringCheck:
- break;
}
LocationSummary* locations =
@@ -5843,13 +5831,7 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
- } else {
- locations->SetInAt(1, Location::Any());
- }
+ locations->SetInAt(1, Location::Any());
// Note that TypeCheckSlowPathX86_64 uses this "out" register too.
locations->SetOut(Location::RequiresRegister());
// When read barriers are enabled, we need a temporary register for
@@ -6078,27 +6060,6 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
}
break;
}
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- out_loc,
- obj_loc,
- class_offset,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, out);
- if (zero.IsLinked()) {
- __ j(kNotEqual, &zero);
- __ movl(out, Immediate(1));
- __ jmp(&done);
- } else {
- __ setcc(kEqual, out);
- // setcc only sets the low byte.
- __ andl(out, Immediate(1));
- }
- break;
- }
}
if (zero.IsLinked()) {
@@ -6125,10 +6086,6 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) {
// Require a register for the interface check since there is a loop that compares the class to
// a memory address.
locations->SetInAt(1, Location::RequiresRegister());
- } else if (type_check_kind == TypeCheckKind::kBitstringCheck) {
- locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
- locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
- locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
} else {
locations->SetInAt(1, Location::Any());
}
@@ -6315,7 +6272,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
break;
}
- case TypeCheckKind::kInterfaceCheck: {
+ case TypeCheckKind::kInterfaceCheck:
// Fast path for the interface check. Try to avoid read barriers to improve the fast path.
// We can not get false positives by doing this.
// /* HeapReference<Class> */ temp = obj->klass_
@@ -6351,20 +6308,6 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
// If `cls` was poisoned above, unpoison it.
__ MaybeUnpoisonHeapReference(cls.AsRegister<CpuRegister>());
break;
- }
-
- case TypeCheckKind::kBitstringCheck: {
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction,
- temp_loc,
- obj_loc,
- class_offset,
- kWithoutReadBarrier);
-
- GenerateBitstringTypeCheckCompare(instruction, temp);
- __ j(kNotEqual, type_check_slow_path->GetEntryLabel());
- break;
- }
}
if (done.IsLinked()) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 97f8ec7ae0..e86123ef01 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -208,7 +208,6 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator {
// the suspend call.
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg);
- void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp);
void HandleBitwiseOperation(HBinaryOperation* operation);
void GenerateRemFP(HRem* rem);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index fbcbe3608e..c88baa8610 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -25,11 +25,6 @@
#include "base/bit_vector-inl.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
-#include "handle.h"
-#include "mirror/class.h"
-#include "obj_ptr-inl.h"
-#include "scoped_thread_state_change-inl.h"
-#include "subtype_check.h"
namespace art {
@@ -553,83 +548,28 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) {
}
}
-void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check,
- size_t input_pos,
- bool check_value,
- uint32_t expected_value,
- const char* name) {
- if (!check->InputAt(input_pos)->IsIntConstant()) {
- AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.",
- check->DebugName(),
- check->GetId(),
- input_pos,
- name,
- check->InputAt(2)->DebugName(),
- check->InputAt(2)->GetId()));
- } else if (check_value) {
- uint32_t actual_value =
- static_cast<uint32_t>(check->InputAt(input_pos)->AsIntConstant()->GetValue());
- if (actual_value != expected_value) {
- AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.",
- check->DebugName(),
- check->GetId(),
- name,
- actual_value,
- expected_value));
- }
- }
-}
-
-void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) {
+void GraphChecker::VisitCheckCast(HCheckCast* check) {
VisitInstruction(check);
HInstruction* input = check->InputAt(1);
- if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
- if (!input->IsNullConstant()) {
- AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.",
- check->DebugName(),
- check->GetId(),
- input->DebugName(),
- input->GetId()));
- }
- bool check_values = false;
- BitString::StorageType expected_path_to_root = 0u;
- BitString::StorageType expected_mask = 0u;
- {
- ScopedObjectAccess soa(Thread::Current());
- ObjPtr<mirror::Class> klass = check->GetClass().Get();
- MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
- SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
- if (state == SubtypeCheckInfo::kAssigned) {
- expected_path_to_root =
- SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass);
- expected_mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass);
- check_values = true;
- } else {
- AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.",
- check->DebugName(),
- check->GetId()));
- }
- }
- CheckTypeCheckBitstringInput(
- check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root");
- CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask");
- } else {
- if (!input->IsLoadClass()) {
- AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.",
- check->DebugName(),
- check->GetId(),
- input->DebugName(),
- input->GetId()));
- }
+ if (!input->IsLoadClass()) {
+ AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
+ check->DebugName(),
+ check->GetId(),
+ input->DebugName(),
+ input->GetId()));
}
}
-void GraphChecker::VisitCheckCast(HCheckCast* check) {
- HandleTypeCheckInstruction(check);
-}
-
void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) {
- HandleTypeCheckInstruction(instruction);
+ VisitInstruction(instruction);
+ HInstruction* input = instruction->InputAt(1);
+ if (!input->IsLoadClass()) {
+ AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ input->DebugName(),
+ input->GetId()));
+ }
}
void GraphChecker::HandleLoop(HBasicBlock* loop_header) {
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index dbedc40518..0f0b49d240 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -71,12 +71,6 @@ class GraphChecker : public HGraphDelegateVisitor {
void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE;
void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
- void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check,
- size_t input_pos,
- bool check_value,
- uint32_t expected_value,
- const char* name);
- void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction);
void HandleLoop(HBasicBlock* loop_header);
void HandleBooleanInput(HInstruction* instruction, size_t input_index);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 55191214a6..12c69889ab 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -389,23 +389,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("load_kind") << load_string->GetLoadKind();
}
- void HandleTypeCheckInstruction(HTypeCheckInstruction* check) {
- StartAttributeStream("check_kind") << check->GetTypeCheckKind();
- StartAttributeStream("must_do_null_check") << std::boolalpha
- << check->MustDoNullCheck() << std::noboolalpha;
- if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
- StartAttributeStream("path_to_root") << std::hex
- << "0x" << check->GetBitstringPathToRoot() << std::dec;
- StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec;
- }
- }
-
void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
- HandleTypeCheckInstruction(check_cast);
+ StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind();
+ StartAttributeStream("must_do_null_check") << std::boolalpha
+ << check_cast->MustDoNullCheck() << std::noboolalpha;
}
void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
- HandleTypeCheckInstruction(instance_of);
+ StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind();
+ StartAttributeStream("must_do_null_check") << std::boolalpha
+ << instance_of->MustDoNullCheck() << std::noboolalpha;
}
void VisitArrayLength(HArrayLength* array_length) OVERRIDE {
@@ -655,32 +648,20 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
<< std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha;
}
- // For the builder and the inliner, we want to add extra information on HInstructions
- // that have reference types, and also HInstanceOf/HCheckcast.
if ((IsPass(HGraphBuilder::kBuilderPassName)
|| IsPass(HInliner::kInlinerPassName))
- && (instruction->GetType() == DataType::Type::kReference ||
- instruction->IsInstanceOf() ||
- instruction->IsCheckCast())) {
- ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference)
- ? instruction->IsLoadClass()
- ? instruction->AsLoadClass()->GetLoadedClassRTI()
- : instruction->GetReferenceTypeInfo()
- : instruction->IsInstanceOf()
- ? instruction->AsInstanceOf()->GetTargetClassRTI()
- : instruction->AsCheckCast()->GetTargetClassRTI();
+ && (instruction->GetType() == DataType::Type::kReference)) {
+ ReferenceTypeInfo info = instruction->IsLoadClass()
+ ? instruction->AsLoadClass()->GetLoadedClassRTI()
+ : instruction->GetReferenceTypeInfo();
ScopedObjectAccess soa(Thread::Current());
if (info.IsValid()) {
StartAttributeStream("klass")
<< mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get());
- if (instruction->GetType() == DataType::Type::kReference) {
- StartAttributeStream("can_be_null")
- << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
- }
+ StartAttributeStream("can_be_null")
+ << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
- } else if (instruction->IsLoadClass() ||
- instruction->IsInstanceOf() ||
- instruction->IsCheckCast()) {
+ } else if (instruction->IsLoadClass()) {
StartAttributeStream("klass") << "unresolved";
} else {
// The NullConstant may be added to the graph during other passes that happen between
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 452be6feae..035e5ce3e1 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -392,8 +392,9 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) {
return single_impl;
}
-static bool AlwaysThrows(ArtMethod* method) {
- CodeItemDataAccessor accessor(method);
+static bool AlwaysThrows(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CodeItemDataAccessor accessor(method->DexInstructionData());
// Skip native methods, methods with try blocks, and methods that are too large.
if (!accessor.HasCodeItem() ||
accessor.TriesSize() != 0 ||
@@ -1418,7 +1419,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
bool same_dex_file = IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *method->GetDexFile());
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
if (!accessor.HasCodeItem()) {
LOG_FAIL_NO_STAT()
@@ -1697,7 +1698,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
uint32_t method_index = resolved_method->GetDexMethodIndex();
- CodeItemDebugInfoAccessor code_item_accessor(resolved_method);
+ CodeItemDebugInfoAccessor code_item_accessor(resolved_method->DexInstructionDebugInfo());
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(),
caller_compilation_unit_.GetDexCache(),
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index e81d97b0a8..02465d37ba 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -18,7 +18,7 @@
#define ART_COMPILER_OPTIMIZING_INLINER_H_
#include "dex/dex_file_types.h"
-#include "invoke_type.h"
+#include "dex/invoke_type.h"
#include "jit/profile_compilation_info.h"
#include "optimization.h"
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 0205c6a4d3..64a1eccf60 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1811,6 +1811,29 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object,
}
}
+static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (cls == nullptr) {
+ return TypeCheckKind::kUnresolvedCheck;
+ } else if (cls->IsInterface()) {
+ return TypeCheckKind::kInterfaceCheck;
+ } else if (cls->IsArrayClass()) {
+ if (cls->GetComponentType()->IsObjectClass()) {
+ return TypeCheckKind::kArrayObjectCheck;
+ } else if (cls->CannotBeAssignedFromOtherTypes()) {
+ return TypeCheckKind::kExactCheck;
+ } else {
+ return TypeCheckKind::kArrayCheck;
+ }
+ } else if (cls->IsFinal()) {
+ return TypeCheckKind::kExactCheck;
+ } else if (cls->IsAbstract()) {
+ return TypeCheckKind::kAbstractClassCheck;
+ } else {
+ return TypeCheckKind::kClassHierarchyCheck;
+ }
+}
+
void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) {
HLoadString* load_string =
new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc);
@@ -1825,8 +1848,22 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_
HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) {
ScopedObjectAccess soa(Thread::Current());
const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
- Handle<mirror::Class> klass = ResolveClass(soa, type_index);
- bool needs_access_check = LoadClassNeedsAccessCheck(klass);
+ Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
+ Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass(
+ soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_));
+
+ bool needs_access_check = true;
+ if (klass != nullptr) {
+ if (klass->IsPublic()) {
+ needs_access_check = false;
+ } else {
+ ObjPtr<mirror::Class> compiling_class = GetCompilingClass();
+ if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) {
+ needs_access_check = false;
+ }
+ }
+ }
+
return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check);
}
@@ -1871,83 +1908,25 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index,
return load_class;
}
-Handle<mirror::Class> HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa,
- dex::TypeIndex type_index) {
- Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
- ObjPtr<mirror::Class> klass = compiler_driver_->ResolveClass(
- soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_);
- // TODO: Avoid creating excessive handles if the method references the same class repeatedly.
- // (Use a map on the local_allocator_.)
- return handles_->NewHandle(klass);
-}
-
-bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) {
- if (klass == nullptr) {
- return true;
- } else if (klass->IsPublic()) {
- return false;
- } else {
- ObjPtr<mirror::Class> compiling_class = GetCompilingClass();
- return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get());
- }
-}
-
void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
uint8_t destination,
uint8_t reference,
dex::TypeIndex type_index,
uint32_t dex_pc) {
HInstruction* object = LoadLocal(reference, DataType::Type::kReference);
+ HLoadClass* cls = BuildLoadClass(type_index, dex_pc);
ScopedObjectAccess soa(Thread::Current());
- const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
- Handle<mirror::Class> klass = ResolveClass(soa, type_index);
- bool needs_access_check = LoadClassNeedsAccessCheck(klass);
- TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind(
- klass.Get(), code_generator_, compiler_driver_, needs_access_check);
-
- HInstruction* class_or_null = nullptr;
- HIntConstant* bitstring_path_to_root = nullptr;
- HIntConstant* bitstring_mask = nullptr;
- if (check_kind == TypeCheckKind::kBitstringCheck) {
- // TODO: Allow using the bitstring check also if we need an access check.
- DCHECK(!needs_access_check);
- class_or_null = graph_->GetNullConstant(dex_pc);
- MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
- uint32_t path_to_root =
- SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass.Get());
- uint32_t mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass.Get());
- bitstring_path_to_root = graph_->GetIntConstant(static_cast<int32_t>(path_to_root), dex_pc);
- bitstring_mask = graph_->GetIntConstant(static_cast<int32_t>(mask), dex_pc);
- } else {
- class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check);
- }
- DCHECK(class_or_null != nullptr);
-
+ TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass());
if (instruction.Opcode() == Instruction::INSTANCE_OF) {
- AppendInstruction(new (allocator_) HInstanceOf(object,
- class_or_null,
- check_kind,
- klass,
- dex_pc,
- allocator_,
- bitstring_path_to_root,
- bitstring_mask));
+ AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc));
UpdateLocal(destination, current_block_->GetLastInstruction());
} else {
DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
// We emit a CheckCast followed by a BoundType. CheckCast is a statement
// which may throw. If it succeeds BoundType sets the new type of `object`
// for all subsequent uses.
- AppendInstruction(
- new (allocator_) HCheckCast(object,
- class_or_null,
- check_kind,
- klass,
- dex_pc,
- allocator_,
- bitstring_path_to_root,
- bitstring_mask));
+ AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc));
AppendInstruction(new (allocator_) HBoundType(object, dex_pc));
UpdateLocal(reference, current_block_->GetLastInstruction());
}
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index f78829232d..4428c53277 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -39,7 +39,6 @@ class DexCompilationUnit;
class HBasicBlockBuilder;
class Instruction;
class OptimizingCompilerStats;
-class ScopedObjectAccess;
class SsaBuilder;
class VariableSizedHandleScope;
@@ -233,12 +232,6 @@ class HInstructionBuilder : public ValueObject {
bool needs_access_check)
REQUIRES_SHARED(Locks::mutator_lock_);
- Handle<mirror::Class> ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Returns the outer-most compiling method's class.
ObjPtr<mirror::Class> GetOutermostCompilingClass() const;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 2538fa38f1..a42a85dc1d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -576,9 +576,7 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst
// Returns whether doing a type test between the class of `object` against `klass` has
// a statically known outcome. The result of the test is stored in `outcome`.
-static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti,
- HInstruction* object,
- /*out*/bool* outcome) {
+static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) {
DCHECK(!object->IsNullConstant()) << "Null constants should be special cased";
ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
ScopedObjectAccess soa(Thread::Current());
@@ -588,6 +586,7 @@ static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti,
return false;
}
+ ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
if (!class_rti.IsValid()) {
// Happens when the loaded class is unresolved.
return false;
@@ -612,8 +611,8 @@ static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti,
void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
HInstruction* object = check_cast->InputAt(0);
- if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck &&
- check_cast->GetTargetClass()->NeedsAccessCheck()) {
+ HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+ if (load_class->NeedsAccessCheck()) {
// If we need to perform an access check we cannot remove the instruction.
return;
}
@@ -631,18 +630,15 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
// Note: The `outcome` is initialized to please valgrind - the compiler can reorder
// the return value check with the `outcome` check, b/27651442 .
bool outcome = false;
- if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) {
+ if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
if (outcome) {
check_cast->GetBlock()->RemoveInstruction(check_cast);
MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast);
- if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) {
- HLoadClass* load_class = check_cast->GetTargetClass();
- if (!load_class->HasUses()) {
- // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
- // However, here we know that it cannot because the checkcast was successfull, hence
- // the class was already loaded.
- load_class->GetBlock()->RemoveInstruction(load_class);
- }
+ if (!load_class->HasUses()) {
+ // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+ // However, here we know that it cannot because the checkcast was successfull, hence
+ // the class was already loaded.
+ load_class->GetBlock()->RemoveInstruction(load_class);
}
} else {
// Don't do anything for exceptional cases for now. Ideally we should remove
@@ -653,8 +649,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
HInstruction* object = instruction->InputAt(0);
- if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck &&
- instruction->GetTargetClass()->NeedsAccessCheck()) {
+ HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass();
+ if (load_class->NeedsAccessCheck()) {
// If we need to perform an access check we cannot remove the instruction.
return;
}
@@ -677,7 +673,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
// Note: The `outcome` is initialized to please valgrind - the compiler can reorder
// the return value check with the `outcome` check, b/27651442 .
bool outcome = false;
- if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) {
+ if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf);
if (outcome && can_be_null) {
// Type test will succeed, we just need a null test.
@@ -690,14 +686,11 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
}
RecordSimplification();
instruction->GetBlock()->RemoveInstruction(instruction);
- if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) {
- HLoadClass* load_class = instruction->GetTargetClass();
- if (!load_class->HasUses()) {
- // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
- // However, here we know that it cannot because the instanceof check was successfull, hence
- // the class was already loaded.
- load_class->GetBlock()->RemoveInstruction(load_class);
- }
+ if (outcome && !load_class->HasUses()) {
+ // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+ // However, here we know that it cannot because the instanceof check was successfull, hence
+ // the class was already loaded.
+ load_class->GetBlock()->RemoveInstruction(load_class);
}
}
}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 6928b70df7..acb830e524 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -19,9 +19,9 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "class_linker.h"
+#include "dex/invoke_type.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
-#include "invoke_type.h"
#include "mirror/dex_cache-inl.h"
#include "nodes.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index aae94b227c..88326d321b 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -25,51 +25,6 @@
#include <iostream>
-/**
- * The general algorithm of load-store elimination (LSE).
- * Load-store analysis in the previous pass collects a list of heap locations
- * and does alias analysis of those heap locations.
- * LSE keeps track of a list of heap values corresponding to the heap
- * locations. It visits basic blocks in reverse post order and for
- * each basic block, visits instructions sequentially, and processes
- * instructions as follows:
- * - If the instruction is a load, and the heap location for that load has a
- * valid heap value, the load can be eliminated. In order to maintain the
- * validity of all heap locations during the optimization phase, the real
- * elimination is delayed till the end of LSE.
- * - If the instruction is a store, it updates the heap value for the heap
- * location of the store with the store instruction. The real heap value
- * can be fetched from the store instruction. Heap values are invalidated
- * for heap locations that may alias with the store instruction's heap
- * location. The store instruction can be eliminated unless the value stored
- * is later needed e.g. by a load from the same/aliased heap location or
- * the heap location persists at method return/deoptimization.
- * The store instruction is also needed if it's not used to track the heap
- * value anymore, e.g. when it fails to merge with the heap values from other
- * predecessors.
- * - A store that stores the same value as the heap value is eliminated.
- * - The list of heap values are merged at basic block entry from the basic
- * block's predecessors. The algorithm is single-pass, so loop side-effects is
- * used as best effort to decide if a heap location is stored inside the loop.
- * - A special type of objects called singletons are instantiated in the method
- * and have a single name, i.e. no aliases. Singletons have exclusive heap
- * locations since they have no aliases. Singletons are helpful in narrowing
- * down the life span of a heap location such that they do not always
- * need to participate in merging heap values. Allocation of a singleton
- * can be eliminated if that singleton is not used and does not persist
- * at method return/deoptimization.
- * - For newly instantiated instances, their heap values are initialized to
- * language defined default values.
- * - Some instructions such as invokes are treated as loading and invalidating
- * all the heap values, depending on the instruction's side effects.
- * - Finalizable objects are considered as persisting at method
- * return/deoptimization.
- * - Currently this LSE algorithm doesn't handle SIMD graph, e.g. with VecLoad
- * and VecStore instructions.
- * - Currently this LSE algorithm doesn't handle graph with try-catch, due to
- * the special block merging structure.
- */
-
namespace art {
// An unknown heap value. Loads with such a value in the heap location cannot be eliminated.
@@ -104,7 +59,8 @@ class LSEVisitor : public HGraphDelegateVisitor {
removed_loads_(allocator_.Adapter(kArenaAllocLSE)),
substitute_instructions_for_loads_(allocator_.Adapter(kArenaAllocLSE)),
possibly_removed_stores_(allocator_.Adapter(kArenaAllocLSE)),
- singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)) {
+ singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)),
+ singleton_new_arrays_(allocator_.Adapter(kArenaAllocLSE)) {
}
void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
@@ -132,26 +88,19 @@ class LSEVisitor : public HGraphDelegateVisitor {
return type_conversion;
}
- // Find an instruction's substitute if it's a removed load.
+ // Find an instruction's substitute if it should be removed.
// Return the same instruction if it should not be removed.
HInstruction* FindSubstitute(HInstruction* instruction) {
- if (!IsLoad(instruction)) {
- return instruction;
- }
size_t size = removed_loads_.size();
for (size_t i = 0; i < size; i++) {
if (removed_loads_[i] == instruction) {
- HInstruction* substitute = substitute_instructions_for_loads_[i];
- // The substitute list is a flat hierarchy.
- DCHECK_EQ(FindSubstitute(substitute), substitute);
- return substitute;
+ return substitute_instructions_for_loads_[i];
}
}
return instruction;
}
void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) {
- DCHECK(IsLoad(load));
DCHECK_EQ(FindSubstitute(heap_value), heap_value) <<
"Unexpected heap_value that has a substitute " << heap_value->DebugName();
removed_loads_.push_back(load);
@@ -258,59 +207,28 @@ class LSEVisitor : public HGraphDelegateVisitor {
new_instance->GetBlock()->RemoveInstruction(new_instance);
}
}
- }
-
- private:
- static bool IsLoad(HInstruction* instruction) {
- if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) {
- return false;
- }
- // Unresolved load is not treated as a load.
- return instruction->IsInstanceFieldGet() ||
- instruction->IsStaticFieldGet() ||
- instruction->IsArrayGet();
- }
-
- static bool IsStore(HInstruction* instruction) {
- if (instruction == kUnknownHeapValue || instruction == kDefaultHeapValue) {
- return false;
- }
- // Unresolved store is not treated as a store.
- return instruction->IsInstanceFieldSet() ||
- instruction->IsArraySet() ||
- instruction->IsStaticFieldSet();
- }
-
- // Returns the real heap value by finding its substitute or by "peeling"
- // a store instruction.
- HInstruction* GetRealHeapValue(HInstruction* heap_value) {
- if (IsLoad(heap_value)) {
- return FindSubstitute(heap_value);
- }
- if (!IsStore(heap_value)) {
- return heap_value;
- }
+ for (HInstruction* new_array : singleton_new_arrays_) {
+ size_t removed = HConstructorFence::RemoveConstructorFences(new_array);
+ MaybeRecordStat(stats_,
+ MethodCompilationStat::kConstructorFenceRemovedLSE,
+ removed);
- // We keep track of store instructions as the heap values which might be
- // eliminated if the stores are later found not necessary. The real stored
- // value needs to be fetched from the store instruction.
- if (heap_value->IsInstanceFieldSet()) {
- heap_value = heap_value->AsInstanceFieldSet()->GetValue();
- } else if (heap_value->IsStaticFieldSet()) {
- heap_value = heap_value->AsStaticFieldSet()->GetValue();
- } else {
- DCHECK(heap_value->IsArraySet());
- heap_value = heap_value->AsArraySet()->GetValue();
+ if (!new_array->HasNonEnvironmentUses()) {
+ new_array->RemoveEnvironmentUsers();
+ new_array->GetBlock()->RemoveInstruction(new_array);
+ }
}
- // heap_value may already be a removed load.
- return FindSubstitute(heap_value);
}
- // If heap_value is a store, need to keep the store.
- // This is necessary if a heap value is killed or replaced by another value,
- // so that the store is no longer used to track heap value.
+ private:
+ // If heap_values[index] is an instance field store, need to keep the store.
+ // This is necessary if a heap value is killed due to merging, or loop side
+ // effects (which is essentially merging also), since a load later from the
+ // location won't be eliminated.
void KeepIfIsStore(HInstruction* heap_value) {
- if (!IsStore(heap_value)) {
+ if (heap_value == kDefaultHeapValue ||
+ heap_value == kUnknownHeapValue ||
+ !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) {
return;
}
auto idx = std::find(possibly_removed_stores_.begin(),
@@ -321,41 +239,26 @@ class LSEVisitor : public HGraphDelegateVisitor {
}
}
- // If a heap location X may alias with heap location at `loc_index`
- // and heap_values of that heap location X holds a store, keep that store.
- // It's needed for a dependent load that's not eliminated since any store
- // that may put value into the load's heap location needs to be kept.
- void KeepStoresIfAliasedToLocation(ScopedArenaVector<HInstruction*>& heap_values,
- size_t loc_index) {
- for (size_t i = 0; i < heap_values.size(); i++) {
- if ((i == loc_index) || heap_location_collector_.MayAlias(i, loc_index)) {
- KeepIfIsStore(heap_values[i]);
- }
- }
- }
-
void HandleLoopSideEffects(HBasicBlock* block) {
DCHECK(block->IsLoopHeader());
int block_id = block->GetBlockId();
ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block_id];
- HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
- ScopedArenaVector<HInstruction*>& pre_header_heap_values =
- heap_values_for_[pre_header->GetBlockId()];
- // Don't eliminate loads in irreducible loops.
- // Also keep the stores before the loop.
+ // Don't eliminate loads in irreducible loops. This is safe for singletons, because
+ // they are always used by the non-eliminated loop-phi.
if (block->GetLoopInformation()->IsIrreducible()) {
if (kIsDebugBuild) {
for (size_t i = 0; i < heap_values.size(); i++) {
DCHECK_EQ(heap_values[i], kUnknownHeapValue);
}
}
- for (size_t i = 0; i < heap_values.size(); i++) {
- KeepIfIsStore(pre_header_heap_values[i]);
- }
return;
}
+ HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
+ ScopedArenaVector<HInstruction*>& pre_header_heap_values =
+ heap_values_for_[pre_header->GetBlockId()];
+
// Inherit the values from pre-header.
for (size_t i = 0; i < heap_values.size(); i++) {
heap_values[i] = pre_header_heap_values[i];
@@ -367,17 +270,18 @@ class LSEVisitor : public HGraphDelegateVisitor {
for (size_t i = 0; i < heap_values.size(); i++) {
HeapLocation* location = heap_location_collector_.GetHeapLocation(i);
ReferenceInfo* ref_info = location->GetReferenceInfo();
- if (ref_info->IsSingleton() && !location->IsValueKilledByLoopSideEffects()) {
- // A singleton's field that's not stored into inside a loop is
+ if (ref_info->IsSingletonAndRemovable() &&
+ !location->IsValueKilledByLoopSideEffects()) {
+ // A removable singleton's field that's not stored into inside a loop is
// invariant throughout the loop. Nothing to do.
} else {
- // heap value is killed by loop side effects.
+ // heap value is killed by loop side effects (stored into directly, or
+ // due to aliasing). Or the heap value may be needed after method return
+ // or deoptimization.
KeepIfIsStore(pre_header_heap_values[i]);
heap_values[i] = kUnknownHeapValue;
}
}
- } else {
- // The loop doesn't kill any value.
}
}
@@ -396,73 +300,45 @@ class LSEVisitor : public HGraphDelegateVisitor {
ScopedArenaVector<HInstruction*>& heap_values = heap_values_for_[block->GetBlockId()];
for (size_t i = 0; i < heap_values.size(); i++) {
HInstruction* merged_value = nullptr;
- // If we can merge the store itself from the predecessors, we keep
- // the store as the heap value as long as possible. In case we cannot
- // merge the store, we try to merge the values of the stores.
- HInstruction* merged_store_value = nullptr;
// Whether merged_value is a result that's merged from all predecessors.
bool from_all_predecessors = true;
ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
- HInstruction* ref = ref_info->GetReference();
HInstruction* singleton_ref = nullptr;
if (ref_info->IsSingleton()) {
- // We do more analysis based on singleton's liveness when merging
- // heap values for such cases.
- singleton_ref = ref;
+ // We do more analysis of liveness when merging heap values for such
+ // cases since stores into such references may potentially be eliminated.
+ singleton_ref = ref_info->GetReference();
}
for (HBasicBlock* predecessor : predecessors) {
HInstruction* pred_value = heap_values_for_[predecessor->GetBlockId()][i];
- if (!IsStore(pred_value)) {
- pred_value = FindSubstitute(pred_value);
- }
- DCHECK(pred_value != nullptr);
- HInstruction* pred_store_value = GetRealHeapValue(pred_value);
if ((singleton_ref != nullptr) &&
!singleton_ref->GetBlock()->Dominates(predecessor)) {
- // singleton_ref is not live in this predecessor. No need to merge
- // since singleton_ref is not live at the beginning of this block.
+ // singleton_ref is not live in this predecessor. Skip this predecessor since
+ // it does not really have the location.
DCHECK_EQ(pred_value, kUnknownHeapValue);
from_all_predecessors = false;
- break;
+ continue;
}
if (merged_value == nullptr) {
// First seen heap value.
- DCHECK(pred_value != nullptr);
merged_value = pred_value;
} else if (pred_value != merged_value) {
// There are conflicting values.
merged_value = kUnknownHeapValue;
- // We may still be able to merge store values.
- }
-
- // Conflicting stores may be storing the same value. We do another merge
- // of real stored values.
- if (merged_store_value == nullptr) {
- // First seen store value.
- DCHECK(pred_store_value != nullptr);
- merged_store_value = pred_store_value;
- } else if (pred_store_value != merged_store_value) {
- // There are conflicting store values.
- merged_store_value = kUnknownHeapValue;
- // There must be conflicting stores also.
- DCHECK_EQ(merged_value, kUnknownHeapValue);
- // No need to merge anymore.
break;
}
}
- if (merged_value == nullptr) {
- DCHECK(!from_all_predecessors);
- DCHECK(singleton_ref != nullptr);
- }
- if (from_all_predecessors) {
- if (ref_info->IsSingletonAndRemovable() &&
- block->IsSingleReturnOrReturnVoidAllowingPhis()) {
- // Values in the singleton are not needed anymore.
- } else if (!IsStore(merged_value)) {
- // We don't track merged value as a store anymore. We have to
- // hold the stores in predecessors live here.
+ if (ref_info->IsSingleton()) {
+ if (ref_info->IsSingletonAndNonRemovable() ||
+ (merged_value == kUnknownHeapValue &&
+ !block->IsSingleReturnOrReturnVoidAllowingPhis())) {
+ // The heap value may be needed after method return or deoptimization,
+ // or there are conflicting heap values from different predecessors and
+ // this block is not a single return,
+ // keep the last store in each predecessor since future loads may not
+ // be eliminated.
for (HBasicBlock* predecessor : predecessors) {
ScopedArenaVector<HInstruction*>& pred_values =
heap_values_for_[predecessor->GetBlockId()];
@@ -470,33 +346,18 @@ class LSEVisitor : public HGraphDelegateVisitor {
}
}
} else {
- DCHECK(singleton_ref != nullptr);
- // singleton_ref is non-existing at the beginning of the block. There is
- // no need to keep the stores.
+ // Currenctly we don't eliminate stores to non-singletons.
}
- if (!from_all_predecessors) {
+ if ((merged_value == nullptr) || !from_all_predecessors) {
DCHECK(singleton_ref != nullptr);
DCHECK((singleton_ref->GetBlock() == block) ||
- !singleton_ref->GetBlock()->Dominates(block))
- << "method: " << GetGraph()->GetMethodName();
+ !singleton_ref->GetBlock()->Dominates(block));
// singleton_ref is not defined before block or defined only in some of its
// predecessors, so block doesn't really have the location at its entry.
heap_values[i] = kUnknownHeapValue;
- } else if (predecessors.size() == 1) {
- // Inherit heap value from the single predecessor.
- DCHECK_EQ(heap_values_for_[predecessors[0]->GetBlockId()][i], merged_value);
- heap_values[i] = merged_value;
} else {
- DCHECK(merged_value == kUnknownHeapValue ||
- merged_value == kDefaultHeapValue ||
- merged_value->GetBlock()->Dominates(block));
- if (merged_value != kUnknownHeapValue) {
- heap_values[i] = merged_value;
- } else {
- // Stores in different predecessors may be storing the same value.
- heap_values[i] = merged_store_value;
- }
+ heap_values[i] = merged_value;
}
}
}
@@ -562,12 +423,23 @@ class LSEVisitor : public HGraphDelegateVisitor {
heap_values[idx] = constant;
return;
}
- heap_value = GetRealHeapValue(heap_value);
+ if (heap_value != kUnknownHeapValue) {
+ if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ HInstruction* store = heap_value;
+ // This load must be from a singleton since it's from the same
+ // field/element that a "removed" store puts the value. That store
+ // must be to a singleton's field/element.
+ DCHECK(ref_info->IsSingleton());
+ // Get the real heap value of the store.
+ heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2);
+ // heap_value may already have a substitute.
+ heap_value = FindSubstitute(heap_value);
+ }
+ }
if (heap_value == kUnknownHeapValue) {
// Load isn't eliminated. Put the load as the value into the HeapLocation.
// This acts like GVN but with better aliasing analysis.
heap_values[idx] = instruction;
- KeepStoresIfAliasedToLocation(heap_values, idx);
} else {
if (DataType::Kind(heap_value->GetType()) != DataType::Kind(instruction->GetType())) {
// The only situation where the same heap location has different type is when
@@ -580,10 +452,6 @@ class LSEVisitor : public HGraphDelegateVisitor {
DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName();
DCHECK(instruction->IsArrayGet()) << instruction->DebugName();
}
- // Load isn't eliminated. Put the load as the value into the HeapLocation.
- // This acts like GVN but with better aliasing analysis.
- heap_values[idx] = instruction;
- KeepStoresIfAliasedToLocation(heap_values, idx);
return;
}
AddRemovedLoad(instruction, heap_value);
@@ -592,21 +460,12 @@ class LSEVisitor : public HGraphDelegateVisitor {
}
bool Equal(HInstruction* heap_value, HInstruction* value) {
- DCHECK(!IsStore(value)) << value->DebugName();
- if (heap_value == kUnknownHeapValue) {
- // Don't compare kUnknownHeapValue with other values.
- return false;
- }
if (heap_value == value) {
return true;
}
if (heap_value == kDefaultHeapValue && GetDefaultValue(value->GetType()) == value) {
return true;
}
- HInstruction* real_heap_value = GetRealHeapValue(heap_value);
- if (real_heap_value != heap_value) {
- return Equal(real_heap_value, value);
- }
return false;
}
@@ -617,7 +476,6 @@ class LSEVisitor : public HGraphDelegateVisitor {
size_t vector_length,
int16_t declaring_class_def_index,
HInstruction* value) {
- DCHECK(!IsStore(value)) << value->DebugName();
// value may already have a substitute.
value = FindSubstitute(value);
HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref);
@@ -628,47 +486,59 @@ class LSEVisitor : public HGraphDelegateVisitor {
ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
HInstruction* heap_value = heap_values[idx];
+ bool same_value = false;
bool possibly_redundant = false;
-
if (Equal(heap_value, value)) {
// Store into the heap location with the same value.
- // This store can be eliminated right away.
- instruction->GetBlock()->RemoveInstruction(instruction);
- return;
- } else {
+ same_value = true;
+ } else if (index != nullptr &&
+ heap_location_collector_.GetHeapLocation(idx)->HasAliasedLocations()) {
+ // For array element, don't eliminate stores if the location can be aliased
+ // (due to either ref or index aliasing).
+ } else if (ref_info->IsSingleton()) {
+ // Store into a field/element of a singleton. The value cannot be killed due to
+ // aliasing/invocation. It can be redundant since future loads can
+ // directly get the value set by this instruction. The value can still be killed due to
+ // merging or loop side effects. Stores whose values are killed due to merging/loop side
+ // effects later will be removed from possibly_removed_stores_ when that is detected.
+ // Stores whose values may be needed after method return or deoptimization
+ // are also removed from possibly_removed_stores_ when that is detected.
+ possibly_redundant = true;
HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
- if (loop_info == nullptr) {
- // Store is not in a loop. We try to precisely track the heap value by
- // the store.
- possibly_redundant = true;
- } else if (!loop_info->IsIrreducible()) {
- // instruction is a store in the loop so the loop must do write.
+ if (loop_info != nullptr) {
+ // instruction is a store in the loop so the loop must does write.
DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
- if (ref_info->IsSingleton() && !loop_info->IsDefinedOutOfTheLoop(original_ref)) {
- // original_ref is created inside the loop. Value stored to it isn't needed at
- // the loop header. This is true for outer loops also.
- possibly_redundant = true;
- } else {
+
+ if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
+ DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
// Keep the store since its value may be needed at the loop header.
+ possibly_redundant = false;
+ } else {
+ // The singleton is created inside the loop. Value stored to it isn't needed at
+ // the loop header. This is true for outer loops also.
}
- } else {
- // Keep the store inside irreducible loops.
}
}
- if (possibly_redundant) {
+ if (same_value || possibly_redundant) {
possibly_removed_stores_.push_back(instruction);
}
- // Put the store as the heap value. If the value is loaded or needed after
- // return/deoptimization later, this store isn't really redundant.
- heap_values[idx] = instruction;
-
+ if (!same_value) {
+ if (possibly_redundant) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet());
+ // Put the store as the heap value. If the value is loaded from heap
+ // by a load later, this store isn't really redundant.
+ heap_values[idx] = instruction;
+ } else {
+ heap_values[idx] = value;
+ }
+ }
// This store may kill values in other heap locations due to aliasing.
for (size_t i = 0; i < heap_values.size(); i++) {
if (i == idx) {
continue;
}
- if (Equal(heap_values[i], value)) {
+ if (heap_values[i] == value) {
// Same value should be kept even if aliasing happens.
continue;
}
@@ -677,9 +547,7 @@ class LSEVisitor : public HGraphDelegateVisitor {
continue;
}
if (heap_location_collector_.MayAlias(i, idx)) {
- // Kill heap locations that may alias and as a result if the heap value
- // is a store, the store needs to be kept.
- KeepIfIsStore(heap_values[i]);
+ // Kill heap locations that may alias.
heap_values[i] = kUnknownHeapValue;
}
}
@@ -765,35 +633,24 @@ class LSEVisitor : public HGraphDelegateVisitor {
const ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
for (HInstruction* heap_value : heap_values) {
+ // Filter out fake instructions before checking instruction kind below.
+ if (heap_value == kUnknownHeapValue || heap_value == kDefaultHeapValue) {
+ continue;
+ }
// A store is kept as the heap value for possibly removed stores.
- // That value stored is generally observeable after deoptimization, except
- // for singletons that don't escape after deoptimization.
- if (IsStore(heap_value)) {
- if (heap_value->IsStaticFieldSet()) {
- KeepIfIsStore(heap_value);
- continue;
- }
+ if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ // Check whether the reference for a store is used by an environment local of
+ // HDeoptimize.
HInstruction* reference = heap_value->InputAt(0);
- if (heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton()) {
- if (reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable()) {
- // Finalizable objects alway escape.
+ DCHECK(heap_location_collector_.FindReferenceInfoOf(reference)->IsSingleton());
+ for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
+ HEnvironment* user = use.GetUser();
+ if (user->GetHolder() == instruction) {
+ // The singleton for the store is visible at this deoptimization
+ // point. Need to keep the store so that the heap value is
+ // seen by the interpreter.
KeepIfIsStore(heap_value);
- continue;
}
- // Check whether the reference for a store is used by an environment local of
- // HDeoptimize. If not, the singleton is not observed after
- // deoptimizion.
- for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
- HEnvironment* user = use.GetUser();
- if (user->GetHolder() == instruction) {
- // The singleton for the store is visible at this deoptimization
- // point. Need to keep the store so that the heap value is
- // seen by the interpreter.
- KeepIfIsStore(heap_value);
- }
- }
- } else {
- KeepIfIsStore(heap_value);
}
}
}
@@ -901,7 +758,7 @@ class LSEVisitor : public HGraphDelegateVisitor {
return;
}
if (ref_info->IsSingletonAndRemovable()) {
- singleton_new_instances_.push_back(new_array);
+ singleton_new_arrays_.push_back(new_array);
}
ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[new_array->GetBlock()->GetBlockId()];
@@ -934,6 +791,7 @@ class LSEVisitor : public HGraphDelegateVisitor {
ScopedArenaVector<HInstruction*> possibly_removed_stores_;
ScopedArenaVector<HInstruction*> singleton_new_instances_;
+ ScopedArenaVector<HInstruction*> singleton_new_arrays_;
DISALLOW_COPY_AND_ASSIGN(LSEVisitor);
};
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 5587f87dae..91e475d737 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -3105,8 +3105,6 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) {
return os << "array_object_check";
case TypeCheckKind::kArrayCheck:
return os << "array_check";
- case TypeCheckKind::kBitstringCheck:
- return os << "bitstring_check";
default:
LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast<int>(rhs);
UNREACHABLE();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b0657d6f1c..43ca2cf874 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -32,11 +32,11 @@
#include "deoptimization_kind.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
+#include "dex/invoke_type.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
#include "intrinsics_enum.h"
-#include "invoke_type.h"
#include "locations.h"
#include "method_reference.h"
#include "mirror/class.h"
@@ -5951,7 +5951,8 @@ class HLoadClass FINAL : public HInstruction {
special_input_(HUserRecord<HInstruction*>(current_method)),
type_index_(type_index),
dex_file_(dex_file),
- klass_(klass) {
+ klass_(klass),
+ loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
// Referrers class should not need access check. We never inline unverified
// methods so we can't possibly end up in this situation.
DCHECK(!is_referrers_class || !needs_access_check);
@@ -5961,7 +5962,6 @@ class HLoadClass FINAL : public HInstruction {
SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
SetPackedFlag<kFlagIsInBootImage>(false);
SetPackedFlag<kFlagGenerateClInitCheck>(false);
- SetPackedFlag<kFlagValidLoadedClassRTI>(false);
}
bool IsClonable() const OVERRIDE { return true; }
@@ -6010,18 +6010,13 @@ class HLoadClass FINAL : public HInstruction {
}
ReferenceTypeInfo GetLoadedClassRTI() {
- if (GetPackedFlag<kFlagValidLoadedClassRTI>()) {
- // Note: The is_exact flag from the return value should not be used.
- return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true);
- } else {
- return ReferenceTypeInfo::CreateInvalid();
- }
+ return loaded_class_rti_;
}
- // Loaded class RTI is marked as valid by RTP if the klass_ is admissible.
- void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(klass_ != nullptr);
- SetPackedFlag<kFlagValidLoadedClassRTI>(true);
+ void SetLoadedClassRTI(ReferenceTypeInfo rti) {
+ // Make sure we only set exact types (the loaded class should never be merged).
+ DCHECK(rti.IsExact());
+ loaded_class_rti_ = rti;
}
dex::TypeIndex GetTypeIndex() const { return type_index_; }
@@ -6074,8 +6069,7 @@ class HLoadClass FINAL : public HInstruction {
static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1;
static constexpr size_t kFieldLoadKindSize =
MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
- static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize;
- static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1;
+ static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize;
static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");
using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
@@ -6103,6 +6097,8 @@ class HLoadClass FINAL : public HInstruction {
const DexFile& dex_file_;
Handle<mirror::Class> klass_;
+
+ ReferenceTypeInfo loaded_class_rti_;
};
std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs);
@@ -6630,143 +6626,49 @@ enum class TypeCheckKind {
kInterfaceCheck, // No optimization yet when checking against an interface.
kArrayObjectCheck, // Can just check if the array is not primitive.
kArrayCheck, // No optimization yet when checking against a generic array.
- kBitstringCheck, // Compare the type check bitstring.
kLast = kArrayCheck
};
std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs);
-// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an
-// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.)
-class HTypeCheckInstruction : public HVariableInputSizeInstruction {
+class HInstanceOf FINAL : public HExpression<2> {
public:
- HTypeCheckInstruction(HInstruction* object,
- HInstruction* target_class_or_null,
- TypeCheckKind check_kind,
- Handle<mirror::Class> klass,
- uint32_t dex_pc,
- ArenaAllocator* allocator,
- HIntConstant* bitstring_path_to_root,
- HIntConstant* bitstring_mask,
- SideEffects side_effects)
- : HVariableInputSizeInstruction(
- side_effects,
- dex_pc,
- allocator,
- /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u,
- kArenaAllocTypeCheckInputs),
- klass_(klass) {
+ HInstanceOf(HInstruction* object,
+ HLoadClass* target_class,
+ TypeCheckKind check_kind,
+ uint32_t dex_pc)
+ : HExpression(DataType::Type::kBool,
+ SideEffectsForArchRuntimeCalls(check_kind),
+ dex_pc) {
SetPackedField<TypeCheckKindField>(check_kind);
SetPackedFlag<kFlagMustDoNullCheck>(true);
- SetPackedFlag<kFlagValidTargetClassRTI>(false);
SetRawInputAt(0, object);
- SetRawInputAt(1, target_class_or_null);
- DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr);
- DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr);
- if (check_kind == TypeCheckKind::kBitstringCheck) {
- DCHECK(target_class_or_null->IsNullConstant());
- SetRawInputAt(2, bitstring_path_to_root);
- SetRawInputAt(3, bitstring_mask);
- } else {
- DCHECK(target_class_or_null->IsLoadClass());
- }
+ SetRawInputAt(1, target_class);
}
HLoadClass* GetTargetClass() const {
- DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
HInstruction* load_class = InputAt(1);
DCHECK(load_class->IsLoadClass());
return load_class->AsLoadClass();
}
- uint32_t GetBitstringPathToRoot() const {
- DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
- HInstruction* path_to_root = InputAt(2);
- DCHECK(path_to_root->IsIntConstant());
- return static_cast<uint32_t>(path_to_root->AsIntConstant()->GetValue());
- }
-
- uint32_t GetBitstringMask() const {
- DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
- HInstruction* mask = InputAt(3);
- DCHECK(mask->IsIntConstant());
- return static_cast<uint32_t>(mask->AsIntConstant()->GetValue());
- }
-
bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
- DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName();
- return GetPackedFields() == down_cast<const HTypeCheckInstruction*>(other)->GetPackedFields();
+ bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
+ return true;
+ }
+
+ bool NeedsEnvironment() const OVERRIDE {
+ return CanCallRuntime(GetTypeCheckKind());
}
+ // Used only in code generation.
bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
- ReferenceTypeInfo GetTargetClassRTI() {
- if (GetPackedFlag<kFlagValidTargetClassRTI>()) {
- // Note: The is_exact flag from the return value should not be used.
- return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true);
- } else {
- return ReferenceTypeInfo::CreateInvalid();
- }
- }
-
- // Target class RTI is marked as valid by RTP if the klass_ is admissible.
- void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(klass_ != nullptr);
- SetPackedFlag<kFlagValidTargetClassRTI>(true);
- }
-
- Handle<mirror::Class> GetClass() const {
- return klass_;
- }
-
- protected:
- DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction);
-
- private:
- static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
- static constexpr size_t kFieldTypeCheckKindSize =
- MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
- static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
- static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1;
- static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1;
- static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
- using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
-
- Handle<mirror::Class> klass_;
-};
-
-class HInstanceOf FINAL : public HTypeCheckInstruction {
- public:
- HInstanceOf(HInstruction* object,
- HInstruction* target_class_or_null,
- TypeCheckKind check_kind,
- Handle<mirror::Class> klass,
- uint32_t dex_pc,
- ArenaAllocator* allocator,
- HIntConstant* bitstring_path_to_root,
- HIntConstant* bitstring_mask)
- : HTypeCheckInstruction(object,
- target_class_or_null,
- check_kind,
- klass,
- dex_pc,
- allocator,
- bitstring_path_to_root,
- bitstring_mask,
- SideEffectsForArchRuntimeCalls(check_kind)) {}
-
- DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; }
-
- bool NeedsEnvironment() const OVERRIDE {
- return CanCallRuntime(GetTypeCheckKind());
- }
-
static bool CanCallRuntime(TypeCheckKind check_kind) {
// Mips currently does runtime calls for any other checks.
return check_kind != TypeCheckKind::kExactCheck;
@@ -6780,6 +6682,15 @@ class HInstanceOf FINAL : public HTypeCheckInstruction {
protected:
DEFAULT_COPY_CONSTRUCTOR(InstanceOf);
+
+ private:
+ static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldTypeCheckKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+ static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+ static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1;
+ static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
};
class HBoundType FINAL : public HExpression<1> {
@@ -6829,25 +6740,31 @@ class HBoundType FINAL : public HExpression<1> {
ReferenceTypeInfo upper_bound_;
};
-class HCheckCast FINAL : public HTypeCheckInstruction {
+class HCheckCast FINAL : public HTemplateInstruction<2> {
public:
HCheckCast(HInstruction* object,
- HInstruction* target_class_or_null,
+ HLoadClass* target_class,
TypeCheckKind check_kind,
- Handle<mirror::Class> klass,
- uint32_t dex_pc,
- ArenaAllocator* allocator,
- HIntConstant* bitstring_path_to_root,
- HIntConstant* bitstring_mask)
- : HTypeCheckInstruction(object,
- target_class_or_null,
- check_kind,
- klass,
- dex_pc,
- allocator,
- bitstring_path_to_root,
- bitstring_mask,
- SideEffects::CanTriggerGC()) {}
+ uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+ SetPackedField<TypeCheckKindField>(check_kind);
+ SetPackedFlag<kFlagMustDoNullCheck>(true);
+ SetRawInputAt(0, object);
+ SetRawInputAt(1, target_class);
+ }
+
+ HLoadClass* GetTargetClass() const {
+ HInstruction* load_class = InputAt(1);
+ DCHECK(load_class->IsLoadClass());
+ return load_class->AsLoadClass();
+ }
+
+ bool IsClonable() const OVERRIDE { return true; }
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
+ return true;
+ }
bool NeedsEnvironment() const OVERRIDE {
// Instruction may throw a CheckCastError.
@@ -6856,10 +6773,24 @@ class HCheckCast FINAL : public HTypeCheckInstruction {
bool CanThrow() const OVERRIDE { return true; }
+ bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
+ void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
+ TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
+ bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
+
DECLARE_INSTRUCTION(CheckCast);
protected:
DEFAULT_COPY_CONSTRUCTOR(CheckCast);
+
+ private:
+ static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeCheckKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+ static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+ static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1;
+ static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
};
/**
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c35c490118..47ef194574 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -382,7 +382,8 @@ class OptimizingCompiler FINAL : public Compiler {
PassObserver* pass_observer,
VariableSizedHandleScope* handles) const;
- void GenerateJitDebugInfo(debug::MethodDebugInfo method_debug_info);
+ void GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo method_debug_info)
+ REQUIRES_SHARED(Locks::mutator_lock_);
std::unique_ptr<OptimizingCompilerStats> compilation_stats_;
@@ -1248,7 +1249,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
info.code_info = nullptr;
info.cfi = jni_compiled_method.GetCfi();
- GenerateJitDebugInfo(info);
+ GenerateJitDebugInfo(method, info);
}
Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed());
@@ -1372,7 +1373,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
info.code_info = stack_map_size == 0 ? nullptr : stack_map_data;
info.cfi = ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data());
- GenerateJitDebugInfo(info);
+ GenerateJitDebugInfo(method, info);
}
Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed());
@@ -1396,7 +1397,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
return true;
}
-void OptimizingCompiler::GenerateJitDebugInfo(debug::MethodDebugInfo info) {
+void OptimizingCompiler::GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo info) {
const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
DCHECK(compiler_options.GenerateAnyDebugInfo());
@@ -1412,6 +1413,11 @@ void OptimizingCompiler::GenerateJitDebugInfo(debug::MethodDebugInfo info) {
MutexLock mu(Thread::Current(), g_jit_debug_mutex);
JITCodeEntry* entry = CreateJITCodeEntry(elf_file);
IncrementJITCodeEntryRefcount(entry, info.code_address);
+
+ VLOG(jit)
+ << "JIT mini-debug-info added for " << ArtMethod::PrettyMethod(method)
+ << " size=" << PrettySize(elf_file.size())
+ << " total_size=" << PrettySize(GetJITCodeEntryMemUsage());
}
} // namespace art
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index a6a2f46d2e..0023265e50 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -99,7 +99,6 @@ enum class MethodCompilationStat {
kConstructorFenceRemovedLSE,
kConstructorFenceRemovedPFRA,
kConstructorFenceRemovedCFRE,
- kBitstringTypeCheck,
kJitOutOfMemoryForCommit,
kLastStat
};
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 59733397bf..f843c008d8 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -34,20 +34,6 @@ void PrepareForRegisterAllocation::Run() {
}
}
-void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) {
- // Record only those bitstring type checks that make it to the codegen stage.
- if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
- MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
- }
-}
-
-void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) {
- // Record only those bitstring type checks that make it to the codegen stage.
- if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
- MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
- }
-}
-
void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) {
check->ReplaceWith(check->InputAt(0));
}
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index f6e4d3ef99..2c64f016c1 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -40,8 +40,6 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor {
"prepare_for_register_allocation";
private:
- void VisitCheckCast(HCheckCast* check_cast) OVERRIDE;
- void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE;
void VisitNullCheck(HNullCheck* check) OVERRIDE;
void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;
void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 178d7fd0f0..8bb124e066 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -87,7 +87,6 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE;
void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
- void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE;
void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
void VisitLoadString(HLoadString* instr) OVERRIDE;
void VisitLoadException(HLoadException* instr) OVERRIDE;
@@ -172,12 +171,6 @@ void ReferenceTypePropagation::ValidateTypes() {
<< "NullCheck " << instr->GetReferenceTypeInfo()
<< "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo();
}
- } else if (instr->IsInstanceOf()) {
- HInstanceOf* iof = instr->AsInstanceOf();
- DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact());
- } else if (instr->IsCheckCast()) {
- HCheckCast* check = instr->AsCheckCast();
- DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact());
}
}
}
@@ -506,7 +499,8 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock*
return;
}
- ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI();
+ HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
+ ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
if (!class_rti.IsValid()) {
// He have loaded an unresolved class. Don't bother bounding the type.
return;
@@ -650,20 +644,15 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet(
void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) {
ScopedObjectAccess soa(Thread::Current());
- if (IsAdmissible(instr->GetClass().Get())) {
- instr->SetValidLoadedClassRTI();
+ Handle<mirror::Class> resolved_class = instr->GetClass();
+ if (IsAdmissible(resolved_class.Get())) {
+ instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
+ resolved_class, /* is_exact */ true));
}
instr->SetReferenceTypeInfo(
ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true));
}
-void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) {
- ScopedObjectAccess soa(Thread::Current());
- if (IsAdmissible(instr->GetClass().Get())) {
- instr->SetValidTargetClassRTI();
- }
-}
-
void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) {
instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
}
@@ -731,6 +720,8 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) {
}
void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
+ HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+ ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
HBoundType* bound_type = check_cast->GetNext()->AsBoundType();
if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) {
// The next instruction is not an uninitialized BoundType. This must be
@@ -739,14 +730,12 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast
}
DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0));
- ScopedObjectAccess soa(Thread::Current());
- Handle<mirror::Class> klass = check_cast->GetClass();
- if (IsAdmissible(klass.Get())) {
+ if (class_rti.IsValid()) {
DCHECK(is_first_run_);
- check_cast->SetValidTargetClassRTI();
+ ScopedObjectAccess soa(Thread::Current());
// This is the first run of RTP and class is resolved.
- bool is_exact = klass->CannotBeAssignedFromOtherTypes();
- bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact),
+ 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.
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index dffef17587..1e49411c72 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -236,75 +236,6 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(
return load_kind;
}
-static inline bool CanUseTypeCheckBitstring(ObjPtr<mirror::Class> klass,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(!klass->IsProxyClass());
- DCHECK(!klass->IsArrayClass());
-
- if (Runtime::Current()->UseJitCompilation()) {
- // If we're JITting, try to assign a type check bitstring (fall through).
- } else if (codegen->GetCompilerOptions().IsBootImage()) {
- const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex());
- if (!compiler_driver->IsImageClass(descriptor)) {
- return false;
- }
- // If the target is a boot image class, try to assign a type check bitstring (fall through).
- // (If --force-determinism, this was already done; repeating is OK and yields the same result.)
- } else {
- // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring
- // already assigned in the boot image.
- return false;
- }
-
- // Try to assign a type check bitstring.
- MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
- if ((false) && // FIXME: Inliner does not respect compiler_driver->IsClassToCompile()
- // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569
- kIsDebugBuild &&
- codegen->GetCompilerOptions().IsBootImage() &&
- codegen->GetCompilerOptions().IsForceDeterminism()) {
- SubtypeCheckInfo::State old_state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
- CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed)
- << klass->PrettyDescriptor() << "/" << old_state
- << " in " << codegen->GetGraph()->PrettyMethod();
- }
- SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass);
- return state == SubtypeCheckInfo::kAssigned;
-}
-
-TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr<mirror::Class> klass,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- bool needs_access_check) {
- if (klass == nullptr) {
- return TypeCheckKind::kUnresolvedCheck;
- } else if (klass->IsInterface()) {
- return TypeCheckKind::kInterfaceCheck;
- } else if (klass->IsArrayClass()) {
- if (klass->GetComponentType()->IsObjectClass()) {
- return TypeCheckKind::kArrayObjectCheck;
- } else if (klass->CannotBeAssignedFromOtherTypes()) {
- return TypeCheckKind::kExactCheck;
- } else {
- return TypeCheckKind::kArrayCheck;
- }
- } else if (klass->IsFinal()) { // TODO: Consider using bitstring for final classes.
- return TypeCheckKind::kExactCheck;
- } else if (kUseBitstringTypeCheck &&
- !needs_access_check &&
- CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) {
- // TODO: We should not need the `!needs_access_check` check but getting rid of that
- // requires rewriting some optimizations in instruction simplifier.
- return TypeCheckKind::kBitstringCheck;
- } else if (klass->IsAbstract()) {
- return TypeCheckKind::kAbstractClassCheck;
- } else {
- return TypeCheckKind::kClassHierarchyCheck;
- }
-}
-
void HSharpening::ProcessLoadString(
HLoadString* load_string,
CodeGenerator* codegen,
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index fa3e948eeb..6df7d6d91e 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -44,10 +44,12 @@ class HSharpening : public HOptimization {
static constexpr const char* kSharpeningPassName = "sharpening";
- // Used by Sharpening and InstructionSimplifier.
- static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver);
+ // Used by the builder.
+ static void ProcessLoadString(HLoadString* load_string,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ const DexCompilationUnit& dex_compilation_unit,
+ VariableSizedHandleScope* handles);
// Used by the builder and the inliner.
static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class,
@@ -56,19 +58,10 @@ class HSharpening : public HOptimization {
const DexCompilationUnit& dex_compilation_unit)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Used by the builder.
- static TypeCheckKind ComputeTypeCheckKind(ObjPtr<mirror::Class> klass,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- bool needs_access_check)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Used by the builder.
- static void ProcessLoadString(HLoadString* load_string,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- const DexCompilationUnit& dex_compilation_unit,
- VariableSizedHandleScope* handles);
+ // Used by Sharpening and InstructionSimplifier.
+ static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver);
private:
CodeGenerator* codegen_;
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 9fcede5e97..8640e2db0e 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -2100,6 +2100,14 @@ void X86Assembler::addl(const Address& address, const Immediate& imm) {
}
+void X86Assembler::addw(const Address& address, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ CHECK(imm.is_uint16() || imm.is_int16()) << imm.value();
+ EmitUint8(0x66);
+ EmitComplex(0, address, imm, /* is_16_op */ true);
+}
+
+
void X86Assembler::adcl(Register reg, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitComplex(2, Operand(reg), imm);
@@ -2751,14 +2759,20 @@ void X86Assembler::EmitOperand(int reg_or_opcode, const Operand& operand) {
}
-void X86Assembler::EmitImmediate(const Immediate& imm) {
- EmitInt32(imm.value());
+void X86Assembler::EmitImmediate(const Immediate& imm, bool is_16_op) {
+ if (is_16_op) {
+ EmitUint8(imm.value() & 0xFF);
+ EmitUint8(imm.value() >> 8);
+ } else {
+ EmitInt32(imm.value());
+ }
}
void X86Assembler::EmitComplex(int reg_or_opcode,
const Operand& operand,
- const Immediate& immediate) {
+ const Immediate& immediate,
+ bool is_16_op) {
CHECK_GE(reg_or_opcode, 0);
CHECK_LT(reg_or_opcode, 8);
if (immediate.is_int8()) {
@@ -2769,11 +2783,11 @@ void X86Assembler::EmitComplex(int reg_or_opcode,
} else if (operand.IsRegister(EAX)) {
// Use short form if the destination is eax.
EmitUint8(0x05 + (reg_or_opcode << 3));
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
} else {
EmitUint8(0x81);
EmitOperand(reg_or_opcode, operand);
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
}
}
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index f3b516cb7e..a085677083 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -634,6 +634,7 @@ class X86Assembler FINAL : public Assembler {
void addl(const Address& address, Register reg);
void addl(const Address& address, const Immediate& imm);
+ void addw(const Address& address, const Immediate& imm);
void adcl(Register dst, Register src);
void adcl(Register reg, const Immediate& imm);
@@ -817,8 +818,9 @@ class X86Assembler FINAL : public Assembler {
inline void EmitOperandSizeOverride();
void EmitOperand(int rm, const Operand& operand);
- void EmitImmediate(const Immediate& imm);
- void EmitComplex(int rm, const Operand& operand, const Immediate& immediate);
+ void EmitImmediate(const Immediate& imm, bool is_16_op = false);
+ void EmitComplex(
+ int rm, const Operand& operand, const Immediate& immediate, bool is_16_op = false);
void EmitLabel(Label* label, int instruction_size);
void EmitLabelLink(Label* label);
void EmitLabelLink(NearLabel* label);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 36c5c3c0c4..937dd80c4e 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -258,6 +258,10 @@ TEST_F(AssemblerX86Test, MovlLoad) {
DriverStr(RepeatRA(&x86::X86Assembler::movl, "movl {mem}, %{reg}"), "movl-load");
}
+TEST_F(AssemblerX86Test, Addw) {
+ DriverStr(RepeatAI(&x86::X86Assembler::addw, /*imm_bytes*/ 2U, "addw ${imm}, {mem}"), "addw");
+}
+
TEST_F(AssemblerX86Test, MovlStore) {
DriverStr(RepeatAR(&x86::X86Assembler::movl, "movl %{reg}, {mem}"), "movl-store");
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 51f61ca756..feabf260af 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -2608,6 +2608,15 @@ void X86_64Assembler::addl(const Address& address, const Immediate& imm) {
}
+void X86_64Assembler::addw(const Address& address, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ CHECK(imm.is_uint16() || imm.is_int16()) << imm.value();
+ EmitUint8(0x66);
+ EmitOptionalRex32(address);
+ EmitComplex(0, address, imm, /* is_16_op */ true);
+}
+
+
void X86_64Assembler::subl(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(dst, src);
@@ -3387,8 +3396,11 @@ void X86_64Assembler::EmitOperand(uint8_t reg_or_opcode, const Operand& operand)
}
-void X86_64Assembler::EmitImmediate(const Immediate& imm) {
- if (imm.is_int32()) {
+void X86_64Assembler::EmitImmediate(const Immediate& imm, bool is_16_op) {
+ if (is_16_op) {
+ EmitUint8(imm.value() & 0xFF);
+ EmitUint8(imm.value() >> 8);
+ } else if (imm.is_int32()) {
EmitInt32(static_cast<int32_t>(imm.value()));
} else {
EmitInt64(imm.value());
@@ -3398,7 +3410,8 @@ void X86_64Assembler::EmitImmediate(const Immediate& imm) {
void X86_64Assembler::EmitComplex(uint8_t reg_or_opcode,
const Operand& operand,
- const Immediate& immediate) {
+ const Immediate& immediate,
+ bool is_16_op) {
CHECK_GE(reg_or_opcode, 0);
CHECK_LT(reg_or_opcode, 8);
if (immediate.is_int8()) {
@@ -3409,11 +3422,11 @@ void X86_64Assembler::EmitComplex(uint8_t reg_or_opcode,
} else if (operand.IsRegister(CpuRegister(RAX))) {
// Use short form if the destination is eax.
EmitUint8(0x05 + (reg_or_opcode << 3));
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
} else {
EmitUint8(0x81);
EmitOperand(reg_or_opcode, operand);
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
}
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 0d24a751c0..7a5fdb502f 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -693,6 +693,7 @@ class X86_64Assembler FINAL : public Assembler {
void addl(CpuRegister reg, const Address& address);
void addl(const Address& address, CpuRegister reg);
void addl(const Address& address, const Immediate& imm);
+ void addw(const Address& address, const Immediate& imm);
void addq(CpuRegister reg, const Immediate& imm);
void addq(CpuRegister dst, CpuRegister src);
@@ -904,8 +905,9 @@ class X86_64Assembler FINAL : public Assembler {
void EmitOperandSizeOverride();
void EmitOperand(uint8_t rm, const Operand& operand);
- void EmitImmediate(const Immediate& imm);
- void EmitComplex(uint8_t rm, const Operand& operand, const Immediate& immediate);
+ void EmitImmediate(const Immediate& imm, bool is_16_op = false);
+ void EmitComplex(
+ uint8_t rm, const Operand& operand, const Immediate& immediate, bool is_16_op = false);
void EmitLabel(Label* label, int instruction_size);
void EmitLabelLink(Label* label);
void EmitLabelLink(NearLabel* label);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 0cb3ffd39f..5e6c83396a 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -578,6 +578,11 @@ TEST_F(AssemblerX86_64Test, AddlImm) {
"add ${imm}, %{reg}"), "addli");
}
+TEST_F(AssemblerX86_64Test, Addw) {
+ DriverStr(
+ RepeatAI(&x86_64::X86_64Assembler::addw, /*imm_bytes*/2U, "addw ${imm}, {mem}"), "addw");
+}
+
TEST_F(AssemblerX86_64Test, ImulqReg1) {
DriverStr(RepeatR(&x86_64::X86_64Assembler::imulq, "imulq %{reg}"), "imulq");
}