MIPS32: Improve method invocation
Improvements include:
- CodeGeneratorMIPS::GenerateStaticOrDirectCall() supports:
- MethodLoadKind::kDirectAddressWithFixup (via literals)
- CodePtrLocation::kCallDirectWithFixup (via literals)
- MethodLoadKind::kDexCachePcRelative
- 32-bit literals to support the above (not ready for general-
purpose applications yet because RA is not saved in leaf
methods, but is clobbered on MIPS32R2 when simulating
PC-relative addressing (MIPS32R6 is OK because it has
PC-relative addressing with the lwpc instruction))
- shorter instruction sequences for recursive static/direct
calls
Tested:
- test-art-host-gtest
- test-art-target-gtest and test-art-target-run-test-optimizing on:
- MIPS32R2 QEMU
- CI20 board
- MIPS32R6 (2nd arch) QEMU
Change-Id: Id5b137ad32d5590487fd154c9a01d3b3e7e044ff
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 4d44c18..37f1c35 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -39,6 +39,10 @@
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr Register kMethodRegisterArgument = A0;
+// We'll maximize the range of a single load instruction for dex cache array accesses
+// by aligning offset -32768 with the offset of the first used element.
+static constexpr uint32_t kDexCacheArrayLwOffset = 0x8000;
+
Location MipsReturnLocation(Primitive::Type return_type) {
switch (return_type) {
case Primitive::kPrimBoolean:
@@ -477,7 +481,12 @@
instruction_visitor_(graph, this),
move_resolver_(graph->GetArena(), this),
assembler_(graph->GetArena(), &isa_features),
- isa_features_(isa_features) {
+ isa_features_(isa_features),
+ method_patches_(MethodReferenceComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ call_patches_(MethodReferenceComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Save RA (containing the return address) to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(RA));
}
@@ -948,6 +957,71 @@
}
}
+void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
+ DCHECK(linker_patches->empty());
+ size_t size =
+ method_patches_.size() +
+ call_patches_.size() +
+ pc_relative_dex_cache_patches_.size();
+ linker_patches->reserve(size);
+ for (const auto& entry : method_patches_) {
+ const MethodReference& target_method = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset,
+ target_method.dex_file,
+ target_method.dex_method_index));
+ }
+ for (const auto& entry : call_patches_) {
+ const MethodReference& target_method = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::CodePatch(literal_offset,
+ target_method.dex_file,
+ target_method.dex_method_index));
+ }
+ 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.high_label.IsBound());
+ uint32_t high_offset = __ GetLabelLocation(&info.high_label);
+ DCHECK(info.pc_rel_label.IsBound());
+ uint32_t pc_rel_offset = __ GetLabelLocation(&info.pc_rel_label);
+ linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(high_offset,
+ &dex_file,
+ pc_rel_offset,
+ base_element_offset));
+ }
+}
+
+CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeDexCacheArrayPatch(
+ const DexFile& dex_file, uint32_t element_offset) {
+ return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
+}
+
+CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::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* CodeGeneratorMIPS::DeduplicateMethodLiteral(MethodReference target_method,
+ MethodToLiteralMap* map) {
+ return map->GetOrCreate(
+ target_method,
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS::DeduplicateMethodAddressLiteral(MethodReference target_method) {
+ return DeduplicateMethodLiteral(target_method, &method_patches_);
+}
+
+Literal* CodeGeneratorMIPS::DeduplicateMethodCodeLiteral(MethodReference target_method) {
+ return DeduplicateMethodLiteral(target_method, &call_patches_);
+}
+
void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) {
MipsLabel done;
Register card = AT;
@@ -3741,12 +3815,38 @@
// art::PrepareForRegisterAllocation.
DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
+ HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
+ HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
+ bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+
+ // kDirectAddressWithFixup and kCallDirectWithFixup need no extra input on R6 because
+ // R6 has PC-relative addressing.
+ bool has_extra_input = !isR6 &&
+ ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
+ (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup));
+
+ if (invoke->HasPcRelativeDexCache()) {
+ // kDexCachePcRelative is mutually exclusive with
+ // kDirectAddressWithFixup/kCallDirectWithFixup.
+ CHECK(!has_extra_input);
+ has_extra_input = true;
+ }
+
IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
if (intrinsic.TryDispatch(invoke)) {
+ if (invoke->GetLocations()->CanCall() && has_extra_input) {
+ invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
+ }
return;
}
HandleInvoke(invoke);
+
+ // Add the extra input register if either the dex cache array base register
+ // or the PC-relative base register for accessing literals is needed.
+ if (has_extra_input) {
+ invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
+ }
}
static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
@@ -3771,42 +3871,103 @@
return HLoadClass::LoadKind::kDexCacheViaMethod;
}
+Register CodeGeneratorMIPS::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
+ Register temp) {
+ CHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
+ Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
+ if (!invoke->GetLocations()->Intrinsified()) {
+ return location.AsRegister<Register>();
+ }
+ // For intrinsics we allow any location, so it may be on the stack.
+ if (!location.IsRegister()) {
+ __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex());
+ return temp;
+ }
+ // For register locations, check if the register was saved. If so, get it from the stack.
+ // Note: There is a chance that the register was saved but not overwritten, so we could
+ // save one load. However, since this is just an intrinsic slow path we prefer this
+ // simple and more robust approach rather that trying to determine if that's the case.
+ SlowPathCode* slow_path = GetCurrentSlowPath();
+ DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
+ if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) {
+ int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>());
+ __ LoadFromOffset(kLoadWord, temp, SP, stack_offset);
+ return temp;
+ }
+ return location.AsRegister<Register>();
+}
+
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
MethodReference target_method ATTRIBUTE_UNUSED) {
- switch (desired_dispatch_info.method_load_kind) {
+ 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.
+ bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops();
+ bool fallback_load = true;
+ bool fallback_call = true;
+ switch (dispatch_info.method_load_kind) {
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement these types. For the moment, we fall back to kDexCacheViaMethod.
- return HInvokeStaticOrDirect::DispatchInfo {
- HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0u
- };
+ fallback_load = has_irreducible_loops;
+ break;
default:
+ fallback_load = false;
break;
}
- switch (desired_dispatch_info.code_ptr_location) {
+ switch (dispatch_info.code_ptr_location) {
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ fallback_call = has_irreducible_loops;
+ break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement these types. For the moment, we fall back to kCallArtMethod.
- return HInvokeStaticOrDirect::DispatchInfo {
- desired_dispatch_info.method_load_kind,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- desired_dispatch_info.method_load_data,
- 0u
- };
+ // TODO: Implement this type.
+ break;
default:
- return desired_dispatch_info;
+ fallback_call = false;
+ break;
}
+ if (fallback_load) {
+ dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
+ dispatch_info.method_load_data = 0;
+ }
+ if (fallback_call) {
+ dispatch_info.code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
+ dispatch_info.direct_code_ptr = 0;
+ }
+ return dispatch_info;
}
void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// All registers are assumed to be correctly set up per the calling convention.
-
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
- switch (invoke->GetMethodLoadKind()) {
+ HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind();
+ HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation();
+ bool isR6 = isa_features_.IsR6();
+ // kDirectAddressWithFixup and kCallDirectWithFixup have no extra input on R6 because
+ // R6 has PC-relative addressing.
+ bool has_extra_input = invoke->HasPcRelativeDexCache() ||
+ (!isR6 &&
+ ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
+ (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup)));
+ Register base_reg = has_extra_input
+ ? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>())
+ : ZERO;
+
+ // For better instruction scheduling we load the direct code pointer before the method pointer.
+ switch (code_ptr_location) {
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
+ // T9 = invoke->GetDirectCodePtr();
+ __ LoadConst32(T9, invoke->GetDirectCodePtr());
+ break;
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ // T9 = code address from literal pool with link-time patch.
+ __ LoadLiteral(T9, base_reg, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
+ break;
+ default:
+ break;
+ }
+
+ switch (method_load_kind) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
// temp = thread->string_init_entrypoint
__ LoadFromOffset(kLoadWord,
@@ -3821,11 +3982,18 @@
__ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement these types.
- // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
- LOG(FATAL) << "Unsupported";
- UNREACHABLE();
+ __ LoadLiteral(temp.AsRegister<Register>(),
+ base_reg,
+ DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
+ break;
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
+ HMipsDexCacheArraysBase* base =
+ invoke->InputAt(invoke->GetSpecialInputIndex())->AsMipsDexCacheArraysBase();
+ int32_t offset =
+ invoke->GetDexCacheArrayOffset() - base->GetElementOffset() - kDexCacheArrayLwOffset;
+ __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset);
+ break;
+ }
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
Register reg = temp.AsRegister<Register>();
@@ -3856,20 +4024,19 @@
}
}
- switch (invoke->GetCodePtrLocation()) {
+ switch (code_ptr_location) {
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
- __ Jalr(&frame_entry_label_, T9);
+ __ Bal(&frame_entry_label_);
break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // LR = invoke->GetDirectCodePtr();
- __ LoadConst32(T9, invoke->GetDirectCodePtr());
- // LR()
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ // T9 prepared above for better instruction scheduling.
+ // T9()
__ Jalr(T9);
__ Nop();
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement these types.
+ // TODO: Implement this type.
// Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
LOG(FATAL) << "Unsupported";
UNREACHABLE();
@@ -5140,6 +5307,57 @@
}
}
+void LocationsBuilderMIPS::VisitMipsComputeBaseMethodAddress(
+ HMipsComputeBaseMethodAddress* insn) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(insn, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorMIPS::VisitMipsComputeBaseMethodAddress(
+ HMipsComputeBaseMethodAddress* insn) {
+ LocationSummary* locations = insn->GetLocations();
+ Register reg = locations->Out().AsRegister<Register>();
+
+ CHECK(!codegen_->GetInstructionSetFeatures().IsR6());
+
+ // Generate a dummy PC-relative call to obtain PC.
+ __ Nal();
+ // Grab the return address off RA.
+ __ Move(reg, RA);
+
+ // Remember this offset (the obtained PC value) for later use with constant area.
+ __ BindPcRelBaseLabel();
+}
+
+void LocationsBuilderMIPS::VisitMipsDexCacheArraysBase(HMipsDexCacheArraysBase* base) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorMIPS::VisitMipsDexCacheArraysBase(HMipsDexCacheArraysBase* base) {
+ Register reg = base->GetLocations()->Out().AsRegister<Register>();
+ CodeGeneratorMIPS::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
+
+ if (codegen_->GetInstructionSetFeatures().IsR6()) {
+ __ Bind(&info->high_label);
+ __ Bind(&info->pc_rel_label);
+ // Add a 32-bit offset to PC.
+ __ Auipc(reg, /* placeholder */ 0x1234);
+ __ Addiu(reg, reg, /* placeholder */ 0x5678);
+ } else {
+ // Generate a dummy PC-relative call to obtain PC.
+ __ Nal();
+ __ Bind(&info->high_label);
+ __ Lui(reg, /* placeholder */ 0x1234);
+ __ Bind(&info->pc_rel_label);
+ __ Ori(reg, reg, /* placeholder */ 0x5678);
+ // Add a 32-bit offset to PC.
+ __ Addu(reg, reg, RA);
+ }
+}
+
void LocationsBuilderMIPS::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
// The trampoline uses the same calling convention as dex calling conventions,
// except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 6487f28..08f74c0 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -285,6 +285,9 @@
MipsAssembler* GetAssembler() OVERRIDE { return &assembler_; }
const MipsAssembler& GetAssembler() const OVERRIDE { return assembler_; }
+ // Emit linker patches.
+ void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
+
void MarkGCCard(Register object, Register value);
// Register allocation.
@@ -372,7 +375,39 @@
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
+ // 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) { }
+ 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 for the instruction loading the most significant half of the offset that's added to PC
+ // to form the base address (the least significant half is loaded with the instruction that
+ // follows).
+ MipsLabel high_label;
+ // Label for the instruction corresponding to PC+0.
+ MipsLabel pc_rel_label;
+ };
+
+ PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset);
+
private:
+ Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
+
+ using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
+
+ 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.
MipsLabel* block_labels_;
MipsLabel frame_entry_label_;
@@ -382,6 +417,12 @@
MipsAssembler assembler_;
const MipsInstructionSetFeatures& isa_features_;
+ // Method patch info, map MethodReference to a literal for method address and method code.
+ MethodToLiteralMap method_patches_;
+ MethodToLiteralMap call_patches_;
+ // PC-relative patch info for each HMipsDexCacheArraysBase.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS);
};
diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc
new file mode 100644
index 0000000..0f42d9c
--- /dev/null
+++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_cache_array_fixups_mips.h"
+
+#include "base/arena_containers.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
+
+namespace art {
+namespace mips {
+
+/**
+ * Finds instructions that need the dex cache arrays base as an input.
+ */
+class DexCacheArrayFixupsVisitor : public HGraphVisitor {
+ public:
+ explicit DexCacheArrayFixupsVisitor(HGraph* graph)
+ : HGraphVisitor(graph),
+ dex_cache_array_bases_(std::less<const DexFile*>(),
+ // Attribute memory use to code generator.
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {}
+
+ void MoveBasesIfNeeded() {
+ for (const auto& entry : dex_cache_array_bases_) {
+ // Bring the base closer to the first use (previously, it was in the
+ // entry block) and relieve some pressure on the register allocator
+ // while avoiding recalculation of the base in a loop.
+ HMipsDexCacheArraysBase* base = entry.second;
+ base->MoveBeforeFirstUserAndOutOfLoops();
+ }
+ }
+
+ private:
+ 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.
+ if (invoke->HasPcRelativeDexCache()) {
+ // Initialize base for target method dex file if needed.
+ MethodReference target_method = invoke->GetTargetMethod();
+ HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(*target_method.dex_file);
+ // Update the element offset in base.
+ DexCacheArraysLayout layout(kMipsPointerSize, target_method.dex_file);
+ base->UpdateElementOffset(layout.MethodOffset(target_method.dex_method_index));
+ // Add the special argument base to the method.
+ DCHECK(!invoke->HasCurrentMethodInput());
+ invoke->AddSpecialInput(base);
+ }
+ }
+
+ HMipsDexCacheArraysBase* GetOrCreateDexCacheArrayBase(const DexFile& dex_file) {
+ return dex_cache_array_bases_.GetOrCreate(
+ &dex_file,
+ [this, &dex_file]() {
+ HMipsDexCacheArraysBase* base =
+ new (GetGraph()->GetArena()) HMipsDexCacheArraysBase(dex_file);
+ HBasicBlock* entry_block = GetGraph()->GetEntryBlock();
+ // Insert the base at the start of the entry block, move it to a better
+ // position later in MoveBaseIfNeeded().
+ entry_block->InsertInstructionBefore(base, entry_block->GetFirstInstruction());
+ return base;
+ });
+ }
+
+ using DexCacheArraysBaseMap =
+ ArenaSafeMap<const DexFile*, HMipsDexCacheArraysBase*, std::less<const DexFile*>>;
+ DexCacheArraysBaseMap dex_cache_array_bases_;
+};
+
+void DexCacheArrayFixups::Run() {
+ if (graph_->HasIrreducibleLoops()) {
+ // Do not run this optimization, as irreducible loops do not work with an instruction
+ // that can be live-in at the irreducible loop header.
+ return;
+ }
+ DexCacheArrayFixupsVisitor visitor(graph_);
+ visitor.VisitInsertionOrder();
+ visitor.MoveBasesIfNeeded();
+}
+
+} // namespace mips
+} // namespace art
diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.h b/compiler/optimizing/dex_cache_array_fixups_mips.h
new file mode 100644
index 0000000..c8def28
--- /dev/null
+++ b/compiler/optimizing/dex_cache_array_fixups_mips.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_DEX_CACHE_ARRAY_FIXUPS_MIPS_H_
+#define ART_COMPILER_OPTIMIZING_DEX_CACHE_ARRAY_FIXUPS_MIPS_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+namespace mips {
+
+class DexCacheArrayFixups : public HOptimization {
+ public:
+ DexCacheArrayFixups(HGraph* graph, OptimizingCompilerStats* stats)
+ : HOptimization(graph, "dex_cache_array_fixups_mips", stats) {}
+
+ void Run() OVERRIDE;
+};
+
+} // namespace mips
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_DEX_CACHE_ARRAY_FIXUPS_MIPS_H_
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index abc8d57..0f0ef26 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1307,7 +1307,13 @@
M(Arm64IntermediateAddress, Instruction)
#endif
+#ifndef ART_ENABLE_CODEGEN_mips
#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M)
+#else
+#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \
+ M(MipsComputeBaseMethodAddress, Instruction) \
+ M(MipsDexCacheArraysBase, Instruction)
+#endif
#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(M)
@@ -6551,6 +6557,9 @@
#ifdef ART_ENABLE_CODEGEN_arm64
#include "nodes_arm64.h"
#endif
+#ifdef ART_ENABLE_CODEGEN_mips
+#include "nodes_mips.h"
+#endif
#ifdef ART_ENABLE_CODEGEN_x86
#include "nodes_x86.h"
#endif
diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h
new file mode 100644
index 0000000..de77245
--- /dev/null
+++ b/compiler/optimizing/nodes_mips.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_NODES_MIPS_H_
+#define ART_COMPILER_OPTIMIZING_NODES_MIPS_H_
+
+namespace art {
+
+// Compute the address of the method for MIPS Constant area support.
+class HMipsComputeBaseMethodAddress : public HExpression<0> {
+ public:
+ // Treat the value as an int32_t, but it is really a 32 bit native pointer.
+ HMipsComputeBaseMethodAddress()
+ : HExpression(Primitive::kPrimInt, SideEffects::None(), kNoDexPc) {}
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ DECLARE_INSTRUCTION(MipsComputeBaseMethodAddress);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HMipsComputeBaseMethodAddress);
+};
+
+class HMipsDexCacheArraysBase : public HExpression<0> {
+ public:
+ explicit HMipsDexCacheArraysBase(const DexFile& dex_file)
+ : HExpression(Primitive::kPrimInt, SideEffects::None(), kNoDexPc),
+ dex_file_(&dex_file),
+ element_offset_(static_cast<size_t>(-1)) { }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ void UpdateElementOffset(size_t element_offset) {
+ // We'll maximize the range of a single load instruction for dex cache array accesses
+ // by aligning offset -32768 with the offset of the first used element.
+ element_offset_ = std::min(element_offset_, element_offset);
+ }
+
+ const DexFile& GetDexFile() const {
+ return *dex_file_;
+ }
+
+ size_t GetElementOffset() const {
+ return element_offset_;
+ }
+
+ DECLARE_INSTRUCTION(MipsDexCacheArraysBase);
+
+ private:
+ const DexFile* dex_file_;
+ size_t element_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(HMipsDexCacheArraysBase);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_NODES_MIPS_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c9a4bfe..d703b0f 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -28,6 +28,11 @@
#include "instruction_simplifier_arm64.h"
#endif
+#ifdef ART_ENABLE_CODEGEN_mips
+#include "dex_cache_array_fixups_mips.h"
+#include "pc_relative_fixups_mips.h"
+#endif
+
#ifdef ART_ENABLE_CODEGEN_x86
#include "pc_relative_fixups_x86.h"
#endif
@@ -462,6 +467,20 @@
break;
}
#endif
+#ifdef ART_ENABLE_CODEGEN_mips
+ case kMips: {
+ mips::PcRelativeFixups* pc_relative_fixups =
+ new (arena) mips::PcRelativeFixups(graph, codegen, stats);
+ mips::DexCacheArrayFixups* dex_cache_array_fixups =
+ new (arena) mips::DexCacheArrayFixups(graph, stats);
+ HOptimization* mips_optimizations[] = {
+ pc_relative_fixups,
+ dex_cache_array_fixups
+ };
+ RunOptimizations(mips_optimizations, arraysize(mips_optimizations), pass_observer);
+ break;
+ }
+#endif
#ifdef ART_ENABLE_CODEGEN_x86
case kX86: {
x86::PcRelativeFixups* pc_relative_fixups =
diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc
new file mode 100644
index 0000000..ba405cd
--- /dev/null
+++ b/compiler/optimizing/pc_relative_fixups_mips.cc
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pc_relative_fixups_mips.h"
+#include "code_generator_mips.h"
+#include "intrinsics_mips.h"
+
+namespace art {
+namespace mips {
+
+/**
+ * Finds instructions that need the constant area base as an input.
+ */
+class PCRelativeHandlerVisitor : public HGraphVisitor {
+ public:
+ PCRelativeHandlerVisitor(HGraph* graph, CodeGenerator* codegen)
+ : HGraphVisitor(graph),
+ codegen_(down_cast<CodeGeneratorMIPS*>(codegen)),
+ base_(nullptr) {}
+
+ void MoveBaseIfNeeded() {
+ if (base_ != nullptr) {
+ // Bring the base closer to the first use (previously, it was in the
+ // entry block) and relieve some pressure on the register allocator
+ // while avoiding recalculation of the base in a loop.
+ base_->MoveBeforeFirstUserAndOutOfLoops();
+ }
+ }
+
+ private:
+ void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
+ HandleInvoke(invoke);
+ }
+
+ void InitializePCRelativeBasePointer() {
+ // Ensure we only initialize the pointer once.
+ if (base_ != nullptr) {
+ return;
+ }
+ // Insert the base at the start of the entry block, move it to a better
+ // position later in MoveBaseIfNeeded().
+ base_ = new (GetGraph()->GetArena()) HMipsComputeBaseMethodAddress();
+ HBasicBlock* entry_block = GetGraph()->GetEntryBlock();
+ entry_block->InsertInstructionBefore(base_, entry_block->GetFirstInstruction());
+ DCHECK(base_ != nullptr);
+ }
+
+ void HandleInvoke(HInvoke* invoke) {
+ // If this is an invoke-static/-direct with PC-relative dex cache array
+ // addressing, we need the PC-relative address base.
+ HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect();
+ if (invoke_static_or_direct != nullptr) {
+ HInvokeStaticOrDirect::MethodLoadKind method_load_kind =
+ invoke_static_or_direct->GetMethodLoadKind();
+ HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
+ invoke_static_or_direct->GetCodePtrLocation();
+
+ bool has_extra_input =
+ (method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) ||
+ (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup);
+
+ // We can't add a pointer to the constant area if we already have a current
+ // method pointer. This may arise when sharpening doesn't remove the current
+ // method pointer from the invoke.
+ if (invoke_static_or_direct->HasCurrentMethodInput()) {
+ DCHECK(!invoke_static_or_direct->HasPcRelativeDexCache());
+ CHECK(!has_extra_input); // TODO: review this.
+ return;
+ }
+
+ if (has_extra_input && !WillHaveCallFreeIntrinsicsCodeGen(invoke)) {
+ InitializePCRelativeBasePointer();
+ // Add the extra parameter base_.
+ invoke_static_or_direct->AddSpecialInput(base_);
+ }
+ }
+ }
+
+ bool WillHaveCallFreeIntrinsicsCodeGen(HInvoke* invoke) {
+ if (invoke->GetIntrinsic() != Intrinsics::kNone) {
+ // This invoke may have intrinsic code generation defined. However, we must
+ // now also determine if this code generation is truly there and call-free
+ // (not unimplemented, no bail on instruction features, or call on slow path).
+ // This is done by actually calling the locations builder on the instruction
+ // and clearing out the locations once result is known. We assume this
+ // call only has creating locations as side effects!
+ IntrinsicLocationsBuilderMIPS builder(codegen_);
+ bool success = builder.TryDispatch(invoke) && !invoke->GetLocations()->CanCall();
+ invoke->SetLocations(nullptr);
+ return success;
+ }
+ return false;
+ }
+
+ CodeGeneratorMIPS* codegen_;
+
+ // The generated HMipsComputeBaseMethodAddress in the entry block needed as an
+ // input to the HMipsLoadFromConstantTable instructions.
+ HMipsComputeBaseMethodAddress* base_;
+};
+
+void PcRelativeFixups::Run() {
+ CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen_);
+ if (mips_codegen->GetInstructionSetFeatures().IsR6()) {
+ // Do nothing for R6 because it has PC-relative addressing.
+ // TODO: review. Move this check into RunArchOptimizations()?
+ return;
+ }
+ if (graph_->HasIrreducibleLoops()) {
+ // Do not run this optimization, as irreducible loops do not work with an instruction
+ // that can be live-in at the irreducible loop header.
+ return;
+ }
+ PCRelativeHandlerVisitor visitor(graph_, codegen_);
+ visitor.VisitInsertionOrder();
+ visitor.MoveBaseIfNeeded();
+}
+
+} // namespace mips
+} // namespace art
diff --git a/compiler/optimizing/pc_relative_fixups_mips.h b/compiler/optimizing/pc_relative_fixups_mips.h
new file mode 100644
index 0000000..1e8b071
--- /dev/null
+++ b/compiler/optimizing/pc_relative_fixups_mips.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_PC_RELATIVE_FIXUPS_MIPS_H_
+#define ART_COMPILER_OPTIMIZING_PC_RELATIVE_FIXUPS_MIPS_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class CodeGenerator;
+
+namespace mips {
+
+class PcRelativeFixups : public HOptimization {
+ public:
+ PcRelativeFixups(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats)
+ : HOptimization(graph, "pc_relative_fixups_mips", stats),
+ codegen_(codegen) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ CodeGenerator* codegen_;
+};
+
+} // namespace mips
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_PC_RELATIVE_FIXUPS_MIPS_H_