Merge "Fix inlining and lse bugs with unresolved access."
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 8750aa8..fb116bb 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -375,6 +375,7 @@
timings_logger_(timer),
compiler_context_(nullptr),
support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
+ dex_files_for_oat_file_(nullptr),
compiled_method_storage_(swap_fd) {
DCHECK(compiler_options_ != nullptr);
DCHECK(verification_results_ != nullptr);
@@ -1371,8 +1372,7 @@
}
DexCacheArraysLayout CompilerDriver::GetDexCacheArraysLayout(const DexFile* dex_file) {
- // Currently only image dex caches have fixed array layout.
- return IsImage() && GetSupportBootImageFixup()
+ return ContainsElement(GetDexFilesForOatFile(), dex_file)
? DexCacheArraysLayout(GetInstructionSetPointerSize(instruction_set_), dex_file)
: DexCacheArraysLayout();
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 485cdcf..4ed4dc6 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -39,6 +39,7 @@
#include "runtime.h"
#include "safe_map.h"
#include "thread_pool.h"
+#include "utils/array_ref.h"
#include "utils/dex_cache_arrays_layout.h"
namespace art {
@@ -101,7 +102,20 @@
~CompilerDriver();
- void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files,
+ // Set dex files that will be stored in the oat file after being compiled.
+ void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
+ dex_files_for_oat_file_ = &dex_files;
+ }
+
+ // Get dex file that will be stored in the oat file after being compiled.
+ ArrayRef<const DexFile* const> GetDexFilesForOatFile() const {
+ return (dex_files_for_oat_file_ != nullptr)
+ ? ArrayRef<const DexFile* const>(*dex_files_for_oat_file_)
+ : ArrayRef<const DexFile* const>();
+ }
+
+ void CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
@@ -661,6 +675,9 @@
bool support_boot_image_fixup_;
+ // List of dex files that will be stored in the oat file.
+ const std::vector<const DexFile*>* dex_files_for_oat_file_;
+
CompiledMethodStorage compiled_method_storage_;
friend class CompileClassVisitor;
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 7e31a7a..21d582e 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -76,6 +76,7 @@
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
dex_file->EnableWrite();
}
+ compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
t.NewTiming("WriteElf");
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index ac38f3d..13754fd 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -36,7 +36,8 @@
// of code. To avoid any alignment discrepancies for the final chunk, we always align the
// offset after reserving of writing any chunk.
uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
- bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset, MethodReference(nullptr, 0u),
+ bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset,
+ MethodReference(nullptr, 0u),
aligned_offset);
if (needs_thunk) {
thunk_locations_.push_back(aligned_offset);
@@ -94,7 +95,8 @@
// We need the MethodReference for that.
if (!unprocessed_patches_.empty() &&
next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
- bool needs_thunk = ReserveSpaceProcessPatches(quick_code_offset, method_ref,
+ bool needs_thunk = ReserveSpaceProcessPatches(quick_code_offset,
+ method_ref,
next_aligned_offset);
if (needs_thunk) {
// A single thunk will cover all pending patches.
@@ -156,7 +158,10 @@
// If still unresolved, check if we have a thunk within range.
if (thunk_locations_.empty() ||
patch_offset - thunk_locations_.back() > max_negative_displacement_) {
- return next_aligned_offset - patch_offset > max_positive_displacement_;
+ // No thunk in range, we need a thunk if the next aligned offset
+ // is out of range, or if we're at the end of all code.
+ return (next_aligned_offset - patch_offset > max_positive_displacement_) ||
+ (quick_code_offset == next_aligned_offset); // End of code.
}
} else {
uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index 5515313..a259cda 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -233,6 +233,36 @@
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
+TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) {
+ constexpr uint32_t missing_method_index = 1024u;
+ auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
+ constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
+ ArrayRef<const uint8_t> method3_code(method3_raw_code);
+ ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
+ LinkerPatch method3_patches[] = {
+ LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index),
+ };
+
+ constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
+ bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ method3_code,
+ ArrayRef<const LinkerPatch>(method3_patches),
+ just_over_max_negative_disp - bl_offset_in_method3);
+ ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
+ ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
+
+ // Check linked code.
+ uint32_t method3_offset = GetMethodOffset(3u);
+ uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2);
+ uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
+ ASSERT_EQ(diff & 1u, 0u);
+ ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
+ auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
+ EXPECT_TRUE(CheckThunk(thunk_offset));
+}
+
TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) {
auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0);
constexpr uint32_t bl_offset_in_method1 = 3u * 2u; // After NOPs.
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
index 2a426b5..0bfef5e 100644
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc
@@ -386,6 +386,39 @@
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
+TEST_F(Arm64RelativePatcherTestDefault, CallTrampolineTooFar) {
+ constexpr uint32_t missing_method_index = 1024u;
+ auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
+ constexpr uint32_t bl_offset_in_last_method = 1u * 4u; // After NOPs.
+ ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
+ ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
+ LinkerPatch last_method_patches[] = {
+ LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index),
+ };
+
+ constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
+ uint32_t last_method_idx = Create2MethodsWithGap(
+ kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
+ ArrayRef<const LinkerPatch>(last_method_patches),
+ just_over_max_negative_disp - bl_offset_in_last_method);
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t last_method_offset = GetMethodOffset(last_method_idx);
+ ASSERT_EQ(method1_offset,
+ last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
+ ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
+
+ // Check linked code.
+ uint32_t thunk_offset =
+ CompiledCode::AlignCode(last_method_offset + last_method_code.size(), kArm64);
+ uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
+ ASSERT_EQ(diff & 3u, 0u);
+ ASSERT_LT(diff, 128 * MB);
+ auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
+ ArrayRef<const uint8_t>(expected_code)));
+ EXPECT_TRUE(CheckThunk(thunk_offset));
+}
+
TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarAfter) {
auto method1_raw_code = GenNopsAndBl(1u, kBlPlus0);
constexpr uint32_t bl_offset_in_method1 = 1u * 4u; // After NOPs.
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 06576cc..ea3cb66 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -98,6 +98,7 @@
jobject class_loader = nullptr;
if (kCompile) {
TimingLogger timings2("OatTest::WriteRead", false, false);
+ compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 8d9794b..3dc3b7f 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -56,6 +56,8 @@
// S registers. Therefore there is no need to block it.
static constexpr DRegister DTMP = D31;
+static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6;
+
#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())->
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
@@ -513,17 +515,6 @@
uint32_t new_position = __ GetAdjustedPosition(old_position);
stack_map_stream_.SetStackMapNativePcOffset(i, new_position);
}
- // Adjust native pc offsets of block labels.
- for (HBasicBlock* block : *block_order_) {
- // Get the label directly from block_labels_ rather than through GetLabelOf() to avoid
- // FirstNonEmptyBlock() which could lead to adjusting a label more than once.
- DCHECK_LT(block->GetBlockId(), GetGraph()->GetBlocks().size());
- Label* block_label = &block_labels_[block->GetBlockId()];
- DCHECK_EQ(block_label->IsBound(), !block->IsSingleJump());
- if (block_label->IsBound()) {
- __ AdjustLabelPosition(block_label);
- }
- }
// Adjust pc offsets for the disassembly information.
if (disasm_info_ != nullptr) {
GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
@@ -538,10 +529,6 @@
it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end);
}
}
- // Adjust pc offsets for relative call patches.
- for (MethodPatchInfo<Label>& info : relative_call_patches_) {
- __ AdjustLabelPosition(&info.label);
- }
CodeGenerator::Finalize(allocator);
}
@@ -732,7 +719,8 @@
}
void CodeGeneratorARM::Bind(HBasicBlock* block) {
- __ Bind(GetLabelOf(block));
+ Label* label = GetLabelOf(block);
+ __ BindTrackedLabel(label);
}
Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
@@ -5255,7 +5243,7 @@
break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
relative_call_patches_.emplace_back(invoke->GetTargetMethod());
- __ Bind(&relative_call_patches_.back().label);
+ __ BindTrackedLabel(&relative_call_patches_.back().label);
// Arbitrarily branch to the BL itself, override at link time.
__ bl(&relative_call_patches_.back().label);
break;
@@ -5378,25 +5366,64 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
+ if (switch_instr->GetNumEntries() >= kPackedSwitchJumpTableThreshold &&
+ codegen_->GetAssembler()->IsThumb()) {
+ locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
+ if (switch_instr->GetStartValue() != 0) {
+ locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
+ }
+ }
}
void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
int32_t lower_bound = switch_instr->GetStartValue();
- int32_t num_entries = switch_instr->GetNumEntries();
+ uint32_t num_entries = switch_instr->GetNumEntries();
LocationSummary* locations = switch_instr->GetLocations();
Register value_reg = locations->InAt(0).AsRegister<Register>();
HBasicBlock* default_block = switch_instr->GetDefaultBlock();
- // Create a series of compare/jumps.
- const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- for (int32_t i = 0; i < num_entries; i++) {
- GenerateCompareWithImmediate(value_reg, lower_bound + i);
- __ b(codegen_->GetLabelOf(successors[i]), EQ);
- }
+ if (num_entries < kPackedSwitchJumpTableThreshold || !codegen_->GetAssembler()->IsThumb()) {
+ // Create a series of compare/jumps.
+ const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
+ for (uint32_t i = 0; i < num_entries; i++) {
+ GenerateCompareWithImmediate(value_reg, lower_bound + i);
+ __ b(codegen_->GetLabelOf(successors[i]), EQ);
+ }
- // And the default for any other value.
- if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
- __ b(codegen_->GetLabelOf(default_block));
+ // And the default for any other value.
+ if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
+ __ b(codegen_->GetLabelOf(default_block));
+ }
+ } else {
+ // Create a table lookup.
+ Register temp_reg = locations->GetTemp(0).AsRegister<Register>();
+
+ // Materialize a pointer to the switch table
+ std::vector<Label*> labels(num_entries);
+ const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
+ for (uint32_t i = 0; i < num_entries; i++) {
+ labels[i] = codegen_->GetLabelOf(successors[i]);
+ }
+ JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg);
+
+ // Remove the bias.
+ Register key_reg;
+ if (lower_bound != 0) {
+ key_reg = locations->GetTemp(1).AsRegister<Register>();
+ __ AddConstant(key_reg, value_reg, -lower_bound);
+ } else {
+ key_reg = value_reg;
+ }
+
+ // Check whether the value is in the table, jump to default block if not.
+ __ CmpConstant(key_reg, num_entries - 1);
+ __ b(codegen_->GetLabelOf(default_block), Condition::HI);
+
+ // Load the displacement from the table.
+ __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2));
+
+ // Dispatch is a direct add to the PC (for Thumb2).
+ __ EmitJumpTableDispatch(table, temp_reg);
}
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 8b28ff9..68fb0ac 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1652,7 +1652,8 @@
// Update the meta information surrounding blocks:
// (1) the graph they are now in,
// (2) the reverse post order of that graph,
- // (3) the potential loop information they are now in.
+ // (3) the potential loop information they are now in,
+ // (4) try block membership.
// We don't add the entry block, the exit block, and the first block, which
// has been merged with `at`.
@@ -1668,41 +1669,47 @@
size_t index_of_at = IndexOfElement(outer_graph->reverse_post_order_, at);
MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
- // Do a reverse post order of the blocks in the callee and do (1), (2),
- // and (3) to the blocks that apply.
- HLoopInformation* info = at->GetLoopInformation();
+ HLoopInformation* loop_info = at->GetLoopInformation();
+ // Copy TryCatchInformation if `at` is a try block, not if it is a catch block.
+ TryCatchInformation* try_catch_info = at->IsTryBlock() ? at->GetTryCatchInformation() : nullptr;
+
+ // Do a reverse post order of the blocks in the callee and do (1), (2), (3)
+ // and (4) to the blocks that apply.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* current = it.Current();
if (current != exit_block_ && current != entry_block_ && current != first) {
DCHECK(!current->IsInLoop());
+ DCHECK(current->GetTryCatchInformation() == nullptr);
DCHECK(current->GetGraph() == this);
current->SetGraph(outer_graph);
outer_graph->AddBlock(current);
outer_graph->reverse_post_order_[++index_of_at] = current;
- if (info != nullptr) {
- current->SetLoopInformation(info);
+ if (loop_info != nullptr) {
+ current->SetLoopInformation(loop_info);
for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) {
loop_it.Current()->Add(current);
}
}
+ current->SetTryCatchInformation(try_catch_info);
}
}
- // Do (1), (2), and (3) to `to`.
+ // Do (1), (2), (3) and (4) to `to`.
to->SetGraph(outer_graph);
outer_graph->AddBlock(to);
outer_graph->reverse_post_order_[++index_of_at] = to;
- if (info != nullptr) {
- to->SetLoopInformation(info);
+ if (loop_info != nullptr) {
+ to->SetLoopInformation(loop_info);
for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) {
loop_it.Current()->Add(to);
}
- if (info->IsBackEdge(*at)) {
+ if (loop_info->IsBackEdge(*at)) {
// Only `to` can become a back edge, as the inlined blocks
// are predecessors of `to`.
- info->ReplaceBackEdge(at, to);
+ loop_info->ReplaceBackEdge(at, to);
}
}
+ to->SetTryCatchInformation(try_catch_info);
}
// Update the next instruction id of the outer graph, so that instructions
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 6632f95..8cb2cfc 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -492,6 +492,8 @@
RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
+ MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
+
// TODO: Update passes incompatible with try/catch so we have the same
// pipeline for all methods.
if (graph->HasTryCatch()) {
@@ -507,8 +509,6 @@
RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
} else {
- MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
-
HOptimization* optimizations2[] = {
// BooleanSimplifier depends on the InstructionSimplifier removing
// redundant suspend checks to recognize empty blocks.
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 6494964..a128079 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -20,6 +20,7 @@
#include "utils/dex_cache_arrays_layout-inl.h"
#include "driver/compiler_driver.h"
#include "nodes.h"
+#include "runtime.h"
namespace art {
@@ -78,7 +79,13 @@
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive;
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf;
} else {
+ bool use_pc_relative_instructions =
+ ((direct_method == 0u || direct_code == static_cast<uintptr_t>(-1))) &&
+ ContainsElement(compiler_driver_->GetDexFilesForOatFile(), target_method.dex_file);
if (direct_method != 0u) { // Should we use a direct pointer to the method?
+ // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
+ // kDirectAddress would be fine for image methods, we don't support it at the moment.
+ DCHECK(!Runtime::Current()->UseJit());
if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now?
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
method_load_data = direct_method;
@@ -87,24 +94,25 @@
}
} else { // Use dex cache.
DCHECK_EQ(target_method.dex_file, &graph_->GetDexFile());
- DexCacheArraysLayout layout =
- compiler_driver_->GetDexCacheArraysLayout(target_method.dex_file);
- if (layout.Valid()) { // Can we use PC-relative access to the dex cache arrays?
+ if (use_pc_relative_instructions) { // Can we use PC-relative access to the dex cache arrays?
+ DCHECK(!Runtime::Current()->UseJit());
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
+ DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
+ &graph_->GetDexFile());
method_load_data = layout.MethodOffset(target_method.dex_method_index);
} else { // We must go through the ArtMethod's pointer to resolved methods.
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
}
}
if (direct_code != 0u) { // Should we use a direct pointer to the code?
+ // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
+ // while kCallDirect would be fine for image methods, we don't support it at the moment.
+ DCHECK(!Runtime::Current()->UseJit());
if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now?
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
direct_code_ptr = direct_code;
- } else if (compiler_driver_->IsImage() ||
- target_method.dex_file == &graph_->GetDexFile()) {
+ } else if (use_pc_relative_instructions) {
// Use PC-relative calls for invokes within a multi-dex oat file.
- // TODO: Recognize when the target dex file is within the current oat file for
- // app compilation. At the moment we recognize only the boot image as multi-dex.
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative;
} else { // The direct pointer will be known at link time.
// NOTE: This is used for app->boot calls when compiling an app against
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 807beda..68e3956 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -16,6 +16,8 @@
#include "assembler_arm.h"
+#include <algorithm>
+
#include "base/bit_utils.h"
#include "base/logging.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -922,5 +924,24 @@
return value | i << 26 | imm3 << 12 | a << 7;
}
+void ArmAssembler::FinalizeTrackedLabels() {
+ if (!tracked_labels_.empty()) {
+ // This array should be sorted, as assembly is generated in linearized order. It isn't
+ // technically required, but GetAdjustedPosition() used in AdjustLabelPosition() can take
+ // advantage of it. So ensure that it's actually the case.
+ DCHECK(std::is_sorted(
+ tracked_labels_.begin(),
+ tracked_labels_.end(),
+ [](const Label* lhs, const Label* rhs) { return lhs->Position() < rhs->Position(); }));
+
+ Label* last_label = nullptr; // Track duplicates, we must not adjust twice.
+ for (Label* label : tracked_labels_) {
+ DCHECK_NE(label, last_label);
+ AdjustLabelPosition(label);
+ last_label = label;
+ }
+ }
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index d59bc6b..4a6e6d7 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -77,6 +77,45 @@
DISALLOW_COPY_AND_ASSIGN(Literal);
};
+// Jump table: table of labels emitted after the literals. Similar to literals.
+class JumpTable {
+ public:
+ explicit JumpTable(std::vector<Label*>&& labels)
+ : label_(), anchor_label_(), labels_(std::move(labels)) {
+ }
+
+ uint32_t GetSize() const {
+ return static_cast<uint32_t>(labels_.size()) * sizeof(uint32_t);
+ }
+
+ const std::vector<Label*>& GetData() const {
+ return labels_;
+ }
+
+ Label* GetLabel() {
+ return &label_;
+ }
+
+ const Label* GetLabel() const {
+ return &label_;
+ }
+
+ Label* GetAnchorLabel() {
+ return &anchor_label_;
+ }
+
+ const Label* GetAnchorLabel() const {
+ return &anchor_label_;
+ }
+
+ private:
+ Label label_;
+ Label anchor_label_;
+ std::vector<Label*> labels_;
+
+ DISALLOW_COPY_AND_ASSIGN(JumpTable);
+};
+
class ShifterOperand {
public:
ShifterOperand() : type_(kUnknown), rm_(kNoRegister), rs_(kNoRegister),
@@ -685,6 +724,8 @@
AddConstant(rd, rd, value, cond, set_cc);
}
+ virtual void CmpConstant(Register rn, int32_t value, Condition cond = AL) = 0;
+
// Load and Store. May clobber IP.
virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
void LoadSImmediate(SRegister sd, float value, Condition cond = AL) {
@@ -996,11 +1037,43 @@
b(label);
}
+ // Jump table support. This is split into three functions:
+ //
+ // * CreateJumpTable creates the internal metadata to track the jump targets, and emits code to
+ // load the base address of the jump table.
+ //
+ // * EmitJumpTableDispatch emits the code to actually jump, assuming that the right table value
+ // has been loaded into a register already.
+ //
+ // * FinalizeTables emits the jump table into the literal pool. This can only be called after the
+ // labels for the jump targets have been finalized.
+
+ // Create a jump table for the given labels that will be emitted when finalizing. Create a load
+ // sequence (or placeholder) that stores the base address into the given register. When the table
+ // is emitted, offsets will be relative to the location EmitJumpTableDispatch was called on (the
+ // anchor).
+ virtual JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) = 0;
+
+ // Emit the jump-table jump, assuming that the right value was loaded into displacement_reg.
+ virtual void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) = 0;
+
+ // Bind a Label that needs to be updated by the assembler in FinalizeCode() if its position
+ // changes due to branch/literal fixup.
+ void BindTrackedLabel(Label* label) {
+ Bind(label);
+ tracked_labels_.push_back(label);
+ }
+
protected:
// Returns whether or not the given register is used for passing parameters.
static int RegisterCompare(const Register* reg1, const Register* reg2) {
return *reg1 - *reg2;
}
+
+ void FinalizeTrackedLabels();
+
+ // Tracked labels. Use a vector, as we need to sort before adjusting.
+ std::vector<Label*> tracked_labels_;
};
// Slowpath entered when Thread::Current()->_exception is non-null
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 6e7c828..a7dbacd 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1385,6 +1385,21 @@
}
}
+void Arm32Assembler::CmpConstant(Register rn, int32_t value, Condition cond) {
+ ShifterOperand shifter_op;
+ if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
+ cmp(rn, shifter_op, cond);
+ } else if (ShifterOperandCanHoldArm32(~value, &shifter_op)) {
+ cmn(rn, shifter_op, cond);
+ } else {
+ movw(IP, Low16Bits(value), cond);
+ uint16_t value_high = High16Bits(value);
+ if (value_high != 0) {
+ movt(IP, value_high, cond);
+ }
+ cmp(rn, ShifterOperand(IP), cond);
+ }
+}
void Arm32Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
ShifterOperand shifter_op;
@@ -1584,6 +1599,23 @@
b(label, NE);
}
+JumpTable* Arm32Assembler::CreateJumpTable(std::vector<Label*>&& labels ATTRIBUTE_UNUSED,
+ Register base_reg ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "CreateJumpTable is not supported on ARM32";
+ UNREACHABLE();
+}
+
+void Arm32Assembler::EmitJumpTableDispatch(JumpTable* jump_table ATTRIBUTE_UNUSED,
+ Register displacement_reg ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "EmitJumpTableDispatch is not supported on ARM32";
+ UNREACHABLE();
+}
+
+void Arm32Assembler::FinalizeCode() {
+ ArmAssembler::FinalizeCode();
+ // Currently the arm32 assembler does not support fixups, and thus no tracking. We must not call
+ // FinalizeTrackedLabels(), which would lead to an abort.
+}
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 4646538..5233dcb 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -261,6 +261,8 @@
void AddConstant(Register rd, Register rn, int32_t value,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+ void CmpConstant(Register rn, int32_t value, Condition cond = AL) OVERRIDE;
+
// Load and Store. May clobber IP.
void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
void MarkExceptionHandler(Label* label) OVERRIDE;
@@ -308,6 +310,11 @@
void MemoryBarrier(ManagedRegister scratch) OVERRIDE;
+ JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) OVERRIDE;
+ void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) OVERRIDE;
+
+ void FinalizeCode() OVERRIDE;
+
private:
void EmitType01(Condition cond,
int type,
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index cc87856..fb3aa1e 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -92,7 +92,7 @@
label->BindTo(bound_pc);
}
-void Thumb2Assembler::BindLiterals() {
+uint32_t Thumb2Assembler::BindLiterals() {
// We don't add the padding here, that's done only after adjusting the Fixup sizes.
uint32_t code_size = buffer_.Size();
for (Literal& lit : literals_) {
@@ -100,6 +100,15 @@
BindLabel(label, code_size);
code_size += lit.GetSize();
}
+ return code_size;
+}
+
+void Thumb2Assembler::BindJumpTables(uint32_t code_size) {
+ for (JumpTable& table : jump_tables_) {
+ Label* label = table.GetLabel();
+ BindLabel(label, code_size);
+ code_size += table.GetSize();
+ }
}
void Thumb2Assembler::AdjustFixupIfNeeded(Fixup* fixup, uint32_t* current_code_size,
@@ -144,7 +153,7 @@
AdjustFixupIfNeeded(fixup, ¤t_code_size, &fixups_to_recalculate);
} while (!fixups_to_recalculate.empty());
- if ((current_code_size & 2) != 0 && !literals_.empty()) {
+ if ((current_code_size & 2) != 0 && (!literals_.empty() || !jump_tables_.empty())) {
// If we need to add padding before literals, this may just push some out of range,
// so recalculate all load literals. This makes up for the fact that we don't mark
// load literal as a dependency of all previous Fixups even though it actually is.
@@ -173,6 +182,13 @@
label->Reinitialize();
label->BindTo(old_position + literals_adjustment);
}
+ for (JumpTable& table : jump_tables_) {
+ Label* label = table.GetLabel();
+ DCHECK(label->IsBound());
+ int old_position = label->Position();
+ label->Reinitialize();
+ label->BindTo(old_position + literals_adjustment);
+ }
}
return current_code_size;
@@ -229,6 +245,43 @@
}
}
+void Thumb2Assembler::EmitJumpTables() {
+ if (!jump_tables_.empty()) {
+ // Jump tables require 4 byte alignment. (We don't support byte and half-word jump tables.)
+ uint32_t code_size = buffer_.Size();
+ DCHECK_ALIGNED(code_size, 2);
+ if ((code_size & 2u) != 0u) {
+ Emit16(0);
+ }
+ for (JumpTable& table : jump_tables_) {
+ // Bulk ensure capacity, as this may be large.
+ size_t orig_size = buffer_.Size();
+ buffer_.ExtendCapacity(orig_size + table.GetSize());
+#ifndef NDEBUG
+ buffer_.has_ensured_capacity_ = true;
+#endif
+
+ DCHECK_EQ(static_cast<size_t>(table.GetLabel()->Position()), buffer_.Size());
+ int32_t anchor_position = table.GetAnchorLabel()->Position() + 4;
+
+ for (Label* target : table.GetData()) {
+ // Ensure that the label was tracked, so that it will have the right position.
+ DCHECK(std::find(tracked_labels_.begin(), tracked_labels_.end(), target) !=
+ tracked_labels_.end());
+
+ int32_t offset = target->Position() - anchor_position;
+ buffer_.Emit<int32_t>(offset);
+ }
+
+#ifndef NDEBUG
+ buffer_.has_ensured_capacity_ = false;
+#endif
+ size_t new_size = buffer_.Size();
+ DCHECK_LE(new_size - orig_size, table.GetSize());
+ }
+ }
+}
+
inline int16_t Thumb2Assembler::BEncoding16(int32_t offset, Condition cond) {
DCHECK_ALIGNED(offset, 2);
int16_t encoding = B15 | B14;
@@ -382,12 +435,34 @@
return B31 | B30 | B29 | B28 | B27 | B23 | B22 | B20 | (rn << 16) | (rt << 12) | offset;
}
+inline int16_t Thumb2Assembler::AdrEncoding16(Register rd, int32_t offset) {
+ DCHECK(IsUint<10>(offset));
+ DCHECK(IsAligned<4>(offset));
+ DCHECK(!IsHighRegister(rd));
+ return B15 | B13 | (rd << 8) | (offset >> 2);
+}
+
+inline int32_t Thumb2Assembler::AdrEncoding32(Register rd, int32_t offset) {
+ DCHECK(IsUint<12>(offset));
+ // Bit 26: offset[11]
+ // Bits 14-12: offset[10-8]
+ // Bits 7-0: offset[7-0]
+ int32_t immediate_mask =
+ ((offset & (1 << 11)) << (26 - 11)) |
+ ((offset & (7 << 8)) << (12 - 8)) |
+ (offset & 0xFF);
+ return B31 | B30 | B29 | B28 | B25 | B19 | B18 | B17 | B16 | (rd << 8) | immediate_mask;
+}
+
void Thumb2Assembler::FinalizeCode() {
ArmAssembler::FinalizeCode();
- BindLiterals();
+ uint32_t size_after_literals = BindLiterals();
+ BindJumpTables(size_after_literals);
uint32_t adjusted_code_size = AdjustFixups();
EmitFixups(adjusted_code_size);
EmitLiterals();
+ FinalizeTrackedLabels();
+ EmitJumpTables();
}
bool Thumb2Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) {
@@ -1770,6 +1845,15 @@
case kLiteralFar:
return 14u;
+ case kLiteralAddr1KiB:
+ return 2u;
+ case kLiteralAddr4KiB:
+ return 4u;
+ case kLiteralAddr64KiB:
+ return 6u;
+ case kLiteralAddrFar:
+ return 10u;
+
case kLongOrFPLiteral1KiB:
return 4u;
case kLongOrFPLiteral256KiB:
@@ -1831,6 +1915,8 @@
case kLiteral1KiB:
case kLiteral4KiB:
case kLongOrFPLiteral1KiB:
+ case kLiteralAddr1KiB:
+ case kLiteralAddr4KiB:
DCHECK(diff >= 0 || (GetSize() == kLiteral1KiB && diff == -2));
diff += LiteralPoolPaddingSize(current_code_size);
// Load literal instructions round down the PC+4 to a multiple of 4, so if the PC
@@ -1843,12 +1929,14 @@
case kLiteral1MiB:
case kLiteral64KiB:
case kLongOrFPLiteral256KiB:
+ case kLiteralAddr64KiB:
DCHECK_GE(diff, 4); // The target must be at least 4 bytes after the ADD rX, PC.
diff -= 4; // One extra 32-bit MOV.
diff += LiteralPoolPaddingSize(current_code_size);
break;
case kLiteralFar:
case kLongOrFPLiteralFar:
+ case kLiteralAddrFar:
DCHECK_GE(diff, 8); // The target must be at least 4 bytes after the ADD rX, PC.
diff -= 8; // Extra MOVW+MOVT; both 32-bit.
diff += LiteralPoolPaddingSize(current_code_size);
@@ -1929,6 +2017,29 @@
// This encoding can reach any target.
break;
+ case kLiteralAddr1KiB:
+ DCHECK(!IsHighRegister(rn_));
+ if (IsUint<10>(GetOffset(current_code_size))) {
+ break;
+ }
+ current_code_size += IncreaseSize(kLiteralAddr4KiB);
+ FALLTHROUGH_INTENDED;
+ case kLiteralAddr4KiB:
+ if (IsUint<12>(GetOffset(current_code_size))) {
+ break;
+ }
+ current_code_size += IncreaseSize(kLiteralAddr64KiB);
+ FALLTHROUGH_INTENDED;
+ case kLiteralAddr64KiB:
+ if (IsUint<16>(GetOffset(current_code_size))) {
+ break;
+ }
+ current_code_size += IncreaseSize(kLiteralAddrFar);
+ FALLTHROUGH_INTENDED;
+ case kLiteralAddrFar:
+ // This encoding can reach any target.
+ break;
+
case kLongOrFPLiteral1KiB:
if (IsUint<10>(GetOffset(current_code_size))) {
break;
@@ -2055,6 +2166,42 @@
break;
}
+ case kLiteralAddr1KiB: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int16_t encoding = AdrEncoding16(rn_, GetOffset(code_size));
+ buffer->Store<int16_t>(location_, encoding);
+ break;
+ }
+ case kLiteralAddr4KiB: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int32_t encoding = AdrEncoding32(rn_, GetOffset(code_size));
+ buffer->Store<int16_t>(location_, encoding >> 16);
+ buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(encoding & 0xffff));
+ break;
+ }
+ case kLiteralAddr64KiB: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int32_t mov_encoding = MovwEncoding32(rn_, GetOffset(code_size));
+ int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
+ buffer->Store<int16_t>(location_, mov_encoding >> 16);
+ buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(mov_encoding & 0xffff));
+ buffer->Store<int16_t>(location_ + 4u, add_pc_encoding);
+ break;
+ }
+ case kLiteralAddrFar: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int32_t offset = GetOffset(code_size);
+ int32_t movw_encoding = MovwEncoding32(rn_, offset & 0xffff);
+ int32_t movt_encoding = MovtEncoding32(rn_, offset & ~0xffff);
+ int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
+ buffer->Store<int16_t>(location_, movw_encoding >> 16);
+ buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(movw_encoding & 0xffff));
+ buffer->Store<int16_t>(location_ + 4u, movt_encoding >> 16);
+ buffer->Store<int16_t>(location_ + 6u, static_cast<int16_t>(movt_encoding & 0xffff));
+ buffer->Store<int16_t>(location_ + 8u, add_pc_encoding);
+ break;
+ }
+
case kLongOrFPLiteral1KiB: {
int32_t encoding = LoadWideOrFpEncoding(PC, GetOffset(code_size)); // DCHECKs type_.
buffer->Store<int16_t>(location_, encoding >> 16);
@@ -3260,6 +3407,25 @@
}
}
+void Thumb2Assembler::CmpConstant(Register rn, int32_t value, Condition cond) {
+ // We prefer to select the shorter code sequence rather than selecting add for
+ // positive values and sub for negatives ones, which would slightly improve
+ // the readability of generated code for some constants.
+ ShifterOperand shifter_op;
+ if (ShifterOperandCanHold(kNoRegister, rn, CMP, value, &shifter_op)) {
+ cmp(rn, shifter_op, cond);
+ } else if (ShifterOperandCanHold(kNoRegister, rn, CMN, ~value, &shifter_op)) {
+ cmn(rn, shifter_op, cond);
+ } else {
+ CHECK(rn != IP);
+ movw(IP, Low16Bits(value), cond);
+ uint16_t value_high = High16Bits(value);
+ if (value_high != 0) {
+ movt(IP, value_high, cond);
+ }
+ cmp(rn, ShifterOperand(IP), cond);
+ }
+}
void Thumb2Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
ShifterOperand shifter_op;
@@ -3476,5 +3642,39 @@
b(label, NE);
}
}
+
+JumpTable* Thumb2Assembler::CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) {
+ jump_tables_.emplace_back(std::move(labels));
+ JumpTable* table = &jump_tables_.back();
+ DCHECK(!table->GetLabel()->IsBound());
+
+ bool use32bit = IsForced32Bit() || IsHighRegister(base_reg);
+ uint32_t location = buffer_.Size();
+ Fixup::Size size = use32bit ? Fixup::kLiteralAddr4KiB : Fixup::kLiteralAddr1KiB;
+ FixupId fixup_id = AddFixup(Fixup::LoadLiteralAddress(location, base_reg, size));
+ Emit16(static_cast<uint16_t>(table->GetLabel()->position_));
+ table->GetLabel()->LinkTo(fixup_id);
+ if (use32bit) {
+ Emit16(0);
+ }
+ DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
+
+ return table;
+}
+
+void Thumb2Assembler::EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) {
+ CHECK(!IsForced32Bit()) << "Forced 32-bit dispatch not implemented yet";
+ // 32-bit ADD doesn't support PC as an input, so we need a two-instruction sequence:
+ // SUB ip, ip, #0
+ // ADD pc, ip, reg
+ // TODO: Implement.
+
+ // The anchor's position needs to be fixed up before we can compute offsets - so make it a tracked
+ // label.
+ BindTrackedLabel(jump_table->GetAnchorLabel());
+
+ add(PC, PC, ShifterOperand(displacement_reg));
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 055b137..38fd244 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_UTILS_ARM_ASSEMBLER_THUMB2_H_
#include <deque>
+#include <utility>
#include <vector>
#include "base/logging.h"
@@ -304,6 +305,8 @@
void AddConstant(Register rd, Register rn, int32_t value,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+ void CmpConstant(Register rn, int32_t value, Condition cond = AL) OVERRIDE;
+
// Load and Store. May clobber IP.
void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
void MarkExceptionHandler(Label* label) OVERRIDE;
@@ -358,6 +361,12 @@
force_32bit_ = true;
}
+ // Emit an ADR (or a sequence of instructions) to load the jump table address into base_reg. This
+ // will generate a fixup.
+ JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) OVERRIDE;
+ // Emit an ADD PC, X to dispatch a jump-table jump. This will generate a fixup.
+ void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) OVERRIDE;
+
private:
typedef uint16_t FixupId;
@@ -399,6 +408,7 @@
kCompareAndBranchXZero, // cbz/cbnz.
kLoadLiteralNarrow, // Load narrrow integer literal.
kLoadLiteralWide, // Load wide integer literal.
+ kLoadLiteralAddr, // Load address of literal (used for jump table).
kLoadFPLiteralSingle, // Load FP literal single.
kLoadFPLiteralDouble, // Load FP literal double.
};
@@ -429,6 +439,16 @@
// MOV rX, imm16 + MOVT rX, imm16 + ADD rX, pc + LDR rX, [rX]; any offset; 14 bytes.
kLiteralFar,
+ // Load literal base addr.
+ // ADR rX, label; X < 8; 8 bit immediate, shifted to 10 bit. 2 bytes.
+ kLiteralAddr1KiB,
+ // ADR rX, label; 4KiB offset. 4 bytes.
+ kLiteralAddr4KiB,
+ // MOV rX, imm16 + ADD rX, pc; 64KiB offset. 6 bytes.
+ kLiteralAddr64KiB,
+ // MOV rX, imm16 + MOVT rX, imm16 + ADD rX, pc; any offset; 10 bytes.
+ kLiteralAddrFar,
+
// Load long or FP literal variants.
// VLDR s/dX, label; 32-bit insn, up to 1KiB offset; 4 bytes.
kLongOrFPLiteral1KiB,
@@ -457,7 +477,7 @@
}
// Load narrow literal.
- static Fixup LoadNarrowLiteral(uint32_t location, Register rt, Size size = kLiteral1KiB) {
+ static Fixup LoadNarrowLiteral(uint32_t location, Register rt, Size size) {
DCHECK(size == kLiteral1KiB || size == kLiteral4KiB || size == kLiteral64KiB ||
size == kLiteral1MiB || size == kLiteralFar);
DCHECK(!IsHighRegister(rt) || (size != kLiteral1KiB && size != kLiteral64KiB));
@@ -493,6 +513,14 @@
AL, kLoadFPLiteralDouble, size, location);
}
+ static Fixup LoadLiteralAddress(uint32_t location, Register rt, Size size) {
+ DCHECK(size == kLiteralAddr1KiB || size == kLiteralAddr4KiB || size == kLiteralAddr64KiB ||
+ size == kLiteralAddrFar);
+ DCHECK(!IsHighRegister(rt) || size != kLiteralAddr1KiB);
+ return Fixup(rt, kNoRegister, kNoSRegister, kNoDRegister,
+ AL, kLoadLiteralAddr, size, location);
+ }
+
Type GetType() const {
return type_;
}
@@ -756,12 +784,14 @@
}
void BindLabel(Label* label, uint32_t bound_pc);
- void BindLiterals();
+ uint32_t BindLiterals();
+ void BindJumpTables(uint32_t code_size);
void AdjustFixupIfNeeded(Fixup* fixup, uint32_t* current_code_size,
std::deque<FixupId>* fixups_to_recalculate);
uint32_t AdjustFixups();
void EmitFixups(uint32_t adjusted_code_size);
void EmitLiterals();
+ void EmitJumpTables();
static int16_t BEncoding16(int32_t offset, Condition cond);
static int32_t BEncoding32(int32_t offset, Condition cond);
@@ -778,6 +808,8 @@
static int32_t VldrdEncoding32(DRegister dd, Register rn, int32_t offset);
static int16_t LdrRtRnImm5Encoding16(Register rt, Register rn, int32_t offset);
static int32_t LdrRtRnImm12Encoding(Register rt, Register rn, int32_t offset);
+ static int16_t AdrEncoding16(Register rd, int32_t offset);
+ static int32_t AdrEncoding32(Register rd, int32_t offset);
std::vector<Fixup> fixups_;
std::unique_ptr<FixupId[]> fixup_dependents_;
@@ -786,6 +818,9 @@
// without invalidating pointers and references to existing elements.
std::deque<Literal> literals_;
+ // Jump table list.
+ std::deque<JumpTable> jump_tables_;
+
// Data for AdjustedPosition(), see the description there.
uint32_t last_position_adjustment_;
uint32_t last_old_position_;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 9c08ce0..cb4b20b 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -17,6 +17,7 @@
#include "assembler_thumb2.h"
#include "base/stl_util.h"
+#include "base/stringprintf.h"
#include "utils/assembler_test.h"
namespace art {
@@ -1011,6 +1012,315 @@
__ GetAdjustedPosition(label.Position()));
}
+TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
+ Label non_tracked, tracked, branch_target;
+
+ // A few dummy loads on entry.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // A branch that will need to be fixed up.
+ __ cbz(arm::R0, &branch_target);
+
+ // Some more dummy loads.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Now insert tracked and untracked label.
+ __ Bind(&non_tracked);
+ __ BindTrackedLabel(&tracked);
+
+ // A lot of dummy loads, to ensure the branch needs resizing.
+ constexpr size_t kLdrR0R0CountLong = 60;
+ for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Bind the branch target.
+ __ Bind(&branch_target);
+
+ // One more load.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ "cmp r0, #0\n" // cbz r0, 1f
+ "beq.n 1f\n" +
+ RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
+ "1:\n"
+ "ldr r0, [r0]\n";
+ DriverStr(expected, "BindTrackedLabel");
+
+ // Expectation is that the tracked label should have moved.
+ EXPECT_LT(non_tracked.Position(), tracked.Position());
+}
+
+TEST_F(AssemblerThumb2Test, JumpTable) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment
+ __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops,
+ } // whereas we emit 0 != nop.
+
+ static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ "adr r1, .Ljump_table\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTable");
+}
+
+// Test for >1K fixup.
+TEST_F(AssemblerThumb2Test, JumpTable4K) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
+ __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
+ }
+
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ "adr r1, .Ljump_table\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTable4K");
+}
+
+// Test for >4K fixup.
+TEST_F(AssemblerThumb2Test, JumpTable64K) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
+ __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
+ }
+
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
+ // (Note: have to use constants, as labels aren't accepted.
+ "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
+ ") * 2 - 4) & 0xFFFF)\n"
+ "add r1, pc\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTable64K");
+}
+
+// Test for >64K fixup.
+TEST_F(AssemblerThumb2Test, JumpTableFar) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
+ __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
+ }
+
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
+ // (Note: have to use constants, as labels aren't accepted.
+ "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
+ ") * 2 - 4) & 0xFFFF)\n"
+ "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
+ ") * 2 - 4) >> 16)\n"
+ ".Lhelp:"
+ "add r1, pc\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTableFar");
+}
+
TEST_F(AssemblerThumb2Test, Clz) {
__ clz(arm::R0, arm::R1);
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index d97a2a4..dfe6bab 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -227,6 +227,8 @@
// Returns the position in the instruction stream.
int GetPosition() { return cursor_ - contents_; }
+ void ExtendCapacity(size_t min_capacity = 0u);
+
private:
// The limit is set to kMinimumGap bytes before the end of the data area.
// This leaves enough space for the longest possible instruction and allows
@@ -261,8 +263,6 @@
return data + capacity - kMinimumGap;
}
- void ExtendCapacity(size_t min_capacity = 0u);
-
friend class AssemblerFixup;
};
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 5347bf0..d6caa3c 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -720,6 +720,14 @@
}
+void X86Assembler::ucomiss(XmmRegister a, const Address& b) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0x2E);
+ EmitOperand(a, b);
+}
+
+
void X86Assembler::ucomisd(XmmRegister a, XmmRegister b) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -729,6 +737,15 @@
}
+void X86Assembler::ucomisd(XmmRegister a, const Address& b) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x2E);
+ EmitOperand(a, b);
+}
+
+
void X86Assembler::roundsd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index b50fda9..655af9c 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -417,7 +417,9 @@
void comiss(XmmRegister a, XmmRegister b);
void comisd(XmmRegister a, XmmRegister b);
void ucomiss(XmmRegister a, XmmRegister b);
+ void ucomiss(XmmRegister a, const Address& b);
void ucomisd(XmmRegister a, XmmRegister b);
+ void ucomisd(XmmRegister a, const Address& b);
void roundsd(XmmRegister dst, XmmRegister src, const Immediate& imm);
void roundss(XmmRegister dst, XmmRegister src, const Immediate& imm);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 16f9db4..a9b991c 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -306,6 +306,19 @@
DriverStr(RepeatRI(&x86::X86Assembler::roll, 1U, "roll ${imm}, %{reg}"), "rolli");
}
+TEST_F(AssemblerX86Test, UComissAddr) {
+ GetAssembler()->ucomiss(x86::XmmRegister(x86::XMM0), x86::Address(x86::EAX, 0));
+ const char* expected = "ucomiss 0(%EAX), %xmm0\n";
+ DriverStr(expected, "ucomiss");
+}
+
+TEST_F(AssemblerX86Test, UComisdAddr) {
+ GetAssembler()->ucomisd(x86::XmmRegister(x86::XMM0), x86::Address(x86::EAX, 0));
+ const char* expected = "ucomisd 0(%EAX), %xmm0\n";
+ DriverStr(expected, "ucomisd");
+}
+
+
/////////////////
// Near labels //
/////////////////
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index efc0ca6..8773169 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1259,6 +1259,7 @@
swap_fd_,
profile_file_));
+ driver_->SetDexFilesForOatFile(dex_files_);
driver_->CompileAll(class_loader, dex_files_, timings_);
}
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index f1f6f9b..1942e1d 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -125,7 +125,7 @@
histogram->CreateHistogram(&cumulative_data);
histogram->PrintConfidenceIntervals(os, 0.99, cumulative_data);
}
- os << "Done Dumping histograms \n";
+ os << "Done Dumping histograms\n";
}
TimingLogger::TimingLogger(const char* name, bool precise, bool verbose)
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 34fb3f8..2dbd7e8 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -49,7 +49,9 @@
if (m_name.compare("f") == 0) {
CHECK_REGS_CONTAIN_REFS(0x03U, true, 8); // v8: this
CHECK_REGS_CONTAIN_REFS(0x06U, true, 8, 1); // v8: this, v1: x
- CHECK_REGS_CONTAIN_REFS(0x08U, true, 8, 3, 1); // v8: this, v3: y, v1: x
+ if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
+ CHECK_REGS_CONTAIN_REFS(0x08U, true, 8, 3, 1); // v8: this, v3: y, v1: x
+ }
CHECK_REGS_CONTAIN_REFS(0x0cU, true, 8, 3, 1); // v8: this, v3: y, v1: x
if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
CHECK_REGS_CONTAIN_REFS(0x0eU, true, 8, 3, 1); // v8: this, v3: y, v1: x
@@ -66,9 +68,10 @@
CHECK_REGS_CONTAIN_REFS(0x13U, false, 3); // v3: y
// Note that v0: ex can be eliminated because it's a dead merge of two different exceptions.
CHECK_REGS_CONTAIN_REFS(0x18U, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex)
- CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1); // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
// v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
+ CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1);
+ // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
CHECK_REGS_CONTAIN_REFS(0x1dU, true, 8, 5, 2, 1);
// v5 is removed from the root set because there is a "merge" operation.
// See 0015: if-nez v2, 001f.
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 22829cd..ffeae7d 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -624,12 +624,13 @@
constantIndexing2(new int[3]);
} catch (ArrayIndexOutOfBoundsException e) {
assertIsManaged(); // This is to ensure that single-frame deoptimization works.
- // Will need to be updated if constantIndexing2 is inlined.
+ // Will need to be updated if constantIndexing2 is inlined.
try {
// This will cause AIOOBE.
constantIndexingForward6(new int[3]);
} catch (ArrayIndexOutOfBoundsException e2) {
- assertIsManaged();
+ // Having deopted, we expect to be running interpreted at this point.
+ // Does not apply to debuggable, however, since we do not inline.
return 99;
}
}
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index d8618e3..979c4c8 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -260,6 +260,29 @@
return arg ^ 0xf00000000000000fL;
}
+ /**
+ * Test that the `-1` constant is not synthesized in a register and that we
+ * instead simply switch between `add` and `sub` instructions with the
+ * constant embedded.
+ * We need two uses (or more) of the constant because the compiler always
+ * defers to immediate value handling to VIXL when it has only one use.
+ */
+
+ /// CHECK-START-ARM64: long Main.addM1(long) register (after)
+ /// CHECK: <<Arg:j\d+>> ParameterValue
+ /// CHECK: <<ConstM1:j\d+>> LongConstant -1
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: Add [<<Arg>>,<<ConstM1>>]
+ /// CHECK: Sub [<<Arg>>,<<ConstM1>>]
+
+ /// CHECK-START-ARM64: long Main.addM1(long) disassembly (after)
+ /// CHECK: sub x{{\d+}}, x{{\d+}}, #0x1
+ /// CHECK: add x{{\d+}}, x{{\d+}}, #0x1
+
+ public static long addM1(long arg) {
+ return (arg + (-1)) | (arg - (-1));
+ }
+
public static void main(String[] args) {
int arg = 0x87654321;
assertIntEquals(and255(arg), 0x21);
@@ -286,5 +309,7 @@
assertLongEquals(xorNot15(longArg), 0xedcba987789abcd1L);
assertLongEquals(xor0xfffffff00000000f(longArg), 0xedcba9888765432eL);
assertLongEquals(xor0xf00000000000000f(longArg), 0xe23456788765432eL);
+
+ assertLongEquals(14, addM1(7));
}
}
diff --git a/test/539-checker-arm64-encodable-immediates/info.txt b/test/539-checker-arm64-encodable-immediates/info.txt
deleted file mode 100644
index efeef33..0000000
--- a/test/539-checker-arm64-encodable-immediates/info.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Basic tests that check the compiler recognizes when constant values can be
-encoded in the immediate field of instructions.
diff --git a/test/539-checker-arm64-encodable-immediates/src/Main.java b/test/539-checker-arm64-encodable-immediates/src/Main.java
deleted file mode 100644
index 7e3ff9f..0000000
--- a/test/539-checker-arm64-encodable-immediates/src/Main.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-public class Main {
-
- public static void assertLongEquals(long expected, long result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
- }
- }
-
- /**
- * Test that the `-1` constant is not synthesized in a register and that we
- * instead simply switch between `add` and `sub` instructions with the
- * constant embedded.
- * We need two uses (or more) of the constant because the compiler always
- * delegates the immediate value handling to VIXL when there is only one use.
- */
-
- /// CHECK-START-ARM64: long Main.addM1(long) register (after)
- /// CHECK: <<Arg:j\d+>> ParameterValue
- /// CHECK: <<ConstM1:j\d+>> LongConstant -1
- /// CHECK-NOT: ParallelMove
- /// CHECK: Add [<<Arg>>,<<ConstM1>>]
- /// CHECK: Sub [<<Arg>>,<<ConstM1>>]
-
- /// CHECK-START-ARM64: long Main.addM1(long) disassembly (after)
- /// CHECK: sub x{{\d+}}, x{{\d+}}, #0x1
- /// CHECK: add x{{\d+}}, x{{\d+}}, #0x1
-
- public static long addM1(long arg) {
- return (arg + (-1)) | (arg - (-1));
- }
-
- public static void main(String[] args) {
- assertLongEquals(14, addM1(7));
- }
-}
diff --git a/test/539-checker-arm64-encodable-immediates/expected.txt b/test/542-inline-trycatch/expected.txt
similarity index 100%
rename from test/539-checker-arm64-encodable-immediates/expected.txt
rename to test/542-inline-trycatch/expected.txt
diff --git a/test/542-inline-trycatch/info.txt b/test/542-inline-trycatch/info.txt
new file mode 100644
index 0000000..b3e50d3
--- /dev/null
+++ b/test/542-inline-trycatch/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler under try/catch.
\ No newline at end of file
diff --git a/test/542-inline-trycatch/src/Main.java b/test/542-inline-trycatch/src/Main.java
new file mode 100644
index 0000000..5a6e06f
--- /dev/null
+++ b/test/542-inline-trycatch/src/Main.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ // The following tests make sure that we inline methods used inside try and catch
+ // blocks, provided they meet other inlining criteria. To do that, we rely on
+ // the compiler recognizing and enforcing the $inline$ and $noinline$ markers.
+
+ // We expect a single block to always be inlined.
+
+ private static int $inline$SingleBlock(String str) throws NumberFormatException {
+ return Integer.parseInt(str);
+ }
+
+ // We expect a "simple" method with multiple blocks to always be inlined.
+
+ private static int $inline$MultipleBlocks(String str, boolean is_hex)
+ throws NumberFormatException {
+ return is_hex ? Integer.parseInt(str, 16) : Integer.parseInt(str);
+ }
+
+ // We expect methods with try/catch to not be inlined. Inlined try/catch
+ // blocks are not supported at the moment.
+
+ private static int $noinline$TryCatch(String str) {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ public static void testSingleBlockFromTry() {
+ int val = 0;
+
+ try {
+ val = $inline$SingleBlock("42");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ $inline$SingleBlock("xyz");
+ unreachable();
+ } catch (NumberFormatException ex) {}
+ }
+
+ public static void testSingleBlockFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$SingleBlock("42");
+ }
+ assertEquals(42, val);
+ }
+
+ public static void testMultipleBlocksFromTry() {
+ int val = 0;
+
+ try {
+ val = $inline$MultipleBlocks("42", false);
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ val = $inline$MultipleBlocks("20", true);
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(32, val);
+
+ try {
+ $inline$MultipleBlocks("xyz", false);
+ unreachable();
+ } catch (NumberFormatException ex) {}
+
+ try {
+ $inline$MultipleBlocks("xyz", true);
+ unreachable();
+ } catch (NumberFormatException ex) {}
+ }
+
+ public static void testMultipleBlocksFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$MultipleBlocks("42", false);
+ }
+ assertEquals(42, val);
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$MultipleBlocks("20", true);
+ }
+ assertEquals(32, val);
+ }
+
+ public static void testTryCatchFromTry() {
+ int val = 0;
+
+ try {
+ val = $noinline$TryCatch("42");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ val = $noinline$TryCatch("xyz");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(-1, val);
+ }
+
+ public static void testTryCatchFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $noinline$TryCatch("42");
+ }
+ assertEquals(42, val);
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $noinline$TryCatch("xyz");
+ }
+ assertEquals(-1, val);
+ }
+
+ public static void main(String[] args) {
+ testSingleBlockFromTry();
+ testSingleBlockFromCatch();
+ testMultipleBlocksFromTry();
+ testMultipleBlocksFromCatch();
+ testTryCatchFromTry();
+ testTryCatchFromCatch();
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void unreachable() {
+ throw new Error("Unreachable");
+ }
+
+ private static void throwException() throws Exception {
+ throw new Exception();
+ }
+}
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 67a7983..4b5a5ca 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -57,7 +57,6 @@
"org.apache.harmony.luni"
"org.apache.harmony.nio"
"org.apache.harmony.regex"
- "org.apache.harmony.security"
"org.apache.harmony.testframework"
"org.apache.harmony.tests.java.io"
"org.apache.harmony.tests.java.lang"
@@ -68,6 +67,10 @@
"tests.java.lang.String"
"jsr166")
+# List of packages we could run, but don't have rights to revert
+# changes in case of failures.
+# "org.apache.harmony.security"
+
vogar_args=$@
while true; do
if [[ "$1" == "--mode=device" ]]; then