summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/builder.cc29
-rw-r--r--compiler/optimizing/builder.h1
-rw-r--r--compiler/optimizing/code_generator.cc1
-rw-r--r--compiler/optimizing/code_generator.h17
-rw-r--r--compiler/optimizing/code_generator_arm.cc276
-rw-r--r--compiler/optimizing/code_generator_arm.h63
-rw-r--r--compiler/optimizing/code_generator_arm64.cc374
-rw-r--r--compiler/optimizing/code_generator_arm64.h69
-rw-r--r--compiler/optimizing/code_generator_mips.cc23
-rw-r--r--compiler/optimizing/code_generator_mips.h5
-rw-r--r--compiler/optimizing/code_generator_mips64.cc23
-rw-r--r--compiler/optimizing/code_generator_mips64.h5
-rw-r--r--compiler/optimizing/code_generator_x86.cc215
-rw-r--r--compiler/optimizing/code_generator_x86.h27
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc205
-rw-r--r--compiler/optimizing/code_generator_x86_64.h28
-rw-r--r--compiler/optimizing/constant_folding.cc18
-rw-r--r--compiler/optimizing/constant_folding_test.cc51
-rw-r--r--compiler/optimizing/dead_code_elimination.cc4
-rw-r--r--compiler/optimizing/dead_code_elimination_test.cc36
-rw-r--r--compiler/optimizing/dex_cache_array_fixups_arm.cc15
-rw-r--r--compiler/optimizing/graph_checker.cc65
-rw-r--r--compiler/optimizing/graph_checker.h8
-rw-r--r--compiler/optimizing/graph_visualizer.cc60
-rw-r--r--compiler/optimizing/induction_var_analysis.cc3
-rw-r--r--compiler/optimizing/induction_var_analysis_test.cc41
-rw-r--r--compiler/optimizing/inliner.cc4
-rw-r--r--compiler/optimizing/instruction_simplifier.cc52
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.h16
-rw-r--r--compiler/optimizing/nodes.cc219
-rw-r--r--compiler/optimizing/nodes.h401
-rw-r--r--compiler/optimizing/optimizing_compiler.cc23
-rw-r--r--compiler/optimizing/pc_relative_fixups_x86.cc9
-rw-r--r--compiler/optimizing/pretty_printer_test.cc22
-rw-r--r--compiler/optimizing/reference_type_propagation.cc8
-rw-r--r--compiler/optimizing/register_allocator.cc2
-rw-r--r--compiler/optimizing/sharpening.cc111
-rw-r--r--compiler/optimizing/sharpening.h1
-rw-r--r--compiler/optimizing/ssa_builder.cc9
39 files changed, 1886 insertions, 653 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 082d15961a..b6b8322f03 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -106,7 +106,6 @@ void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
DCHECK(branch_target != nullptr);
DCHECK(fallthrough_target != nullptr);
- PotentiallyAddSuspendCheck(branch_target, dex_pc);
HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
T* comparison = new (arena_) T(first, second, dex_pc);
@@ -125,7 +124,6 @@ void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
DCHECK(branch_target != nullptr);
DCHECK(fallthrough_target != nullptr);
- PotentiallyAddSuspendCheck(branch_target, dex_pc);
HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
current_block_->AddInstruction(comparison);
@@ -1788,7 +1786,6 @@ void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t
int32_t target_offset, uint32_t dex_pc) {
HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
DCHECK(case_target != nullptr);
- PotentiallyAddSuspendCheck(case_target, dex_pc);
// The current case's value.
HInstruction* this_case_value = graph_->GetIntConstant(case_value_int, dex_pc);
@@ -1824,23 +1821,6 @@ void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t
}
}
-void HGraphBuilder::PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc) {
- int32_t target_offset = target->GetDexPc() - dex_pc;
- if (target_offset <= 0) {
- // DX generates back edges to the first encountered return. We can save
- // time of later passes by not adding redundant suspend checks.
- HInstruction* last_in_target = target->GetLastInstruction();
- if (last_in_target != nullptr &&
- (last_in_target->IsReturn() || last_in_target->IsReturnVoid())) {
- return;
- }
-
- // Add a suspend check to backward branches which may potentially loop. We
- // can remove them after we recognize loops in the graph.
- current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_pc));
- }
-}
-
bool HGraphBuilder::CanDecodeQuickenedInfo() const {
return interpreter_metadata_ != nullptr;
}
@@ -1972,7 +1952,6 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
int32_t offset = instruction.GetTargetOffset();
HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
DCHECK(target != nullptr);
- PotentiallyAddSuspendCheck(target, dex_pc);
current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
current_block_->AddSuccessor(target);
current_block_ = nullptr;
@@ -2766,20 +2745,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::CONST_STRING: {
uint32_t string_index = instruction.VRegB_21c();
- bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
- *dex_file_, string_index);
current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
case Instruction::CONST_STRING_JUMBO: {
uint32_t string_index = instruction.VRegB_31c();
- bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
- *dex_file_, string_index);
current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index e3dd0e8216..48f5316222 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -133,7 +133,6 @@ class HGraphBuilder : public ValueObject {
HLocal* GetLocalAt(uint32_t register_index) const;
void UpdateLocal(uint32_t register_index, HInstruction* instruction, uint32_t dex_pc) const;
HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type, uint32_t dex_pc) const;
- void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
void InitializeParameters(uint16_t number_of_parameters);
// Returns whether the current method needs access check for the type.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 32869ec0b4..f1139218be 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -54,7 +54,6 @@
#include "parallel_move_resolver.h"
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
-#include "verifier/dex_gc_map.h"
#include "vmap_table.h"
namespace art {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index e56323ff0f..cad55296bc 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -443,6 +443,11 @@ class CodeGenerator {
uint32_t dex_pc,
SlowPathCode* slow_path) = 0;
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ virtual HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) = 0;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -471,6 +476,18 @@ class CodeGenerator {
LabelType label;
};
+ // String patch info used for recording locations of required linker patches and
+ // target strings. The actual string address can be absolute or PC-relative.
+ template <typename LabelType>
+ struct StringPatchInfo {
+ StringPatchInfo(const DexFile& df, uint32_t index)
+ : dex_file(df), string_index(index), label() { }
+
+ const DexFile& dex_file;
+ uint32_t string_index;
+ LabelType label;
+ };
+
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
size_t number_of_fpu_registers,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 4c9c25fbd5..98577d67ea 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -779,13 +779,19 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph,
move_resolver_(graph->GetArena(), this),
assembler_(),
isa_features_(isa_features),
+ uint32_literals_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
method_patches_(MethodReferenceComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
call_patches_(MethodReferenceComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- dex_cache_arrays_base_labels_(std::less<HArmDexCacheArraysBase*>(),
- graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_address_patches_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Always save the LR register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(LR));
}
@@ -1412,13 +1418,13 @@ void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instructio
// Nothing to do. The code always falls through.
return;
} else if (cond->IsIntConstant()) {
- // Constant condition, statically compared against 1.
- if (cond->AsIntConstant()->IsOne()) {
+ // Constant condition, statically compared against "true" (integer value 1).
+ if (cond->AsIntConstant()->IsTrue()) {
if (true_target != nullptr) {
__ b(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsZero());
+ DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
if (false_target != nullptr) {
__ b(false_target);
}
@@ -3221,7 +3227,7 @@ void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) {
if (rhs.IsConstant()) {
uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
// Map all rotations to +ve. equivalents on the interval [0,63].
- rot &= kMaxLongShiftValue;
+ rot &= kMaxLongShiftDistance;
// For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
// logic below to a simple pair of binary orr.
// (e.g. 34 bits == in_reg swap + 2 bits right.)
@@ -3374,7 +3380,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
if (second.IsRegister()) {
Register second_reg = second.AsRegister<Register>();
// ARM doesn't mask the shift count so we need to do it ourselves.
- __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue));
+ __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftDistance));
if (op->IsShl()) {
__ Lsl(out_reg, first_reg, out_reg);
} else if (op->IsShr()) {
@@ -3384,7 +3390,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
}
} else {
int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
- uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue);
+ uint32_t shift_value = cst & kMaxIntShiftDistance;
if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
__ Mov(out_reg, first_reg);
} else if (op->IsShl()) {
@@ -3410,7 +3416,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
Register second_reg = second.AsRegister<Register>();
if (op->IsShl()) {
- __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue));
+ __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftDistance));
// Shift the high part
__ Lsl(o_h, high, o_l);
// Shift the low part and `or` what overflew on the high part
@@ -3424,7 +3430,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
// Shift the low part
__ Lsl(o_l, low, o_l);
} else if (op->IsShr()) {
- __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue));
+ __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
// Shift the low part
__ Lsr(o_l, low, o_h);
// Shift the high part and `or` what underflew on the low part
@@ -3438,7 +3444,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
// Shift the high part
__ Asr(o_h, high, o_h);
} else {
- __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue));
+ __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance));
// same as Shr except we use `Lsr`s and not `Asr`s
__ Lsr(o_l, low, o_h);
__ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord));
@@ -3454,7 +3460,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) {
DCHECK_NE(o_l, high);
DCHECK_NE(o_h, low);
int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
- uint32_t shift_value = static_cast<uint32_t>(cst & kMaxLongShiftValue);
+ uint32_t shift_value = cst & kMaxLongShiftDistance;
if (shift_value > 32) {
if (op->IsShl()) {
__ Lsl(o_h, low, shift_value - 32);
@@ -5221,12 +5227,57 @@ void InstructionCodeGeneratorARM::GenerateClassInitializationCheck(
__ Bind(slow_path->GetExitLabel());
}
+HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit());
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops()) {
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ }
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod ||
+ load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
@@ -5234,16 +5285,73 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
- Register current_method = locations->InAt(0).AsRegister<Register>();
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ CodeGeneratorARM::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
+ __ BindTrackedLabel(&labels->movw_label);
+ __ movw(out, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->movt_label);
+ __ movt(out, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->add_pc_label);
+ __ add(out, out, ShifterOperand(PC));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives
+ // a 128B range. To try and reduce the number of literals if we load multiple strings,
+ // simply split the dex cache address to a 128B aligned base loaded from a literal
+ // and the remaining offset embedded in the load.
+ static_assert(sizeof(GcRoot<mirror::String>) == 4u, "Expected GC root to be 4 bytes.");
+ DCHECK_ALIGNED(load->GetAddress(), 4u);
+ constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2;
+ uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits);
+ uint32_t offset = address & MaxInt<uint32_t>(offset_bits);
+ __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ GenerateGcRootFieldLoad(load, out_loc, out, offset);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ Register base_reg = locations->InAt(0).AsRegister<Register>();
+ HArmDexCacheArraysBase* base = load->InputAt(0)->AsArmDexCacheArraysBase();
+ int32_t offset = load->GetDexCacheElementOffset() - base->GetElementOffset();
+ GenerateGcRootFieldLoad(load, out_loc, base_reg, offset);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
@@ -6220,6 +6328,8 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOr
HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
+ // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+ // with irreducible loops.
if (GetGraph()->HasIrreducibleLoops() &&
(dispatch_info.method_load_kind ==
HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
@@ -6399,13 +6509,49 @@ void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp
__ blx(LR);
}
+CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatch(
+ const DexFile& dex_file, uint32_t string_index) {
+ return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
+}
+
+CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch(
+ const DexFile& dex_file, uint32_t element_offset) {
+ return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
+}
+
+CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativePatch(
+ const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
+ patches->emplace_back(dex_file, offset_or_index);
+ return &patches->back();
+}
+
+Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ uint32_t string_index) {
+ return boot_image_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) {
+ bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+ Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+ return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) {
+ return DeduplicateUint32Literal(address, &uint32_literals_);
+}
+
void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
call_patches_.size() +
relative_call_patches_.size() +
- /* MOVW+MOVT for each base */ 2u * dex_cache_arrays_base_labels_.size();
+ /* MOVW+MOVT for each base */ 2u * pc_relative_dex_cache_patches_.size() +
+ boot_image_string_patches_.size() +
+ /* MOVW+MOVT for each base */ 2u * pc_relative_string_patches_.size() +
+ boot_image_address_patches_.size();
linker_patches->reserve(size);
for (const auto& entry : method_patches_) {
const MethodReference& target_method = entry.first;
@@ -6431,41 +6577,75 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche
info.target_method.dex_file,
info.target_method.dex_method_index));
}
- for (const auto& pair : dex_cache_arrays_base_labels_) {
- HArmDexCacheArraysBase* base = pair.first;
- const DexCacheArraysBaseLabels* labels = &pair.second;
- const DexFile& dex_file = base->GetDexFile();
- size_t base_element_offset = base->GetElementOffset();
- DCHECK(labels->add_pc_label.IsBound());
- uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(labels->add_pc_label.Position());
+ for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) {
+ const DexFile& dex_file = info.target_dex_file;
+ size_t base_element_offset = info.offset_or_index;
+ DCHECK(info.add_pc_label.IsBound());
+ uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position());
// Add MOVW patch.
- DCHECK(labels->movw_label.IsBound());
- uint32_t movw_offset = dchecked_integral_cast<uint32_t>(labels->movw_label.Position());
+ DCHECK(info.movw_label.IsBound());
+ uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position());
linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movw_offset,
&dex_file,
add_pc_offset,
base_element_offset));
// Add MOVT patch.
- DCHECK(labels->movt_label.IsBound());
- uint32_t movt_offset = dchecked_integral_cast<uint32_t>(labels->movt_label.Position());
+ DCHECK(info.movt_label.IsBound());
+ uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position());
linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movt_offset,
&dex_file,
add_pc_offset,
base_element_offset));
}
+ for (const auto& entry : boot_image_string_patches_) {
+ const StringReference& target_string = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = literal->GetLabel()->Position();
+ linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+ target_string.dex_file,
+ target_string.string_index));
+ }
+ for (const PcRelativePatchInfo& info : pc_relative_string_patches_) {
+ const DexFile& dex_file = info.target_dex_file;
+ uint32_t string_index = info.offset_or_index;
+ DCHECK(info.add_pc_label.IsBound());
+ uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position());
+ // Add MOVW patch.
+ DCHECK(info.movw_label.IsBound());
+ uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position());
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(movw_offset,
+ &dex_file,
+ add_pc_offset,
+ string_index));
+ // Add MOVT patch.
+ DCHECK(info.movt_label.IsBound());
+ uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position());
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(movt_offset,
+ &dex_file,
+ add_pc_offset,
+ string_index));
+ }
+ for (const auto& entry : boot_image_address_patches_) {
+ DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = literal->GetLabel()->Position();
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+}
+
+Literal* CodeGeneratorARM::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
+ return map->GetOrCreate(
+ value,
+ [this, value]() { return __ NewLiteral<uint32_t>(value); });
}
Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method,
MethodToLiteralMap* map) {
- // Look up the literal for target_method.
- auto lb = map->lower_bound(target_method);
- if (lb != map->end() && !map->key_comp()(target_method, lb->first)) {
- return lb->second;
- }
- // We don't have a literal for this method yet, insert a new one.
- Literal* literal = __ NewLiteral<uint32_t>(0u);
- map->PutBefore(lb, target_method, literal);
- return literal;
+ return map->GetOrCreate(
+ target_method,
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
}
Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) {
@@ -6600,16 +6780,16 @@ void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr)
void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
locations->SetOut(Location::RequiresRegister());
- codegen_->AddDexCacheArraysBase(base);
}
void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
Register base_reg = base->GetLocations()->Out().AsRegister<Register>();
- CodeGeneratorARM::DexCacheArraysBaseLabels* labels = codegen_->GetDexCacheArraysBaseLabels(base);
+ CodeGeneratorARM::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
__ BindTrackedLabel(&labels->movw_label);
- __ movw(base_reg, 0u);
+ __ movw(base_reg, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->movt_label);
- __ movt(base_reg, 0u);
+ __ movt(base_reg, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->add_pc_label);
__ add(base_reg, base_reg, ShifterOperand(PC));
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index cc4aa144c0..84341284a3 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -23,6 +23,7 @@
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm/assembler_thumb2.h"
+#include "utils/string_reference.h"
namespace art {
namespace arm {
@@ -403,6 +404,11 @@ class CodeGeneratorARM : public CodeGenerator {
Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -414,32 +420,34 @@ class CodeGeneratorARM : public CodeGenerator {
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
- void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
-
- // The PC-relative base address is loaded with three instructions, MOVW+MOVT
+ // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
+ // and boot image strings. The only difference is the interpretation of the offset_or_index.
+ // The PC-relative address is loaded with three instructions, MOVW+MOVT
// to load the offset to base_reg and then ADD base_reg, PC. The offset is
// calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we
// currently emit these 3 instructions together, instruction scheduling could
// split this sequence apart, so we keep separate labels for each of them.
- struct DexCacheArraysBaseLabels {
- DexCacheArraysBaseLabels() = default;
- DexCacheArraysBaseLabels(DexCacheArraysBaseLabels&& other) = default;
-
+ struct PcRelativePatchInfo {
+ PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
+ : target_dex_file(dex_file), offset_or_index(off_or_idx) { }
+ PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
+
+ const DexFile& target_dex_file;
+ // Either the dex cache array element offset or the string index.
+ uint32_t offset_or_index;
Label movw_label;
Label movt_label;
Label add_pc_label;
};
- void AddDexCacheArraysBase(HArmDexCacheArraysBase* base) {
- DexCacheArraysBaseLabels labels;
- dex_cache_arrays_base_labels_.Put(base, std::move(labels));
- }
+ PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+ PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset);
+ Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index);
+ Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
+ Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
- DexCacheArraysBaseLabels* GetDexCacheArraysBaseLabels(HArmDexCacheArraysBase* base) {
- auto it = dex_cache_arrays_base_labels_.find(base);
- DCHECK(it != dex_cache_arrays_base_labels_.end());
- return &it->second;
- }
+ void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
@@ -525,14 +533,19 @@ class CodeGeneratorARM : public CodeGenerator {
Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
- using DexCacheArraysBaseToLabelsMap = ArenaSafeMap<HArmDexCacheArraysBase*,
- DexCacheArraysBaseLabels,
- std::less<HArmDexCacheArraysBase*>>;
+ using BootStringToLiteralMap = ArenaSafeMap<StringReference,
+ Literal*,
+ StringReferenceValueComparator>;
+ Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
Literal* DeduplicateMethodAddressLiteral(MethodReference target_method);
Literal* DeduplicateMethodCodeLiteral(MethodReference target_method);
+ PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
+ uint32_t offset_or_index,
+ ArenaDeque<PcRelativePatchInfo>* patches);
// Labels for each block that will be compiled.
Label* block_labels_; // Indexed by block id.
@@ -543,14 +556,22 @@ class CodeGeneratorARM : public CodeGenerator {
Thumb2Assembler assembler_;
const ArmInstructionSetFeatures& isa_features_;
+ // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+ Uint32ToLiteralMap uint32_literals_;
// Method patch info, map MethodReference to a literal for method address and method code.
MethodToLiteralMap method_patches_;
MethodToLiteralMap call_patches_;
// Relative call patch info.
// Using ArenaDeque<> which retains element addresses on push/emplace_back().
ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
-
- DexCacheArraysBaseToLabelsMap dex_cache_arrays_base_labels_;
+ // PC-relative patch info for each HArmDexCacheArraysBase.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+ // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+ BootStringToLiteralMap boot_image_string_patches_;
+ // PC-relative String patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for patchable boot image addresses.
+ Uint32ToLiteralMap boot_image_address_patches_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM);
};
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 088dbb3693..491014d3e5 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -905,6 +905,8 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
instruction_visitor_(graph, this),
move_resolver_(graph->GetArena(), this),
isa_features_(isa_features),
+ uint32_literals_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
uint64_literals_(std::less<uint64_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
method_patches_(MethodReferenceComparator(),
@@ -912,7 +914,12 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
call_patches_(MethodReferenceComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_address_patches_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Save the link register (containing the return address) to mimic Quick.
AddAllocatedRegister(LocationFrom(lr));
}
@@ -1818,9 +1825,8 @@ void InstructionCodeGeneratorARM64::HandleShift(HBinaryOperation* instr) {
Register lhs = InputRegisterAt(instr, 0);
Operand rhs = InputOperandAt(instr, 1);
if (rhs.IsImmediate()) {
- uint32_t shift_value = (type == Primitive::kPrimInt)
- ? static_cast<uint32_t>(rhs.immediate() & kMaxIntShiftValue)
- : static_cast<uint32_t>(rhs.immediate() & kMaxLongShiftValue);
+ uint32_t shift_value = rhs.immediate() &
+ (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
if (instr->IsShl()) {
__ Lsl(dst, lhs, shift_value);
} else if (instr->IsShr()) {
@@ -1921,9 +1927,8 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp(
// conversion) can have a different type from the current instruction's type,
// so we manually indicate the type.
Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type);
- int64_t shift_amount = (type == Primitive::kPrimInt)
- ? static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxIntShiftValue)
- : static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxLongShiftValue);
+ int64_t shift_amount = instruction->GetShiftAmount() &
+ (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
Operand right_operand(0);
@@ -1949,7 +1954,7 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp(
__ And(out, left, right_operand);
break;
case HInstruction::kNeg:
- DCHECK(instruction->InputAt(0)->AsConstant()->IsZero());
+ DCHECK(instruction->InputAt(0)->AsConstant()->IsArithmeticZero());
__ Neg(out, right_operand);
break;
case HInstruction::kOr:
@@ -1994,7 +1999,7 @@ void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr)
HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
if (instr->GetOpKind() == HInstruction::kSub &&
accumulator->IsConstant() &&
- accumulator->AsConstant()->IsZero()) {
+ accumulator->AsConstant()->IsArithmeticZero()) {
// Don't allocate register for Mneg instruction.
} else {
locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
@@ -2033,7 +2038,7 @@ void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate*
} else {
DCHECK(instr->GetOpKind() == HInstruction::kSub);
HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
- if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) {
+ if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsArithmeticZero()) {
__ Mneg(res, mul_left, mul_right);
} else {
Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
@@ -2378,9 +2383,35 @@ void InstructionCodeGeneratorARM64::VisitClinitCheck(HClinitCheck* check) {
GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
}
-static bool IsFloatingPointZeroConstant(HInstruction* instruction) {
- return (instruction->IsFloatConstant() && (instruction->AsFloatConstant()->GetValue() == 0.0f))
- || (instruction->IsDoubleConstant() && (instruction->AsDoubleConstant()->GetValue() == 0.0));
+static bool IsFloatingPointZeroConstant(HInstruction* inst) {
+ return (inst->IsFloatConstant() && (inst->AsFloatConstant()->IsArithmeticZero()))
+ || (inst->IsDoubleConstant() && (inst->AsDoubleConstant()->IsArithmeticZero()));
+}
+
+void InstructionCodeGeneratorARM64::GenerateFcmp(HInstruction* instruction) {
+ FPRegister lhs_reg = InputFPRegisterAt(instruction, 0);
+ Location rhs_loc = instruction->GetLocations()->InAt(1);
+ if (rhs_loc.IsConstant()) {
+ // 0.0 is the only immediate that can be encoded directly in
+ // an FCMP instruction.
+ //
+ // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
+ // specify that in a floating-point comparison, positive zero
+ // and negative zero are considered equal, so we can use the
+ // literal 0.0 for both cases here.
+ //
+ // Note however that some methods (Float.equal, Float.compare,
+ // Float.compareTo, Double.equal, Double.compare,
+ // Double.compareTo, Math.max, Math.min, StrictMath.max,
+ // StrictMath.min) consider 0.0 to be (strictly) greater than
+ // -0.0. So if we ever translate calls to these methods into a
+ // HCompare instruction, we must handle the -0.0 case with
+ // care here.
+ DCHECK(IsFloatingPointZeroConstant(rhs_loc.GetConstant()));
+ __ Fcmp(lhs_reg, 0.0);
+ } else {
+ __ Fcmp(lhs_reg, InputFPRegisterAt(instruction, 1));
+ }
}
void LocationsBuilderARM64::VisitCompare(HCompare* compare) {
@@ -2438,14 +2469,7 @@ void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) {
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
Register result = OutputRegister(compare);
- FPRegister left = InputFPRegisterAt(compare, 0);
- if (compare->GetLocations()->InAt(1).IsConstant()) {
- DCHECK(IsFloatingPointZeroConstant(compare->GetLocations()->InAt(1).GetConstant()));
- // 0.0 is the only immediate that can be encoded directly in an FCMP instruction.
- __ Fcmp(left, 0.0);
- } else {
- __ Fcmp(left, InputFPRegisterAt(compare, 1));
- }
+ GenerateFcmp(compare);
__ Cset(result, ne);
__ Cneg(result, result, ARM64FPCondition(kCondLT, compare->IsGtBias()));
break;
@@ -2485,14 +2509,7 @@ void InstructionCodeGeneratorARM64::HandleCondition(HCondition* instruction) {
IfCondition if_cond = instruction->GetCondition();
if (Primitive::IsFloatingPointType(instruction->InputAt(0)->GetType())) {
- FPRegister lhs = InputFPRegisterAt(instruction, 0);
- if (locations->InAt(1).IsConstant()) {
- DCHECK(IsFloatingPointZeroConstant(locations->InAt(1).GetConstant()));
- // 0.0 is the only immediate that can be encoded directly in an FCMP instruction.
- __ Fcmp(lhs, 0.0);
- } else {
- __ Fcmp(lhs, InputFPRegisterAt(instruction, 1));
- }
+ GenerateFcmp(instruction);
__ Cset(res, ARM64FPCondition(if_cond, instruction->IsGtBias()));
} else {
// Integer cases.
@@ -2823,13 +2840,13 @@ void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruct
// Nothing to do. The code always falls through.
return;
} else if (cond->IsIntConstant()) {
- // Constant condition, statically compared against 1.
- if (cond->AsIntConstant()->IsOne()) {
+ // Constant condition, statically compared against "true" (integer value 1).
+ if (cond->AsIntConstant()->IsTrue()) {
if (true_target != nullptr) {
__ B(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsZero());
+ DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
if (false_target != nullptr) {
__ B(false_target);
}
@@ -2861,14 +2878,7 @@ void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruct
Primitive::Type type = condition->InputAt(0)->GetType();
if (Primitive::IsFloatingPointType(type)) {
- FPRegister lhs = InputFPRegisterAt(condition, 0);
- if (condition->GetLocations()->InAt(1).IsConstant()) {
- DCHECK(IsFloatingPointZeroConstant(condition->GetLocations()->InAt(1).GetConstant()));
- // 0.0 is the only immediate that can be encoded directly in an FCMP instruction.
- __ Fcmp(lhs, 0.0);
- } else {
- __ Fcmp(lhs, InputFPRegisterAt(condition, 1));
- }
+ GenerateFcmp(condition);
if (true_target == nullptr) {
IfCondition opposite_condition = condition->GetOppositeCondition();
__ B(ARM64FPCondition(opposite_condition, condition->IsGtBias()), false_target);
@@ -3052,13 +3062,7 @@ void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) {
csel_cond = HasSwappedInputs(variant) ? eq : ne;
}
} else if (IsConditionOnFloatingPointValues(cond)) {
- Location rhs = cond->GetLocations()->InAt(1);
- if (rhs.IsConstant()) {
- DCHECK(IsFloatingPointZeroConstant(rhs.GetConstant()));
- __ Fcmp(InputFPRegisterAt(cond, 0), 0.0);
- } else {
- __ Fcmp(InputFPRegisterAt(cond, 0), InputFPRegisterAt(cond, 1));
- }
+ GenerateFcmp(cond);
csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
} else {
__ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
@@ -3665,23 +3669,21 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
// Add ADRP with its PC-relative DexCache access patch.
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetDexCacheArrayOffset());
- vixl::Label* pc_insn_label = &pc_relative_dex_cache_patches_.back().label;
+ const DexFile& dex_file = *invoke->GetTargetMethod().dex_file;
+ uint32_t element_offset = invoke->GetDexCacheArrayOffset();
+ vixl::Label* adrp_label = NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
{
vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
- __ Bind(pc_insn_label);
- __ adrp(XRegisterFrom(temp), 0);
+ __ Bind(adrp_label);
+ __ adrp(XRegisterFrom(temp), /* offset placeholder */ 0);
}
- pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label;
// Add LDR with its PC-relative DexCache access patch.
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetDexCacheArrayOffset());
+ vixl::Label* ldr_label =
+ NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
{
vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
- __ Bind(&pc_relative_dex_cache_patches_.back().label);
- __ ldr(XRegisterFrom(temp), MemOperand(XRegisterFrom(temp), 0));
- pc_relative_dex_cache_patches_.back().pc_insn_label = pc_insn_label;
+ __ Bind(ldr_label);
+ __ ldr(XRegisterFrom(temp), MemOperand(XRegisterFrom(temp), /* offset placeholder */ 0));
}
break;
}
@@ -3775,13 +3777,58 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te
__ Blr(lr);
}
+vixl::Label* CodeGeneratorARM64::NewPcRelativeStringPatch(const DexFile& dex_file,
+ uint32_t string_index,
+ vixl::Label* adrp_label) {
+ return NewPcRelativePatch(dex_file, string_index, adrp_label, &pc_relative_string_patches_);
+}
+
+vixl::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset,
+ vixl::Label* adrp_label) {
+ return NewPcRelativePatch(dex_file, element_offset, adrp_label, &pc_relative_dex_cache_patches_);
+}
+
+vixl::Label* CodeGeneratorARM64::NewPcRelativePatch(const DexFile& dex_file,
+ uint32_t offset_or_index,
+ vixl::Label* adrp_label,
+ ArenaDeque<PcRelativePatchInfo>* patches) {
+ // Add a patch entry and return the label.
+ patches->emplace_back(dex_file, offset_or_index);
+ PcRelativePatchInfo* info = &patches->back();
+ vixl::Label* label = &info->label;
+ // If adrp_label is null, this is the ADRP patch and needs to point to its own label.
+ info->pc_insn_label = (adrp_label != nullptr) ? adrp_label : label;
+ return label;
+}
+
+vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageStringLiteral(
+ const DexFile& dex_file, uint32_t string_index) {
+ return boot_image_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
+}
+
+vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageAddressLiteral(uint64_t address) {
+ bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+ Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+ return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
+vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateDexCacheAddressLiteral(uint64_t address) {
+ return DeduplicateUint64Literal(address);
+}
+
void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
call_patches_.size() +
relative_call_patches_.size() +
- pc_relative_dex_cache_patches_.size();
+ pc_relative_dex_cache_patches_.size() +
+ boot_image_string_patches_.size() +
+ pc_relative_string_patches_.size() +
+ boot_image_address_patches_.size();
linker_patches->reserve(size);
for (const auto& entry : method_patches_) {
const MethodReference& target_method = entry.first;
@@ -3802,38 +3849,51 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc
info.target_method.dex_file,
info.target_method.dex_method_index));
}
- for (const PcRelativeDexCacheAccessInfo& info : pc_relative_dex_cache_patches_) {
+ for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) {
linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.location(),
&info.target_dex_file,
info.pc_insn_label->location(),
- info.element_offset));
+ info.offset_or_index));
+ }
+ for (const auto& entry : boot_image_string_patches_) {
+ const StringReference& target_string = entry.first;
+ vixl::Literal<uint32_t>* literal = entry.second;
+ linker_patches->push_back(LinkerPatch::StringPatch(literal->offset(),
+ target_string.dex_file,
+ target_string.string_index));
+ }
+ for (const PcRelativePatchInfo& info : pc_relative_string_patches_) {
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(info.label.location(),
+ &info.target_dex_file,
+ info.pc_insn_label->location(),
+ info.offset_or_index));
}
+ for (const auto& entry : boot_image_address_patches_) {
+ DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+ vixl::Literal<uint32_t>* literal = entry.second;
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal->offset()));
+ }
+}
+
+vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value,
+ Uint32ToLiteralMap* map) {
+ return map->GetOrCreate(
+ value,
+ [this, value]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(value); });
}
vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateUint64Literal(uint64_t value) {
- // Look up the literal for value.
- auto lb = uint64_literals_.lower_bound(value);
- if (lb != uint64_literals_.end() && !uint64_literals_.key_comp()(value, lb->first)) {
- return lb->second;
- }
- // We don't have a literal for this value, insert a new one.
- vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(value);
- uint64_literals_.PutBefore(lb, value, literal);
- return literal;
+ return uint64_literals_.GetOrCreate(
+ value,
+ [this, value]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(value); });
}
vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodLiteral(
MethodReference target_method,
MethodToLiteralMap* map) {
- // Look up the literal for target_method.
- auto lb = map->lower_bound(target_method);
- if (lb != map->end() && !map->key_comp()(target_method, lb->first)) {
- return lb->second;
- }
- // We don't have a literal for this method yet, insert a new one.
- vixl::Literal<uint64_t>* literal = __ CreateLiteralDestroyedWithPool<uint64_t>(0u);
- map->PutBefore(lb, target_method, literal);
- return literal;
+ return map->GetOrCreate(
+ target_method,
+ [this]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(/* placeholder */ 0u); });
}
vixl::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodAddressLiteral(
@@ -3958,28 +4018,135 @@ void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UN
// Nothing to do, this is driven by the code generator.
}
+HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
Location out_loc = load->GetLocations()->Out();
Register out = OutputRegister(load);
- Register current_method = InputRegisterAt(load, 0);
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ return; // No dex cache slow path.
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ // Add ADRP with its PC-relative String patch.
+ const DexFile& dex_file = load->GetDexFile();
+ uint32_t string_index = load->GetStringIndex();
+ vixl::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(adrp_label);
+ __ adrp(out.X(), /* offset placeholder */ 0);
+ }
+ // Add ADD with its PC-relative String patch.
+ vixl::Label* add_label =
+ codegen_->NewPcRelativeStringPatch(dex_file, string_index, adrp_label);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(add_label);
+ __ add(out.X(), out.X(), Operand(/* offset placeholder */ 0));
+ }
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK(load->GetAddress() != 0u && IsUint<32>(load->GetAddress()));
+ __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(load->GetAddress()));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ // LDR immediate has a 12-bit offset multiplied by the size and for 32-bit loads
+ // that gives a 16KiB range. To try and reduce the number of literals if we load
+ // multiple strings, simply split the dex cache address to a 16KiB aligned base
+ // loaded from a literal and the remaining offset embedded in the load.
+ static_assert(sizeof(GcRoot<mirror::String>) == 4u, "Expected GC root to be 4 bytes.");
+ DCHECK_ALIGNED(load->GetAddress(), 4u);
+ constexpr size_t offset_bits = /* encoded bits */ 12 + /* scale */ 2;
+ uint64_t base_address = load->GetAddress() & ~MaxInt<uint64_t>(offset_bits);
+ uint32_t offset = load->GetAddress() & MaxInt<uint64_t>(offset_bits);
+ __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ GenerateGcRootFieldLoad(load, out_loc, out.X(), offset);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ // Add ADRP with its PC-relative DexCache access patch.
+ const DexFile& dex_file = load->GetDexFile();
+ uint32_t element_offset = load->GetDexCacheElementOffset();
+ vixl::Label* adrp_label = codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(adrp_label);
+ __ adrp(out.X(), /* offset placeholder */ 0);
+ }
+ // Add LDR with its PC-relative DexCache access patch.
+ vixl::Label* ldr_label =
+ codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
+ GenerateGcRootFieldLoad(load, out_loc, out.X(), /* offset placeholder */ 0, ldr_label);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ Register current_method = InputRegisterAt(load, 0);
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
@@ -4794,7 +4961,8 @@ void InstructionCodeGeneratorARM64::GenerateReferenceLoadTwoRegisters(HInstructi
void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
vixl::Register obj,
- uint32_t offset) {
+ uint32_t offset,
+ vixl::Label* fixup_label) {
Register root_reg = RegisterFrom(root, Primitive::kPrimNot);
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
@@ -4807,7 +4975,13 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad(HInstruction* instru
// }
// /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ Ldr(root_reg, MemOperand(obj, offset));
+ if (fixup_label == nullptr) {
+ __ Ldr(root_reg, MemOperand(obj, offset));
+ } else {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(fixup_label);
+ __ ldr(root_reg, MemOperand(obj, offset));
+ }
static_assert(
sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
"art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
@@ -4832,14 +5006,26 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad(HInstruction* instru
// GC root loaded through a slow path for read barriers other
// than Baker's.
// /* GcRoot<mirror::Object>* */ root = obj + offset
- __ Add(root_reg.X(), obj.X(), offset);
+ if (fixup_label == nullptr) {
+ __ Add(root_reg.X(), obj.X(), offset);
+ } else {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(fixup_label);
+ __ add(root_reg.X(), obj.X(), offset);
+ }
// /* mirror::Object* */ root = root->Read()
codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
}
} else {
// Plain GC root load with no read barrier.
// /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ Ldr(root_reg, MemOperand(obj, offset));
+ if (fixup_label == nullptr) {
+ __ Ldr(root_reg, MemOperand(obj, offset));
+ } else {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(fixup_label);
+ __ ldr(root_reg, MemOperand(obj, offset));
+ }
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index a1f686e39d..8ec753159a 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
+#include "arch/arm64/quick_method_frame_info_arm64.h"
#include "code_generator.h"
#include "common_arm64.h"
#include "dex/compiler_enums.h"
@@ -24,9 +25,9 @@
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm64/assembler_arm64.h"
+#include "utils/string_reference.h"
#include "vixl/a64/disasm-a64.h"
#include "vixl/a64/macro-assembler-a64.h"
-#include "arch/arm64/quick_method_frame_info_arm64.h"
namespace art {
namespace arm64 {
@@ -255,7 +256,11 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
vixl::Register obj,
- uint32_t offset);
+ uint32_t offset,
+ vixl::Label* fixup_label = nullptr);
+
+ // Generate a floating-point comparison.
+ void GenerateFcmp(HInstruction* instruction);
void HandleShift(HBinaryOperation* instr);
void GenerateTestAndBranch(HInstruction* instruction,
@@ -450,6 +455,11 @@ class CodeGeneratorARM64 : public CodeGenerator {
return false;
}
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -464,6 +474,27 @@ class CodeGeneratorARM64 : public CodeGenerator {
UNIMPLEMENTED(FATAL);
}
+ // Add a new PC-relative string patch for an instruction and return the label
+ // to be bound before the instruction. The instruction will be either the
+ // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
+ // to the associated ADRP patch label).
+ vixl::Label* NewPcRelativeStringPatch(const DexFile& dex_file,
+ uint32_t string_index,
+ vixl::Label* adrp_label = nullptr);
+
+ // Add a new PC-relative dex cache array patch for an instruction and return
+ // the label to be bound before the instruction. The instruction will be
+ // either the ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label`
+ // pointing to the associated ADRP patch label).
+ vixl::Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset,
+ vixl::Label* adrp_label = nullptr);
+
+ vixl::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ uint32_t string_index);
+ vixl::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address);
+ vixl::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address);
+
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
// Fast path implementation of ReadBarrier::Barrier for a heap
@@ -551,26 +582,39 @@ class CodeGeneratorARM64 : public CodeGenerator {
bool use_load_acquire);
using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, vixl::Literal<uint64_t>*>;
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::Literal<uint32_t>*>;
using MethodToLiteralMap = ArenaSafeMap<MethodReference,
vixl::Literal<uint64_t>*,
MethodReferenceComparator>;
+ using BootStringToLiteralMap = ArenaSafeMap<StringReference,
+ vixl::Literal<uint32_t>*,
+ StringReferenceValueComparator>;
+ vixl::Literal<uint32_t>* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
vixl::Literal<uint64_t>* DeduplicateUint64Literal(uint64_t value);
vixl::Literal<uint64_t>* DeduplicateMethodLiteral(MethodReference target_method,
MethodToLiteralMap* map);
vixl::Literal<uint64_t>* DeduplicateMethodAddressLiteral(MethodReference target_method);
vixl::Literal<uint64_t>* DeduplicateMethodCodeLiteral(MethodReference target_method);
- struct PcRelativeDexCacheAccessInfo {
- PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off)
- : target_dex_file(dex_file), element_offset(element_off), label(), pc_insn_label() { }
+ // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
+ // and boot image strings. The only difference is the interpretation of the offset_or_index.
+ struct PcRelativePatchInfo {
+ PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
+ : target_dex_file(dex_file), offset_or_index(off_or_idx), label(), pc_insn_label() { }
const DexFile& target_dex_file;
- uint32_t element_offset;
+ // Either the dex cache array element offset or the string index.
+ uint32_t offset_or_index;
vixl::Label label;
vixl::Label* pc_insn_label;
};
+ vixl::Label* NewPcRelativePatch(const DexFile& dex_file,
+ uint32_t offset_or_index,
+ vixl::Label* adrp_label,
+ ArenaDeque<PcRelativePatchInfo>* patches);
+
void EmitJumpTables();
// Labels for each block that will be compiled.
@@ -584,7 +628,10 @@ class CodeGeneratorARM64 : public CodeGenerator {
Arm64Assembler assembler_;
const Arm64InstructionSetFeatures& isa_features_;
- // Deduplication map for 64-bit literals, used for non-patchable method address and method code.
+ // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+ Uint32ToLiteralMap uint32_literals_;
+ // Deduplication map for 64-bit literals, used for non-patchable method address, method code
+ // or string dex cache address.
Uint64ToLiteralMap uint64_literals_;
// Method patch info, map MethodReference to a literal for method address and method code.
MethodToLiteralMap method_patches_;
@@ -593,7 +640,13 @@ class CodeGeneratorARM64 : public CodeGenerator {
// Using ArenaDeque<> which retains element addresses on push/emplace_back().
ArenaDeque<MethodPatchInfo<vixl::Label>> relative_call_patches_;
// PC-relative DexCache access info.
- ArenaDeque<PcRelativeDexCacheAccessInfo> pc_relative_dex_cache_patches_;
+ ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+ // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+ BootStringToLiteralMap boot_image_string_patches_;
+ // PC-relative String patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for patchable boot image addresses.
+ Uint32ToLiteralMap boot_image_address_patches_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64);
};
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 4c9206320f..8b19f84e1c 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1455,9 +1455,8 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) {
bool use_imm = rhs_location.IsConstant();
Register rhs_reg = use_imm ? ZERO : rhs_location.AsRegister<Register>();
int64_t rhs_imm = use_imm ? CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()) : 0;
- const uint32_t shift_mask = (type == Primitive::kPrimInt)
- ? kMaxIntShiftValue
- : kMaxLongShiftValue;
+ const uint32_t shift_mask =
+ (type == Primitive::kPrimInt) ? kMaxIntShiftDistance : kMaxLongShiftDistance;
const uint32_t shift_value = rhs_imm & shift_mask;
// Are the INS (Insert Bit Field) and ROTR instructions supported?
bool has_ins_rotr = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
@@ -3272,13 +3271,13 @@ void InstructionCodeGeneratorMIPS::GenerateTestAndBranch(HInstruction* instructi
// Nothing to do. The code always falls through.
return;
} else if (cond->IsIntConstant()) {
- // Constant condition, statically compared against 1.
- if (cond->AsIntConstant()->IsOne()) {
+ // Constant condition, statically compared against "true" (integer value 1).
+ if (cond->AsIntConstant()->IsTrue()) {
if (true_target != nullptr) {
__ B(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsZero());
+ DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
if (false_target != nullptr) {
__ B(false_target);
}
@@ -3817,6 +3816,12 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen
return false;
}
+HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
+ // TODO: Implement other kinds.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+}
+
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
MethodReference target_method ATTRIBUTE_UNUSED) {
@@ -4067,9 +4072,9 @@ void InstructionCodeGeneratorMIPS::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNU
}
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->IsInDexCache()
- ? LocationSummary::kNoCall
- : LocationSummary::kCallOnSlowPath;
+ LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index b720573897..afe7917cc4 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -345,6 +345,11 @@ class CodeGeneratorMIPS : public CodeGenerator {
return type == Primitive::kPrimLong;
}
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 955c9a490e..2f9eca6ac3 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1208,9 +1208,8 @@ void InstructionCodeGeneratorMIPS64::HandleShift(HBinaryOperation* instr) {
}
if (use_imm) {
- uint32_t shift_value = (type == Primitive::kPrimInt)
- ? static_cast<uint32_t>(rhs_imm & kMaxIntShiftValue)
- : static_cast<uint32_t>(rhs_imm & kMaxLongShiftValue);
+ uint32_t shift_value = rhs_imm &
+ (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
if (shift_value == 0) {
if (dst != lhs) {
@@ -2596,13 +2595,13 @@ void InstructionCodeGeneratorMIPS64::GenerateTestAndBranch(HInstruction* instruc
// Nothing to do. The code always falls through.
return;
} else if (cond->IsIntConstant()) {
- // Constant condition, statically compared against 1.
- if (cond->AsIntConstant()->IsOne()) {
+ // Constant condition, statically compared against "true" (integer value 1).
+ if (cond->AsIntConstant()->IsTrue()) {
if (true_target != nullptr) {
__ Bc(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsZero());
+ DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
if (false_target != nullptr) {
__ Bc(false_target);
}
@@ -3031,6 +3030,12 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codeg
return false;
}
+HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
+ // TODO: Implement other kinds.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+}
+
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
MethodReference target_method ATTRIBUTE_UNUSED) {
@@ -3285,9 +3290,9 @@ void InstructionCodeGeneratorMIPS64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_U
}
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->IsInDexCache()
- ? LocationSummary::kNoCall
- : LocationSummary::kCallOnSlowPath;
+ LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 9464a140ad..94767cba4b 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -337,6 +337,11 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const { return false; }
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index fb3216c895..715b5be2c8 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -799,6 +799,8 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph,
method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Use a fake return address register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
@@ -1391,13 +1393,13 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio
// Nothing to do. The code always falls through.
return;
} else if (cond->IsIntConstant()) {
- // Constant condition, statically compared against 1.
- if (cond->AsIntConstant()->IsOne()) {
+ // Constant condition, statically compared against "true" (integer value 1).
+ if (cond->AsIntConstant()->IsTrue()) {
if (true_target != nullptr) {
__ jmp(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsZero());
+ DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
if (false_target != nullptr) {
__ jmp(false_target);
}
@@ -3775,7 +3777,7 @@ void InstructionCodeGeneratorX86::HandleShift(HBinaryOperation* op) {
__ shrl(first_reg, second_reg);
}
} else {
- int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue;
+ int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance;
if (shift == 0) {
return;
}
@@ -3803,7 +3805,7 @@ void InstructionCodeGeneratorX86::HandleShift(HBinaryOperation* op) {
}
} else {
// Shift by a constant.
- int shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue;
+ int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance;
// Nothing to do if the shift is 0, as the input is already the output.
if (shift != 0) {
if (op->IsShl()) {
@@ -3960,7 +3962,7 @@ void InstructionCodeGeneratorX86::VisitRor(HRor* ror) {
Register second_reg = second.AsRegister<Register>();
__ rorl(first_reg, second_reg);
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance);
__ rorl(first_reg, imm);
}
return;
@@ -3981,7 +3983,7 @@ void InstructionCodeGeneratorX86::VisitRor(HRor* ror) {
__ cmovl(kNotEqual, first_reg_hi, first_reg_lo);
__ cmovl(kNotEqual, first_reg_lo, temp_reg);
} else {
- int32_t shift_amt = CodeGenerator::GetInt64ValueOf(second.GetConstant()) & kMaxLongShiftValue;
+ int32_t shift_amt = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance;
if (shift_amt == 0) {
// Already fine.
return;
@@ -4340,6 +4342,8 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86::GetSupportedInvokeStaticOr
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
+ // TODO: Create as many X86ComputeBaseMethodAddress instructions
+ // as needed for methods with irreducible loops.
if (GetGraph()->HasIrreducibleLoops() &&
(dispatch_info.method_load_kind ==
HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
@@ -4401,18 +4405,17 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
__ movl(temp.AsRegister<Register>(), Immediate(invoke->GetMethodAddress()));
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- __ movl(temp.AsRegister<Register>(), Immediate(0)); // Placeholder.
+ __ movl(temp.AsRegister<Register>(), Immediate(/* placeholder */ 0));
method_patches_.emplace_back(invoke->GetTargetMethod());
__ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn.
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke,
temp.AsRegister<Register>());
- uint32_t offset = invoke->GetDexCacheArrayOffset();
__ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset));
- // Add the patch entry and bind its label at the end of the instruction.
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, offset);
- __ Bind(&pc_relative_dex_cache_patches_.back().label);
+ // Bind a new fixup label at the end of the "movl" insn.
+ uint32_t offset = invoke->GetDexCacheArrayOffset();
+ __ Bind(NewPcRelativeDexCacheArrayPatch(*invoke->GetTargetMethod().dex_file, offset));
break;
}
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
@@ -4494,12 +4497,33 @@ void CodeGeneratorX86::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp
temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
}
+void CodeGeneratorX86::RecordSimplePatch() {
+ if (GetCompilerOptions().GetIncludePatchInformation()) {
+ simple_patches_.emplace_back();
+ __ Bind(&simple_patches_.back());
+ }
+}
+
+void CodeGeneratorX86::RecordStringPatch(HLoadString* load_string) {
+ string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex());
+ __ Bind(&string_patches_.back().label);
+}
+
+Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset) {
+ // Add the patch entry and bind its label at the end of the instruction.
+ pc_relative_dex_cache_patches_.emplace_back(dex_file, element_offset);
+ return &pc_relative_dex_cache_patches_.back().label;
+}
+
void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
relative_call_patches_.size() +
- pc_relative_dex_cache_patches_.size();
+ pc_relative_dex_cache_patches_.size() +
+ simple_patches_.size() +
+ string_patches_.size();
linker_patches->reserve(size);
// The label points to the end of the "movl" insn but the literal offset for method
// patch needs to point to the embedded constant which occupies the last 4 bytes.
@@ -4523,6 +4547,26 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche
GetMethodAddressOffset(),
info.element_offset));
}
+ for (const Label& label : simple_patches_) {
+ uint32_t literal_offset = label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+ if (GetCompilerOptions().GetCompilePic()) {
+ for (const StringPatchInfo<Label>& info : string_patches_) {
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(literal_offset,
+ &info.dex_file,
+ GetMethodAddressOffset(),
+ info.string_index));
+ }
+ } else {
+ for (const StringPatchInfo<Label>& info : string_patches_) {
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+ &info.dex_file,
+ info.string_index));
+ }
+ }
}
void CodeGeneratorX86::MarkGCCard(Register temp,
@@ -5916,14 +5960,15 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
DCHECK(!cls->MustGenerateClinitCheck());
// /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
GenerateGcRootFieldLoad(
- cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
// /* GcRoot<mirror::Class>[] */ out =
// current_method.ptr_sized_fields_->dex_cache_resolved_types_
__ movl(out, Address(current_method,
ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
// /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
@@ -5972,12 +6017,58 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck(
// No need for memory fence, thanks to the X86 memory model.
}
+HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ FALLTHROUGH_INTENDED;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit()); // Note: boot image is also non-JIT.
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops()) {
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ }
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod ||
+ load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
@@ -5985,16 +6076,61 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
- Register current_method = locations->InAt(0).AsRegister<Register>();
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ movl(out, Immediate(/* placeholder */ 0));
+ codegen_->RecordStringPatch(load);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ Register method_address = locations->InAt(0).AsRegister<Register>();
+ __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
+ codegen_->RecordStringPatch(load);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ movl(out, Immediate(address));
+ codegen_->RecordSimplePatch();
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ GenerateGcRootFieldLoad(load, out_loc, Address::Absolute(address));
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ Register base_reg = locations->InAt(0).AsRegister<Register>();
+ uint32_t offset = load->GetDexCacheElementOffset();
+ Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset);
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
@@ -6692,21 +6828,24 @@ void InstructionCodeGeneratorX86::GenerateReferenceLoadTwoRegisters(HInstruction
void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- Register obj,
- uint32_t offset) {
+ const Address& address,
+ Label* fixup_label) {
Register root_reg = root.AsRegister<Register>();
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = obj.field;
+ // root = *address;
// if (Thread::Current()->GetIsGcMarking()) {
// root = ReadBarrier::Mark(root)
// }
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
static_assert(
sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
"art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
@@ -6727,15 +6866,21 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(HInstruction* instruct
} else {
// GC root loaded through a slow path for read barriers other
// than Baker's.
- // /* GcRoot<mirror::Object>* */ root = obj + offset
- __ leal(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object>* */ root = address
+ __ leal(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// /* mirror::Object* */ root = root->Read()
codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
}
} else {
// Plain GC root load with no read barrier.
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index c397899892..1fa22fcfbe 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -258,13 +258,13 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator {
Location maybe_temp);
// Generate a GC root reference load:
//
- // root <- *(obj + offset)
+ // root <- *address
//
// while honoring read barriers (if any).
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- Register obj,
- uint32_t offset);
+ const Address& address,
+ Label* fixup_label = nullptr);
// Push value to FPU stack. `is_fp` specifies whether the value is floating point or not.
// `is_wide` specifies whether it is long/double or not.
@@ -388,6 +388,11 @@ class CodeGeneratorX86 : public CodeGenerator {
// Helper method to move a 64bits value between two locations.
void Move64(Location destination, Location source);
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -399,6 +404,10 @@ class CodeGeneratorX86 : public CodeGenerator {
// Generate a call to a virtual method.
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
+ void RecordSimplePatch();
+ void RecordStringPatch(HLoadString* load_string);
+ Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
+
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
// Emit linker patches.
@@ -542,6 +551,10 @@ class CodeGeneratorX86 : public CodeGenerator {
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
+ // When we don't know the proper offset for the value, we use kDummy32BitOffset.
+ // The correct value will be inserted when processing Assembler fixups.
+ static constexpr int32_t kDummy32BitOffset = 256;
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
@@ -578,6 +591,10 @@ class CodeGeneratorX86 : public CodeGenerator {
ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
// PC-relative DexCache access info.
ArenaDeque<PcRelativeDexCacheAccessInfo> pc_relative_dex_cache_patches_;
+ // Patch locations for patchoat where the linker doesn't do any other work.
+ ArenaDeque<Label> simple_patches_;
+ // String patch locations.
+ ArenaDeque<StringPatchInfo<Label>> string_patches_;
// Offset to the start of the constant area in the assembled code.
// Used for fixups to the constant area.
@@ -592,10 +609,6 @@ class CodeGeneratorX86 : public CodeGenerator {
// instruction gives the address of the start of this method.
int32_t method_address_offset_;
- // When we don't know the proper offset for the value, we use kDummy32BitOffset.
- // The correct value will be inserted when processing Assembler fixups.
- static constexpr int32_t kDummy32BitOffset = 256;
-
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86);
};
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 7648f61e75..cc46a07dcb 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -784,14 +784,14 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo
method_patches_.emplace_back(invoke->GetTargetMethod());
__ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn.
break;
- case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
- invoke->GetDexCacheArrayOffset());
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
__ movq(temp.AsRegister<CpuRegister>(),
Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
- // Bind the label at the end of the "movl" insn.
- __ Bind(&pc_relative_dex_cache_patches_.back().label);
+ // Bind a new fixup label at the end of the "movl" insn.
+ uint32_t offset = invoke->GetDexCacheArrayOffset();
+ __ Bind(NewPcRelativeDexCacheArrayPatch(*invoke->GetTargetMethod().dex_file, offset));
break;
+ }
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
Register method_reg;
@@ -873,12 +873,33 @@ void CodeGeneratorX86_64::GenerateVirtualCall(HInvokeVirtual* invoke, Location t
kX86_64WordSize).SizeValue()));
}
+void CodeGeneratorX86_64::RecordSimplePatch() {
+ if (GetCompilerOptions().GetIncludePatchInformation()) {
+ simple_patches_.emplace_back();
+ __ Bind(&simple_patches_.back());
+ }
+}
+
+void CodeGeneratorX86_64::RecordStringPatch(HLoadString* load_string) {
+ string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex());
+ __ Bind(&string_patches_.back().label);
+}
+
+Label* CodeGeneratorX86_64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset) {
+ // Add a patch entry and return the label.
+ pc_relative_dex_cache_patches_.emplace_back(dex_file, element_offset);
+ return &pc_relative_dex_cache_patches_.back().label;
+}
+
void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
method_patches_.size() +
relative_call_patches_.size() +
- pc_relative_dex_cache_patches_.size();
+ pc_relative_dex_cache_patches_.size() +
+ simple_patches_.size() +
+ string_patches_.size();
linker_patches->reserve(size);
// The label points to the end of the "movl" insn but the literal offset for method
// patch needs to point to the embedded constant which occupies the last 4 bytes.
@@ -902,6 +923,18 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat
info.label.Position(),
info.element_offset));
}
+ for (const Label& label : simple_patches_) {
+ uint32_t literal_offset = label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+ for (const StringPatchInfo<Label>& info : string_patches_) {
+ // These are always PC-relative, see GetSupportedLoadStringKind().
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RelativeStringPatch(literal_offset,
+ &info.dex_file,
+ info.label.Position(),
+ info.string_index));
+ }
}
void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
@@ -978,6 +1011,8 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph,
method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
@@ -1402,13 +1437,13 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc
// Nothing to do. The code always falls through.
return;
} else if (cond->IsIntConstant()) {
- // Constant condition, statically compared against 1.
- if (cond->AsIntConstant()->IsOne()) {
+ // Constant condition, statically compared against "true" (integer value 1).
+ if (cond->AsIntConstant()->IsTrue()) {
if (true_target != nullptr) {
__ jmp(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsZero());
+ DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
if (false_target != nullptr) {
__ jmp(false_target);
}
@@ -2799,7 +2834,7 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
} else if (in.IsConstant()) {
int64_t v = in.GetConstant()->AsLongConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- codegen_->Load64BitValue(dest, static_cast<double>(v));
+ codegen_->Load32BitValue(dest, static_cast<float>(v));
} else {
__ cvtsi2ss(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()), true);
@@ -3799,7 +3834,7 @@ void InstructionCodeGeneratorX86_64::HandleShift(HBinaryOperation* op) {
__ shrl(first_reg, second_reg);
}
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance);
if (op->IsShl()) {
__ shll(first_reg, imm);
} else if (op->IsShr()) {
@@ -3821,7 +3856,7 @@ void InstructionCodeGeneratorX86_64::HandleShift(HBinaryOperation* op) {
__ shrq(first_reg, second_reg);
}
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue);
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance);
if (op->IsShl()) {
__ shlq(first_reg, imm);
} else if (op->IsShr()) {
@@ -3868,7 +3903,7 @@ void InstructionCodeGeneratorX86_64::VisitRor(HRor* ror) {
CpuRegister second_reg = second.AsRegister<CpuRegister>();
__ rorl(first_reg, second_reg);
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance);
__ rorl(first_reg, imm);
}
break;
@@ -3877,7 +3912,7 @@ void InstructionCodeGeneratorX86_64::VisitRor(HRor* ror) {
CpuRegister second_reg = second.AsRegister<CpuRegister>();
__ rorq(first_reg, second_reg);
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue);
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance);
__ rorq(first_reg, imm);
}
break;
@@ -5249,6 +5284,12 @@ void ParallelMoveResolverX86_64::Exchange32(int mem1, int mem2) {
CpuRegister(ensure_scratch.GetRegister()));
}
+void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg1, CpuRegister reg2) {
+ __ movq(CpuRegister(TMP), reg1);
+ __ movq(reg1, reg2);
+ __ movq(reg2, CpuRegister(TMP));
+}
+
void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg, int mem) {
__ movq(CpuRegister(TMP), Address(CpuRegister(RSP), mem));
__ movq(Address(CpuRegister(RSP), mem), reg);
@@ -5286,7 +5327,7 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) {
Location destination = move->GetDestination();
if (source.IsRegister() && destination.IsRegister()) {
- __ xchgq(destination.AsRegister<CpuRegister>(), source.AsRegister<CpuRegister>());
+ Exchange64(source.AsRegister<CpuRegister>(), destination.AsRegister<CpuRegister>());
} else if (source.IsRegister() && destination.IsStackSlot()) {
Exchange32(source.AsRegister<CpuRegister>(), destination.GetStackIndex());
} else if (source.IsStackSlot() && destination.IsRegister()) {
@@ -5365,14 +5406,15 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
DCHECK(!cls->MustGenerateClinitCheck());
// /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
GenerateGcRootFieldLoad(
- cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
// /* GcRoot<mirror::Class>[] */ out =
// current_method.ptr_sized_fields_->dex_cache_resolved_types_
__ movq(out, Address(current_method,
ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
// /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
@@ -5410,12 +5452,49 @@ void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) {
check->GetLocations()->InAt(0).AsRegister<CpuRegister>());
}
+HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ // We prefer the always-available RIP-relative address for the x86-64 boot image.
+ return HLoadString::LoadKind::kBootImageLinkTimePcRelative;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJit());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
+}
+
void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
+ if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
locations->SetOut(Location::RequiresRegister());
}
@@ -5423,16 +5502,59 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
- CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
- __ movq(out, Address(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
- // /* GcRoot<mirror::String> */ out = out[string_index]
- GenerateGcRootFieldLoad(
- load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ switch (load->GetLoadKind()) {
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
+ codegen_->RecordStringPatch(load);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ movl(out, Immediate(address)); // Zero-extended.
+ codegen_->RecordSimplePatch();
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ if (IsUint<32>(load->GetAddress())) {
+ Address address = Address::Absolute(load->GetAddress(), /* no_rip */ true);
+ GenerateGcRootFieldLoad(load, out_loc, address);
+ } else {
+ // TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address).
+ __ movq(out, Immediate(load->GetAddress()));
+ GenerateGcRootFieldLoad(load, out_loc, Address(out, 0));
+ }
+ break;
+ }
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ uint32_t offset = load->GetDexCacheElementOffset();
+ Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset);
+ Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
+ /* no_rip */ false);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label);
+ break;
+ }
+ case HLoadString::LoadKind::kDexCacheViaMethod: {
+ CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
+
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+ // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
+ __ movq(out, Address(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind();
+ UNREACHABLE();
+ }
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
@@ -6171,21 +6293,24 @@ void InstructionCodeGeneratorX86_64::GenerateReferenceLoadTwoRegisters(HInstruct
void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- CpuRegister obj,
- uint32_t offset) {
+ const Address& address,
+ Label* fixup_label) {
CpuRegister root_reg = root.AsRegister<CpuRegister>();
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = obj.field;
+ // root = *address;
// if (Thread::Current()->GetIsGcMarking()) {
// root = ReadBarrier::Mark(root)
// }
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
static_assert(
sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
"art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
@@ -6207,15 +6332,21 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(HInstruction* instr
} else {
// GC root loaded through a slow path for read barriers other
// than Baker's.
- // /* GcRoot<mirror::Object>* */ root = obj + offset
- __ leaq(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object>* */ root = address
+ __ leaq(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// /* mirror::Object* */ root = root->Read()
codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
}
} else {
// Plain GC root load with no read barrier.
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- __ movl(root_reg, Address(obj, offset));
+ // /* GcRoot<mirror::Object> */ root = *address
+ __ movl(root_reg, address);
+ if (fixup_label != nullptr) {
+ __ Bind(fixup_label);
+ }
// Note that GC roots are not affected by heap poisoning, thus we
// do not have to unpoison `root_reg` here.
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index c3fce6e824..7ebce58f6c 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -142,6 +142,7 @@ class ParallelMoveResolverX86_64 : public ParallelMoveResolverWithSwap {
void Exchange32(CpuRegister reg, int mem);
void Exchange32(XmmRegister reg, int mem);
void Exchange32(int mem1, int mem2);
+ void Exchange64(CpuRegister reg1, CpuRegister reg2);
void Exchange64(CpuRegister reg, int mem);
void Exchange64(XmmRegister reg, int mem);
void Exchange64(int mem1, int mem2);
@@ -252,13 +253,13 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator {
Location maybe_temp);
// Generate a GC root reference load:
//
- // root <- *(obj + offset)
+ // root <- *address
//
// while honoring read barriers (if any).
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
- CpuRegister obj,
- uint32_t offset);
+ const Address& address,
+ Label* fixup_label = nullptr);
void PushOntoFPStack(Location source, uint32_t temp_offset,
uint32_t stack_adjustment, bool is_float);
@@ -384,6 +385,11 @@ class CodeGeneratorX86_64 : public CodeGenerator {
return false;
}
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -393,6 +399,10 @@ class CodeGeneratorX86_64 : public CodeGenerator {
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
+ void RecordSimplePatch();
+ void RecordStringPatch(HLoadString* load_string);
+ Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
+
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
@@ -515,6 +525,10 @@ class CodeGeneratorX86_64 : public CodeGenerator {
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
+ // When we don't know the proper offset for the value, we use kDummy32BitOffset.
+ // We will fix this up in the linker later to have the right value.
+ static constexpr int32_t kDummy32BitOffset = 256;
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
@@ -552,10 +566,10 @@ class CodeGeneratorX86_64 : public CodeGenerator {
ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
// PC-relative DexCache access info.
ArenaDeque<PcRelativeDexCacheAccessInfo> pc_relative_dex_cache_patches_;
-
- // When we don't know the proper offset for the value, we use kDummy32BitOffset.
- // We will fix this up in the linker later to have the right value.
- static constexpr int32_t kDummy32BitOffset = 256;
+ // Patch locations for patchoat where the linker doesn't do any other work.
+ ArenaDeque<Label> simple_patches_;
+ // String patch locations.
+ ArenaDeque<StringPatchInfo<Label>> string_patches_;
// Fixups for jump tables need to be handled specially.
ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_;
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 7ddabdee78..0614945ddc 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -120,7 +120,7 @@ void HConstantFoldingVisitor::VisitTypeConversion(HTypeConversion* inst) {
void HConstantFoldingVisitor::VisitDivZeroCheck(HDivZeroCheck* inst) {
// We can safely remove the check if the input is a non-null constant.
HInstruction* check_input = inst->InputAt(0);
- if (check_input->IsConstant() && !check_input->AsConstant()->IsZero()) {
+ if (check_input->IsConstant() && !check_input->AsConstant()->IsArithmeticZero()) {
inst->ReplaceWith(check_input);
inst->GetBlock()->RemoveInstruction(inst);
}
@@ -130,7 +130,7 @@ void HConstantFoldingVisitor::VisitDivZeroCheck(HDivZeroCheck* inst) {
void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instruction) {
DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
HInstruction* left = instruction->GetLeft();
- if (left->IsConstant() && left->AsConstant()->IsZero()) {
+ if (left->IsConstant() && left->AsConstant()->IsArithmeticZero()) {
// Replace code looking like
// SHL dst, 0, shift_amount
// with
@@ -142,7 +142,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instr
void InstructionWithAbsorbingInputSimplifier::VisitAbove(HAbove* instruction) {
if (instruction->GetLeft()->IsConstant() &&
- instruction->GetLeft()->AsConstant()->IsZero()) {
+ instruction->GetLeft()->AsConstant()->IsArithmeticZero()) {
// Replace code looking like
// ABOVE dst, 0, src // unsigned 0 > src is always false
// with
@@ -154,7 +154,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitAbove(HAbove* instruction) {
void InstructionWithAbsorbingInputSimplifier::VisitAboveOrEqual(HAboveOrEqual* instruction) {
if (instruction->GetRight()->IsConstant() &&
- instruction->GetRight()->AsConstant()->IsZero()) {
+ instruction->GetRight()->AsConstant()->IsArithmeticZero()) {
// Replace code looking like
// ABOVE_OR_EQUAL dst, src, 0 // unsigned src >= 0 is always true
// with
@@ -166,7 +166,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitAboveOrEqual(HAboveOrEqual* i
void InstructionWithAbsorbingInputSimplifier::VisitBelow(HBelow* instruction) {
if (instruction->GetRight()->IsConstant() &&
- instruction->GetRight()->AsConstant()->IsZero()) {
+ instruction->GetRight()->AsConstant()->IsArithmeticZero()) {
// Replace code looking like
// BELOW dst, src, 0 // unsigned src < 0 is always false
// with
@@ -178,7 +178,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitBelow(HBelow* instruction) {
void InstructionWithAbsorbingInputSimplifier::VisitBelowOrEqual(HBelowOrEqual* instruction) {
if (instruction->GetLeft()->IsConstant() &&
- instruction->GetLeft()->AsConstant()->IsZero()) {
+ instruction->GetLeft()->AsConstant()->IsArithmeticZero()) {
// Replace code looking like
// BELOW_OR_EQUAL dst, 0, src // unsigned 0 <= src is always true
// with
@@ -190,7 +190,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitBelowOrEqual(HBelowOrEqual* i
void InstructionWithAbsorbingInputSimplifier::VisitAnd(HAnd* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
- if ((input_cst != nullptr) && input_cst->IsZero()) {
+ if ((input_cst != nullptr) && input_cst->IsZeroBitPattern()) {
// Replace code looking like
// AND dst, src, 0
// with
@@ -224,7 +224,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitMul(HMul* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
Primitive::Type type = instruction->GetType();
if (Primitive::IsIntOrLongType(type) &&
- (input_cst != nullptr) && input_cst->IsZero()) {
+ (input_cst != nullptr) && input_cst->IsArithmeticZero()) {
// Replace code looking like
// MUL dst, src, 0
// with
@@ -264,7 +264,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitRem(HRem* instruction) {
HBasicBlock* block = instruction->GetBlock();
if (instruction->GetLeft()->IsConstant() &&
- instruction->GetLeft()->AsConstant()->IsZero()) {
+ instruction->GetLeft()->AsConstant()->IsArithmeticZero()) {
// Replace code looking like
// REM dst, 0, src
// with
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 9c69f8c75b..1e54a0adfa 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -598,42 +598,41 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) {
" 5: IntConstant [9]\n" // v1 <- 2
" 13: IntConstant [14]\n" // const 5
" 18: IntConstant [19]\n" // const 4
- " 24: IntConstant [25]\n" // const 8
- " 30: SuspendCheck\n"
- " 31: Goto 1\n"
+ " 23: IntConstant [24]\n" // const 8
+ " 29: SuspendCheck\n"
+ " 30: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
" 9: Add(3, 5) [19]\n" // v2 <- v0 + v1 = 1 + 2 = 3
" 11: Goto 3\n" // goto L2
"BasicBlock 2, pred: 3, succ: 4\n" // L1:
- " 14: Add(19, 13) [25]\n" // v1 <- v0 + 3 = 7 + 5 = 12
+ " 14: Add(19, 13) [24]\n" // v1 <- v0 + 3 = 7 + 5 = 12
" 16: Goto 4\n" // goto L3
"BasicBlock 3, pred: 1, succ: 2\n" // L2:
" 19: Add(9, 18) [14]\n" // v0 <- v2 + 2 = 3 + 4 = 7
- " 21: SuspendCheck\n"
- " 22: Goto 2\n" // goto L1
+ " 21: Goto 2\n" // goto L1
"BasicBlock 4, pred: 2, succ: 5\n" // L3:
- " 25: Add(14, 24) [28]\n" // v2 <- v1 + 4 = 12 + 8 = 20
- " 28: Return(25)\n" // return v2
+ " 24: Add(14, 23) [27]\n" // v2 <- v1 + 4 = 12 + 8 = 20
+ " 27: Return(24)\n" // return v2
"BasicBlock 5, pred: 4\n"
- " 29: Exit\n";
+ " 28: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
{ " 3: IntConstant [9]\n", " 3: IntConstant\n" },
- { " 5: IntConstant [9]\n", " 5: IntConstant []\n" },
+ { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
{ " 13: IntConstant [14]\n", " 13: IntConstant\n" },
{ " 18: IntConstant [19]\n", " 18: IntConstant\n" },
- { " 24: IntConstant [25]\n", " 24: IntConstant\n" },
- { " 30: SuspendCheck\n", " 30: SuspendCheck\n"
- " 32: IntConstant []\n"
- " 33: IntConstant []\n"
- " 34: IntConstant\n"
- " 35: IntConstant [28]\n" },
+ { " 23: IntConstant [24]\n", " 23: IntConstant\n" },
+ { " 29: SuspendCheck\n", " 29: SuspendCheck\n"
+ " 31: IntConstant\n"
+ " 32: IntConstant\n"
+ " 33: IntConstant\n"
+ " 34: IntConstant [27]\n" },
{ " 9: Add(3, 5) [19]\n", removed },
- { " 14: Add(19, 13) [25]\n", removed },
+ { " 14: Add(19, 13) [24]\n", removed },
{ " 19: Add(9, 18) [14]\n", removed },
- { " 25: Add(14, 24) [28]\n", removed },
- { " 28: Return(25)\n", " 28: Return(35)\n"}
+ { " 24: Add(14, 23) [27]\n", removed },
+ { " 27: Return(24)\n", " 27: Return(34)\n"}
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -656,17 +655,13 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) {
// Expected difference after dead code elimination.
std::string expected_after_dce =
"BasicBlock 0, succ: 1\n"
- " 5: IntConstant []\n"
- " 30: SuspendCheck\n"
- " 32: IntConstant []\n"
- " 33: IntConstant []\n"
- " 35: IntConstant [28]\n"
- " 31: Goto 1\n"
+ " 29: SuspendCheck\n"
+ " 34: IntConstant [27]\n"
+ " 30: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5\n"
- " 21: SuspendCheck\n"
- " 28: Return(35)\n"
+ " 27: Return(34)\n"
"BasicBlock 5, pred: 1\n"
- " 29: Exit\n";
+ " 28: Exit\n";
TestCode(data,
expected_before,
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index d7bf16e0cc..5f11024996 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -41,11 +41,11 @@ static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
HIf* if_instruction = last_instruction->AsIf();
HInstruction* condition = if_instruction->InputAt(0);
if (condition->IsIntConstant()) {
- if (condition->AsIntConstant()->IsOne()) {
+ if (condition->AsIntConstant()->IsTrue()) {
live_successors = live_successors.SubArray(0u, 1u);
DCHECK_EQ(live_successors[0], if_instruction->IfTrueSuccessor());
} else {
- DCHECK(condition->AsIntConstant()->IsZero());
+ DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue();
live_successors = live_successors.SubArray(1u, 1u);
DCHECK_EQ(live_successors[0], if_instruction->IfFalseSuccessor());
}
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 930795b4f6..83e724ba29 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -149,44 +149,32 @@ TEST_F(DeadCodeEliminationTest, AdditionsAndInconditionalJumps) {
" 5: IntConstant [9]\n"
" 13: IntConstant [14]\n"
" 18: IntConstant [19]\n"
- " 24: IntConstant [25]\n"
- " 29: SuspendCheck\n"
- " 30: Goto 1\n"
+ " 23: IntConstant [24]\n"
+ " 28: SuspendCheck\n"
+ " 29: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
" 9: Add(3, 5) [19]\n"
" 11: Goto 3\n"
"BasicBlock 2, pred: 3, succ: 4\n"
- " 14: Add(19, 13) [25]\n"
+ " 14: Add(19, 13) [24]\n"
" 16: Goto 4\n"
"BasicBlock 3, pred: 1, succ: 2\n"
" 19: Add(9, 18) [14]\n"
- " 21: SuspendCheck\n"
- " 22: Goto 2\n"
+ " 21: Goto 2\n"
"BasicBlock 4, pred: 2, succ: 5\n"
- " 25: Add(14, 24)\n"
- " 27: ReturnVoid\n"
+ " 24: Add(14, 23)\n"
+ " 26: ReturnVoid\n"
"BasicBlock 5, pred: 4\n"
- " 28: Exit\n";
+ " 27: Exit\n";
- // The SuspendCheck instruction following this Add instruction
- // inserts the latter in an environment, thus making it "used" and
- // therefore non removable. It ensures that some other Add and
- // IntConstant instructions cannot be removed, as they are direct
- // or indirect inputs of the initial Add instruction.
std::string expected_after =
"BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9]\n"
- " 5: IntConstant [9]\n"
- " 18: IntConstant [19]\n"
- " 29: SuspendCheck\n"
- " 30: Goto 1\n"
+ " 28: SuspendCheck\n"
+ " 29: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5\n"
- " 9: Add(3, 5) [19]\n"
- " 19: Add(9, 18) []\n"
- " 21: SuspendCheck\n"
- " 27: ReturnVoid\n"
+ " 26: ReturnVoid\n"
"BasicBlock 5, pred: 1\n"
- " 28: Exit\n";
+ " 27: Exit\n";
TestCode(data, expected_before, expected_after);
}
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 3db254a004..e9072b9c77 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -44,6 +44,21 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor {
}
private:
+ void VisitLoadString(HLoadString* load_string) OVERRIDE {
+ // If this is a load with PC-relative access to the dex cache methods array,
+ // we need to add the dex cache arrays base as the special input.
+ if (load_string->GetLoadKind() == HLoadString::LoadKind::kDexCachePcRelative) {
+ // Initialize base for target dex file if needed.
+ const DexFile& dex_file = load_string->GetDexFile();
+ HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file);
+ // Update the element offset in base.
+ DexCacheArraysLayout layout(kArmPointerSize, &dex_file);
+ base->UpdateElementOffset(layout.StringOffset(load_string->GetStringIndex()));
+ // Add the special argument base to the load.
+ load_string->AddSpecialInput(base);
+ }
+ }
+
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
// If this is an invoke with PC-relative access to the dex cache methods array,
// we need to add the dex cache arrays base as the special input.
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 0c22903602..f2e77e28a6 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -32,11 +32,9 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
// Check consistency with respect to predecessors of `block`.
// Note: Counting duplicates with a sorted vector uses up to 6x less memory
- // than ArenaSafeMap<HBasicBlock*, size_t>.
- ArenaVector<HBasicBlock*> sorted_predecessors(
- block->GetPredecessors().begin(),
- block->GetPredecessors().end(),
- GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
+ // than ArenaSafeMap<HBasicBlock*, size_t> and also allows storage reuse.
+ ArenaVector<HBasicBlock*>& sorted_predecessors = blocks_storage_;
+ sorted_predecessors.assign(block->GetPredecessors().begin(), block->GetPredecessors().end());
std::sort(sorted_predecessors.begin(), sorted_predecessors.end());
for (auto it = sorted_predecessors.begin(), end = sorted_predecessors.end(); it != end; ) {
HBasicBlock* p = *it++;
@@ -57,11 +55,9 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
// Check consistency with respect to successors of `block`.
// Note: Counting duplicates with a sorted vector uses up to 6x less memory
- // than ArenaSafeMap<HBasicBlock*, size_t>.
- ArenaVector<HBasicBlock*> sorted_successors(
- block->GetSuccessors().begin(),
- block->GetSuccessors().end(),
- GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
+ // than ArenaSafeMap<HBasicBlock*, size_t> and also allows storage reuse.
+ ArenaVector<HBasicBlock*>& sorted_successors = blocks_storage_;
+ sorted_successors.assign(block->GetSuccessors().begin(), block->GetSuccessors().end());
std::sort(sorted_successors.begin(), sorted_successors.end());
for (auto it = sorted_successors.begin(), end = sorted_successors.end(); it != end; ) {
HBasicBlock* s = *it++;
@@ -811,10 +807,11 @@ void GraphChecker::VisitPhi(HPhi* phi) {
phi->GetRegNumber(),
type_str.str().c_str()));
} else {
- ArenaBitVector visited(GetGraph()->GetArena(),
- 0,
- /* expandable */ true,
- kArenaAllocGraphChecker);
+ // If we get here, make sure we allocate all the necessary storage at once
+ // because the BitVector reallocation strategy has very bad worst-case behavior.
+ ArenaBitVector& visited = visited_storage_;
+ visited.SetBit(GetGraph()->GetCurrentInstructionId());
+ visited.ClearAllBits();
if (!IsConstantEquivalent(phi, other_phi, &visited)) {
AddError(StringPrintf("Two phis (%d and %d) found for VReg %d but they "
"are not equivalents of constants.",
@@ -840,17 +837,11 @@ void GraphChecker::HandleBooleanInput(HInstruction* instruction, size_t input_in
static_cast<int>(input_index),
value));
}
- } else if (input->GetType() == Primitive::kPrimInt
- && (input->IsPhi() ||
- input->IsAnd() ||
- input->IsOr() ||
- input->IsXor() ||
- input->IsSelect())) {
- // TODO: We need a data-flow analysis to determine if the Phi or Select or
- // binary operation is actually Boolean. Allow for now.
- } else if (input->GetType() != Primitive::kPrimBoolean) {
+ } else if (Primitive::PrimitiveKind(input->GetType()) != Primitive::kPrimInt) {
+ // TODO: We need a data-flow analysis to determine if an input like Phi,
+ // Select or a binary operation is actually Boolean. Allow for now.
AddError(StringPrintf(
- "%s instruction %d has a non-Boolean input %d whose type is: %s.",
+ "%s instruction %d has a non-integer input %d whose type is: %s.",
instruction->DebugName(),
instruction->GetId(),
static_cast<int>(input_index),
@@ -937,9 +928,12 @@ void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
Primitive::Type lhs_type = op->InputAt(0)->GetType();
Primitive::Type rhs_type = op->InputAt(1)->GetType();
Primitive::Type result_type = op->GetType();
+
+ // Type consistency between inputs.
if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) {
if (Primitive::PrimitiveKind(rhs_type) != Primitive::kPrimInt) {
- AddError(StringPrintf("Shift operation %s %d has a non-int kind second input: %s of type %s.",
+ AddError(StringPrintf("Shift/rotate operation %s %d has a non-int kind second input: "
+ "%s of type %s.",
op->DebugName(), op->GetId(),
op->InputAt(1)->DebugName(),
Primitive::PrettyDescriptor(rhs_type)));
@@ -953,21 +947,38 @@ void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
}
}
+ // Type consistency between result and input(s).
if (op->IsCompare()) {
if (result_type != Primitive::kPrimInt) {
AddError(StringPrintf("Compare operation %d has a non-int result type: %s.",
op->GetId(),
Primitive::PrettyDescriptor(result_type)));
}
+ } else if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) {
+ // Only check the first input (value), as the second one (distance)
+ // must invariably be of kind `int`.
+ if (result_type != Primitive::PrimitiveKind(lhs_type)) {
+ AddError(StringPrintf("Shift/rotate operation %s %d has a result type different "
+ "from its left-hand side (value) input kind: %s vs %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(result_type),
+ Primitive::PrettyDescriptor(lhs_type)));
+ }
} else {
- // Use the first input, so that we can also make this check for shift and rotate operations.
if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(lhs_type)) {
AddError(StringPrintf("Binary operation %s %d has a result kind different "
- "from its input kind: %s vs %s.",
+ "from its left-hand side input kind: %s vs %s.",
op->DebugName(), op->GetId(),
Primitive::PrettyDescriptor(result_type),
Primitive::PrettyDescriptor(lhs_type)));
}
+ if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(rhs_type)) {
+ AddError(StringPrintf("Binary operation %s %d has a result kind different "
+ "from its right-hand side input kind: %s vs %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(result_type),
+ Primitive::PrettyDescriptor(rhs_type)));
+ }
}
}
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 27d5621887..a536b30fcd 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -33,7 +33,9 @@ class GraphChecker : public HGraphDelegateVisitor {
seen_ids_(graph->GetArena(),
graph->GetCurrentInstructionId(),
false,
- kArenaAllocGraphChecker) {}
+ kArenaAllocGraphChecker),
+ blocks_storage_(graph->GetArena()->Adapter(kArenaAllocGraphChecker)),
+ visited_storage_(graph->GetArena(), 0u, true, kArenaAllocGraphChecker) {}
// Check the whole graph (in reverse post-order).
void Run() {
@@ -102,6 +104,10 @@ class GraphChecker : public HGraphDelegateVisitor {
const char* const dump_prefix_;
ArenaBitVector seen_ids_;
+ // To reduce the total arena memory allocation, we reuse the same storage.
+ ArenaVector<HBasicBlock*> blocks_storage_;
+ ArenaBitVector visited_storage_;
+
DISALLOW_COPY_AND_ASSIGN(GraphChecker);
};
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 3a9d242df2..fe47f7db7d 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -178,41 +178,47 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
: nullptr),
indent_(0) {}
+ void Flush() {
+ // We use "\n" instead of std::endl to avoid implicit flushing which
+ // generates too many syscalls during debug-GC tests (b/27826765).
+ output_ << std::flush;
+ }
+
void StartTag(const char* name) {
AddIndent();
- output_ << "begin_" << name << std::endl;
+ output_ << "begin_" << name << "\n";
indent_++;
}
void EndTag(const char* name) {
indent_--;
AddIndent();
- output_ << "end_" << name << std::endl;
+ output_ << "end_" << name << "\n";
}
void PrintProperty(const char* name, const char* property) {
AddIndent();
- output_ << name << " \"" << property << "\"" << std::endl;
+ output_ << name << " \"" << property << "\"\n";
}
void PrintProperty(const char* name, const char* property, int id) {
AddIndent();
- output_ << name << " \"" << property << id << "\"" << std::endl;
+ output_ << name << " \"" << property << id << "\"\n";
}
void PrintEmptyProperty(const char* name) {
AddIndent();
- output_ << name << std::endl;
+ output_ << name << "\n";
}
void PrintTime(const char* name) {
AddIndent();
- output_ << name << " " << time(nullptr) << std::endl;
+ output_ << name << " " << time(nullptr) << "\n";
}
void PrintInt(const char* name, int value) {
AddIndent();
- output_ << name << " " << value << std::endl;
+ output_ << name << " " << value << "\n";
}
void AddIndent() {
@@ -249,7 +255,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
if (block->IsEntryBlock() && (disasm_info_ != nullptr)) {
output_ << " \"" << kDisassemblyBlockFrameEntry << "\" ";
}
- output_<< std::endl;
+ output_<< "\n";
}
void PrintSuccessors(HBasicBlock* block) {
@@ -258,7 +264,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
for (HBasicBlock* successor : block->GetNormalSuccessors()) {
output_ << " \"B" << successor->GetBlockId() << "\" ";
}
- output_<< std::endl;
+ output_<< "\n";
}
void PrintExceptionHandlers(HBasicBlock* block) {
@@ -272,7 +278,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
!disasm_info_->GetSlowPathIntervals().empty()) {
output_ << " \"" << kDisassemblyBlockSlowPaths << "\" ";
}
- output_<< std::endl;
+ output_<< "\n";
}
void DumpLocation(std::ostream& stream, const Location& location) {
@@ -367,6 +373,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
<< load_class->NeedsAccessCheck() << std::noboolalpha;
}
+ void VisitLoadString(HLoadString* load_string) OVERRIDE {
+ StartAttributeStream("load_kind") << load_string->GetLoadKind();
+ }
+
void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind();
StartAttributeStream("must_do_null_check") << std::boolalpha
@@ -418,6 +428,20 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
}
+ void VisitInstanceFieldGet(HInstanceFieldGet* iget) OVERRIDE {
+ StartAttributeStream("field_name") << PrettyField(iget->GetFieldInfo().GetFieldIndex(),
+ iget->GetFieldInfo().GetDexFile(),
+ /* with type */ false);
+ StartAttributeStream("field_type") << iget->GetFieldType();
+ }
+
+ void VisitInstanceFieldSet(HInstanceFieldSet* iset) OVERRIDE {
+ StartAttributeStream("field_name") << PrettyField(iset->GetFieldInfo().GetFieldIndex(),
+ iset->GetFieldInfo().GetDexFile(),
+ /* with type */ false);
+ StartAttributeStream("field_type") << iset->GetFieldType();
+ }
+
void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE {
StartAttributeStream("field_type") << field_access->GetFieldType();
}
@@ -574,7 +598,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
auto it = disasm_info_->GetInstructionIntervals().find(instruction);
if (it != disasm_info_->GetInstructionIntervals().end()
&& it->second.start != it->second.end) {
- output_ << std::endl;
+ output_ << "\n";
disassembler_->Disassemble(output_, it->second.start, it->second.end);
}
}
@@ -594,7 +618,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
output_ << bci << " " << num_uses << " "
<< GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
PrintInstruction(instruction);
- output_ << " " << kEndInstructionMarker << std::endl;
+ output_ << " " << kEndInstructionMarker << "\n";
}
}
@@ -638,10 +662,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
output_ << " 0 0 disasm " << kDisassemblyBlockFrameEntry << " ";
GeneratedCodeInterval frame_entry = disasm_info_->GetFrameEntryInterval();
if (frame_entry.start != frame_entry.end) {
- output_ << std::endl;
+ output_ << "\n";
disassembler_->Disassemble(output_, frame_entry.start, frame_entry.end);
}
- output_ << kEndInstructionMarker << std::endl;
+ output_ << kEndInstructionMarker << "\n";
DumpEndOfDisassemblyBlock();
}
@@ -657,9 +681,9 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
GetGraph()->HasExitBlock() ? GetGraph()->GetExitBlock()->GetBlockId() : -1,
-1);
for (SlowPathCodeInfo info : disasm_info_->GetSlowPathIntervals()) {
- output_ << " 0 0 disasm " << info.slow_path->GetDescription() << std::endl;
+ output_ << " 0 0 disasm " << info.slow_path->GetDescription() << "\n";
disassembler_->Disassemble(output_, info.code_interval.start, info.code_interval.end);
- output_ << kEndInstructionMarker << std::endl;
+ output_ << kEndInstructionMarker << "\n";
}
DumpEndOfDisassemblyBlock();
}
@@ -680,6 +704,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
DumpDisassemblyBlockForSlowPaths();
}
EndTag("cfg");
+ Flush();
}
void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
@@ -719,7 +744,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
output_ << inputs.Current()->GetId() << " ";
}
- output_ << "]" << std::endl;
+ output_ << "]\n";
}
EndTag("locals");
EndTag("states");
@@ -761,6 +786,7 @@ void HGraphVisualizer::PrintHeader(const char* method_name) const {
printer.PrintProperty("method", method_name);
printer.PrintTime("date");
printer.EndTag("compilation");
+ printer.Flush();
}
void HGraphVisualizer::DumpGraph(const char* pass_name,
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 266cb10ab3..c06d19dce0 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -141,7 +141,7 @@ void HInductionVarAnalysis::VisitLoop(HLoopInformation* loop) {
DCHECK(stack_.empty());
map_.clear();
- // Determine the loop's trip count.
+ // Determine the loop's trip-count.
VisitControl(loop);
}
@@ -917,6 +917,7 @@ bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
info1->induction_class == info2->induction_class &&
info1->operation == info2->operation &&
info1->fetch == info2->fetch &&
+ info1->type == info2->type &&
InductionEqual(info1->op_a, info2->op_a) &&
InductionEqual(info1->op_b, info2->op_b);
}
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 0fbb67d0d9..580d24b74b 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -157,6 +157,13 @@ class InductionVarAnalysisTest : public CommonCompilerTest {
iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
}
+ // Returns true if instructions have identical induction.
+ bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) {
+ return HInductionVarAnalysis::InductionEqual(
+ iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1),
+ iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
+ }
+
// Performs InductionVarAnalysis (after proper set up).
void PerformInductionVarAnalysis() {
graph_->BuildDominatorTree();
@@ -228,6 +235,9 @@ TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
+ // Offset matters!
+ EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
+
// Trip-count.
EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
@@ -320,6 +330,10 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
PerformInductionVarAnalysis();
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+
+ // Both increments get same induction.
+ EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
+ EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
}
TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
@@ -570,6 +584,33 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
}
}
+TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) {
+ // Setup:
+ // for (int i = 0; i < 100; i++) {
+ // k = (byte) i;
+ // a[k] = 0;
+ // a[i] = 0;
+ // }
+ BuildLoopNest(1);
+ HInstruction *conv = InsertInstruction(
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+ HInstruction* store1 = InsertArrayStore(conv, 0);
+ HInstruction* store2 = InsertArrayStore(basic_[0], 0);
+ PerformInductionVarAnalysis();
+
+ // Regular int induction (i) is "transferred" over conversion into byte induction (k).
+ EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
+ EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
+ EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Type matters!
+ EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
+
+ // Trip-count.
+ EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
+ GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
+}
+
TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
// Setup:
// for (byte i = -128; i < 127; i++) { // just fits!
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 440a2821c1..fce9ab8b85 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -920,8 +920,7 @@ bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
bool needs_constructor_barrier = false;
for (size_t i = 0; i != number_of_iputs; ++i) {
HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, iput_args[i]);
- if (!value->IsConstant() ||
- (!value->AsConstant()->IsZero() && !value->IsNullConstant())) {
+ if (!value->IsConstant() || !value->AsConstant()->IsZeroBitPattern()) {
if (dex_cache.GetReference() == nullptr) {
dex_cache = handles_->NewHandle(resolved_method->GetDexCache());
}
@@ -1013,7 +1012,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
DexCompilationUnit dex_compilation_unit(
- nullptr,
caller_compilation_unit_.GetClassLoader(),
class_linker,
callee_dex_file,
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 820c696033..1249b48e1e 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -56,7 +56,6 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
bool TryDeMorganNegationFactoring(HBinaryOperation* op);
void VisitShift(HBinaryOperation* shift);
- void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE;
void VisitEqual(HEqual* equal) OVERRIDE;
void VisitNotEqual(HNotEqual* equal) OVERRIDE;
void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE;
@@ -240,8 +239,9 @@ void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) {
if (input_cst != nullptr) {
int64_t cst = Int64FromConstant(input_cst);
- int64_t mask =
- (input_other->GetType() == Primitive::kPrimLong) ? kMaxLongShiftValue : kMaxIntShiftValue;
+ int64_t mask = (input_other->GetType() == Primitive::kPrimLong)
+ ? kMaxLongShiftDistance
+ : kMaxIntShiftDistance;
if ((cst & mask) == 0) {
// Replace code looking like
// SHL dst, src, 0
@@ -554,22 +554,6 @@ void InstructionSimplifierVisitor::VisitStaticFieldSet(HStaticFieldSet* instruct
}
}
-void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
- HBasicBlock* block = check->GetBlock();
- // Currently always keep the suspend check at entry.
- if (block->IsEntryBlock()) return;
-
- // Currently always keep suspend checks at loop entry.
- if (block->IsLoopHeader() && block->GetFirstInstruction() == check) {
- DCHECK(block->GetLoopInformation()->GetSuspendCheck() == check);
- return;
- }
-
- // Remove the suspend check that was added at build time for the baseline
- // compiler.
- block->RemoveInstruction(check);
-}
-
static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* arena, HInstruction* cond) {
HInstruction *lhs = cond->InputAt(0);
HInstruction *rhs = cond->InputAt(1);
@@ -608,12 +592,12 @@ void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) {
HBasicBlock* block = equal->GetBlock();
// We are comparing the boolean to a constant which is of type int and can
// be any constant.
- if (input_const->AsIntConstant()->IsOne()) {
+ if (input_const->AsIntConstant()->IsTrue()) {
// Replace (bool_value == true) with bool_value
equal->ReplaceWith(input_value);
block->RemoveInstruction(equal);
RecordSimplification();
- } else if (input_const->AsIntConstant()->IsZero()) {
+ } else if (input_const->AsIntConstant()->IsFalse()) {
equal->ReplaceWith(GetGraph()->InsertOppositeCondition(input_value, equal));
block->RemoveInstruction(equal);
RecordSimplification();
@@ -639,11 +623,11 @@ void InstructionSimplifierVisitor::VisitNotEqual(HNotEqual* not_equal) {
HBasicBlock* block = not_equal->GetBlock();
// We are comparing the boolean to a constant which is of type int and can
// be any constant.
- if (input_const->AsIntConstant()->IsOne()) {
+ if (input_const->AsIntConstant()->IsTrue()) {
not_equal->ReplaceWith(GetGraph()->InsertOppositeCondition(input_value, not_equal));
block->RemoveInstruction(not_equal);
RecordSimplification();
- } else if (input_const->AsIntConstant()->IsZero()) {
+ } else if (input_const->AsIntConstant()->IsFalse()) {
// Replace (bool_value != false) with bool_value
not_equal->ReplaceWith(input_value);
block->RemoveInstruction(not_equal);
@@ -668,10 +652,10 @@ void InstructionSimplifierVisitor::VisitBooleanNot(HBooleanNot* bool_not) {
if (input->IsIntConstant()) {
// Replace !(true/false) with false/true.
- if (input->AsIntConstant()->IsOne()) {
+ if (input->AsIntConstant()->IsTrue()) {
replace_with = GetGraph()->GetIntConstant(0);
} else {
- DCHECK(input->AsIntConstant()->IsZero());
+ DCHECK(input->AsIntConstant()->IsFalse()) << input->AsIntConstant()->GetValue();
replace_with = GetGraph()->GetIntConstant(1);
}
} else if (input->IsBooleanNot()) {
@@ -712,19 +696,19 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) {
// Replace (cond ? x : x) with (x).
replace_with = true_value;
} else if (condition->IsIntConstant()) {
- if (condition->AsIntConstant()->IsOne()) {
+ if (condition->AsIntConstant()->IsTrue()) {
// Replace (true ? x : y) with (x).
replace_with = true_value;
} else {
// Replace (false ? x : y) with (y).
- DCHECK(condition->AsIntConstant()->IsZero());
+ DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue();
replace_with = false_value;
}
} else if (true_value->IsIntConstant() && false_value->IsIntConstant()) {
- if (true_value->AsIntConstant()->IsOne() && false_value->AsIntConstant()->IsZero()) {
+ if (true_value->AsIntConstant()->IsTrue() && false_value->AsIntConstant()->IsFalse()) {
// Replace (cond ? true : false) with (cond).
replace_with = condition;
- } else if (true_value->AsIntConstant()->IsZero() && false_value->AsIntConstant()->IsOne()) {
+ } else if (true_value->AsIntConstant()->IsFalse() && false_value->AsIntConstant()->IsTrue()) {
// Replace (cond ? false : true) with (!cond).
replace_with = GetGraph()->InsertOppositeCondition(condition, select);
}
@@ -904,7 +888,7 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct
void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
HInstruction* input_other = instruction->GetLeastConstantLeft();
- if ((input_cst != nullptr) && input_cst->IsZero()) {
+ if ((input_cst != nullptr) && input_cst->IsArithmeticZero()) {
// Replace code looking like
// ADD dst, src, 0
// with
@@ -1335,7 +1319,7 @@ void InstructionSimplifierVisitor::VisitOr(HOr* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
HInstruction* input_other = instruction->GetLeastConstantLeft();
- if ((input_cst != nullptr) && input_cst->IsZero()) {
+ if ((input_cst != nullptr) && input_cst->IsZeroBitPattern()) {
// Replace code looking like
// OR dst, src, 0
// with
@@ -1380,7 +1364,7 @@ void InstructionSimplifierVisitor::VisitSub(HSub* instruction) {
return;
}
- if ((input_cst != nullptr) && input_cst->IsZero()) {
+ if ((input_cst != nullptr) && input_cst->IsArithmeticZero()) {
// Replace code looking like
// SUB dst, src, 0
// with
@@ -1461,7 +1445,7 @@ void InstructionSimplifierVisitor::VisitXor(HXor* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
HInstruction* input_other = instruction->GetLeastConstantLeft();
- if ((input_cst != nullptr) && input_cst->IsZero()) {
+ if ((input_cst != nullptr) && input_cst->IsZeroBitPattern()) {
// Replace code looking like
// XOR dst, src, 0
// with
@@ -1745,7 +1729,7 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
void InstructionSimplifierVisitor::VisitDeoptimize(HDeoptimize* deoptimize) {
HInstruction* cond = deoptimize->InputAt(0);
if (cond->IsConstant()) {
- if (cond->AsIntConstant()->IsZero()) {
+ if (cond->AsIntConstant()->IsFalse()) {
// Never deopt: instruction can be removed.
deoptimize->GetBlock()->RemoveInstruction(deoptimize);
} else {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index 338120bbbc..da269980e8 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -51,6 +51,22 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor {
return TryMergeIntoShifterOperand(use, bitfield_op, true);
}
+ /**
+ * This simplifier uses a special-purpose BB visitor.
+ * (1) No need to visit Phi nodes.
+ * (2) Since statements can be removed in a "forward" fashion,
+ * the visitor should test if each statement is still there.
+ */
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
+ // TODO: fragile iteration, provide more robust iterators?
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsInBlock()) {
+ instruction->Accept(this);
+ }
+ }
+ }
+
// HInstruction visitors, sorted alphabetically.
void VisitAnd(HAnd* instruction) OVERRIDE;
void VisitArrayGet(HArrayGet* instruction) OVERRIDE;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c83340b1f6..950448136d 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -86,11 +86,7 @@ void HGraph::FindBackEdges(ArenaBitVector* visited) {
}
}
-static void RemoveAsUser(HInstruction* instruction) {
- for (size_t i = 0; i < instruction->InputCount(); i++) {
- instruction->RemoveAsUserOfInput(i);
- }
-
+static void RemoveEnvironmentUses(HInstruction* instruction) {
for (HEnvironment* environment = instruction->GetEnvironment();
environment != nullptr;
environment = environment->GetParent()) {
@@ -102,6 +98,14 @@ static void RemoveAsUser(HInstruction* instruction) {
}
}
+static void RemoveAsUser(HInstruction* instruction) {
+ for (size_t i = 0; i < instruction->InputCount(); i++) {
+ instruction->RemoveAsUserOfInput(i);
+ }
+
+ RemoveEnvironmentUses(instruction);
+}
+
void HGraph::RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const {
for (size_t i = 0; i < blocks_.size(); ++i) {
if (!visited.IsBitSet(i)) {
@@ -162,7 +166,7 @@ GraphAnalysisResult HGraph::BuildDominatorTree() {
// (6) Compute the dominance information and the reverse post order.
ComputeDominanceInformation();
- // (7) Analyze loops discover through back edge analysis, and
+ // (7) Analyze loops discovered through back edge analysis, and
// set the loop information on each block.
GraphAnalysisResult result = AnalyzeLoops();
if (result != kAnalysisSuccess) {
@@ -247,7 +251,7 @@ void HGraph::ComputeDominanceInformation() {
}
// Populate `dominated_blocks_` information after computing all dominators.
- // The potential presence of irreducible loops require to do it after.
+ // The potential presence of irreducible loops requires to do it after.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (!block->IsEntryBlock()) {
@@ -460,7 +464,7 @@ void HGraph::SimplifyCFG() {
if (block->IsLoopHeader()) {
SimplifyLoop(block);
} else if (!block->IsEntryBlock() && block->GetFirstInstruction()->IsSuspendCheck()) {
- // We are being called by the dead code elimiation pass, and what used to be
+ // We are being called by the dead code elimination pass, and what used to be
// a loop got dismantled. Just remove the suspend check.
block->RemoveInstruction(block->GetFirstInstruction());
}
@@ -1007,6 +1011,11 @@ bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const {
}
}
+void HInstruction::RemoveEnvironment() {
+ RemoveEnvironmentUses(this);
+ environment_ = nullptr;
+}
+
void HInstruction::ReplaceWith(HInstruction* other) {
DCHECK(other != nullptr);
for (HUseIterator<HInstruction*> it(GetUses()); !it.Done(); it.Advance()) {
@@ -1654,21 +1663,77 @@ void HBasicBlock::DisconnectAndDelete() {
// iteration.
DCHECK(dominated_blocks_.empty());
- // (1) Remove the block from all loops it is included in.
- for (HLoopInformationOutwardIterator it(*this); !it.Done(); it.Advance()) {
- HLoopInformation* loop_info = it.Current();
- loop_info->Remove(this);
- if (loop_info->IsBackEdge(*this)) {
- // If this was the last back edge of the loop, we deliberately leave the
- // loop in an inconsistent state and will fail GraphChecker unless the
- // entire loop is removed during the pass.
- loop_info->RemoveBackEdge(this);
+ // The following steps gradually remove the block from all its dependants in
+ // post order (b/27683071).
+
+ // (1) Store a basic block that we'll use in step (5) to find loops to be updated.
+ // We need to do this before step (4) which destroys the predecessor list.
+ HBasicBlock* loop_update_start = this;
+ if (IsLoopHeader()) {
+ HLoopInformation* loop_info = GetLoopInformation();
+ // All other blocks in this loop should have been removed because the header
+ // was their dominator.
+ // Note that we do not remove `this` from `loop_info` as it is unreachable.
+ DCHECK(!loop_info->IsIrreducible());
+ DCHECK_EQ(loop_info->GetBlocks().NumSetBits(), 1u);
+ DCHECK_EQ(static_cast<uint32_t>(loop_info->GetBlocks().GetHighestBitSet()), GetBlockId());
+ loop_update_start = loop_info->GetPreHeader();
+ }
+
+ // (2) Disconnect the block from its successors and update their phis.
+ for (HBasicBlock* successor : successors_) {
+ // Delete this block from the list of predecessors.
+ size_t this_index = successor->GetPredecessorIndexOf(this);
+ successor->predecessors_.erase(successor->predecessors_.begin() + this_index);
+
+ // Check that `successor` has other predecessors, otherwise `this` is the
+ // dominator of `successor` which violates the order DCHECKed at the top.
+ DCHECK(!successor->predecessors_.empty());
+
+ // Remove this block's entries in the successor's phis. Skip exceptional
+ // successors because catch phi inputs do not correspond to predecessor
+ // blocks but throwing instructions. The inputs of the catch phis will be
+ // updated in step (3).
+ if (!successor->IsCatchBlock()) {
+ if (successor->predecessors_.size() == 1u) {
+ // The successor has just one predecessor left. Replace phis with the only
+ // remaining input.
+ for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ HPhi* phi = phi_it.Current()->AsPhi();
+ phi->ReplaceWith(phi->InputAt(1 - this_index));
+ successor->RemovePhi(phi);
+ }
+ } else {
+ for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ phi_it.Current()->AsPhi()->RemoveInputAt(this_index);
+ }
+ }
}
}
+ successors_.clear();
- // (2) Disconnect the block from its predecessors and update their
+ // (3) Remove instructions and phis. Instructions should have no remaining uses
+ // except in catch phis. If an instruction is used by a catch phi at `index`,
+ // remove `index`-th input of all phis in the catch block since they are
+ // guaranteed dead. Note that we may miss dead inputs this way but the
+ // graph will always remain consistent.
+ for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* insn = it.Current();
+ RemoveUsesOfDeadInstruction(insn);
+ RemoveInstruction(insn);
+ }
+ for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* insn = it.Current()->AsPhi();
+ RemoveUsesOfDeadInstruction(insn);
+ RemovePhi(insn);
+ }
+
+ // (4) Disconnect the block from its predecessors and update their
// control-flow instructions.
for (HBasicBlock* predecessor : predecessors_) {
+ // We should not see any back edges as they would have been removed by step (3).
+ DCHECK(!IsInLoop() || !GetLoopInformation()->IsBackEdge(*predecessor));
+
HInstruction* last_instruction = predecessor->GetLastInstruction();
if (last_instruction->IsTryBoundary() && !IsCatchBlock()) {
// This block is the only normal-flow successor of the TryBoundary which
@@ -1712,58 +1777,25 @@ void HBasicBlock::DisconnectAndDelete() {
}
predecessors_.clear();
- // (3) Disconnect the block from its successors and update their phis.
- for (HBasicBlock* successor : successors_) {
- // Delete this block from the list of predecessors.
- size_t this_index = successor->GetPredecessorIndexOf(this);
- successor->predecessors_.erase(successor->predecessors_.begin() + this_index);
-
- // Check that `successor` has other predecessors, otherwise `this` is the
- // dominator of `successor` which violates the order DCHECKed at the top.
- DCHECK(!successor->predecessors_.empty());
-
- // Remove this block's entries in the successor's phis. Skip exceptional
- // successors because catch phi inputs do not correspond to predecessor
- // blocks but throwing instructions. Their inputs will be updated in step (4).
- if (!successor->IsCatchBlock()) {
- if (successor->predecessors_.size() == 1u) {
- // The successor has just one predecessor left. Replace phis with the only
- // remaining input.
- for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- HPhi* phi = phi_it.Current()->AsPhi();
- phi->ReplaceWith(phi->InputAt(1 - this_index));
- successor->RemovePhi(phi);
- }
- } else {
- for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- phi_it.Current()->AsPhi()->RemoveInputAt(this_index);
- }
- }
+ // (5) Remove the block from all loops it is included in. Skip the inner-most
+ // loop if this is the loop header (see definition of `loop_update_start`)
+ // because the loop header's predecessor list has been destroyed in step (4).
+ for (HLoopInformationOutwardIterator it(*loop_update_start); !it.Done(); it.Advance()) {
+ HLoopInformation* loop_info = it.Current();
+ loop_info->Remove(this);
+ if (loop_info->IsBackEdge(*this)) {
+ // If this was the last back edge of the loop, we deliberately leave the
+ // loop in an inconsistent state and will fail GraphChecker unless the
+ // entire loop is removed during the pass.
+ loop_info->RemoveBackEdge(this);
}
}
- successors_.clear();
- // (4) Remove instructions and phis. Instructions should have no remaining uses
- // except in catch phis. If an instruction is used by a catch phi at `index`,
- // remove `index`-th input of all phis in the catch block since they are
- // guaranteed dead. Note that we may miss dead inputs this way but the
- // graph will always remain consistent.
- for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* insn = it.Current();
- RemoveUsesOfDeadInstruction(insn);
- RemoveInstruction(insn);
- }
- for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
- HPhi* insn = it.Current()->AsPhi();
- RemoveUsesOfDeadInstruction(insn);
- RemovePhi(insn);
- }
-
- // Disconnect from the dominator.
+ // (6) Disconnect from the dominator.
dominator_->RemoveDominatedBlock(this);
SetDominator(nullptr);
- // Delete from the graph, update reverse post order.
+ // (7) Delete from the graph, update reverse post order.
graph_->DeleteDeadEmptyBlock(this);
SetGraph(nullptr);
}
@@ -2364,6 +2396,59 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckReq
}
}
+bool HLoadString::InstructionDataEquals(HInstruction* other) const {
+ HLoadString* other_load_string = other->AsLoadString();
+ if (string_index_ != other_load_string->string_index_ ||
+ GetPackedFields() != other_load_string->GetPackedFields()) {
+ return false;
+ }
+ LoadKind load_kind = GetLoadKind();
+ if (HasAddress(load_kind)) {
+ return GetAddress() == other_load_string->GetAddress();
+ } else if (HasStringReference(load_kind)) {
+ return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile());
+ } else {
+ DCHECK(HasDexCacheReference(load_kind)) << load_kind;
+ // If the string indexes and dex files are the same, dex cache element offsets
+ // must also be the same, so we don't need to compare them.
+ return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile());
+ }
+}
+
+void HLoadString::SetLoadKindInternal(LoadKind load_kind) {
+ // Once sharpened, the load kind should not be changed again.
+ DCHECK_EQ(GetLoadKind(), LoadKind::kDexCacheViaMethod);
+ SetPackedField<LoadKindField>(load_kind);
+
+ if (load_kind != LoadKind::kDexCacheViaMethod) {
+ RemoveAsUserOfInput(0u);
+ SetRawInputAt(0u, nullptr);
+ }
+ if (!NeedsEnvironment()) {
+ RemoveEnvironment();
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) {
+ switch (rhs) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ return os << "BootImageLinkTimeAddress";
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ return os << "BootImageLinkTimePcRelative";
+ case HLoadString::LoadKind::kBootImageAddress:
+ return os << "BootImageAddress";
+ case HLoadString::LoadKind::kDexCacheAddress:
+ return os << "DexCacheAddress";
+ case HLoadString::LoadKind::kDexCachePcRelative:
+ return os << "DexCachePcRelative";
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ return os << "DexCacheViaMethod";
+ default:
+ LOG(FATAL) << "Unknown HLoadString::LoadKind: " << static_cast<int>(rhs);
+ UNREACHABLE();
+ }
+}
+
void HInstruction::RemoveEnvironmentUsers() {
for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
HUseListNode<HEnvironment*>* user_node = use_it.Current();
@@ -2373,7 +2458,7 @@ void HInstruction::RemoveEnvironmentUsers() {
env_uses_.Clear();
}
-// Returns an instruction with the opposite boolean value from 'cond'.
+// Returns an instruction with the opposite Boolean value from 'cond'.
HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) {
ArenaAllocator* allocator = GetArena();
@@ -2402,10 +2487,10 @@ HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction*
return replacement;
} else if (cond->IsIntConstant()) {
HIntConstant* int_const = cond->AsIntConstant();
- if (int_const->IsZero()) {
+ if (int_const->IsFalse()) {
return GetIntConstant(1);
} else {
- DCHECK(int_const->IsOne());
+ DCHECK(int_const->IsTrue()) << int_const->GetValue();
return GetIntConstant(0);
}
} else {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a09ad00cdd..ba4242112b 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -72,8 +72,10 @@ static const int kDefaultNumberOfExceptionalPredecessors = 0;
static const int kDefaultNumberOfDominatedBlocks = 1;
static const int kDefaultNumberOfBackEdges = 1;
-static constexpr uint32_t kMaxIntShiftValue = 0x1f;
-static constexpr uint64_t kMaxLongShiftValue = 0x3f;
+// The maximum (meaningful) distance (31) that can be used in an integer shift/rotate operation.
+static constexpr int32_t kMaxIntShiftDistance = 0x1f;
+// The maximum (meaningful) distance (63) that can be used in a long shift/rotate operation.
+static constexpr int32_t kMaxLongShiftDistance = 0x3f;
static constexpr uint32_t kUnknownFieldIndex = static_cast<uint32_t>(-1);
static constexpr uint16_t kUnknownClassDefIndex = static_cast<uint16_t>(-1);
@@ -1993,6 +1995,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
environment_ = environment;
}
+ void RemoveEnvironment();
+
// Set the environment of this instruction, copying it from `environment`. While
// copying, the uses lists are being updated.
void CopyEnvironmentFrom(HEnvironment* environment) {
@@ -2427,8 +2431,13 @@ class HConstant : public HExpression<0> {
bool CanBeMoved() const OVERRIDE { return true; }
+ // Is this constant -1 in the arithmetic sense?
virtual bool IsMinusOne() const { return false; }
- virtual bool IsZero() const { return false; }
+ // Is this constant 0 in the arithmetic sense?
+ virtual bool IsArithmeticZero() const { return false; }
+ // Is this constant a 0-bit pattern?
+ virtual bool IsZeroBitPattern() const { return false; }
+ // Is this constant 1 in the arithmetic sense?
virtual bool IsOne() const { return false; }
virtual uint64_t GetValueAsUint64() const = 0;
@@ -2449,6 +2458,9 @@ class HNullConstant : public HConstant {
size_t ComputeHashCode() const OVERRIDE { return 0; }
+ // The null constant representation is a 0-bit pattern.
+ virtual bool IsZeroBitPattern() const { return true; }
+
DECLARE_INSTRUCTION(NullConstant);
private:
@@ -2476,9 +2488,15 @@ class HIntConstant : public HConstant {
size_t ComputeHashCode() const OVERRIDE { return GetValue(); }
bool IsMinusOne() const OVERRIDE { return GetValue() == -1; }
- bool IsZero() const OVERRIDE { return GetValue() == 0; }
+ bool IsArithmeticZero() const OVERRIDE { return GetValue() == 0; }
+ bool IsZeroBitPattern() const OVERRIDE { return GetValue() == 0; }
bool IsOne() const OVERRIDE { return GetValue() == 1; }
+ // Integer constants are used to encode Boolean values as well,
+ // where 1 means true and 0 means false.
+ bool IsTrue() const { return GetValue() == 1; }
+ bool IsFalse() const { return GetValue() == 0; }
+
DECLARE_INSTRUCTION(IntConstant);
private:
@@ -2509,7 +2527,8 @@ class HLongConstant : public HConstant {
size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
bool IsMinusOne() const OVERRIDE { return GetValue() == -1; }
- bool IsZero() const OVERRIDE { return GetValue() == 0; }
+ bool IsArithmeticZero() const OVERRIDE { return GetValue() == 0; }
+ bool IsZeroBitPattern() const OVERRIDE { return GetValue() == 0; }
bool IsOne() const OVERRIDE { return GetValue() == 1; }
DECLARE_INSTRUCTION(LongConstant);
@@ -2542,7 +2561,16 @@ class HFloatConstant : public HConstant {
bool IsMinusOne() const OVERRIDE {
return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f));
}
- bool IsZero() const OVERRIDE {
+ bool IsArithmeticZero() const OVERRIDE {
+ return std::fpclassify(value_) == FP_ZERO;
+ }
+ bool IsArithmeticPositiveZero() const {
+ return IsArithmeticZero() && !std::signbit(value_);
+ }
+ bool IsArithmeticNegativeZero() const {
+ return IsArithmeticZero() && std::signbit(value_);
+ }
+ bool IsZeroBitPattern() const OVERRIDE {
return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(0.0f);
}
bool IsOne() const OVERRIDE {
@@ -2584,7 +2612,16 @@ class HDoubleConstant : public HConstant {
bool IsMinusOne() const OVERRIDE {
return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0));
}
- bool IsZero() const OVERRIDE {
+ bool IsArithmeticZero() const OVERRIDE {
+ return std::fpclassify(value_) == FP_ZERO;
+ }
+ bool IsArithmeticPositiveZero() const {
+ return IsArithmeticZero() && !std::signbit(value_);
+ }
+ bool IsArithmeticNegativeZero() const {
+ return IsArithmeticZero() && std::signbit(value_);
+ }
+ bool IsZeroBitPattern() const OVERRIDE {
return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((0.0));
}
bool IsOne() const OVERRIDE {
@@ -3442,10 +3479,8 @@ class HCompare : public HBinaryOperation {
SideEffectsForArchRuntimeCalls(comparison_type),
dex_pc) {
SetPackedField<ComparisonBiasField>(bias);
- if (kIsDebugBuild) {
- DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(first->GetType()));
- DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(second->GetType()));
- }
+ DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(first->GetType()));
+ DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(second->GetType()));
}
template <typename T>
@@ -4447,37 +4482,39 @@ class HDivZeroCheck : public HExpression<1> {
class HShl : public HBinaryOperation {
public:
HShl(Primitive::Type result_type,
- HInstruction* left,
- HInstruction* right,
+ HInstruction* value,
+ HInstruction* distance,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+ DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+ DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
+ }
- template <typename T, typename U, typename V>
- T Compute(T x, U y, V max_shift_value) const {
- static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
- "V is not the unsigned integer type corresponding to T");
- return x << (y & max_shift_value);
+ template <typename T>
+ T Compute(T value, int32_t distance, int32_t max_shift_distance) const {
+ return value << (distance & max_shift_distance);
}
- HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+ HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+ UNREACHABLE();
}
- HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
- HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+ HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for float values";
UNREACHABLE();
}
- HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
- HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+ HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for double values";
UNREACHABLE();
}
@@ -4491,37 +4528,39 @@ class HShl : public HBinaryOperation {
class HShr : public HBinaryOperation {
public:
HShr(Primitive::Type result_type,
- HInstruction* left,
- HInstruction* right,
+ HInstruction* value,
+ HInstruction* distance,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+ DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+ DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
+ }
- template <typename T, typename U, typename V>
- T Compute(T x, U y, V max_shift_value) const {
- static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
- "V is not the unsigned integer type corresponding to T");
- return x >> (y & max_shift_value);
+ template <typename T>
+ T Compute(T value, int32_t distance, int32_t max_shift_distance) const {
+ return value >> (distance & max_shift_distance);
}
- HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+ HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+ UNREACHABLE();
}
- HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
- HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+ HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for float values";
UNREACHABLE();
}
- HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
- HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+ HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for double values";
UNREACHABLE();
}
@@ -4535,38 +4574,41 @@ class HShr : public HBinaryOperation {
class HUShr : public HBinaryOperation {
public:
HUShr(Primitive::Type result_type,
- HInstruction* left,
- HInstruction* right,
+ HInstruction* value,
+ HInstruction* distance,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+ DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+ DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
+ }
- template <typename T, typename U, typename V>
- T Compute(T x, U y, V max_shift_value) const {
- static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
- "V is not the unsigned integer type corresponding to T");
- V ux = static_cast<V>(x);
- return static_cast<T>(ux >> (y & max_shift_value));
+ template <typename T>
+ T Compute(T value, int32_t distance, int32_t max_shift_distance) const {
+ typedef typename std::make_unsigned<T>::type V;
+ V ux = static_cast<V>(value);
+ return static_cast<T>(ux >> (distance & max_shift_distance));
}
- HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+ HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+ UNREACHABLE();
}
- HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
- HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+ HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for float values";
UNREACHABLE();
}
- HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
- HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+ HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for double values";
UNREACHABLE();
}
@@ -4692,45 +4734,43 @@ class HRor : public HBinaryOperation {
public:
HRor(Primitive::Type result_type, HInstruction* value, HInstruction* distance)
: HBinaryOperation(result_type, value, distance) {
- if (kIsDebugBuild) {
- DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
- DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
- }
+ DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType()));
+ DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType()));
}
- template <typename T, typename U, typename V>
- T Compute(T x, U y, V max_shift_value) const {
- static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value,
- "V is not the unsigned integer type corresponding to T");
- V ux = static_cast<V>(x);
- if ((y & max_shift_value) == 0) {
+ template <typename T>
+ T Compute(T value, int32_t distance, int32_t max_shift_value) const {
+ typedef typename std::make_unsigned<T>::type V;
+ V ux = static_cast<V>(value);
+ if ((distance & max_shift_value) == 0) {
return static_cast<T>(ux);
} else {
const V reg_bits = sizeof(T) * 8;
- return static_cast<T>(ux >> (y & max_shift_value)) |
- (x << (reg_bits - (y & max_shift_value)));
+ return static_cast<T>(ux >> (distance & max_shift_value)) |
+ (value << (reg_bits - (distance & max_shift_value)));
}
}
- HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
+ HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc());
}
- HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
+ HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED,
+ HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for the (long, long) case.";
+ UNREACHABLE();
}
- HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
- HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED,
+ HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for float values";
UNREACHABLE();
}
- HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
- HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED,
+ HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for double values";
UNREACHABLE();
}
@@ -5519,32 +5559,117 @@ class HLoadClass : public HExpression<1> {
class HLoadString : public HExpression<1> {
public:
+ // Determines how to load the String.
+ enum class LoadKind {
+ // Use boot image String* address that will be known at link time.
+ // Used for boot image strings referenced by boot image code in non-PIC mode.
+ kBootImageLinkTimeAddress,
+
+ // Use PC-relative boot image String* address that will be known at link time.
+ // Used for boot image strings referenced by boot image code in PIC mode.
+ kBootImageLinkTimePcRelative,
+
+ // Use a known boot image String* address, embedded in the code by the codegen.
+ // Used for boot image strings referenced by apps in AOT- and JIT-compiled code.
+ // Note: codegen needs to emit a linker patch if indicated by compiler options'
+ // GetIncludePatchInformation().
+ kBootImageAddress,
+
+ // Load from the resolved strings array at an absolute address.
+ // Used for strings outside the boot image referenced by JIT-compiled code.
+ kDexCacheAddress,
+
+ // Load from resolved strings array in the dex cache using a PC-relative load.
+ // Used for strings outside boot image when we know that we can access
+ // the dex cache arrays using a PC-relative load.
+ kDexCachePcRelative,
+
+ // Load from resolved strings array accessed through the class loaded from
+ // the compiled method's own ArtMethod*. This is the default access type when
+ // all other types are unavailable.
+ kDexCacheViaMethod,
+
+ kLast = kDexCacheViaMethod
+ };
+
HLoadString(HCurrentMethod* current_method,
uint32_t string_index,
- uint32_t dex_pc,
- bool is_in_dex_cache)
+ const DexFile& dex_file,
+ uint32_t dex_pc)
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
string_index_(string_index) {
- SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
+ SetPackedFlag<kFlagIsInDexCache>(false);
+ SetPackedField<LoadKindField>(LoadKind::kDexCacheViaMethod);
+ load_data_.ref.dex_file = &dex_file;
SetRawInputAt(0, current_method);
}
- bool CanBeMoved() const OVERRIDE { return true; }
+ void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) {
+ DCHECK(HasAddress(load_kind));
+ load_data_.address = address;
+ SetLoadKindInternal(load_kind);
+ }
- bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return other->AsLoadString()->string_index_ == string_index_;
+ void SetLoadKindWithStringReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t string_index) {
+ DCHECK(HasStringReference(load_kind));
+ load_data_.ref.dex_file = &dex_file;
+ string_index_ = string_index;
+ SetLoadKindInternal(load_kind);
+ }
+
+ void SetLoadKindWithDexCacheReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t element_index) {
+ DCHECK(HasDexCacheReference(load_kind));
+ load_data_.ref.dex_file = &dex_file;
+ load_data_.ref.dex_cache_element_index = element_index;
+ SetLoadKindInternal(load_kind);
}
+ LoadKind GetLoadKind() const {
+ return GetPackedField<LoadKindField>();
+ }
+
+ const DexFile& GetDexFile() const;
+
+ uint32_t GetStringIndex() const {
+ DCHECK(HasStringReference(GetLoadKind()) || /* For slow paths. */ !IsInDexCache());
+ return string_index_;
+ }
+
+ uint32_t GetDexCacheElementOffset() const;
+
+ uint64_t GetAddress() const {
+ DCHECK(HasAddress(GetLoadKind()));
+ return load_data_.address;
+ }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE;
+
size_t ComputeHashCode() const OVERRIDE { return string_index_; }
- uint32_t GetStringIndex() const { return string_index_; }
+ // Will call the runtime if we need to load the string through
+ // the dex cache and the string is not guaranteed to be there yet.
+ bool NeedsEnvironment() const OVERRIDE {
+ LoadKind load_kind = GetLoadKind();
+ if (load_kind == LoadKind::kBootImageLinkTimeAddress ||
+ load_kind == LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == LoadKind::kBootImageAddress) {
+ return false;
+ }
+ return !IsInDexCache();
+ }
- // Will call the runtime if the string is not already in the dex cache.
- bool NeedsEnvironment() const OVERRIDE { return !IsInDexCache(); }
+ bool NeedsDexCacheOfDeclaringClass() const OVERRIDE {
+ return GetLoadKind() == LoadKind::kDexCacheViaMethod;
+ }
- bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
bool CanBeNull() const OVERRIDE { return false; }
- bool CanThrow() const OVERRIDE { return !IsInDexCache(); }
+ bool CanThrow() const OVERRIDE { return NeedsEnvironment(); }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
@@ -5552,17 +5677,83 @@ class HLoadString : public HExpression<1> {
bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+ void MarkInDexCache() {
+ SetPackedFlag<kFlagIsInDexCache>(true);
+ DCHECK(!NeedsEnvironment());
+ RemoveEnvironment();
+ }
+
+ size_t InputCount() const OVERRIDE {
+ return (InputAt(0) != nullptr) ? 1u : 0u;
+ }
+
+ void AddSpecialInput(HInstruction* special_input);
+
DECLARE_INSTRUCTION(LoadString);
private:
static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits;
- static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1;
+ static constexpr size_t kFieldLoadKind = kFlagIsInDexCache + 1;
+ static constexpr size_t kFieldLoadKindSize =
+ MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
+ static constexpr size_t kNumberOfLoadStringPackedBits = kFieldLoadKind + kFieldLoadKindSize;
static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
+
+ static bool HasStringReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageLinkTimeAddress ||
+ load_kind == LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == LoadKind::kDexCacheViaMethod;
+ }
+
+ static bool HasAddress(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageAddress || load_kind == LoadKind::kDexCacheAddress;
+ }
+
+ static bool HasDexCacheReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kDexCachePcRelative;
+ }
+
+ void SetLoadKindInternal(LoadKind load_kind);
+
+ // String index serves also as the hash code and it's also needed for slow-paths,
+ // so it must not be overwritten with other load data.
+ uint32_t string_index_;
- const uint32_t string_index_;
+ union {
+ struct {
+ const DexFile* dex_file; // For string reference and dex cache reference.
+ uint32_t dex_cache_element_index; // Only for dex cache reference.
+ } ref;
+ uint64_t address; // Up to 64-bit, needed for kDexCacheAddress on 64-bit targets.
+ } load_data_;
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
+std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs);
+
+// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
+inline const DexFile& HLoadString::GetDexFile() const {
+ DCHECK(HasStringReference(GetLoadKind()) || HasDexCacheReference(GetLoadKind()))
+ << GetLoadKind();
+ return *load_data_.ref.dex_file;
+}
+
+// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
+inline uint32_t HLoadString::GetDexCacheElementOffset() const {
+ DCHECK(HasDexCacheReference(GetLoadKind())) << GetLoadKind();
+ return load_data_.ref.dex_cache_element_index;
+}
+
+// Note: defined outside class to see operator<<(., HLoadString::LoadKind).
+inline void HLoadString::AddSpecialInput(HInstruction* special_input) {
+ // The special input is used for PC-relative loads on some architectures.
+ DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
+ GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind();
+ DCHECK(InputAt(0) == nullptr);
+ SetRawInputAt(0u, special_input);
+ special_input->AddUseAt(this, 0);
+}
/**
* Performs an initialization check on its Class object input.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 125c00d275..886c9e2ad4 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -259,8 +259,7 @@ class OptimizingCompiler FINAL : public Compiler {
explicit OptimizingCompiler(CompilerDriver* driver);
~OptimizingCompiler();
- bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
- OVERRIDE;
+ bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file) const OVERRIDE;
CompiledMethod* Compile(const DexFile::CodeItem* code_item,
uint32_t access_flags,
@@ -283,8 +282,6 @@ class OptimizingCompiler FINAL : public Compiler {
InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet())));
}
- void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
-
void Init() OVERRIDE;
void UnInit() const OVERRIDE;
@@ -365,12 +362,8 @@ OptimizingCompiler::~OptimizingCompiler() {
}
}
-void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const {
-}
-
bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED,
- const DexFile& dex_file ATTRIBUTE_UNUSED,
- CompilationUnit* cu ATTRIBUTE_UNUSED) const {
+ const DexFile& dex_file ATTRIBUTE_UNUSED) const {
return true;
}
@@ -662,9 +655,15 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
}
DexCompilationUnit dex_compilation_unit(
- nullptr, class_loader, Runtime::Current()->GetClassLinker(), dex_file, code_item,
- class_def_idx, method_idx, access_flags,
- nullptr, dex_cache);
+ class_loader,
+ Runtime::Current()->GetClassLinker(),
+ dex_file,
+ code_item,
+ class_def_idx,
+ method_idx,
+ access_flags,
+ /* verified_method */ nullptr,
+ dex_cache);
bool requires_barrier = dex_compilation_unit.IsConstructor()
&& compiler_driver->RequiresConstructorBarrier(Thread::Current(),
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index d281a9fc6c..dafbd3d7d1 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -80,6 +80,15 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
HandleInvoke(invoke);
}
+ void VisitLoadString(HLoadString* load_string) OVERRIDE {
+ HLoadString::LoadKind load_kind = load_string->GetLoadKind();
+ if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+ InitializePCRelativeBasePointer();
+ load_string->AddSpecialInput(base_);
+ }
+ }
+
void BinaryFP(HBinaryOperation* bin) {
HConstant* rhs = bin->InputAt(1)->AsConstant();
if (rhs != nullptr && Primitive::IsFloatingPointType(rhs->GetType())) {
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 2de0c1be72..d5b95d284a 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -135,13 +135,13 @@ TEST_F(PrettyPrinterTest, CFG3) {
TEST_F(PrettyPrinterTest, CFG4) {
const char* expected =
"BasicBlock 0, succ: 3\n"
- " 3: SuspendCheck\n"
- " 4: Goto 3\n"
+ " 2: SuspendCheck\n"
+ " 3: Goto 3\n"
"BasicBlock 1, pred: 3, 1, succ: 1\n"
- " 0: SuspendCheck\n"
- " 1: Goto 1\n"
+ " 5: SuspendCheck\n"
+ " 0: Goto 1\n"
"BasicBlock 3, pred: 0, succ: 1\n"
- " 5: Goto 1\n";
+ " 4: Goto 1\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::NOP,
@@ -204,20 +204,20 @@ TEST_F(PrettyPrinterTest, CFG7) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 1: IntConstant [5, 5]\n"
- " 11: SuspendCheck\n"
- " 12: Goto 1\n"
+ " 10: SuspendCheck\n"
+ " 11: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 5, 6\n"
" 5: Equal(1, 1) [6]\n"
" 6: If(5)\n"
"BasicBlock 2, pred: 6, 3, succ: 3\n"
" 7: Goto 3\n"
"BasicBlock 3, pred: 5, 2, succ: 2\n"
- " 8: SuspendCheck\n"
- " 9: Goto 2\n"
+ " 14: SuspendCheck\n"
+ " 8: Goto 2\n"
"BasicBlock 5, pred: 1, succ: 3\n"
- " 13: Goto 3\n"
+ " 12: Goto 3\n"
"BasicBlock 6, pred: 1, succ: 2\n"
- " 14: Goto 2\n";
+ " 13: Goto 2\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 75356c848b..95f10e0720 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -291,12 +291,12 @@ static bool MatchIfInstanceOf(HIf* ifInstruction,
if (rhs != nullptr) {
HInstruction* lhs = input->AsEqual()->GetLeastConstantLeft();
if (lhs->IsInstanceOf() && rhs->IsIntConstant()) {
- if (rhs->AsIntConstant()->IsOne()) {
+ if (rhs->AsIntConstant()->IsTrue()) {
// Case (1a)
*trueBranch = ifInstruction->IfTrueSuccessor();
} else {
// Case (2a)
- DCHECK(rhs->AsIntConstant()->IsZero());
+ DCHECK(rhs->AsIntConstant()->IsFalse()) << rhs->AsIntConstant()->GetValue();
*trueBranch = ifInstruction->IfFalseSuccessor();
}
*instanceOf = lhs->AsInstanceOf();
@@ -308,12 +308,12 @@ static bool MatchIfInstanceOf(HIf* ifInstruction,
if (rhs != nullptr) {
HInstruction* lhs = input->AsNotEqual()->GetLeastConstantLeft();
if (lhs->IsInstanceOf() && rhs->IsIntConstant()) {
- if (rhs->AsIntConstant()->IsZero()) {
+ if (rhs->AsIntConstant()->IsFalse()) {
// Case (1b)
*trueBranch = ifInstruction->IfTrueSuccessor();
} else {
// Case (2b)
- DCHECK(rhs->AsIntConstant()->IsOne());
+ DCHECK(rhs->AsIntConstant()->IsTrue()) << rhs->AsIntConstant()->GetValue();
*trueBranch = ifInstruction->IfFalseSuccessor();
}
*instanceOf = lhs->AsInstanceOf();
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 44bede8ac0..b1f9cbcdfa 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1931,7 +1931,7 @@ void RegisterAllocator::Resolve() {
// `GetSiblingAt` returns the sibling that contains a position, but there could be
// a lifetime hole in it. `CoversSlow` returns whether the interval is live at that
// position.
- if (sibling->CoversSlow(block->GetLifetimeStart())) {
+ if ((sibling != nullptr) && sibling->CoversSlow(block->GetLifetimeStart())) {
DCHECK(!sibling->HasRegister());
}
}
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 5e1d1d9954..7a1bb316e4 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -16,11 +16,20 @@
#include "sharpening.h"
+#include "base/casts.h"
+#include "class_linker.h"
#include "code_generator.h"
+#include "driver/dex_compilation_unit.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "driver/compiler_driver.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "handle_scope-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/string.h"
#include "nodes.h"
#include "runtime.h"
+#include "scoped_thread_state_change.h"
namespace art {
@@ -31,12 +40,13 @@ void HSharpening::Run() {
HInstruction* instruction = it.Current();
if (instruction->IsInvokeStaticOrDirect()) {
ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect());
+ } else if (instruction->IsLoadString()) {
+ ProcessLoadString(instruction->AsLoadString());
}
// TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder
// here. Rewrite it to avoid the CompilerDriver's reliance on verifier data
// because we know the type better when inlining.
- // TODO: HLoadClass, HLoadString - select PC relative dex cache array access if
- // available.
+ // TODO: HLoadClass - select better load kind if available.
}
}
}
@@ -143,4 +153,101 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
invoke->SetDispatchInfo(dispatch_info);
}
+void HSharpening::ProcessLoadString(HLoadString* load_string) {
+ DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
+ DCHECK(!load_string->IsInDexCache());
+
+ const DexFile& dex_file = load_string->GetDexFile();
+ uint32_t string_index = load_string->GetStringIndex();
+
+ bool is_in_dex_cache = false;
+ HLoadString::LoadKind desired_load_kind;
+ uint64_t address = 0u; // String or dex cache element address.
+ {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
+ ? compilation_unit_.GetDexCache()
+ : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
+
+ if (compiler_driver_->IsBootImage()) {
+ // Compiling boot image. Resolve the string and allocate it if needed.
+ DCHECK(!runtime->UseJit());
+ mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
+ CHECK(string != nullptr);
+ if (!compiler_driver_->GetSupportBootImageFixup()) {
+ // MIPS/MIPS64 or compiler_driver_test. Do not sharpen.
+ desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod;
+ } else {
+ DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
+ is_in_dex_cache = true;
+ desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
+ ? HLoadString::LoadKind::kBootImageLinkTimePcRelative
+ : HLoadString::LoadKind::kBootImageLinkTimeAddress;
+ }
+ } else if (runtime->UseJit()) {
+ // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+ // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+ mirror::String* string = dex_cache->GetResolvedString(string_index);
+ is_in_dex_cache = (string != nullptr);
+ if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
+ desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
+ address = reinterpret_cast64<uint64_t>(string);
+ } else {
+ // Note: If the string is not in the dex cache, the instruction needs environment
+ // and will not be inlined across dex files. Within a dex file, the slow-path helper
+ // loads the correct string and inlined frames are used correctly for OOM stack trace.
+ // TODO: Write a test for this.
+ desired_load_kind = HLoadString::LoadKind::kDexCacheAddress;
+ void* dex_cache_element_address = &dex_cache->GetStrings()[string_index];
+ address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
+ }
+ } else {
+ // AOT app compilation. Try to lookup the string without allocating if not found.
+ mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache);
+ if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
+ if (codegen_->GetCompilerOptions().GetCompilePic()) {
+ // Use PC-relative load from the dex cache if the dex file belongs
+ // to the oat file that we're currently compiling.
+ desired_load_kind = ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)
+ ? HLoadString::LoadKind::kDexCachePcRelative
+ : HLoadString::LoadKind::kDexCacheViaMethod;
+ } else {
+ desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
+ address = reinterpret_cast64<uint64_t>(string);
+ }
+ } else {
+ // Not JIT and the string is not in boot image.
+ desired_load_kind = HLoadString::LoadKind::kDexCachePcRelative;
+ }
+ }
+ }
+ if (is_in_dex_cache) {
+ load_string->MarkInDexCache();
+ }
+
+ HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind);
+ switch (load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ load_string->SetLoadKindWithStringReference(load_kind, dex_file, string_index);
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ case HLoadString::LoadKind::kDexCacheAddress:
+ DCHECK_NE(address, 0u);
+ load_string->SetLoadKindWithAddress(load_kind, address);
+ break;
+ case HLoadString::LoadKind::kDexCachePcRelative: {
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ DexCacheArraysLayout layout(pointer_size, &dex_file);
+ size_t element_index = layout.StringOffset(string_index);
+ load_string->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index);
+ break;
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index adae7007dd..24152f6b71 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -47,6 +47,7 @@ class HSharpening : public HOptimization {
private:
void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
+ void ProcessLoadString(HLoadString* load_string);
CodeGenerator* codegen_;
const DexCompilationUnit& compilation_unit_;
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 09ca8b7b44..294d00f8e2 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -458,6 +458,7 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() {
}
for (HNewInstance* new_instance : uninitialized_strings_) {
+ DCHECK(new_instance->IsInBlock());
// Replace NewInstance of String with NullConstant if not used prior to
// calling StringFactory. In case of deoptimization, the interpreter is
// expected to skip null check on the `this` argument of the StringFactory call.
@@ -972,7 +973,13 @@ void SsaBuilder::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
// Replacing the NewInstance might render it redundant. Keep a list of these
// to be visited once it is clear whether it is has remaining uses.
if (arg_this->IsNewInstance()) {
- uninitialized_strings_.push_back(arg_this->AsNewInstance());
+ HNewInstance* new_instance = arg_this->AsNewInstance();
+ // Note that in some rare cases (b/27847265), the same NewInstance may be seen
+ // multiple times. We should only consider it once for removal, so we
+ // ensure it is not added more than once.
+ if (!ContainsElement(uninitialized_strings_, new_instance)) {
+ uninitialized_strings_.push_back(new_instance);
+ }
} else {
DCHECK(arg_this->IsPhi());
// NewInstance is not the direct input of the StringFactory call. It might