summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--cmdline/cmdline_parser_test.cc5
-rw-r--r--cmdline/cmdline_types.h2
-rw-r--r--compiler/Android.mk1
-rw-r--r--compiler/elf_writer_debug.cc170
-rw-r--r--compiler/optimizing/bounds_check_elimination.cc9
-rw-r--r--compiler/optimizing/builder.h5
-rw-r--r--compiler/optimizing/code_generator_arm.cc30
-rw-r--r--compiler/optimizing/code_generator_arm64.cc35
-rw-r--r--compiler/optimizing/code_generator_mips.cc1057
-rw-r--r--compiler/optimizing/code_generator_mips.h12
-rw-r--r--compiler/optimizing/code_generator_mips64.cc49
-rw-r--r--compiler/optimizing/code_generator_x86.cc86
-rw-r--r--compiler/optimizing/code_generator_x86.h10
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc773
-rw-r--r--compiler/optimizing/code_generator_x86_64.h105
-rw-r--r--compiler/optimizing/codegen_test.cc333
-rw-r--r--compiler/optimizing/constant_folding_test.cc4
-rw-r--r--compiler/optimizing/dead_code_elimination_test.cc9
-rw-r--r--compiler/optimizing/graph_checker.cc12
-rw-r--r--compiler/optimizing/graph_checker_test.cc10
-rw-r--r--compiler/optimizing/graph_visualizer.cc19
-rw-r--r--compiler/optimizing/gvn_test.cc20
-rw-r--r--compiler/optimizing/induction_var_analysis.cc71
-rw-r--r--compiler/optimizing/induction_var_analysis.h4
-rw-r--r--compiler/optimizing/induction_var_analysis_test.cc8
-rw-r--r--compiler/optimizing/induction_var_range.cc119
-rw-r--r--compiler/optimizing/induction_var_range.h92
-rw-r--r--compiler/optimizing/induction_var_range_test.cc291
-rw-r--r--compiler/optimizing/inliner.cc67
-rw-r--r--compiler/optimizing/instruction_simplifier.cc4
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.cc1
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc43
-rw-r--r--compiler/optimizing/licm_test.cc17
-rw-r--r--compiler/optimizing/linearize_test.cc21
-rw-r--r--compiler/optimizing/live_ranges_test.cc18
-rw-r--r--compiler/optimizing/liveness_test.cc30
-rw-r--r--compiler/optimizing/nodes.cc49
-rw-r--r--compiler/optimizing/nodes.h304
-rw-r--r--compiler/optimizing/optimizing_compiler.cc51
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h8
-rw-r--r--compiler/optimizing/optimizing_unit_test.h11
-rw-r--r--compiler/optimizing/pc_relative_fixups_x86.cc5
-rw-r--r--compiler/optimizing/primitive_type_propagation.cc133
-rw-r--r--compiler/optimizing/primitive_type_propagation.h52
-rw-r--r--compiler/optimizing/reference_type_propagation.cc143
-rw-r--r--compiler/optimizing/register_allocator_test.cc34
-rw-r--r--compiler/optimizing/ssa_builder.cc695
-rw-r--r--compiler/optimizing/ssa_builder.h42
-rw-r--r--compiler/optimizing/ssa_phi_elimination.cc18
-rw-r--r--compiler/optimizing/ssa_test.cc31
-rw-r--r--compiler/utils/assembler_test.h75
-rw-r--r--compiler/utils/mips/assembler_mips.cc274
-rw-r--r--compiler/utils/mips/assembler_mips.h170
-rw-r--r--compiler/utils/mips/assembler_mips_test.cc321
-rw-r--r--dexdump/dexdump.cc17
-rw-r--r--dexlist/dexlist.cc10
-rw-r--r--disassembler/disassembler_mips.cc40
-rw-r--r--runtime/Android.mk1
-rw-r--r--runtime/base/logging.h1
-rw-r--r--runtime/code_simulator_container.cc55
-rw-r--r--runtime/code_simulator_container.h54
-rw-r--r--runtime/debugger.cc50
-rw-r--r--runtime/dex_file.cc280
-rw-r--r--runtime/dex_file.h89
-rw-r--r--runtime/gc/heap.cc22
-rw-r--r--runtime/jdwp/jdwp_expand_buf.cc2
-rw-r--r--runtime/jit/jit_code_cache.h1
-rw-r--r--runtime/parsed_options.cc1
-rw-r--r--runtime/parsed_options_test.cc2
-rw-r--r--runtime/simulator/Android.mk105
-rw-r--r--runtime/simulator/code_simulator.cc35
-rw-r--r--runtime/simulator/code_simulator.h46
-rw-r--r--runtime/simulator/code_simulator_arm64.cc69
-rw-r--r--runtime/simulator/code_simulator_arm64.h57
-rw-r--r--test/008-exceptions/src/Main.java6
-rw-r--r--test/444-checker-nce/src/Main.java52
-rw-r--r--test/450-checker-types/src/Main.java211
-rw-r--r--test/477-checker-bound-type/src/Main.java8
-rw-r--r--test/530-checker-loops/src/Main.java193
-rw-r--r--test/530-checker-lse/src/Main.java12
-rw-r--r--test/540-checker-rtp-bug/src/Main.java8
-rw-r--r--test/549-checker-types-merge/src/Main.java22
-rw-r--r--test/552-checker-primitive-typeprop/info.txt2
-rw-r--r--test/552-checker-primitive-typeprop/smali/ArrayGet.smali245
-rw-r--r--test/552-checker-primitive-typeprop/smali/SsaBuilder.smali52
-rw-r--r--test/552-checker-primitive-typeprop/smali/TypePropagation.smali136
-rw-r--r--test/552-checker-primitive-typeprop/src/Main.java43
-rw-r--r--test/554-jit-profile-file/expected.txt7
-rw-r--r--test/554-jit-profile-file/info.txt1
-rw-r--r--test/554-jit-profile-file/offline_profile.cc59
-rw-r--r--test/554-jit-profile-file/run23
-rw-r--r--test/554-jit-profile-file/src/Main.java145
-rw-r--r--test/558-switch/expected.txt (renamed from test/552-checker-primitive-typeprop/expected.txt)0
-rw-r--r--test/558-switch/info.txt2
-rw-r--r--test/558-switch/src/Main.java (renamed from test/554-jit-profile-file/src-multidex/OtherDex.java)26
-rw-r--r--test/Android.libarttest.mk3
-rw-r--r--test/Android.run-test.mk73
-rwxr-xr-xtest/run-test2
-rw-r--r--tools/libcore_failures.txt7
100 files changed, 4698 insertions, 3536 deletions
diff --git a/Android.mk b/Android.mk
index 3438beba3f..97a82e2077 100644
--- a/Android.mk
+++ b/Android.mk
@@ -77,6 +77,7 @@ include $(art_path)/build/Android.cpplint.mk
# product rules
include $(art_path)/runtime/Android.mk
+include $(art_path)/runtime/simulator/Android.mk
include $(art_path)/compiler/Android.mk
include $(art_path)/dexdump/Android.mk
include $(art_path)/dexlist/Android.mk
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 529143d93d..fe83ba9e14 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -243,8 +243,8 @@ TEST_F(CmdlineParserTest, TestSimpleFailures) {
TEST_F(CmdlineParserTest, TestLogVerbosity) {
{
const char* log_args = "-verbose:"
- "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,startup,third-party-jni,"
- "threads,verifier";
+ "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,simulator,startup,"
+ "third-party-jni,threads,verifier";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.class_linker = true;
@@ -256,6 +256,7 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) {
log_verbosity.monitor = true;
log_verbosity.profiler = true;
log_verbosity.signals = true;
+ log_verbosity.simulator = true;
log_verbosity.startup = true;
log_verbosity.third_party_jni = true;
log_verbosity.threads = true;
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 47519894eb..6c0a0e1f4f 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -606,6 +606,8 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
log_verbosity.profiler = true;
} else if (verbose_options[j] == "signals") {
log_verbosity.signals = true;
+ } else if (verbose_options[j] == "simulator") {
+ log_verbosity.simulator = true;
} else if (verbose_options[j] == "startup") {
log_verbosity.startup = true;
} else if (verbose_options[j] == "third-party-jni") {
diff --git a/compiler/Android.mk b/compiler/Android.mk
index f0bf4997c6..bdd9a84433 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -92,6 +92,7 @@ LIBART_COMPILER_SRC_FILES := \
optimizing/parallel_move_resolver.cc \
optimizing/pc_relative_fixups_x86.cc \
optimizing/prepare_for_register_allocation.cc \
+ optimizing/primitive_type_propagation.cc \
optimizing/reference_type_propagation.cc \
optimizing/register_allocator.cc \
optimizing/sharpening.cc \
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index e806acf874..06553a6d62 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -301,40 +301,30 @@ namespace {
uint32_t high_pc_ = 0;
};
- struct LocalVariable {
- uint16_t vreg;
- uint32_t dex_pc_low;
- uint32_t dex_pc_high;
- const char* name;
- const char* type;
- const char* sig;
- };
+ typedef std::vector<DexFile::LocalInfo> LocalInfos;
- struct DebugInfoCallback {
- static void NewLocal(void* ctx,
- uint16_t vreg,
- uint32_t start,
- uint32_t end,
- const char* name,
- const char* type,
- const char* sig) {
- auto* context = static_cast<DebugInfoCallback*>(ctx);
- if (name != nullptr && type != nullptr) {
- context->local_variables_.push_back({vreg, start, end, name, type, sig});
- }
- }
- std::vector<LocalVariable> local_variables_;
- };
+ void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) {
+ static_cast<LocalInfos*>(ctx)->push_back(entry);
+ }
+
+ typedef std::vector<DexFile::PositionInfo> PositionInfos;
+
+ bool PositionInfoCallback(void* ctx, const DexFile::PositionInfo& entry) {
+ static_cast<PositionInfos*>(ctx)->push_back(entry);
+ return false;
+ }
std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
std::vector<const char*> names;
- const uint8_t* stream = mi->dex_file_->GetDebugInfoStream(mi->code_item_);
- if (stream != nullptr) {
- DecodeUnsignedLeb128(&stream); // line.
- uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
- for (uint32_t i = 0; i < parameters_size; ++i) {
- uint32_t id = DecodeUnsignedLeb128P1(&stream);
- names.push_back(mi->dex_file_->StringDataByIdx(id));
+ if (mi->code_item_ != nullptr) {
+ const uint8_t* stream = mi->dex_file_->GetDebugInfoStream(mi->code_item_);
+ if (stream != nullptr) {
+ DecodeUnsignedLeb128(&stream); // line.
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ for (uint32_t i = 0; i < parameters_size; ++i) {
+ uint32_t id = DecodeUnsignedLeb128P1(&stream);
+ names.push_back(mi->dex_file_->StringDataByIdx(id));
+ }
}
}
return names;
@@ -454,6 +444,7 @@ class DebugInfoWriter {
const char* last_dex_class_desc = nullptr;
for (auto mi : compilation_unit.methods_) {
const DexFile* dex = mi->dex_file_;
+ const DexFile::CodeItem* dex_code = mi->code_item_;
const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index_);
const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
@@ -473,19 +464,6 @@ class DebugInfoWriter {
last_dex_class_desc = dex_class_desc;
}
- // Collect information about local variables and parameters.
- DebugInfoCallback debug_info_callback;
- std::vector<const char*> param_names;
- if (mi->code_item_ != nullptr) {
- dex->DecodeDebugInfo(mi->code_item_,
- is_static,
- mi->dex_method_index_,
- nullptr,
- DebugInfoCallback::NewLocal,
- &debug_info_callback);
- param_names = GetParamNames(mi);
- }
-
int start_depth = info_.Depth();
info_.StartTag(DW_TAG_subprogram);
WriteName(dex->GetMethodName(dex_method));
@@ -494,50 +472,70 @@ class DebugInfoWriter {
uint8_t frame_base[] = { DW_OP_call_frame_cfa };
info_.WriteExprLoc(DW_AT_frame_base, &frame_base, sizeof(frame_base));
WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
- uint32_t vreg = mi->code_item_ == nullptr ? 0 :
- mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
+
+ // Write parameters. DecodeDebugLocalInfo returns them as well, but it does not
+ // guarantee order or uniqueness so it is safer to iterate over them manually.
+ // DecodeDebugLocalInfo might not also be available if there is no debug info.
+ std::vector<const char*> param_names = GetParamNames(mi);
+ uint32_t arg_reg = 0;
if (!is_static) {
info_.StartTag(DW_TAG_formal_parameter);
WriteName("this");
info_.WriteFlag(DW_AT_artificial, true);
WriteLazyType(dex_class_desc);
- const bool is64bitValue = false;
- WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
- vreg++;
+ if (dex_code != nullptr) {
+ // Write the stack location of the parameter.
+ const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ const bool is64bitValue = false;
+ WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
+ }
+ arg_reg++;
info_.EndTag();
}
if (dex_params != nullptr) {
for (uint32_t i = 0; i < dex_params->Size(); ++i) {
info_.StartTag(DW_TAG_formal_parameter);
// Parameter names may not be always available.
- if (i < param_names.size() && param_names[i] != nullptr) {
+ if (i < param_names.size()) {
WriteName(param_names[i]);
}
// Write the type.
const char* type_desc = dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_);
WriteLazyType(type_desc);
- // Write the stack location of the parameter.
const bool is64bitValue = type_desc[0] == 'D' || type_desc[0] == 'J';
- WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
- vreg += is64bitValue ? 2 : 1;
+ if (dex_code != nullptr) {
+ // Write the stack location of the parameter.
+ const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
+ }
+ arg_reg += is64bitValue ? 2 : 1;
info_.EndTag();
}
- if (mi->code_item_ != nullptr) {
- CHECK_EQ(vreg, mi->code_item_->registers_size_);
+ if (dex_code != nullptr) {
+ DCHECK_EQ(arg_reg, dex_code->ins_size_);
}
}
- for (const LocalVariable& var : debug_info_callback.local_variables_) {
- const uint32_t first_arg = mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
- if (var.vreg < first_arg) {
- info_.StartTag(DW_TAG_variable);
- WriteName(var.name);
- WriteLazyType(var.type);
- bool is64bitValue = var.type[0] == 'D' || var.type[0] == 'J';
- WriteRegLocation(mi, var.vreg, is64bitValue, compilation_unit.low_pc_,
- var.dex_pc_low, var.dex_pc_high);
- info_.EndTag();
+
+ // Write local variables.
+ LocalInfos local_infos;
+ if (dex->DecodeDebugLocalInfo(dex_code,
+ is_static,
+ mi->dex_method_index_,
+ LocalInfoCallback,
+ &local_infos)) {
+ for (const DexFile::LocalInfo& var : local_infos) {
+ if (var.reg_ < dex_code->registers_size_ - dex_code->ins_size_) {
+ info_.StartTag(DW_TAG_variable);
+ WriteName(var.name_);
+ WriteLazyType(var.descriptor_);
+ bool is64bitValue = var.descriptor_[0] == 'D' || var.descriptor_[0] == 'J';
+ WriteRegLocation(mi, var.reg_, is64bitValue, compilation_unit.low_pc_,
+ var.start_address_, var.end_address_);
+ info_.EndTag();
+ }
}
}
+
info_.EndTag();
CHECK_EQ(info_.Depth(), start_depth); // Balanced start/end.
}
@@ -708,8 +706,7 @@ class DebugInfoWriter {
// to be enclosed in the right set of namespaces. Therefore we
// just define all types lazily at the end of compilation unit.
void WriteLazyType(const char* type_descriptor) {
- DCHECK(type_descriptor != nullptr);
- if (type_descriptor[0] != 'V') {
+ if (type_descriptor != nullptr && type_descriptor[0] != 'V') {
lazy_types_.emplace(type_descriptor, info_.size());
info_.WriteRef4(DW_AT_type, 0);
}
@@ -724,7 +721,9 @@ class DebugInfoWriter {
private:
void WriteName(const char* name) {
- info_.WriteStrp(DW_AT_name, owner_->WriteString(name));
+ if (name != nullptr) {
+ info_.WriteStrp(DW_AT_name, owner_->WriteString(name));
+ }
}
// Helper which writes DWARF expression referencing a register.
@@ -976,29 +975,15 @@ class DebugLineWriter {
continue;
}
- // Create mapping table from dex to source line.
- struct DebugInfoCallbacks {
- static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
- auto* context = static_cast<DebugInfoCallbacks*>(ctx);
- context->dex2line_.push_back({address, static_cast<int32_t>(line)});
- return false;
- }
- DefaultSrcMap dex2line_;
- } debug_info_callbacks;
-
Elf_Addr method_address = text_address + mi->low_pc_;
+ PositionInfos position_infos;
const DexFile* dex = mi->dex_file_;
- if (mi->code_item_ != nullptr) {
- dex->DecodeDebugInfo(mi->code_item_,
- (mi->access_flags_ & kAccStatic) != 0,
- mi->dex_method_index_,
- DebugInfoCallbacks::NewPosition,
- nullptr,
- &debug_info_callbacks);
+ if (!dex->DecodeDebugPositionInfo(mi->code_item_, PositionInfoCallback, &position_infos)) {
+ continue;
}
- if (debug_info_callbacks.dex2line_.empty()) {
+ if (position_infos.empty()) {
continue;
}
@@ -1053,20 +1038,23 @@ class DebugLineWriter {
opcodes.SetFile(file_index);
// Generate mapping opcodes from PC to Java lines.
- const DefaultSrcMap& dex2line_map = debug_info_callbacks.dex2line_;
- if (file_index != 0 && !dex2line_map.empty()) {
+ if (file_index != 0) {
bool first = true;
for (SrcMapElem pc2dex : src_mapping_table) {
uint32_t pc = pc2dex.from_;
int dex_pc = pc2dex.to_;
- auto dex2line = dex2line_map.Find(static_cast<uint32_t>(dex_pc));
- if (dex2line.first) {
- int line = dex2line.second;
+ // Find mapping with address with is greater than our dex pc; then go back one step.
+ auto ub = std::upper_bound(position_infos.begin(), position_infos.end(), dex_pc,
+ [](uint32_t address, const DexFile::PositionInfo& entry) {
+ return address < entry.address_;
+ });
+ if (ub != position_infos.begin()) {
+ int line = (--ub)->line_;
if (first) {
first = false;
if (pc > 0) {
// Assume that any preceding code is prologue.
- int first_line = dex2line_map.front().to_;
+ int first_line = position_infos.front().line_;
// Prologue is not a sensible place for a breakpoint.
opcodes.NegateStmt();
opcodes.AddRow(method_address, first_line);
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index dc75ff1abc..4c3f66aa4f 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1590,18 +1590,15 @@ class BCEVisitor : public HGraphVisitor {
HGraph* graph = GetGraph();
HInstruction* zero;
switch (type) {
- case Primitive::kPrimNot: zero = graph->GetNullConstant(); break;
- case Primitive::kPrimFloat: zero = graph->GetFloatConstant(0); break;
- case Primitive::kPrimDouble: zero = graph->GetDoubleConstant(0); break;
+ case Primitive::Type::kPrimNot: zero = graph->GetNullConstant(); break;
+ case Primitive::Type::kPrimFloat: zero = graph->GetFloatConstant(0); break;
+ case Primitive::Type::kPrimDouble: zero = graph->GetDoubleConstant(0); break;
default: zero = graph->GetConstant(type, 0); break;
}
HPhi* phi = new (graph->GetArena())
HPhi(graph->GetArena(), kNoRegNumber, /*number_of_inputs*/ 2, HPhi::ToPhiType(type));
phi->SetRawInputAt(0, instruction);
phi->SetRawInputAt(1, zero);
- if (type == Primitive::kPrimNot) {
- phi->SetReferenceTypeInfo(instruction->GetReferenceTypeInfo());
- }
new_preheader->AddPhi(phi);
return phi;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index ca71c32802..c3979f3dd1 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -90,9 +90,8 @@ class HGraphBuilder : public ValueObject {
static constexpr const char* kBuilderPassName = "builder";
- // The number of entries in a packed switch before we use a jump table or specified
- // compare/jump series.
- static constexpr uint16_t kSmallSwitchThreshold = 3;
+ // The number of entries in a packed switch before we use a jump table.
+ static constexpr uint16_t kSmallSwitchThreshold = 5;
private:
// Analyzes the dex instruction and adds HInstruction to the graph
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 9fda83840c..3630dbec24 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -59,7 +59,7 @@ static constexpr SRegister kFpuCalleeSaves[] =
// S registers. Therefore there is no need to block it.
static constexpr DRegister DTMP = D31;
-static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
+static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6;
#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())->
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
@@ -6250,7 +6250,7 @@ void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
- if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
+ 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) {
@@ -6266,30 +6266,12 @@ void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr)
Register value_reg = locations->InAt(0).AsRegister<Register>();
HBasicBlock* default_block = switch_instr->GetDefaultBlock();
- if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) {
+ if (num_entries < kPackedSwitchJumpTableThreshold || !codegen_->GetAssembler()->IsThumb()) {
// Create a series of compare/jumps.
- Register temp_reg = IP;
- // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
- // the immediate, because IP is used as the destination register. For the other
- // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
- // and they can be encoded in the instruction without making use of IP register.
- __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound);
-
const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- // Jump to successors[0] if value == lower_bound.
- __ b(codegen_->GetLabelOf(successors[0]), EQ);
- int32_t last_index = 0;
- for (; num_entries - last_index > 2; last_index += 2) {
- __ AddConstantSetFlags(temp_reg, temp_reg, -2);
- // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
- __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO);
- // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
- __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ);
- }
- if (num_entries - last_index == 2) {
- // The last missing case_value.
- GenerateCompareWithImmediate(temp_reg, 1);
- __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ);
+ 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.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 52058302be..451470f271 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -71,10 +71,10 @@ using helpers::ARM64EncodableConstantOrRegister;
using helpers::ArtVixlRegCodeCoherentForRegSet;
static constexpr int kCurrentMethodStackOffset = 0;
-// The compare/jump sequence will generate about (1.5 * num_entries + 3) instructions. While jump
+// The compare/jump sequence will generate about (2 * num_entries + 1) instructions. While jump
// table version generates 7 instructions and num_entries literals. Compare/jump sequence will
// generates less code/data with a small num_entries.
-static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
+static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6;
inline Condition ARM64Condition(IfCondition cond) {
switch (cond) {
@@ -546,7 +546,7 @@ class ArraySetSlowPathARM64 : public SlowPathCodeARM64 {
void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) {
uint32_t num_entries = switch_instr_->GetNumEntries();
- DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
+ DCHECK_GE(num_entries, kPackedSwitchJumpTableThreshold);
// We are about to use the assembler to place literals directly. Make sure we have enough
// underlying code buffer and we have generated the jump table with right size.
@@ -4582,29 +4582,20 @@ void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_inst
// ranges and emit the tables only as required.
static constexpr int32_t kJumpTableInstructionThreshold = 1* MB / kMaxExpectedSizePerHInstruction;
- if (num_entries <= kPackedSwitchCompareJumpThreshold ||
+ if (num_entries < kPackedSwitchJumpTableThreshold ||
// Current instruction id is an upper bound of the number of HIRs in the graph.
GetGraph()->GetCurrentInstructionId() > kJumpTableInstructionThreshold) {
// Create a series of compare/jumps.
- UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
- Register temp = temps.AcquireW();
- __ Subs(temp, value_reg, Operand(lower_bound));
-
const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- // Jump to successors[0] if value == lower_bound.
- __ B(eq, codegen_->GetLabelOf(successors[0]));
- int32_t last_index = 0;
- for (; num_entries - last_index > 2; last_index += 2) {
- __ Subs(temp, temp, Operand(2));
- // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
- __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
- // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
- __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
- }
- if (num_entries - last_index == 2) {
- // The last missing case_value.
- __ Cmp(temp, Operand(1));
- __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
+ for (uint32_t i = 0; i < num_entries; i++) {
+ int32_t case_value = lower_bound + i;
+ vixl::Label* succ = codegen_->GetLabelOf(successors[i]);
+ if (case_value == 0) {
+ __ Cbz(value_reg, succ);
+ } else {
+ __ Cmp(value_reg, Operand(case_value));
+ __ B(eq, succ);
+ }
}
// And the default for any other value.
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index ce7cbcd9d6..5dc101b199 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1956,11 +1956,8 @@ void InstructionCodeGeneratorMIPS::VisitClinitCheck(HClinitCheck* check) {
void LocationsBuilderMIPS::VisitCompare(HCompare* compare) {
Primitive::Type in_type = compare->InputAt(0)->GetType();
- LocationSummary::CallKind call_kind = Primitive::IsFloatingPointType(in_type)
- ? LocationSummary::kCall
- : LocationSummary::kNoCall;
-
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, call_kind);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (in_type) {
case Primitive::kPrimLong:
@@ -1971,13 +1968,11 @@ void LocationsBuilderMIPS::VisitCompare(HCompare* compare) {
break;
case Primitive::kPrimFloat:
- case Primitive::kPrimDouble: {
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
- locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
- locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
- }
default:
LOG(FATAL) << "Unexpected type for compare operation " << in_type;
@@ -1986,7 +1981,10 @@ void LocationsBuilderMIPS::VisitCompare(HCompare* compare) {
void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
LocationSummary* locations = instruction->GetLocations();
+ Register res = locations->Out().AsRegister<Register>();
Primitive::Type in_type = instruction->InputAt(0)->GetType();
+ bool gt_bias = instruction->IsGtBias();
+ bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
// 0 if: left == right
// 1 if: left > right
@@ -1994,7 +1992,6 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
switch (in_type) {
case Primitive::kPrimLong: {
MipsLabel done;
- Register res = locations->Out().AsRegister<Register>();
Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
Register rhs_high = locations->InAt(1).AsRegisterPairHigh<Register>();
@@ -2011,45 +2008,82 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
break;
}
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble: {
- int32_t entry_point_offset;
- bool direct;
- if (in_type == Primitive::kPrimFloat) {
- if (instruction->IsGtBias()) {
- entry_point_offset = QUICK_ENTRY_POINT(pCmpgFloat);
- direct = IsDirectEntrypoint(kQuickCmpgFloat);
+ case Primitive::kPrimFloat: {
+ FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
+ FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
+ MipsLabel done;
+ if (isR6) {
+ __ CmpEqS(FTMP, lhs, rhs);
+ __ LoadConst32(res, 0);
+ __ Bc1nez(FTMP, &done);
+ if (gt_bias) {
+ __ CmpLtS(FTMP, lhs, rhs);
+ __ LoadConst32(res, -1);
+ __ Bc1nez(FTMP, &done);
+ __ LoadConst32(res, 1);
} else {
- entry_point_offset = QUICK_ENTRY_POINT(pCmplFloat);
- direct = IsDirectEntrypoint(kQuickCmplFloat);
+ __ CmpLtS(FTMP, rhs, lhs);
+ __ LoadConst32(res, 1);
+ __ Bc1nez(FTMP, &done);
+ __ LoadConst32(res, -1);
}
} else {
- if (instruction->IsGtBias()) {
- entry_point_offset = QUICK_ENTRY_POINT(pCmpgDouble);
- direct = IsDirectEntrypoint(kQuickCmpgDouble);
+ if (gt_bias) {
+ __ ColtS(0, lhs, rhs);
+ __ LoadConst32(res, -1);
+ __ Bc1t(0, &done);
+ __ CeqS(0, lhs, rhs);
+ __ LoadConst32(res, 1);
+ __ Movt(res, ZERO, 0);
} else {
- entry_point_offset = QUICK_ENTRY_POINT(pCmplDouble);
- direct = IsDirectEntrypoint(kQuickCmplDouble);
+ __ ColtS(0, rhs, lhs);
+ __ LoadConst32(res, 1);
+ __ Bc1t(0, &done);
+ __ CeqS(0, lhs, rhs);
+ __ LoadConst32(res, -1);
+ __ Movt(res, ZERO, 0);
}
}
- codegen_->InvokeRuntime(entry_point_offset,
- instruction,
- instruction->GetDexPc(),
- nullptr,
- direct);
- if (in_type == Primitive::kPrimFloat) {
- if (instruction->IsGtBias()) {
- CheckEntrypointTypes<kQuickCmpgFloat, int32_t, float, float>();
+ __ Bind(&done);
+ break;
+ }
+ case Primitive::kPrimDouble: {
+ FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
+ FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
+ MipsLabel done;
+ if (isR6) {
+ __ CmpEqD(FTMP, lhs, rhs);
+ __ LoadConst32(res, 0);
+ __ Bc1nez(FTMP, &done);
+ if (gt_bias) {
+ __ CmpLtD(FTMP, lhs, rhs);
+ __ LoadConst32(res, -1);
+ __ Bc1nez(FTMP, &done);
+ __ LoadConst32(res, 1);
} else {
- CheckEntrypointTypes<kQuickCmplFloat, int32_t, float, float>();
+ __ CmpLtD(FTMP, rhs, lhs);
+ __ LoadConst32(res, 1);
+ __ Bc1nez(FTMP, &done);
+ __ LoadConst32(res, -1);
}
} else {
- if (instruction->IsGtBias()) {
- CheckEntrypointTypes<kQuickCmpgDouble, int32_t, double, double>();
+ if (gt_bias) {
+ __ ColtD(0, lhs, rhs);
+ __ LoadConst32(res, -1);
+ __ Bc1t(0, &done);
+ __ CeqD(0, lhs, rhs);
+ __ LoadConst32(res, 1);
+ __ Movt(res, ZERO, 0);
} else {
- CheckEntrypointTypes<kQuickCmplDouble, int32_t, double, double>();
+ __ ColtD(0, rhs, lhs);
+ __ LoadConst32(res, 1);
+ __ Bc1t(0, &done);
+ __ CeqD(0, lhs, rhs);
+ __ LoadConst32(res, -1);
+ __ Movt(res, ZERO, 0);
}
}
+ __ Bind(&done);
break;
}
@@ -2060,8 +2094,19 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
void LocationsBuilderMIPS::VisitCondition(HCondition* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ switch (instruction->InputAt(0)->GetType()) {
+ default:
+ case Primitive::kPrimLong:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ break;
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ break;
+ }
if (instruction->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
@@ -2071,151 +2116,45 @@ void InstructionCodeGeneratorMIPS::VisitCondition(HCondition* instruction) {
if (!instruction->NeedsMaterialization()) {
return;
}
- // TODO: generalize to long
- DCHECK_NE(instruction->InputAt(0)->GetType(), Primitive::kPrimLong);
+ Primitive::Type type = instruction->InputAt(0)->GetType();
LocationSummary* locations = instruction->GetLocations();
Register dst = locations->Out().AsRegister<Register>();
+ MipsLabel true_label;
- Register lhs = locations->InAt(0).AsRegister<Register>();
- Location rhs_location = locations->InAt(1);
-
- Register rhs_reg = ZERO;
- int64_t rhs_imm = 0;
- bool use_imm = rhs_location.IsConstant();
- if (use_imm) {
- rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
- } else {
- rhs_reg = rhs_location.AsRegister<Register>();
- }
-
- IfCondition if_cond = instruction->GetCondition();
+ switch (type) {
+ default:
+ // Integer case.
+ GenerateIntCompare(instruction->GetCondition(), locations);
+ return;
- switch (if_cond) {
- case kCondEQ:
- case kCondNE:
- if (use_imm && IsUint<16>(rhs_imm)) {
- __ Xori(dst, lhs, rhs_imm);
- } else {
- if (use_imm) {
- rhs_reg = TMP;
- __ LoadConst32(rhs_reg, rhs_imm);
- }
- __ Xor(dst, lhs, rhs_reg);
- }
- if (if_cond == kCondEQ) {
- __ Sltiu(dst, dst, 1);
- } else {
- __ Sltu(dst, ZERO, dst);
- }
+ case Primitive::kPrimLong:
+ // TODO: don't use branches.
+ GenerateLongCompareAndBranch(instruction->GetCondition(), locations, &true_label);
break;
- case kCondLT:
- case kCondGE:
- if (use_imm && IsInt<16>(rhs_imm)) {
- __ Slti(dst, lhs, rhs_imm);
- } else {
- if (use_imm) {
- rhs_reg = TMP;
- __ LoadConst32(rhs_reg, rhs_imm);
- }
- __ Slt(dst, lhs, rhs_reg);
- }
- if (if_cond == kCondGE) {
- // Simulate lhs >= rhs via !(lhs < rhs) since there's
- // only the slt instruction but no sge.
- __ Xori(dst, dst, 1);
- }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ // TODO: don't use branches.
+ GenerateFpCompareAndBranch(instruction->GetCondition(),
+ instruction->IsGtBias(),
+ type,
+ locations,
+ &true_label);
break;
+ }
- case kCondLE:
- case kCondGT:
- if (use_imm && IsInt<16>(rhs_imm + 1)) {
- // Simulate lhs <= rhs via lhs < rhs + 1.
- __ Slti(dst, lhs, rhs_imm + 1);
- if (if_cond == kCondGT) {
- // Simulate lhs > rhs via !(lhs <= rhs) since there's
- // only the slti instruction but no sgti.
- __ Xori(dst, dst, 1);
- }
- } else {
- if (use_imm) {
- rhs_reg = TMP;
- __ LoadConst32(rhs_reg, rhs_imm);
- }
- __ Slt(dst, rhs_reg, lhs);
- if (if_cond == kCondLE) {
- // Simulate lhs <= rhs via !(rhs < lhs) since there's
- // only the slt instruction but no sle.
- __ Xori(dst, dst, 1);
- }
- }
- break;
+ // Convert the branches into the result.
+ MipsLabel done;
- case kCondB:
- case kCondAE:
- // Use sltiu instruction if rhs_imm is in range [0, 32767] or in
- // [max_unsigned - 32767 = 0xffff8000, max_unsigned = 0xffffffff].
- if (use_imm &&
- (IsUint<15>(rhs_imm) ||
- IsUint<15>(rhs_imm - (MaxInt<uint64_t>(32) - MaxInt<uint64_t>(15))))) {
- if (IsUint<15>(rhs_imm)) {
- __ Sltiu(dst, lhs, rhs_imm);
- } else {
- // 16-bit value (in range [0x8000, 0xffff]) passed to sltiu is sign-extended
- // and then used as unsigned integer (range [0xffff8000, 0xffffffff]).
- __ Sltiu(dst, lhs, rhs_imm - (MaxInt<uint64_t>(32) - MaxInt<uint64_t>(16)));
- }
- } else {
- if (use_imm) {
- rhs_reg = TMP;
- __ LoadConst32(rhs_reg, rhs_imm);
- }
- __ Sltu(dst, lhs, rhs_reg);
- }
- if (if_cond == kCondAE) {
- // Simulate lhs >= rhs via !(lhs < rhs) since there's
- // only the sltu instruction but no sgeu.
- __ Xori(dst, dst, 1);
- }
- break;
+ // False case: result = 0.
+ __ LoadConst32(dst, 0);
+ __ B(&done);
- case kCondBE:
- case kCondA:
- // Use sltiu instruction if rhs_imm is in range [0, 32766] or in
- // [max_unsigned - 32767 - 1 = 0xffff7fff, max_unsigned - 1 = 0xfffffffe].
- // lhs <= rhs is simulated via lhs < rhs + 1.
- if (use_imm && (rhs_imm != -1) &&
- (IsUint<15>(rhs_imm + 1) ||
- IsUint<15>(rhs_imm + 1 - (MaxInt<uint64_t>(32) - MaxInt<uint64_t>(15))))) {
- if (IsUint<15>(rhs_imm + 1)) {
- // Simulate lhs <= rhs via lhs < rhs + 1.
- __ Sltiu(dst, lhs, rhs_imm + 1);
- } else {
- // 16-bit value (in range [0x8000, 0xffff]) passed to sltiu is sign-extended
- // and then used as unsigned integer (range [0xffff8000, 0xffffffff] where rhs_imm
- // is in range [0xffff7fff, 0xfffffffe] since lhs <= rhs is simulated via lhs < rhs + 1).
- __ Sltiu(dst, lhs, rhs_imm + 1 - (MaxInt<uint64_t>(32) - MaxInt<uint64_t>(16)));
- }
- if (if_cond == kCondA) {
- // Simulate lhs > rhs via !(lhs <= rhs) since there's
- // only the sltiu instruction but no sgtiu.
- __ Xori(dst, dst, 1);
- }
- } else {
- if (use_imm) {
- rhs_reg = TMP;
- __ LoadConst32(rhs_reg, rhs_imm);
- }
- __ Sltu(dst, rhs_reg, lhs);
- if (if_cond == kCondBE) {
- // Simulate lhs <= rhs via !(rhs < lhs) since there's
- // only the sltu instruction but no sleu.
- __ Xori(dst, dst, 1);
- }
- }
- break;
- }
+ // True case: result = 1.
+ __ Bind(&true_label);
+ __ LoadConst32(dst, 1);
+ __ Bind(&done);
}
void InstructionCodeGeneratorMIPS::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
@@ -2574,6 +2513,627 @@ void InstructionCodeGeneratorMIPS::VisitTryBoundary(HTryBoundary* try_boundary)
}
}
+void InstructionCodeGeneratorMIPS::GenerateIntCompare(IfCondition cond,
+ LocationSummary* locations) {
+ Register dst = locations->Out().AsRegister<Register>();
+ Register lhs = locations->InAt(0).AsRegister<Register>();
+ Location rhs_location = locations->InAt(1);
+ Register rhs_reg = ZERO;
+ int64_t rhs_imm = 0;
+ bool use_imm = rhs_location.IsConstant();
+ if (use_imm) {
+ rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
+ } else {
+ rhs_reg = rhs_location.AsRegister<Register>();
+ }
+
+ switch (cond) {
+ case kCondEQ:
+ case kCondNE:
+ if (use_imm && IsUint<16>(rhs_imm)) {
+ __ Xori(dst, lhs, rhs_imm);
+ } else {
+ if (use_imm) {
+ rhs_reg = TMP;
+ __ LoadConst32(rhs_reg, rhs_imm);
+ }
+ __ Xor(dst, lhs, rhs_reg);
+ }
+ if (cond == kCondEQ) {
+ __ Sltiu(dst, dst, 1);
+ } else {
+ __ Sltu(dst, ZERO, dst);
+ }
+ break;
+
+ case kCondLT:
+ case kCondGE:
+ if (use_imm && IsInt<16>(rhs_imm)) {
+ __ Slti(dst, lhs, rhs_imm);
+ } else {
+ if (use_imm) {
+ rhs_reg = TMP;
+ __ LoadConst32(rhs_reg, rhs_imm);
+ }
+ __ Slt(dst, lhs, rhs_reg);
+ }
+ if (cond == kCondGE) {
+ // Simulate lhs >= rhs via !(lhs < rhs) since there's
+ // only the slt instruction but no sge.
+ __ Xori(dst, dst, 1);
+ }
+ break;
+
+ case kCondLE:
+ case kCondGT:
+ if (use_imm && IsInt<16>(rhs_imm + 1)) {
+ // Simulate lhs <= rhs via lhs < rhs + 1.
+ __ Slti(dst, lhs, rhs_imm + 1);
+ if (cond == kCondGT) {
+ // Simulate lhs > rhs via !(lhs <= rhs) since there's
+ // only the slti instruction but no sgti.
+ __ Xori(dst, dst, 1);
+ }
+ } else {
+ if (use_imm) {
+ rhs_reg = TMP;
+ __ LoadConst32(rhs_reg, rhs_imm);
+ }
+ __ Slt(dst, rhs_reg, lhs);
+ if (cond == kCondLE) {
+ // Simulate lhs <= rhs via !(rhs < lhs) since there's
+ // only the slt instruction but no sle.
+ __ Xori(dst, dst, 1);
+ }
+ }
+ break;
+
+ case kCondB:
+ case kCondAE:
+ if (use_imm && IsInt<16>(rhs_imm)) {
+ // Sltiu sign-extends its 16-bit immediate operand before
+ // the comparison and thus lets us compare directly with
+ // unsigned values in the ranges [0, 0x7fff] and
+ // [0xffff8000, 0xffffffff].
+ __ Sltiu(dst, lhs, rhs_imm);
+ } else {
+ if (use_imm) {
+ rhs_reg = TMP;
+ __ LoadConst32(rhs_reg, rhs_imm);
+ }
+ __ Sltu(dst, lhs, rhs_reg);
+ }
+ if (cond == kCondAE) {
+ // Simulate lhs >= rhs via !(lhs < rhs) since there's
+ // only the sltu instruction but no sgeu.
+ __ Xori(dst, dst, 1);
+ }
+ break;
+
+ case kCondBE:
+ case kCondA:
+ if (use_imm && (rhs_imm != -1) && IsInt<16>(rhs_imm + 1)) {
+ // Simulate lhs <= rhs via lhs < rhs + 1.
+ // Note that this only works if rhs + 1 does not overflow
+ // to 0, hence the check above.
+ // Sltiu sign-extends its 16-bit immediate operand before
+ // the comparison and thus lets us compare directly with
+ // unsigned values in the ranges [0, 0x7fff] and
+ // [0xffff8000, 0xffffffff].
+ __ Sltiu(dst, lhs, rhs_imm + 1);
+ if (cond == kCondA) {
+ // Simulate lhs > rhs via !(lhs <= rhs) since there's
+ // only the sltiu instruction but no sgtiu.
+ __ Xori(dst, dst, 1);
+ }
+ } else {
+ if (use_imm) {
+ rhs_reg = TMP;
+ __ LoadConst32(rhs_reg, rhs_imm);
+ }
+ __ Sltu(dst, rhs_reg, lhs);
+ if (cond == kCondBE) {
+ // Simulate lhs <= rhs via !(rhs < lhs) since there's
+ // only the sltu instruction but no sleu.
+ __ Xori(dst, dst, 1);
+ }
+ }
+ break;
+ }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateIntCompareAndBranch(IfCondition cond,
+ LocationSummary* locations,
+ MipsLabel* label) {
+ Register lhs = locations->InAt(0).AsRegister<Register>();
+ Location rhs_location = locations->InAt(1);
+ Register rhs_reg = ZERO;
+ int32_t rhs_imm = 0;
+ bool use_imm = rhs_location.IsConstant();
+ if (use_imm) {
+ rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
+ } else {
+ rhs_reg = rhs_location.AsRegister<Register>();
+ }
+
+ if (use_imm && rhs_imm == 0) {
+ switch (cond) {
+ case kCondEQ:
+ case kCondBE: // <= 0 if zero
+ __ Beqz(lhs, label);
+ break;
+ case kCondNE:
+ case kCondA: // > 0 if non-zero
+ __ Bnez(lhs, label);
+ break;
+ case kCondLT:
+ __ Bltz(lhs, label);
+ break;
+ case kCondGE:
+ __ Bgez(lhs, label);
+ break;
+ case kCondLE:
+ __ Blez(lhs, label);
+ break;
+ case kCondGT:
+ __ Bgtz(lhs, label);
+ break;
+ case kCondB: // always false
+ break;
+ case kCondAE: // always true
+ __ B(label);
+ break;
+ }
+ } else {
+ if (use_imm) {
+ // TODO: more efficient comparison with 16-bit constants without loading them into TMP.
+ rhs_reg = TMP;
+ __ LoadConst32(rhs_reg, rhs_imm);
+ }
+ switch (cond) {
+ case kCondEQ:
+ __ Beq(lhs, rhs_reg, label);
+ break;
+ case kCondNE:
+ __ Bne(lhs, rhs_reg, label);
+ break;
+ case kCondLT:
+ __ Blt(lhs, rhs_reg, label);
+ break;
+ case kCondGE:
+ __ Bge(lhs, rhs_reg, label);
+ break;
+ case kCondLE:
+ __ Bge(rhs_reg, lhs, label);
+ break;
+ case kCondGT:
+ __ Blt(rhs_reg, lhs, label);
+ break;
+ case kCondB:
+ __ Bltu(lhs, rhs_reg, label);
+ break;
+ case kCondAE:
+ __ Bgeu(lhs, rhs_reg, label);
+ break;
+ case kCondBE:
+ __ Bgeu(rhs_reg, lhs, label);
+ break;
+ case kCondA:
+ __ Bltu(rhs_reg, lhs, label);
+ break;
+ }
+ }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateLongCompareAndBranch(IfCondition cond,
+ LocationSummary* locations,
+ MipsLabel* label) {
+ Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
+ Location rhs_location = locations->InAt(1);
+ Register rhs_high = ZERO;
+ Register rhs_low = ZERO;
+ int64_t imm = 0;
+ uint32_t imm_high = 0;
+ uint32_t imm_low = 0;
+ bool use_imm = rhs_location.IsConstant();
+ if (use_imm) {
+ imm = rhs_location.GetConstant()->AsLongConstant()->GetValue();
+ imm_high = High32Bits(imm);
+ imm_low = Low32Bits(imm);
+ } else {
+ rhs_high = rhs_location.AsRegisterPairHigh<Register>();
+ rhs_low = rhs_location.AsRegisterPairLow<Register>();
+ }
+
+ if (use_imm && imm == 0) {
+ switch (cond) {
+ case kCondEQ:
+ case kCondBE: // <= 0 if zero
+ __ Or(TMP, lhs_high, lhs_low);
+ __ Beqz(TMP, label);
+ break;
+ case kCondNE:
+ case kCondA: // > 0 if non-zero
+ __ Or(TMP, lhs_high, lhs_low);
+ __ Bnez(TMP, label);
+ break;
+ case kCondLT:
+ __ Bltz(lhs_high, label);
+ break;
+ case kCondGE:
+ __ Bgez(lhs_high, label);
+ break;
+ case kCondLE:
+ __ Or(TMP, lhs_high, lhs_low);
+ __ Sra(AT, lhs_high, 31);
+ __ Bgeu(AT, TMP, label);
+ break;
+ case kCondGT:
+ __ Or(TMP, lhs_high, lhs_low);
+ __ Sra(AT, lhs_high, 31);
+ __ Bltu(AT, TMP, label);
+ break;
+ case kCondB: // always false
+ break;
+ case kCondAE: // always true
+ __ B(label);
+ break;
+ }
+ } else if (use_imm) {
+ // TODO: more efficient comparison with constants without loading them into TMP/AT.
+ switch (cond) {
+ case kCondEQ:
+ __ LoadConst32(TMP, imm_high);
+ __ Xor(TMP, TMP, lhs_high);
+ __ LoadConst32(AT, imm_low);
+ __ Xor(AT, AT, lhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondNE:
+ __ LoadConst32(TMP, imm_high);
+ __ Xor(TMP, TMP, lhs_high);
+ __ LoadConst32(AT, imm_low);
+ __ Xor(AT, AT, lhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Bnez(TMP, label);
+ break;
+ case kCondLT:
+ __ LoadConst32(TMP, imm_high);
+ __ Blt(lhs_high, TMP, label);
+ __ Slt(TMP, TMP, lhs_high);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, lhs_low, AT);
+ __ Blt(TMP, AT, label);
+ break;
+ case kCondGE:
+ __ LoadConst32(TMP, imm_high);
+ __ Blt(TMP, lhs_high, label);
+ __ Slt(TMP, lhs_high, TMP);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, lhs_low, AT);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondLE:
+ __ LoadConst32(TMP, imm_high);
+ __ Blt(lhs_high, TMP, label);
+ __ Slt(TMP, TMP, lhs_high);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, AT, lhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondGT:
+ __ LoadConst32(TMP, imm_high);
+ __ Blt(TMP, lhs_high, label);
+ __ Slt(TMP, lhs_high, TMP);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, AT, lhs_low);
+ __ Blt(TMP, AT, label);
+ break;
+ case kCondB:
+ __ LoadConst32(TMP, imm_high);
+ __ Bltu(lhs_high, TMP, label);
+ __ Sltu(TMP, TMP, lhs_high);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, lhs_low, AT);
+ __ Blt(TMP, AT, label);
+ break;
+ case kCondAE:
+ __ LoadConst32(TMP, imm_high);
+ __ Bltu(TMP, lhs_high, label);
+ __ Sltu(TMP, lhs_high, TMP);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, lhs_low, AT);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondBE:
+ __ LoadConst32(TMP, imm_high);
+ __ Bltu(lhs_high, TMP, label);
+ __ Sltu(TMP, TMP, lhs_high);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, AT, lhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondA:
+ __ LoadConst32(TMP, imm_high);
+ __ Bltu(TMP, lhs_high, label);
+ __ Sltu(TMP, lhs_high, TMP);
+ __ LoadConst32(AT, imm_low);
+ __ Sltu(AT, AT, lhs_low);
+ __ Blt(TMP, AT, label);
+ break;
+ }
+ } else {
+ switch (cond) {
+ case kCondEQ:
+ __ Xor(TMP, lhs_high, rhs_high);
+ __ Xor(AT, lhs_low, rhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondNE:
+ __ Xor(TMP, lhs_high, rhs_high);
+ __ Xor(AT, lhs_low, rhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Bnez(TMP, label);
+ break;
+ case kCondLT:
+ __ Blt(lhs_high, rhs_high, label);
+ __ Slt(TMP, rhs_high, lhs_high);
+ __ Sltu(AT, lhs_low, rhs_low);
+ __ Blt(TMP, AT, label);
+ break;
+ case kCondGE:
+ __ Blt(rhs_high, lhs_high, label);
+ __ Slt(TMP, lhs_high, rhs_high);
+ __ Sltu(AT, lhs_low, rhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondLE:
+ __ Blt(lhs_high, rhs_high, label);
+ __ Slt(TMP, rhs_high, lhs_high);
+ __ Sltu(AT, rhs_low, lhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondGT:
+ __ Blt(rhs_high, lhs_high, label);
+ __ Slt(TMP, lhs_high, rhs_high);
+ __ Sltu(AT, rhs_low, lhs_low);
+ __ Blt(TMP, AT, label);
+ break;
+ case kCondB:
+ __ Bltu(lhs_high, rhs_high, label);
+ __ Sltu(TMP, rhs_high, lhs_high);
+ __ Sltu(AT, lhs_low, rhs_low);
+ __ Blt(TMP, AT, label);
+ break;
+ case kCondAE:
+ __ Bltu(rhs_high, lhs_high, label);
+ __ Sltu(TMP, lhs_high, rhs_high);
+ __ Sltu(AT, lhs_low, rhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondBE:
+ __ Bltu(lhs_high, rhs_high, label);
+ __ Sltu(TMP, rhs_high, lhs_high);
+ __ Sltu(AT, rhs_low, lhs_low);
+ __ Or(TMP, TMP, AT);
+ __ Beqz(TMP, label);
+ break;
+ case kCondA:
+ __ Bltu(rhs_high, lhs_high, label);
+ __ Sltu(TMP, lhs_high, rhs_high);
+ __ Sltu(AT, rhs_low, lhs_low);
+ __ Blt(TMP, AT, label);
+ break;
+ }
+ }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateFpCompareAndBranch(IfCondition cond,
+ bool gt_bias,
+ Primitive::Type type,
+ LocationSummary* locations,
+ MipsLabel* label) {
+ FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
+ FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
+ bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+ if (type == Primitive::kPrimFloat) {
+ if (isR6) {
+ switch (cond) {
+ case kCondEQ:
+ __ CmpEqS(FTMP, lhs, rhs);
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondNE:
+ __ CmpEqS(FTMP, lhs, rhs);
+ __ Bc1eqz(FTMP, label);
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ CmpLtS(FTMP, lhs, rhs);
+ } else {
+ __ CmpUltS(FTMP, lhs, rhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ CmpLeS(FTMP, lhs, rhs);
+ } else {
+ __ CmpUleS(FTMP, lhs, rhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ CmpUltS(FTMP, rhs, lhs);
+ } else {
+ __ CmpLtS(FTMP, rhs, lhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ CmpUleS(FTMP, rhs, lhs);
+ } else {
+ __ CmpLeS(FTMP, rhs, lhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected non-floating-point condition";
+ }
+ } else {
+ switch (cond) {
+ case kCondEQ:
+ __ CeqS(0, lhs, rhs);
+ __ Bc1t(0, label);
+ break;
+ case kCondNE:
+ __ CeqS(0, lhs, rhs);
+ __ Bc1f(0, label);
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ ColtS(0, lhs, rhs);
+ } else {
+ __ CultS(0, lhs, rhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ ColeS(0, lhs, rhs);
+ } else {
+ __ CuleS(0, lhs, rhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ CultS(0, rhs, lhs);
+ } else {
+ __ ColtS(0, rhs, lhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ CuleS(0, rhs, lhs);
+ } else {
+ __ ColeS(0, rhs, lhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected non-floating-point condition";
+ }
+ }
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimDouble);
+ if (isR6) {
+ switch (cond) {
+ case kCondEQ:
+ __ CmpEqD(FTMP, lhs, rhs);
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondNE:
+ __ CmpEqD(FTMP, lhs, rhs);
+ __ Bc1eqz(FTMP, label);
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ CmpLtD(FTMP, lhs, rhs);
+ } else {
+ __ CmpUltD(FTMP, lhs, rhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ CmpLeD(FTMP, lhs, rhs);
+ } else {
+ __ CmpUleD(FTMP, lhs, rhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ CmpUltD(FTMP, rhs, lhs);
+ } else {
+ __ CmpLtD(FTMP, rhs, lhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ CmpUleD(FTMP, rhs, lhs);
+ } else {
+ __ CmpLeD(FTMP, rhs, lhs);
+ }
+ __ Bc1nez(FTMP, label);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected non-floating-point condition";
+ }
+ } else {
+ switch (cond) {
+ case kCondEQ:
+ __ CeqD(0, lhs, rhs);
+ __ Bc1t(0, label);
+ break;
+ case kCondNE:
+ __ CeqD(0, lhs, rhs);
+ __ Bc1f(0, label);
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ ColtD(0, lhs, rhs);
+ } else {
+ __ CultD(0, lhs, rhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ ColeD(0, lhs, rhs);
+ } else {
+ __ CuleD(0, lhs, rhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ CultD(0, rhs, lhs);
+ } else {
+ __ ColtD(0, rhs, lhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ CuleD(0, rhs, lhs);
+ } else {
+ __ ColeD(0, rhs, lhs);
+ }
+ __ Bc1t(0, label);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected non-floating-point condition";
+ }
+ }
+ }
+}
+
void InstructionCodeGeneratorMIPS::GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
MipsLabel* true_target,
@@ -2610,7 +3170,7 @@ void InstructionCodeGeneratorMIPS::GenerateTestAndBranch(HInstruction* instructi
// The condition instruction has been materialized, compare the output to 0.
Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
DCHECK(cond_val.IsRegister());
- if (true_target == nullptr) {
+ if (true_target == nullptr) {
__ Beqz(cond_val.AsRegister<Register>(), false_target);
} else {
__ Bnez(cond_val.AsRegister<Register>(), true_target);
@@ -2619,98 +3179,27 @@ void InstructionCodeGeneratorMIPS::GenerateTestAndBranch(HInstruction* instructi
// The condition instruction has not been materialized, use its inputs as
// the comparison and its condition as the branch condition.
HCondition* condition = cond->AsCondition();
+ Primitive::Type type = condition->InputAt(0)->GetType();
+ LocationSummary* locations = cond->GetLocations();
+ IfCondition if_cond = condition->GetCondition();
+ MipsLabel* branch_target = true_target;
- Register lhs = condition->GetLocations()->InAt(0).AsRegister<Register>();
- Location rhs_location = condition->GetLocations()->InAt(1);
- Register rhs_reg = ZERO;
- int32_t rhs_imm = 0;
- bool use_imm = rhs_location.IsConstant();
- if (use_imm) {
- rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant());
- } else {
- rhs_reg = rhs_location.AsRegister<Register>();
- }
-
- IfCondition if_cond;
- MipsLabel* non_fallthrough_target;
if (true_target == nullptr) {
if_cond = condition->GetOppositeCondition();
- non_fallthrough_target = false_target;
- } else {
- if_cond = condition->GetCondition();
- non_fallthrough_target = true_target;
+ branch_target = false_target;
}
- if (use_imm && rhs_imm == 0) {
- switch (if_cond) {
- case kCondEQ:
- __ Beqz(lhs, non_fallthrough_target);
- break;
- case kCondNE:
- __ Bnez(lhs, non_fallthrough_target);
- break;
- case kCondLT:
- __ Bltz(lhs, non_fallthrough_target);
- break;
- case kCondGE:
- __ Bgez(lhs, non_fallthrough_target);
- break;
- case kCondLE:
- __ Blez(lhs, non_fallthrough_target);
- break;
- case kCondGT:
- __ Bgtz(lhs, non_fallthrough_target);
- break;
- case kCondB:
- break; // always false
- case kCondBE:
- __ Beqz(lhs, non_fallthrough_target); // <= 0 if zero
- break;
- case kCondA:
- __ Bnez(lhs, non_fallthrough_target); // > 0 if non-zero
- break;
- case kCondAE:
- __ B(non_fallthrough_target); // always true
- break;
- }
- } else {
- if (use_imm) {
- // TODO: more efficient comparison with 16-bit constants without loading them into TMP.
- rhs_reg = TMP;
- __ LoadConst32(rhs_reg, rhs_imm);
- }
- switch (if_cond) {
- case kCondEQ:
- __ Beq(lhs, rhs_reg, non_fallthrough_target);
- break;
- case kCondNE:
- __ Bne(lhs, rhs_reg, non_fallthrough_target);
- break;
- case kCondLT:
- __ Blt(lhs, rhs_reg, non_fallthrough_target);
- break;
- case kCondGE:
- __ Bge(lhs, rhs_reg, non_fallthrough_target);
- break;
- case kCondLE:
- __ Bge(rhs_reg, lhs, non_fallthrough_target);
- break;
- case kCondGT:
- __ Blt(rhs_reg, lhs, non_fallthrough_target);
- break;
- case kCondB:
- __ Bltu(lhs, rhs_reg, non_fallthrough_target);
- break;
- case kCondAE:
- __ Bgeu(lhs, rhs_reg, non_fallthrough_target);
- break;
- case kCondBE:
- __ Bgeu(rhs_reg, lhs, non_fallthrough_target);
- break;
- case kCondA:
- __ Bltu(rhs_reg, lhs, non_fallthrough_target);
- break;
- }
+ switch (type) {
+ default:
+ GenerateIntCompareAndBranch(if_cond, locations, branch_target);
+ break;
+ case Primitive::kPrimLong:
+ GenerateLongCompareAndBranch(if_cond, locations, branch_target);
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ GenerateFpCompareAndBranch(if_cond, condition->IsGtBias(), type, locations, branch_target);
+ break;
}
}
@@ -3413,9 +3902,9 @@ void InstructionCodeGeneratorMIPS::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNU
}
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
- ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
+ LocationSummary::CallKind call_kind = load->IsInDexCache()
+ ? LocationSummary::kNoCall
+ : LocationSummary::kCallOnSlowPath;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
@@ -4408,31 +4897,19 @@ void InstructionCodeGeneratorMIPS::VisitPackedSwitch(HPackedSwitch* switch_instr
HBasicBlock* default_block = switch_instr->GetDefaultBlock();
// Create a set of compare/jumps.
- Register temp_reg = TMP;
- __ Addiu32(temp_reg, value_reg, -lower_bound);
- // Jump to default if index is negative
- // Note: We don't check the case that index is positive while value < lower_bound, because in
- // this case, index >= num_entries must be true. So that we can save one branch instruction.
- __ Bltz(temp_reg, codegen_->GetLabelOf(default_block));
-
const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- // Jump to successors[0] if value == lower_bound.
- __ Beqz(temp_reg, codegen_->GetLabelOf(successors[0]));
- int32_t last_index = 0;
- for (; num_entries - last_index > 2; last_index += 2) {
- __ Addiu(temp_reg, temp_reg, -2);
- // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
- __ Bltz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
- // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
- __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 2]));
- }
- if (num_entries - last_index == 2) {
- // The last missing case_value.
- __ Addiu(temp_reg, temp_reg, -1);
- __ Beqz(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
- }
-
- // And the default for any other value.
+ for (int32_t i = 0; i < num_entries; ++i) {
+ int32_t case_value = lower_bound + i;
+ MipsLabel* successor_label = codegen_->GetLabelOf(successors[i]);
+ if (case_value == 0) {
+ __ Beqz(value_reg, successor_label);
+ } else {
+ __ LoadConst32(TMP, case_value);
+ __ Beq(value_reg, TMP, successor_label);
+ }
+ }
+
+ // Insert the default branch for every other value.
if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
__ B(codegen_->GetLabelOf(default_block));
}
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index caf3174455..1ee6bdef8e 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -225,6 +225,18 @@ class InstructionCodeGeneratorMIPS : public HGraphVisitor {
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
+ void GenerateIntCompare(IfCondition cond, LocationSummary* locations);
+ void GenerateIntCompareAndBranch(IfCondition cond,
+ LocationSummary* locations,
+ MipsLabel* label);
+ void GenerateLongCompareAndBranch(IfCondition cond,
+ LocationSummary* locations,
+ MipsLabel* label);
+ void GenerateFpCompareAndBranch(IfCondition cond,
+ bool gt_bias,
+ Primitive::Type type,
+ LocationSummary* locations,
+ MipsLabel* label);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
MipsLabel* true_target,
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 1a9de15c6f..99f58dd2c5 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3051,7 +3051,8 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
} else {
__ LoadFromOffset(kLoadDoubleword, out, current_method,
ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
- __ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ __ LoadFromOffset(
+ kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
// TODO: We will need a read barrier here.
if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
@@ -3105,9 +3106,9 @@ void InstructionCodeGeneratorMIPS64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_U
}
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
- ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
+ LocationSummary::CallKind call_kind = load->IsInDexCache()
+ ? LocationSummary::kNoCall
+ : LocationSummary::kCallOnSlowPath;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
@@ -3120,7 +3121,8 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
__ LoadFromOffset(kLoadUnsignedWord, out, current_method,
ArtMethod::DeclaringClassOffset().Int32Value());
__ LoadFromOffset(kLoadDoubleword, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
- __ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ __ LoadFromOffset(
+ kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
// TODO: We will need a read barrier here.
if (!load->IsInDexCache()) {
@@ -3989,34 +3991,17 @@ void InstructionCodeGeneratorMIPS64::VisitPackedSwitch(HPackedSwitch* switch_ins
GpuRegister value_reg = locations->InAt(0).AsRegister<GpuRegister>();
HBasicBlock* default_block = switch_instr->GetDefaultBlock();
- // Create a set of compare/jumps.
- GpuRegister temp_reg = TMP;
- if (IsInt<16>(-lower_bound)) {
- __ Addiu(temp_reg, value_reg, -lower_bound);
- } else {
- __ LoadConst32(AT, -lower_bound);
- __ Addu(temp_reg, value_reg, AT);
- }
- // Jump to default if index is negative
- // Note: We don't check the case that index is positive while value < lower_bound, because in
- // this case, index >= num_entries must be true. So that we can save one branch instruction.
- __ Bltzc(temp_reg, codegen_->GetLabelOf(default_block));
-
+ // Create a series of compare/jumps.
const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- // Jump to successors[0] if value == lower_bound.
- __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[0]));
- int32_t last_index = 0;
- for (; num_entries - last_index > 2; last_index += 2) {
- __ Addiu(temp_reg, temp_reg, -2);
- // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
- __ Bltzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
- // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
- __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 2]));
- }
- if (num_entries - last_index == 2) {
- // The last missing case_value.
- __ Addiu(temp_reg, temp_reg, -1);
- __ Beqzc(temp_reg, codegen_->GetLabelOf(successors[last_index + 1]));
+ for (int32_t i = 0; i < num_entries; i++) {
+ int32_t case_value = lower_bound + i;
+ Mips64Label* succ = codegen_->GetLabelOf(successors[i]);
+ if (case_value == 0) {
+ __ Beqzc(value_reg, succ);
+ } else {
+ __ LoadConst32(TMP, case_value);
+ __ Beqc(value_reg, TMP, succ);
+ }
}
// And the default for any other value.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 469dd49a8e..bc3256ec8c 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -42,6 +42,7 @@ namespace x86 {
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr Register kMethodRegisterArgument = EAX;
+
static constexpr Register kCoreCalleeSaves[] = { EBP, ESI, EDI };
static constexpr int kC2ConditionMask = 0x400;
@@ -6751,65 +6752,29 @@ void LocationsBuilderX86::VisitPackedSwitch(HPackedSwitch* switch_instr) {
locations->SetInAt(0, Location::RequiresRegister());
}
-void InstructionCodeGeneratorX86::GenPackedSwitchWithCompares(Register value_reg,
- int32_t lower_bound,
- uint32_t num_entries,
- HBasicBlock* switch_block,
- HBasicBlock* default_block) {
- // Figure out the correct compare values and jump conditions.
- // Handle the first compare/branch as a special case because it might
- // jump to the default case.
- DCHECK_GT(num_entries, 2u);
- Condition first_condition;
- uint32_t index;
- const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
- if (lower_bound != 0) {
- first_condition = kLess;
- __ cmpl(value_reg, Immediate(lower_bound));
- __ j(first_condition, codegen_->GetLabelOf(default_block));
- __ j(kEqual, codegen_->GetLabelOf(successors[0]));
-
- index = 1;
- } else {
- // Handle all the compare/jumps below.
- first_condition = kBelow;
- index = 0;
- }
-
- // Handle the rest of the compare/jumps.
- for (; index + 1 < num_entries; index += 2) {
- int32_t compare_to_value = lower_bound + index + 1;
- __ cmpl(value_reg, Immediate(compare_to_value));
- // Jump to successors[index] if value < case_value[index].
- __ j(first_condition, codegen_->GetLabelOf(successors[index]));
- // Jump to successors[index + 1] if value == case_value[index + 1].
- __ j(kEqual, codegen_->GetLabelOf(successors[index + 1]));
- }
-
- if (index != num_entries) {
- // There are an odd number of entries. Handle the last one.
- DCHECK_EQ(index + 1, num_entries);
- __ cmpl(value_reg, Immediate(lower_bound + index));
- __ j(kEqual, codegen_->GetLabelOf(successors[index]));
- }
-
- // And the default for any other value.
- if (!codegen_->GoesToNextBlock(switch_block, default_block)) {
- __ jmp(codegen_->GetLabelOf(default_block));
- }
-}
-
void InstructionCodeGeneratorX86::VisitPackedSwitch(HPackedSwitch* switch_instr) {
int32_t lower_bound = switch_instr->GetStartValue();
- uint32_t num_entries = switch_instr->GetNumEntries();
+ int32_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 (int i = 0; i < num_entries; i++) {
+ int32_t case_value = lower_bound + i;
+ if (case_value == 0) {
+ __ testl(value_reg, value_reg);
+ } else {
+ __ cmpl(value_reg, Immediate(case_value));
+ }
+ __ j(kEqual, codegen_->GetLabelOf(successors[i]));
+ }
- GenPackedSwitchWithCompares(value_reg,
- lower_bound,
- num_entries,
- switch_instr->GetBlock(),
- switch_instr->GetDefaultBlock());
+ // And the default for any other value.
+ if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
+ __ jmp(codegen_->GetLabelOf(default_block));
+ }
}
void LocationsBuilderX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) {
@@ -6826,20 +6791,11 @@ void LocationsBuilderX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) {
void InstructionCodeGeneratorX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) {
int32_t lower_bound = switch_instr->GetStartValue();
- uint32_t num_entries = switch_instr->GetNumEntries();
+ int32_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();
- if (num_entries <= kPackedSwitchJumpTableThreshold) {
- GenPackedSwitchWithCompares(value_reg,
- lower_bound,
- num_entries,
- switch_instr->GetBlock(),
- default_block);
- return;
- }
-
// Optimizing has a jump area.
Register temp_reg = locations->GetTemp(0).AsRegister<Register>();
Register constant_area = locations->InAt(1).AsRegister<Register>();
@@ -6851,7 +6807,7 @@ void InstructionCodeGeneratorX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_
}
// Is the value in range?
- DCHECK_GE(num_entries, 1u);
+ DCHECK_GE(num_entries, 1);
__ cmpl(value_reg, Immediate(num_entries - 1));
__ j(kAbove, codegen_->GetLabelOf(default_block));
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 712179920b..7c292fa103 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -195,11 +195,6 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
X86Assembler* GetAssembler() const { return assembler_; }
- // The compare/jump sequence will generate about (1.5 * num_entries) instructions. A jump
- // table version generates 7 instructions and num_entries literals. Compare/jump sequence will
- // generates less code/data with a small num_entries.
- static constexpr uint32_t kPackedSwitchJumpTableThreshold = 5;
-
private:
// Generate code for the given suspend check. If not null, `successor`
// is the block to branch to if the suspend check is not needed, and after
@@ -274,11 +269,6 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label);
void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
- void GenPackedSwitchWithCompares(Register value_reg,
- int32_t lower_bound,
- uint32_t num_entries,
- HBasicBlock* switch_block,
- HBasicBlock* default_block);
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 1fee192e36..92cef5f226 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -41,10 +41,6 @@ namespace x86_64 {
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr Register kMethodRegisterArgument = RDI;
-// The compare/jump sequence will generate about (1.5 * num_entries) instructions. A jump
-// table version generates 7 instructions and num_entries literals. Compare/jump sequence will
-// generates less code/data with a small num_entries.
-static constexpr uint32_t kPackedSwitchJumpTableThreshold = 5;
static constexpr Register kCoreCalleeSaves[] = { RBX, RBP, R12, R13, R14, R15 };
static constexpr FloatRegister kFpuCalleeSaves[] = { XMM12, XMM13, XMM14, XMM15 };
@@ -456,6 +452,56 @@ class ArraySetSlowPathX86_64 : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86_64);
};
+// Slow path marking an object during a read barrier.
+class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode {
+ public:
+ ReadBarrierMarkSlowPathX86_64(HInstruction* instruction, Location out, Location obj)
+ : instruction_(instruction), out_(out), obj_(obj) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathX86_64"; }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ Register reg_out = out_.AsRegister<Register>();
+ DCHECK(locations->CanCall());
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsLoadClass() ||
+ instruction_->IsLoadString() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast())
+ << "Unexpected instruction in read barrier marking slow path: "
+ << instruction_->DebugName();
+
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+ x86_64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), obj_);
+ x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierMark),
+ instruction_,
+ instruction_->GetDexPc(),
+ this);
+ CheckEntrypointTypes<kQuickReadBarrierMark, mirror::Object*, mirror::Object*>();
+ x86_64_codegen->Move(out_, Location::RegisterLocation(RAX));
+
+ RestoreLiveRegisters(codegen, locations);
+ __ jmp(GetExitLabel());
+ }
+
+ private:
+ HInstruction* const instruction_;
+ const Location out_;
+ const Location obj_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathX86_64);
+};
+
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
public:
@@ -477,7 +523,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
// reference load to be instrumented, e.g.:
//
// __ movl(out, Address(out, offset));
- // codegen_->GenerateReadBarrier(instruction, out_loc, out_loc, out_loc, offset);
+ // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
//
// In that case, we have lost the information about the original
// object, and the emitted read barrier cannot work properly.
@@ -493,7 +539,9 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.AsRegister())) << out_;
DCHECK(!instruction_->IsInvoke() ||
(instruction_->IsInvokeStaticOrDirect() &&
- instruction_->GetLocations()->Intrinsified()));
+ instruction_->GetLocations()->Intrinsified()))
+ << "Unexpected instruction in read barrier for heap reference slow path: "
+ << instruction_->DebugName();
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
@@ -634,13 +682,17 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
class ReadBarrierForRootSlowPathX86_64 : public SlowPathCode {
public:
ReadBarrierForRootSlowPathX86_64(HInstruction* instruction, Location out, Location root)
- : instruction_(instruction), out_(out), root_(root) {}
+ : instruction_(instruction), out_(out), root_(root) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg()));
- DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString());
+ DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
+ << "Unexpected instruction in read barrier for GC root slow path: "
+ << instruction_->DebugName();
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
@@ -731,7 +783,7 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
// temp = thread->string_init_entrypoint
__ gs()->movl(temp.AsRegister<CpuRegister>(),
- Address::Absolute(invoke->GetStringInitOffset(), true));
+ Address::Absolute(invoke->GetStringInitOffset(), /* no_rip */ true));
break;
case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
@@ -748,7 +800,7 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo
pc_relative_dex_cache_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
invoke->GetDexCacheArrayOffset());
__ movq(temp.AsRegister<CpuRegister>(),
- Address::Absolute(kDummy32BitOffset, false /* no_rip */));
+ Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
// Bind the label at the end of the "movl" insn.
__ Bind(&pc_relative_dex_cache_patches_.back().label);
break;
@@ -907,7 +959,7 @@ void CodeGeneratorX86_64::InvokeRuntime(int32_t entry_point_offset,
uint32_t dex_pc,
SlowPathCode* slow_path) {
ValidateInvokeRuntime(instruction, slow_path);
- __ gs()->call(Address::Absolute(entry_point_offset, true));
+ __ gs()->call(Address::Absolute(entry_point_offset, /* no_rip */ true));
RecordPcInfo(instruction, dex_pc, slow_path);
}
@@ -1939,7 +1991,7 @@ void LocationsBuilderX86_64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier)
}
void InstructionCodeGeneratorX86_64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
- GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
+ codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
}
void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) {
@@ -2667,7 +2719,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
} else {
DCHECK(in.GetConstant()->IsIntConstant());
__ movl(out.AsRegister<CpuRegister>(),
- Immediate(static_cast<uint16_t>(in.GetConstant()->AsIntConstant()->GetValue())));
+ Immediate(static_cast<uint16_t>(
+ in.GetConstant()->AsIntConstant()->GetValue())));
}
break;
@@ -2911,7 +2964,8 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
__ addss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ addss(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralFloatAddress(second.GetConstant()->AsFloatConstant()->GetValue()));
+ codegen_->LiteralFloatAddress(
+ second.GetConstant()->AsFloatConstant()->GetValue()));
} else {
DCHECK(second.IsStackSlot());
__ addss(first.AsFpuRegister<XmmRegister>(),
@@ -2925,7 +2979,8 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
__ addsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ addsd(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralDoubleAddress(second.GetConstant()->AsDoubleConstant()->GetValue()));
+ codegen_->LiteralDoubleAddress(
+ second.GetConstant()->AsDoubleConstant()->GetValue()));
} else {
DCHECK(second.IsDoubleStackSlot());
__ addsd(first.AsFpuRegister<XmmRegister>(),
@@ -3000,7 +3055,8 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
__ subss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ subss(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralFloatAddress(second.GetConstant()->AsFloatConstant()->GetValue()));
+ codegen_->LiteralFloatAddress(
+ second.GetConstant()->AsFloatConstant()->GetValue()));
} else {
DCHECK(second.IsStackSlot());
__ subss(first.AsFpuRegister<XmmRegister>(),
@@ -3014,7 +3070,8 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
__ subsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ subsd(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralDoubleAddress(second.GetConstant()->AsDoubleConstant()->GetValue()));
+ codegen_->LiteralDoubleAddress(
+ second.GetConstant()->AsDoubleConstant()->GetValue()));
} else {
DCHECK(second.IsDoubleStackSlot());
__ subsd(first.AsFpuRegister<XmmRegister>(),
@@ -3121,7 +3178,8 @@ void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) {
__ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ mulss(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralFloatAddress(second.GetConstant()->AsFloatConstant()->GetValue()));
+ codegen_->LiteralFloatAddress(
+ second.GetConstant()->AsFloatConstant()->GetValue()));
} else {
DCHECK(second.IsStackSlot());
__ mulss(first.AsFpuRegister<XmmRegister>(),
@@ -3136,7 +3194,8 @@ void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) {
__ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ mulsd(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralDoubleAddress(second.GetConstant()->AsDoubleConstant()->GetValue()));
+ codegen_->LiteralDoubleAddress(
+ second.GetConstant()->AsDoubleConstant()->GetValue()));
} else {
DCHECK(second.IsDoubleStackSlot());
__ mulsd(first.AsFpuRegister<XmmRegister>(),
@@ -3542,7 +3601,8 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) {
__ divss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ divss(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralFloatAddress(second.GetConstant()->AsFloatConstant()->GetValue()));
+ codegen_->LiteralFloatAddress(
+ second.GetConstant()->AsFloatConstant()->GetValue()));
} else {
DCHECK(second.IsStackSlot());
__ divss(first.AsFpuRegister<XmmRegister>(),
@@ -3556,7 +3616,8 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) {
__ divsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
__ divsd(first.AsFpuRegister<XmmRegister>(),
- codegen_->LiteralDoubleAddress(second.GetConstant()->AsDoubleConstant()->GetValue()));
+ codegen_->LiteralDoubleAddress(
+ second.GetConstant()->AsDoubleConstant()->GetValue()));
} else {
DCHECK(second.IsDoubleStackSlot());
__ divsd(first.AsFpuRegister<XmmRegister>(),
@@ -3960,10 +4021,10 @@ void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED
LOG(FATAL) << "Unimplemented";
}
-void InstructionCodeGeneratorX86_64::GenerateMemoryBarrier(MemBarrierKind kind) {
+void CodeGeneratorX86_64::GenerateMemoryBarrier(MemBarrierKind kind) {
/*
* According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence.
- * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86 memory model.
+ * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86-64 memory model.
* For those cases, all we need to ensure is that there is a scheduling barrier in place.
*/
switch (kind) {
@@ -4003,6 +4064,11 @@ void LocationsBuilderX86_64::HandleFieldGet(HInstruction* instruction) {
Location::RequiresRegister(),
object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
+ if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+ // We need a temporary register for the read barrier marking slow
+ // path in CodeGeneratorX86_64::GenerateFieldLoadWithBakerReadBarrier.
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
@@ -4038,12 +4104,36 @@ void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
break;
}
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
+ case Primitive::kPrimInt: {
__ movl(out.AsRegister<CpuRegister>(), Address(base, offset));
break;
}
+ case Primitive::kPrimNot: {
+ // /* HeapReference<Object> */ out = *(base + offset)
+ if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ Location temp_loc = locations->GetTemp(0);
+ // Note that a potential implicit null check is handled in this
+ // CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier call.
+ codegen_->GenerateFieldLoadWithBakerReadBarrier(
+ instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
+ if (is_volatile) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
+ } else {
+ __ movl(out.AsRegister<CpuRegister>(), Address(base, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ if (is_volatile) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset);
+ }
+ break;
+ }
+
case Primitive::kPrimLong: {
__ movq(out.AsRegister<CpuRegister>(), Address(base, offset));
break;
@@ -4064,14 +4154,20 @@ void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
UNREACHABLE();
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
-
- if (is_volatile) {
- GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ if (field_type == Primitive::kPrimNot) {
+ // Potential implicit null checks, in the case of reference
+ // fields, are handled in the previous switch statement.
+ } else {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
- if (field_type == Primitive::kPrimNot) {
- codegen_->MaybeGenerateReadBarrier(instruction, out, out, base_loc, offset);
+ if (is_volatile) {
+ if (field_type == Primitive::kPrimNot) {
+ // Memory barriers, in the case of references, are also handled
+ // in the previous switch statement.
+ } else {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
}
}
@@ -4125,7 +4221,7 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
if (is_volatile) {
- GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
}
bool maybe_record_implicit_null_check_done = false;
@@ -4231,7 +4327,7 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
}
if (is_volatile) {
- GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
}
}
@@ -4408,6 +4504,11 @@ void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
Location::RequiresRegister(),
object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
+ // We need a temporary register for the read barrier marking slow
+ // path in CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier.
+ if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
@@ -4415,12 +4516,13 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
Location obj_loc = locations->InAt(0);
CpuRegister obj = obj_loc.AsRegister<CpuRegister>();
Location index = locations->InAt(1);
- Primitive::Type type = instruction->GetType();
+ Location out_loc = locations->Out();
+ Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movzxb(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
@@ -4432,7 +4534,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimByte: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movsxb(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset));
@@ -4444,7 +4546,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimShort: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movsxw(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
@@ -4456,7 +4558,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimChar: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movzxw(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset));
@@ -4466,13 +4568,9 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
break;
}
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
+ case Primitive::kPrimInt: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movl(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
@@ -4482,9 +4580,46 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
break;
}
+ case Primitive::kPrimNot: {
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ // /* HeapReference<Object> */ out =
+ // *(obj + data_offset + index * sizeof(HeapReference<Object>))
+ if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ Location temp = locations->GetTemp(0);
+ // Note that a potential implicit null check is handled in this
+ // CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier call.
+ codegen_->GenerateArrayLoadWithBakerReadBarrier(
+ instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true);
+ } else {
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
+ if (index.IsConstant()) {
+ uint32_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ movl(out, Address(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
+ } else {
+ __ movl(out, Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(
+ instruction, out_loc, out_loc, obj_loc, data_offset, index);
+ }
+ }
+ break;
+ }
+
case Primitive::kPrimLong: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movq(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
@@ -4496,7 +4631,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimFloat: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+ XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movss(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
@@ -4508,7 +4643,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimDouble: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+ XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movsd(out, Address(obj,
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
@@ -4522,20 +4657,12 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
if (type == Primitive::kPrimNot) {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- Location out = locations->Out();
- if (index.IsConstant()) {
- uint32_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset);
- } else {
- codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, data_offset, index);
- }
+ // Potential implicit null checks, in the case of reference
+ // arrays, are handled in the previous switch statement.
+ } else {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
}
@@ -4659,12 +4786,12 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
// __ movl(temp2, temp);
// // /* HeapReference<Class> */ temp = temp->component_type_
// __ movl(temp, Address(temp, component_offset));
- // codegen_->GenerateReadBarrier(
+ // codegen_->GenerateReadBarrierSlow(
// instruction, temp_loc, temp_loc, temp2_loc, component_offset);
//
// // /* HeapReference<Class> */ temp2 = register_value->klass_
// __ movl(temp2, Address(register_value, class_offset));
- // codegen_->GenerateReadBarrier(
+ // codegen_->GenerateReadBarrierSlow(
// instruction, temp2_loc, temp2_loc, value, class_offset, temp_loc);
//
// __ cmpl(temp, temp2);
@@ -4890,8 +5017,8 @@ void CodeGeneratorX86_64::MarkGCCard(CpuRegister temp,
__ testl(value, value);
__ j(kEqual, &is_null);
}
- __ gs()->movq(card, Address::Absolute(
- Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true));
+ __ gs()->movq(card, Address::Absolute(Thread::CardTableOffset<kX86_64WordSize>().Int32Value(),
+ /* no_rip */ true));
__ movq(temp, object);
__ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
__ movb(Address(temp, card, TIMES_1, 0), card);
@@ -4950,8 +5077,9 @@ void InstructionCodeGeneratorX86_64::GenerateSuspendCheck(HSuspendCheck* instruc
DCHECK_EQ(slow_path->GetSuccessor(), successor);
}
- __ gs()->cmpw(Address::Absolute(
- Thread::ThreadFlagsOffset<kX86_64WordSize>().Int32Value(), true), Immediate(0));
+ __ gs()->cmpw(Address::Absolute(Thread::ThreadFlagsOffset<kX86_64WordSize>().Int32Value(),
+ /* no_rip */ true),
+ Immediate(0));
if (successor == nullptr) {
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetReturnLabel());
@@ -5175,7 +5303,7 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck(
Immediate(mirror::Class::kStatusInitialized));
__ j(kLess, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
- // No need for memory fence, thanks to the X86_64 memory model.
+ // No need for memory fence, thanks to the x86-64 memory model.
}
void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
@@ -5206,32 +5334,16 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
if (cls->IsReferrersClass()) {
DCHECK(!cls->CanCallRuntime());
DCHECK(!cls->MustGenerateClinitCheck());
- uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value();
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_)
- __ leaq(out, Address(current_method, declaring_class_offset));
- // /* mirror::Class* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- __ movl(out, Address(current_method, declaring_class_offset));
- }
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
} else {
// /* GcRoot<mirror::Class>[] */ out =
// current_method.ptr_sized_fields_->dex_cache_resolved_types_
__ movq(out, Address(current_method,
ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
-
- size_t cache_offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::Class>* */ out = &out[type_index]
- __ leaq(out, Address(out, cache_offset));
- // /* mirror::Class* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::Class> */ out = out[type_index]
- __ movl(out, Address(out, cache_offset));
- }
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
@@ -5284,30 +5396,14 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
CpuRegister out = out_loc.AsRegister<CpuRegister>();
CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
- uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value();
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_)
- __ leaq(out, Address(current_method, declaring_class_offset));
- // /* mirror::Class* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- __ movl(out, Address(current_method, declaring_class_offset));
- }
-
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
// /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
__ movq(out, Address(out, mirror::Class::DexCacheStringsOffset().Uint32Value()));
-
- size_t cache_offset = CodeGenerator::GetCacheOffset(load->GetStringIndex());
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::String>* */ out = &out[string_index]
- __ leaq(out, Address(out, cache_offset));
- // /* mirror::String* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::String> */ out = out[string_index]
- __ movl(out, Address(out, cache_offset));
- }
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
@@ -5319,7 +5415,8 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
}
static Address GetExceptionTlsAddress() {
- return Address::Absolute(Thread::ExceptionOffset<kX86_64WordSize>().Int32Value(), true);
+ return Address::Absolute(Thread::ExceptionOffset<kX86_64WordSize>().Int32Value(),
+ /* no_rip */ true);
}
void LocationsBuilderX86_64::VisitLoadException(HLoadException* load) {
@@ -5355,6 +5452,14 @@ void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) {
CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
}
+static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
+ return kEmitCompilerReadBarrier &&
+ (kUseBakerReadBarrier ||
+ type_check_kind == TypeCheckKind::kAbstractClassCheck ||
+ type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
+ type_check_kind == TypeCheckKind::kArrayObjectCheck);
+}
+
void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
@@ -5380,21 +5485,22 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetOut(Location::RequiresRegister());
// When read barriers are enabled, we need a temporary register for
// some cases.
- if (kEmitCompilerReadBarrier &&
- (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
- type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
- type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
+ if (TypeCheckNeedsATemporary(type_check_kind)) {
locations->AddTemp(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
+ TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
CpuRegister obj = obj_loc.AsRegister<CpuRegister>();
Location cls = locations->InAt(1);
Location out_loc = locations->Out();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
+ Location temp_loc = TypeCheckNeedsATemporary(type_check_kind) ?
+ locations->GetTemp(0) :
+ Location::NoLocation();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
@@ -5410,10 +5516,9 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
}
// /* HeapReference<Class> */ out = obj->klass_
- __ movl(out, Address(obj, class_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, temp_loc);
- switch (instruction->GetTypeCheckKind()) {
+ switch (type_check_kind) {
case TypeCheckKind::kExactCheck: {
if (cls.IsRegister()) {
__ cmpl(out, cls.AsRegister<CpuRegister>());
@@ -5439,17 +5544,8 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
// object to avoid doing a comparison we know will fail.
NearLabel loop, success;
__ Bind(&loop);
- Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `out` into `temp` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
- __ movl(temp, out);
- }
// /* HeapReference<Class> */ out = out->super_class_
- __ movl(out, Address(out, super_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc);
__ testl(out, out);
// If `out` is null, we use it for the result, and jump to `done`.
__ j(kEqual, &done);
@@ -5478,17 +5574,8 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
__ cmpl(out, Address(CpuRegister(RSP), cls.GetStackIndex()));
}
__ j(kEqual, &success);
- Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `out` into `temp` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
- __ movl(temp, out);
- }
// /* HeapReference<Class> */ out = out->super_class_
- __ movl(out, Address(out, super_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc);
__ testl(out, out);
__ j(kNotEqual, &loop);
// If `out` is null, we use it for the result, and jump to `done`.
@@ -5512,17 +5599,8 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
}
__ j(kEqual, &exact_check);
// Otherwise, we need to check that the object's class is a non-primitive array.
- Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `out` into `temp` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
- __ movl(temp, out);
- }
// /* HeapReference<Class> */ out = out->component_type_
- __ movl(out, Address(out, component_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, component_offset);
+ GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, temp_loc);
__ testl(out, out);
// If `out` is null, we use it for the result, and jump to `done`.
__ j(kEqual, &done);
@@ -5566,6 +5644,13 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
// HInstanceOf instruction (following the runtime calling
// convention), which might be cluttered by the potential first
// read barrier emission at the beginning of this method.
+ //
+ // TODO: Introduce a new runtime entry point taking the object
+ // to test (instead of its class) as argument, and let it deal
+ // with the read barrier issues. This will let us refactor this
+ // case of the `switch` code as it was previously (with a direct
+ // call to the runtime not using a type checking slow path).
+ // This should also be beneficial for the other cases above.
DCHECK(locations->OnlyCallsOnSlowPath());
slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathX86_64(instruction,
/* is_fatal */ false);
@@ -5618,27 +5703,27 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) {
locations->AddTemp(Location::RequiresRegister());
// When read barriers are enabled, we need an additional temporary
// register for some cases.
- if (kEmitCompilerReadBarrier &&
- (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
- type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
- type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
+ if (TypeCheckNeedsATemporary(type_check_kind)) {
locations->AddTemp(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
+ TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
CpuRegister obj = obj_loc.AsRegister<CpuRegister>();
Location cls = locations->InAt(1);
Location temp_loc = locations->GetTemp(0);
CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
+ Location temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ?
+ locations->GetTemp(1) :
+ Location::NoLocation();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
- TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
bool is_type_check_slow_path_fatal =
(type_check_kind == TypeCheckKind::kExactCheck ||
type_check_kind == TypeCheckKind::kAbstractClassCheck ||
@@ -5650,7 +5735,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
is_type_check_slow_path_fatal);
codegen_->AddSlowPath(type_check_slow_path);
- NearLabel done;
+ Label done;
// Avoid null check if we know obj is not null.
if (instruction->MustDoNullCheck()) {
__ testl(obj, obj);
@@ -5658,8 +5743,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
}
// /* HeapReference<Class> */ temp = obj->klass_
- __ movl(temp, Address(obj, class_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
switch (type_check_kind) {
case TypeCheckKind::kExactCheck:
@@ -5681,18 +5765,8 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
// object to avoid doing a comparison we know will fail.
NearLabel loop, compare_classes;
__ Bind(&loop);
- Location temp2_loc =
- kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `temp` into `temp2` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- CpuRegister temp2 = temp2_loc.AsRegister<CpuRegister>();
- __ movl(temp2, temp);
- }
// /* HeapReference<Class> */ temp = temp->super_class_
- __ movl(temp, Address(temp, super_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc);
// If the class reference currently in `temp` is not null, jump
// to the `compare_classes` label to compare it with the checked
@@ -5705,8 +5779,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
// going into the slow path, as it has been overwritten in the
// meantime.
// /* HeapReference<Class> */ temp = obj->klass_
- __ movl(temp, Address(obj, class_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ jmp(type_check_slow_path->GetEntryLabel());
__ Bind(&compare_classes);
@@ -5732,18 +5805,8 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
}
__ j(kEqual, &done);
- Location temp2_loc =
- kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `temp` into `temp2` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- CpuRegister temp2 = temp2_loc.AsRegister<CpuRegister>();
- __ movl(temp2, temp);
- }
// /* HeapReference<Class> */ temp = temp->super_class_
- __ movl(temp, Address(temp, super_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc);
// If the class reference currently in `temp` is not null, jump
// back at the beginning of the loop.
@@ -5755,8 +5818,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
// going into the slow path, as it has been overwritten in the
// meantime.
// /* HeapReference<Class> */ temp = obj->klass_
- __ movl(temp, Address(obj, class_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ jmp(type_check_slow_path->GetEntryLabel());
break;
}
@@ -5773,19 +5835,8 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
__ j(kEqual, &done);
// Otherwise, we need to check that the object's class is a non-primitive array.
- Location temp2_loc =
- kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `temp` into `temp2` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- CpuRegister temp2 = temp2_loc.AsRegister<CpuRegister>();
- __ movl(temp2, temp);
- }
// /* HeapReference<Class> */ temp = temp->component_type_
- __ movl(temp, Address(temp, component_offset));
- codegen_->MaybeGenerateReadBarrier(
- instruction, temp_loc, temp_loc, temp2_loc, component_offset);
+ GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, temp2_loc);
// If the component type is not null (i.e. the object is indeed
// an array), jump to label `check_non_primitive_component_type`
@@ -5799,8 +5850,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
// going into the slow path, as it has been overwritten in the
// meantime.
// /* HeapReference<Class> */ temp = obj->klass_
- __ movl(temp, Address(obj, class_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ jmp(type_check_slow_path->GetEntryLabel());
__ Bind(&check_non_primitive_component_type);
@@ -5808,8 +5858,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
__ j(kEqual, &done);
// Same comment as above regarding `temp` and the slow path.
// /* HeapReference<Class> */ temp = obj->klass_
- __ movl(temp, Address(obj, class_offset));
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ jmp(type_check_slow_path->GetEntryLabel());
break;
}
@@ -5826,6 +5875,13 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
// instruction (following the runtime calling convention), which
// might be cluttered by the potential first read barrier
// emission at the beginning of this method.
+ //
+ // TODO: Introduce a new runtime entry point taking the object
+ // to test (instead of its class) as argument, and let it deal
+ // with the read barrier issues. This will let us refactor this
+ // case of the `switch` code as it was previously (with a direct
+ // call to the runtime not using a type checking slow path).
+ // This should also be beneficial for the other cases above.
__ jmp(type_check_slow_path->GetEntryLabel());
break;
}
@@ -5969,14 +6025,227 @@ void InstructionCodeGeneratorX86_64::HandleBitwiseOperation(HBinaryOperation* in
}
}
-void CodeGeneratorX86_64::GenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index) {
+void InstructionCodeGeneratorX86_64::GenerateReferenceLoadOneRegister(HInstruction* instruction,
+ Location out,
+ uint32_t offset,
+ Location temp) {
+ CpuRegister out_reg = out.AsRegister<CpuRegister>();
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ // Load with fast path based Baker's read barrier.
+ // /* HeapReference<Object> */ out = *(out + offset)
+ codegen_->GenerateFieldLoadWithBakerReadBarrier(
+ instruction, out, out_reg, offset, temp, /* needs_null_check */ false);
+ } else {
+ // Load with slow path based read barrier.
+ // Save the value of `out` into `temp` before overwriting it
+ // in the following move operation, as we will need it for the
+ // read barrier below.
+ __ movl(temp.AsRegister<CpuRegister>(), out_reg);
+ // /* HeapReference<Object> */ out = *(out + offset)
+ __ movl(out_reg, Address(out_reg, offset));
+ codegen_->GenerateReadBarrierSlow(instruction, out, out, temp, offset);
+ }
+ } else {
+ // Plain load with no read barrier.
+ // /* HeapReference<Object> */ out = *(out + offset)
+ __ movl(out_reg, Address(out_reg, offset));
+ __ MaybeUnpoisonHeapReference(out_reg);
+ }
+}
+
+void InstructionCodeGeneratorX86_64::GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
+ Location out,
+ Location obj,
+ uint32_t offset,
+ Location temp) {
+ CpuRegister out_reg = out.AsRegister<CpuRegister>();
+ CpuRegister obj_reg = obj.AsRegister<CpuRegister>();
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ // Load with fast path based Baker's read barrier.
+ // /* HeapReference<Object> */ out = *(obj + offset)
+ codegen_->GenerateFieldLoadWithBakerReadBarrier(
+ instruction, out, obj_reg, offset, temp, /* needs_null_check */ false);
+ } else {
+ // Load with slow path based read barrier.
+ // /* HeapReference<Object> */ out = *(obj + offset)
+ __ movl(out_reg, Address(obj_reg, offset));
+ codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
+ }
+ } else {
+ // Plain load with no read barrier.
+ // /* HeapReference<Object> */ out = *(obj + offset)
+ __ movl(out_reg, Address(obj_reg, offset));
+ __ MaybeUnpoisonHeapReference(out_reg);
+ }
+}
+
+void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(HInstruction* instruction,
+ Location root,
+ CpuRegister obj,
+ uint32_t offset) {
+ CpuRegister root_reg = root.AsRegister<CpuRegister>();
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ // Fast path implementation of art::ReadBarrier::BarrierForRoot when
+ // Baker's read barrier are used:
+ //
+ // root = obj.field;
+ // if (Thread::Current()->GetIsGcMarking()) {
+ // root = ReadBarrier::Mark(root)
+ // }
+
+ // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+ __ movl(root_reg, Address(obj, offset));
+ static_assert(
+ sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
+ "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
+ "have different sizes.");
+ static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::CompressedReference<mirror::Object> and int32_t "
+ "have different sizes.");
+
+ // Slow path used to mark the GC root `root`.
+ SlowPathCode* slow_path =
+ new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(instruction, root, root);
+ codegen_->AddSlowPath(slow_path);
+
+ __ gs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86_64WordSize>().Int32Value(),
+ /* no_rip */ true),
+ Immediate(0));
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ } else {
+ // GC root loaded through a slow path for read barriers other
+ // than Baker's.
+ // /* GcRoot<mirror::Object>* */ root = obj + offset
+ __ leaq(root_reg, Address(obj, offset));
+ // /* mirror::Object* */ root = root->Read()
+ codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
+ }
+ } else {
+ // Plain GC root load with no read barrier.
+ // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+ __ movl(root_reg, Address(obj, offset));
+ }
+}
+
+void CodeGeneratorX86_64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ CpuRegister obj,
+ uint32_t offset,
+ Location temp,
+ bool needs_null_check) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // /* HeapReference<Object> */ ref = *(obj + offset)
+ Address src(obj, offset);
+ GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, temp, needs_null_check);
+}
+
+void CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ CpuRegister obj,
+ uint32_t data_offset,
+ Location index,
+ Location temp,
+ bool needs_null_check) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // /* HeapReference<Object> */ ref =
+ // *(obj + data_offset + index * sizeof(HeapReference<Object>))
+ Address src = index.IsConstant() ?
+ Address(obj, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset) :
+ Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset);
+ GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, temp, needs_null_check);
+}
+
+void CodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ CpuRegister obj,
+ const Address& src,
+ Location temp,
+ bool needs_null_check) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // In slow path based read barriers, the read barrier call is
+ // inserted after the original load. However, in fast path based
+ // Baker's read barriers, we need to perform the load of
+ // mirror::Object::monitor_ *before* the original reference load.
+ // This load-load ordering is required by the read barrier.
+ // The fast path/slow path (for Baker's algorithm) should look like:
+ //
+ // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
+ // lfence; // Load fence or artificial data dependency to prevent load-load reordering
+ // HeapReference<Object> ref = *src; // Original reference load.
+ // bool is_gray = (rb_state == ReadBarrier::gray_ptr_);
+ // if (is_gray) {
+ // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path.
+ // }
+ //
+ // Note: the original implementation in ReadBarrier::Barrier is
+ // slightly more complex as:
+ // - it implements the load-load fence using a data dependency on
+ // the high-bits of rb_state, which are expected to be all zeroes;
+ // - it performs additional checks that we do not do here for
+ // performance reasons.
+
+ CpuRegister ref_reg = ref.AsRegister<CpuRegister>();
+ CpuRegister temp_reg = temp.AsRegister<CpuRegister>();
+ uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
+
+ // /* int32_t */ monitor = obj->monitor_
+ __ movl(temp_reg, Address(obj, monitor_offset));
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ // /* LockWord */ lock_word = LockWord(monitor)
+ static_assert(sizeof(LockWord) == sizeof(int32_t),
+ "art::LockWord and int32_t have different sizes.");
+ // /* uint32_t */ rb_state = lock_word.ReadBarrierState()
+ __ shrl(temp_reg, Immediate(LockWord::kReadBarrierStateShift));
+ __ andl(temp_reg, Immediate(LockWord::kReadBarrierStateMask));
+ static_assert(
+ LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_,
+ "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_.");
+
+ // Load fence to prevent load-load reordering.
+ // Note that this is a no-op, thanks to the x86-64 memory model.
+ GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+
+ // The actual reference load.
+ // /* HeapReference<Object> */ ref = *src
+ __ movl(ref_reg, src);
+
+ // Object* ref = ref_addr->AsMirrorPtr()
+ __ MaybeUnpoisonHeapReference(ref_reg);
+
+ // Slow path used to mark the object `ref` when it is gray.
+ SlowPathCode* slow_path =
+ new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(instruction, ref, ref);
+ AddSlowPath(slow_path);
+
+ // if (rb_state == ReadBarrier::gray_ptr_)
+ // ref = ReadBarrier::Mark(ref);
+ __ cmpl(temp_reg, Immediate(ReadBarrier::gray_ptr_));
+ __ j(kEqual, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+}
+
+void CodeGeneratorX86_64::GenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index) {
DCHECK(kEmitCompilerReadBarrier);
+ // Insert a slow path based read barrier *after* the reference load.
+ //
// If heap poisoning is enabled, the unpoisoning of the loaded
// reference will be carried out by the runtime within the slow
// path.
@@ -5990,57 +6259,41 @@ void CodeGeneratorX86_64::GenerateReadBarrier(HInstruction* instruction,
ReadBarrierForHeapReferenceSlowPathX86_64(instruction, out, ref, obj, offset, index);
AddSlowPath(slow_path);
- // TODO: When read barrier has a fast path, add it here.
- /* Currently the read barrier call is inserted after the original load.
- * However, if we have a fast path, we need to perform the load of obj.LockWord *before* the
- * original load. This load-load ordering is required by the read barrier.
- * The fast path/slow path (for Baker's algorithm) should look like:
- *
- * bool isGray = obj.LockWord & kReadBarrierMask;
- * lfence; // load fence or artificial data dependence to prevent load-load reordering
- * ref = obj.field; // this is the original load
- * if (isGray) {
- * ref = Mark(ref); // ideally the slow path just does Mark(ref)
- * }
- */
-
__ jmp(slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
-void CodeGeneratorX86_64::MaybeGenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index) {
+void CodeGeneratorX86_64::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index) {
if (kEmitCompilerReadBarrier) {
+ // Baker's read barriers shall be handled by the fast path
+ // (CodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier).
+ DCHECK(!kUseBakerReadBarrier);
// If heap poisoning is enabled, unpoisoning will be taken care of
// by the runtime within the slow path.
- GenerateReadBarrier(instruction, out, ref, obj, offset, index);
+ GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
} else if (kPoisonHeapReferences) {
__ UnpoisonHeapReference(out.AsRegister<CpuRegister>());
}
}
-void CodeGeneratorX86_64::GenerateReadBarrierForRoot(HInstruction* instruction,
- Location out,
- Location root) {
+void CodeGeneratorX86_64::GenerateReadBarrierForRootSlow(HInstruction* instruction,
+ Location out,
+ Location root) {
DCHECK(kEmitCompilerReadBarrier);
+ // Insert a slow path based read barrier *after* the GC root load.
+ //
// Note that GC roots are not affected by heap poisoning, so we do
// not need to do anything special for this here.
SlowPathCode* slow_path =
new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathX86_64(instruction, out, root);
AddSlowPath(slow_path);
- // TODO: Implement a fast path for ReadBarrierForRoot, performing
- // the following operation (for Baker's algorithm):
- //
- // if (thread.tls32_.is_gc_marking) {
- // root = Mark(root);
- // }
-
__ jmp(slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
@@ -6078,58 +6331,11 @@ void LocationsBuilderX86_64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
void InstructionCodeGeneratorX86_64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
int32_t lower_bound = switch_instr->GetStartValue();
- uint32_t num_entries = switch_instr->GetNumEntries();
+ int32_t num_entries = switch_instr->GetNumEntries();
LocationSummary* locations = switch_instr->GetLocations();
CpuRegister value_reg_in = locations->InAt(0).AsRegister<CpuRegister>();
CpuRegister temp_reg = locations->GetTemp(0).AsRegister<CpuRegister>();
CpuRegister base_reg = locations->GetTemp(1).AsRegister<CpuRegister>();
- HBasicBlock* default_block = switch_instr->GetDefaultBlock();
-
- // Should we generate smaller inline compare/jumps?
- if (num_entries <= kPackedSwitchJumpTableThreshold) {
- // Figure out the correct compare values and jump conditions.
- // Handle the first compare/branch as a special case because it might
- // jump to the default case.
- DCHECK_GT(num_entries, 2u);
- Condition first_condition;
- uint32_t index;
- const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- if (lower_bound != 0) {
- first_condition = kLess;
- __ cmpl(value_reg_in, Immediate(lower_bound));
- __ j(first_condition, codegen_->GetLabelOf(default_block));
- __ j(kEqual, codegen_->GetLabelOf(successors[0]));
-
- index = 1;
- } else {
- // Handle all the compare/jumps below.
- first_condition = kBelow;
- index = 0;
- }
-
- // Handle the rest of the compare/jumps.
- for (; index + 1 < num_entries; index += 2) {
- int32_t compare_to_value = lower_bound + index + 1;
- __ cmpl(value_reg_in, Immediate(compare_to_value));
- // Jump to successors[index] if value < case_value[index].
- __ j(first_condition, codegen_->GetLabelOf(successors[index]));
- // Jump to successors[index + 1] if value == case_value[index + 1].
- __ j(kEqual, codegen_->GetLabelOf(successors[index + 1]));
- }
-
- if (index != num_entries) {
- // There are an odd number of entries. Handle the last one.
- DCHECK_EQ(index + 1, num_entries);
- __ cmpl(value_reg_in, Immediate(lower_bound + index));
- __ j(kEqual, codegen_->GetLabelOf(successors[index]));
- }
-
- // And the default for any other value.
- if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
- __ jmp(codegen_->GetLabelOf(default_block));
- }
- return;
- }
// Remove the bias, if needed.
Register value_reg_out = value_reg_in.AsRegister();
@@ -6140,6 +6346,7 @@ void InstructionCodeGeneratorX86_64::VisitPackedSwitch(HPackedSwitch* switch_ins
CpuRegister value_reg(value_reg_out);
// Is the value in range?
+ HBasicBlock* default_block = switch_instr->GetDefaultBlock();
__ cmpl(value_reg, Immediate(num_entries - 1));
__ j(kAbove, codegen_->GetLabelOf(default_block));
@@ -6289,7 +6496,7 @@ Address CodeGeneratorX86_64::LiteralInt64Address(int64_t v) {
// TODO: trg as memory.
void CodeGeneratorX86_64::MoveFromReturnRegister(Location trg, Primitive::Type type) {
if (!trg.IsValid()) {
- DCHECK(type == Primitive::kPrimVoid);
+ DCHECK_EQ(type, Primitive::kPrimVoid);
return;
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 7351fed0fa..dda9ea22d9 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -213,11 +213,44 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor {
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleShift(HBinaryOperation* operation);
- void GenerateMemoryBarrier(MemBarrierKind kind);
+
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+
+ // Generate a heap reference load using one register `out`:
+ //
+ // out <- *(out + offset)
+ //
+ // while honoring heap poisoning and/or read barriers (if any).
+ // Register `temp` is used when generating a read barrier.
+ void GenerateReferenceLoadOneRegister(HInstruction* instruction,
+ Location out,
+ uint32_t offset,
+ Location temp);
+ // Generate a heap reference load using two different registers
+ // `out` and `obj`:
+ //
+ // out <- *(obj + offset)
+ //
+ // while honoring heap poisoning and/or read barriers (if any).
+ // Register `temp` is used when generating a Baker's read barrier.
+ void GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
+ Location out,
+ Location obj,
+ uint32_t offset,
+ Location temp);
+ // Generate a GC root reference load:
+ //
+ // root <- *(obj + offset)
+ //
+ // while honoring read barriers (if any).
+ void GenerateGcRootFieldLoad(HInstruction* instruction,
+ Location root,
+ CpuRegister obj,
+ uint32_t offset);
+
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
void PushOntoFPStack(Location source, uint32_t temp_offset,
@@ -324,6 +357,8 @@ class CodeGeneratorX86_64 : public CodeGenerator {
CpuRegister value,
bool value_can_be_null);
+ void GenerateMemoryBarrier(MemBarrierKind kind);
+
// Helper method to move a value between two locations.
void Move(Location destination, Location source);
@@ -356,7 +391,26 @@ class CodeGeneratorX86_64 : public CodeGenerator {
return isa_features_;
}
- // Generate a read barrier for a heap reference within `instruction`.
+ // Fast path implementation of ReadBarrier::Barrier for a heap
+ // reference field load when Baker's read barriers are used.
+ void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location out,
+ CpuRegister obj,
+ uint32_t offset,
+ Location temp,
+ bool needs_null_check);
+ // Fast path implementation of ReadBarrier::Barrier for a heap
+ // reference array load when Baker's read barriers are used.
+ void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location out,
+ CpuRegister obj,
+ uint32_t data_offset,
+ Location index,
+ Location temp,
+ bool needs_null_check);
+
+ // Generate a read barrier for a heap reference within `instruction`
+ // using a slow path.
//
// A read barrier for an object reference read from the heap is
// implemented as a call to the artReadBarrierSlow runtime entry
@@ -373,23 +427,25 @@ class CodeGeneratorX86_64 : public CodeGenerator {
// When `index` provided (i.e., when it is different from
// Location::NoLocation()), the offset value passed to
// artReadBarrierSlow is adjusted to take `index` into account.
- void GenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index = Location::NoLocation());
-
- // If read barriers are enabled, generate a read barrier for a heap reference.
- // If heap poisoning is enabled, also unpoison the reference in `out`.
- void MaybeGenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index = Location::NoLocation());
-
- // Generate a read barrier for a GC root within `instruction`.
+ void GenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index = Location::NoLocation());
+
+ // If read barriers are enabled, generate a read barrier for a heap
+ // reference using a slow path. If heap poisoning is enabled, also
+ // unpoison the reference in `out`.
+ void MaybeGenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index = Location::NoLocation());
+
+ // Generate a read barrier for a GC root within `instruction` using
+ // a slow path.
//
// A read barrier for an object reference GC root is implemented as
// a call to the artReadBarrierForRootSlow runtime entry point,
@@ -399,7 +455,7 @@ class CodeGeneratorX86_64 : public CodeGenerator {
//
// The `out` location contains the value returned by
// artReadBarrierForRootSlow.
- void GenerateReadBarrierForRoot(HInstruction* instruction, Location out, Location root);
+ void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root);
int ConstantAreaStart() const {
return constant_area_start_;
@@ -424,6 +480,15 @@ class CodeGeneratorX86_64 : public CodeGenerator {
HInstruction* instruction);
private:
+ // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ CpuRegister obj,
+ const Address& src,
+ Location temp,
+ bool needs_null_check);
+
struct PcRelativeDexCacheAccessInfo {
PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off)
: target_dex_file(dex_file), element_offset(element_off), label() { }
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 57de41f557..d970704368 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -35,6 +35,7 @@
#include "code_generator_mips64.h"
#include "code_generator_x86.h"
#include "code_generator_x86_64.h"
+#include "code_simulator_container.h"
#include "common_compiler_test.h"
#include "dex_file.h"
#include "dex_instruction.h"
@@ -124,26 +125,85 @@ class InternalCodeAllocator : public CodeAllocator {
DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator);
};
+static bool CanExecuteOnHardware(InstructionSet target_isa) {
+ return (target_isa == kRuntimeISA)
+ // Handle the special case of ARM, with two instructions sets (ARM32 and Thumb-2).
+ || (kRuntimeISA == kArm && target_isa == kThumb2);
+}
+
+static bool CanExecute(InstructionSet target_isa) {
+ CodeSimulatorContainer simulator(target_isa);
+ return CanExecuteOnHardware(target_isa) || simulator.CanSimulate();
+}
+
+template <typename Expected>
+static Expected SimulatorExecute(CodeSimulator* simulator, Expected (*f)());
+
+template <>
+bool SimulatorExecute<bool>(CodeSimulator* simulator, bool (*f)()) {
+ simulator->RunFrom(reinterpret_cast<intptr_t>(f));
+ return simulator->GetCReturnBool();
+}
+
+template <>
+int32_t SimulatorExecute<int32_t>(CodeSimulator* simulator, int32_t (*f)()) {
+ simulator->RunFrom(reinterpret_cast<intptr_t>(f));
+ return simulator->GetCReturnInt32();
+}
+
+template <>
+int64_t SimulatorExecute<int64_t>(CodeSimulator* simulator, int64_t (*f)()) {
+ simulator->RunFrom(reinterpret_cast<intptr_t>(f));
+ return simulator->GetCReturnInt64();
+}
+
+template <typename Expected>
+static void VerifyGeneratedCode(InstructionSet target_isa,
+ Expected (*f)(),
+ bool has_result,
+ Expected expected) {
+ ASSERT_TRUE(CanExecute(target_isa)) << "Target isa is not executable.";
+
+ // Verify on simulator.
+ CodeSimulatorContainer simulator(target_isa);
+ if (simulator.CanSimulate()) {
+ Expected result = SimulatorExecute<Expected>(simulator.Get(), f);
+ if (has_result) {
+ ASSERT_EQ(expected, result);
+ }
+ }
+
+ // Verify on hardware.
+ if (CanExecuteOnHardware(target_isa)) {
+ Expected result = f();
+ if (has_result) {
+ ASSERT_EQ(expected, result);
+ }
+ }
+}
+
template <typename Expected>
static void Run(const InternalCodeAllocator& allocator,
const CodeGenerator& codegen,
bool has_result,
Expected expected) {
+ InstructionSet target_isa = codegen.GetInstructionSet();
+
typedef Expected (*fptr)();
CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize());
fptr f = reinterpret_cast<fptr>(allocator.GetMemory());
- if (codegen.GetInstructionSet() == kThumb2) {
+ if (target_isa == kThumb2) {
// For thumb we need the bottom bit set.
f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1);
}
- Expected result = f();
- if (has_result) {
- ASSERT_EQ(expected, result);
- }
+ VerifyGeneratedCode(target_isa, f, has_result, expected);
}
template <typename Expected>
-static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
+static void RunCodeBaseline(InstructionSet target_isa,
+ HGraph* graph,
+ bool has_result,
+ Expected expected) {
InternalCodeAllocator allocator;
CompilerOptions compiler_options;
@@ -153,7 +213,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
// We avoid doing a stack overflow check that requires the runtime being setup,
// by making sure the compiler knows the methods we are running are leaf methods.
codegenX86.CompileBaseline(&allocator, true);
- if (kRuntimeISA == kX86) {
+ if (target_isa == kX86) {
Run(allocator, codegenX86, has_result, expected);
}
@@ -161,7 +221,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
ArmInstructionSetFeatures::FromCppDefines());
TestCodeGeneratorARM codegenARM(graph, *features_arm.get(), compiler_options);
codegenARM.CompileBaseline(&allocator, true);
- if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
+ if (target_isa == kArm || target_isa == kThumb2) {
Run(allocator, codegenARM, has_result, expected);
}
@@ -169,7 +229,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
X86_64InstructionSetFeatures::FromCppDefines());
x86_64::CodeGeneratorX86_64 codegenX86_64(graph, *features_x86_64.get(), compiler_options);
codegenX86_64.CompileBaseline(&allocator, true);
- if (kRuntimeISA == kX86_64) {
+ if (target_isa == kX86_64) {
Run(allocator, codegenX86_64, has_result, expected);
}
@@ -177,7 +237,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
Arm64InstructionSetFeatures::FromCppDefines());
arm64::CodeGeneratorARM64 codegenARM64(graph, *features_arm64.get(), compiler_options);
codegenARM64.CompileBaseline(&allocator, true);
- if (kRuntimeISA == kArm64) {
+ if (target_isa == kArm64) {
Run(allocator, codegenARM64, has_result, expected);
}
@@ -193,7 +253,7 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
Mips64InstructionSetFeatures::FromCppDefines());
mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options);
codegenMIPS64.CompileBaseline(&allocator, true);
- if (kRuntimeISA == kMips64) {
+ if (target_isa == kMips64) {
Run(allocator, codegenMIPS64, has_result, expected);
}
}
@@ -221,37 +281,38 @@ static void RunCodeOptimized(CodeGenerator* codegen,
}
template <typename Expected>
-static void RunCodeOptimized(HGraph* graph,
+static void RunCodeOptimized(InstructionSet target_isa,
+ HGraph* graph,
std::function<void(HGraph*)> hook_before_codegen,
bool has_result,
Expected expected) {
CompilerOptions compiler_options;
- if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
- TestCodeGeneratorARM codegenARM(graph,
- *ArmInstructionSetFeatures::FromCppDefines(),
- compiler_options);
+ if (target_isa == kArm || target_isa == kThumb2) {
+ std::unique_ptr<const ArmInstructionSetFeatures> features_arm(
+ ArmInstructionSetFeatures::FromCppDefines());
+ TestCodeGeneratorARM codegenARM(graph, *features_arm.get(), compiler_options);
RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected);
- } else if (kRuntimeISA == kArm64) {
- arm64::CodeGeneratorARM64 codegenARM64(graph,
- *Arm64InstructionSetFeatures::FromCppDefines(),
- compiler_options);
+ } else if (target_isa == kArm64) {
+ std::unique_ptr<const Arm64InstructionSetFeatures> features_arm64(
+ Arm64InstructionSetFeatures::FromCppDefines());
+ arm64::CodeGeneratorARM64 codegenARM64(graph, *features_arm64.get(), compiler_options);
RunCodeOptimized(&codegenARM64, graph, hook_before_codegen, has_result, expected);
- } else if (kRuntimeISA == kX86) {
+ } else if (target_isa == kX86) {
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), compiler_options);
RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected);
- } else if (kRuntimeISA == kX86_64) {
+ } else if (target_isa == kX86_64) {
std::unique_ptr<const X86_64InstructionSetFeatures> features_x86_64(
X86_64InstructionSetFeatures::FromCppDefines());
x86_64::CodeGeneratorX86_64 codegenX86_64(graph, *features_x86_64.get(), compiler_options);
RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected);
- } else if (kRuntimeISA == kMips) {
+ } else if (target_isa == kMips) {
std::unique_ptr<const MipsInstructionSetFeatures> features_mips(
MipsInstructionSetFeatures::FromCppDefines());
mips::CodeGeneratorMIPS codegenMIPS(graph, *features_mips.get(), compiler_options);
RunCodeOptimized(&codegenMIPS, graph, hook_before_codegen, has_result, expected);
- } else if (kRuntimeISA == kMips64) {
+ } else if (target_isa == kMips64) {
std::unique_ptr<const Mips64InstructionSetFeatures> features_mips64(
Mips64InstructionSetFeatures::FromCppDefines());
mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options);
@@ -259,7 +320,10 @@ static void RunCodeOptimized(HGraph* graph,
}
}
-static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) {
+static void TestCode(InstructionSet target_isa,
+ const uint16_t* data,
+ bool has_result = false,
+ int32_t expected = 0) {
ArenaPool pool;
ArenaAllocator arena(&pool);
HGraph* graph = CreateGraph(&arena);
@@ -269,10 +333,13 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe
ASSERT_TRUE(graph_built);
// Remove suspend checks, they cannot be executed in this context.
RemoveSuspendChecks(graph);
- RunCodeBaseline(graph, has_result, expected);
+ RunCodeBaseline(target_isa, graph, has_result, expected);
}
-static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) {
+static void TestCodeLong(InstructionSet target_isa,
+ const uint16_t* data,
+ bool has_result,
+ int64_t expected) {
ArenaPool pool;
ArenaAllocator arena(&pool);
HGraph* graph = CreateGraph(&arena);
@@ -282,108 +349,110 @@ static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected
ASSERT_TRUE(graph_built);
// Remove suspend checks, they cannot be executed in this context.
RemoveSuspendChecks(graph);
- RunCodeBaseline(graph, has_result, expected);
+ RunCodeBaseline(target_isa, graph, has_result, expected);
}
-TEST(CodegenTest, ReturnVoid) {
+class CodegenTest: public ::testing::TestWithParam<InstructionSet> {};
+
+TEST_P(CodegenTest, ReturnVoid) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID);
- TestCode(data);
+ TestCode(GetParam(), data);
}
-TEST(CodegenTest, CFG1) {
+TEST_P(CodegenTest, CFG1) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x100,
Instruction::RETURN_VOID);
- TestCode(data);
+ TestCode(GetParam(), data);
}
-TEST(CodegenTest, CFG2) {
+TEST_P(CodegenTest, CFG2) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x100,
Instruction::GOTO | 0x100,
Instruction::RETURN_VOID);
- TestCode(data);
+ TestCode(GetParam(), data);
}
-TEST(CodegenTest, CFG3) {
+TEST_P(CodegenTest, CFG3) {
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x200,
Instruction::RETURN_VOID,
Instruction::GOTO | 0xFF00);
- TestCode(data1);
+ TestCode(GetParam(), data1);
const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO_16, 3,
Instruction::RETURN_VOID,
Instruction::GOTO_16, 0xFFFF);
- TestCode(data2);
+ TestCode(GetParam(), data2);
const uint16_t data3[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO_32, 4, 0,
Instruction::RETURN_VOID,
Instruction::GOTO_32, 0xFFFF, 0xFFFF);
- TestCode(data3);
+ TestCode(GetParam(), data3);
}
-TEST(CodegenTest, CFG4) {
+TEST_P(CodegenTest, CFG4) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID,
Instruction::GOTO | 0x100,
Instruction::GOTO | 0xFE00);
- TestCode(data);
+ TestCode(GetParam(), data);
}
-TEST(CodegenTest, CFG5) {
+TEST_P(CodegenTest, CFG5) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3,
Instruction::GOTO | 0x100,
Instruction::RETURN_VOID);
- TestCode(data);
+ TestCode(GetParam(), data);
}
-TEST(CodegenTest, IntConstant) {
+TEST_P(CodegenTest, IntConstant) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::RETURN_VOID);
- TestCode(data);
+ TestCode(GetParam(), data);
}
-TEST(CodegenTest, Return1) {
+TEST_P(CodegenTest, Return1) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::RETURN | 0);
- TestCode(data, true, 0);
+ TestCode(GetParam(), data, true, 0);
}
-TEST(CodegenTest, Return2) {
+TEST_P(CodegenTest, Return2) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::CONST_4 | 0 | 1 << 8,
Instruction::RETURN | 1 << 8);
- TestCode(data, true, 0);
+ TestCode(GetParam(), data, true, 0);
}
-TEST(CodegenTest, Return3) {
+TEST_P(CodegenTest, Return3) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::CONST_4 | 1 << 8 | 1 << 12,
Instruction::RETURN | 1 << 8);
- TestCode(data, true, 1);
+ TestCode(GetParam(), data, true, 1);
}
-TEST(CodegenTest, ReturnIf1) {
+TEST_P(CodegenTest, ReturnIf1) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::CONST_4 | 1 << 8 | 1 << 12,
@@ -391,10 +460,10 @@ TEST(CodegenTest, ReturnIf1) {
Instruction::RETURN | 0 << 8,
Instruction::RETURN | 1 << 8);
- TestCode(data, true, 1);
+ TestCode(GetParam(), data, true, 1);
}
-TEST(CodegenTest, ReturnIf2) {
+TEST_P(CodegenTest, ReturnIf2) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::CONST_4 | 1 << 8 | 1 << 12,
@@ -402,12 +471,12 @@ TEST(CodegenTest, ReturnIf2) {
Instruction::RETURN | 0 << 8,
Instruction::RETURN | 1 << 8);
- TestCode(data, true, 0);
+ TestCode(GetParam(), data, true, 0);
}
// Exercise bit-wise (one's complement) not-int instruction.
#define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \
-TEST(CodegenTest, TEST_NAME) { \
+TEST_P(CodegenTest, TEST_NAME) { \
const int32_t input = INPUT; \
const uint16_t input_lo = Low16Bits(input); \
const uint16_t input_hi = High16Bits(input); \
@@ -416,7 +485,7 @@ TEST(CodegenTest, TEST_NAME) { \
Instruction::NOT_INT | 1 << 8 | 0 << 12 , \
Instruction::RETURN | 1 << 8); \
\
- TestCode(data, true, EXPECTED_OUTPUT); \
+ TestCode(GetParam(), data, true, EXPECTED_OUTPUT); \
}
NOT_INT_TEST(ReturnNotIntMinus2, -2, 1)
@@ -432,7 +501,7 @@ NOT_INT_TEST(ReturnNotIntINT32_MAX, 2147483647, -2147483648) // -(2^31)
// Exercise bit-wise (one's complement) not-long instruction.
#define NOT_LONG_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \
-TEST(CodegenTest, TEST_NAME) { \
+TEST_P(CodegenTest, TEST_NAME) { \
const int64_t input = INPUT; \
const uint16_t word0 = Low16Bits(Low32Bits(input)); /* LSW. */ \
const uint16_t word1 = High16Bits(Low32Bits(input)); \
@@ -443,7 +512,7 @@ TEST(CodegenTest, TEST_NAME) { \
Instruction::NOT_LONG | 2 << 8 | 0 << 12, \
Instruction::RETURN_WIDE | 2 << 8); \
\
- TestCodeLong(data, true, EXPECTED_OUTPUT); \
+ TestCodeLong(GetParam(), data, true, EXPECTED_OUTPUT); \
}
NOT_LONG_TEST(ReturnNotLongMinus2, INT64_C(-2), INT64_C(1))
@@ -482,7 +551,7 @@ NOT_LONG_TEST(ReturnNotLongINT64_MAX,
#undef NOT_LONG_TEST
-TEST(CodegenTest, IntToLongOfLongToInt) {
+TEST_P(CodegenTest, IntToLongOfLongToInt) {
const int64_t input = INT64_C(4294967296); // 2^32
const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW.
const uint16_t word1 = High16Bits(Low32Bits(input));
@@ -496,48 +565,48 @@ TEST(CodegenTest, IntToLongOfLongToInt) {
Instruction::INT_TO_LONG | 2 << 8 | 4 << 12,
Instruction::RETURN_WIDE | 2 << 8);
- TestCodeLong(data, true, 1);
+ TestCodeLong(GetParam(), data, true, 1);
}
-TEST(CodegenTest, ReturnAdd1) {
+TEST_P(CodegenTest, ReturnAdd1) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 3 << 12 | 0,
Instruction::CONST_4 | 4 << 12 | 1 << 8,
Instruction::ADD_INT, 1 << 8 | 0,
Instruction::RETURN);
- TestCode(data, true, 7);
+ TestCode(GetParam(), data, true, 7);
}
-TEST(CodegenTest, ReturnAdd2) {
+TEST_P(CodegenTest, ReturnAdd2) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 3 << 12 | 0,
Instruction::CONST_4 | 4 << 12 | 1 << 8,
Instruction::ADD_INT_2ADDR | 1 << 12,
Instruction::RETURN);
- TestCode(data, true, 7);
+ TestCode(GetParam(), data, true, 7);
}
-TEST(CodegenTest, ReturnAdd3) {
+TEST_P(CodegenTest, ReturnAdd3) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0 << 8,
Instruction::ADD_INT_LIT8, 3 << 8 | 0,
Instruction::RETURN);
- TestCode(data, true, 7);
+ TestCode(GetParam(), data, true, 7);
}
-TEST(CodegenTest, ReturnAdd4) {
+TEST_P(CodegenTest, ReturnAdd4) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0 << 8,
Instruction::ADD_INT_LIT16, 3,
Instruction::RETURN);
- TestCode(data, true, 7);
+ TestCode(GetParam(), data, true, 7);
}
-TEST(CodegenTest, NonMaterializedCondition) {
+TEST_P(CodegenTest, NonMaterializedCondition) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
@@ -583,30 +652,30 @@ TEST(CodegenTest, NonMaterializedCondition) {
block->InsertInstructionBefore(move, block->GetLastInstruction());
};
- RunCodeOptimized(graph, hook_before_codegen, true, 0);
+ RunCodeOptimized(GetParam(), graph, hook_before_codegen, true, 0);
}
-TEST(CodegenTest, ReturnMulInt) {
+TEST_P(CodegenTest, ReturnMulInt) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 3 << 12 | 0,
Instruction::CONST_4 | 4 << 12 | 1 << 8,
Instruction::MUL_INT, 1 << 8 | 0,
Instruction::RETURN);
- TestCode(data, true, 12);
+ TestCode(GetParam(), data, true, 12);
}
-TEST(CodegenTest, ReturnMulInt2addr) {
+TEST_P(CodegenTest, ReturnMulInt2addr) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 3 << 12 | 0,
Instruction::CONST_4 | 4 << 12 | 1 << 8,
Instruction::MUL_INT_2ADDR | 1 << 12,
Instruction::RETURN);
- TestCode(data, true, 12);
+ TestCode(GetParam(), data, true, 12);
}
-TEST(CodegenTest, ReturnMulLong) {
+TEST_P(CodegenTest, ReturnMulLong) {
const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 3 << 12 | 0,
Instruction::CONST_4 | 0 << 12 | 1 << 8,
@@ -615,10 +684,10 @@ TEST(CodegenTest, ReturnMulLong) {
Instruction::MUL_LONG, 2 << 8 | 0,
Instruction::RETURN_WIDE);
- TestCodeLong(data, true, 12);
+ TestCodeLong(GetParam(), data, true, 12);
}
-TEST(CodegenTest, ReturnMulLong2addr) {
+TEST_P(CodegenTest, ReturnMulLong2addr) {
const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 3 << 12 | 0 << 8,
Instruction::CONST_4 | 0 << 12 | 1 << 8,
@@ -627,28 +696,28 @@ TEST(CodegenTest, ReturnMulLong2addr) {
Instruction::MUL_LONG_2ADDR | 2 << 12,
Instruction::RETURN_WIDE);
- TestCodeLong(data, true, 12);
+ TestCodeLong(GetParam(), data, true, 12);
}
-TEST(CodegenTest, ReturnMulIntLit8) {
+TEST_P(CodegenTest, ReturnMulIntLit8) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0 << 8,
Instruction::MUL_INT_LIT8, 3 << 8 | 0,
Instruction::RETURN);
- TestCode(data, true, 12);
+ TestCode(GetParam(), data, true, 12);
}
-TEST(CodegenTest, ReturnMulIntLit16) {
+TEST_P(CodegenTest, ReturnMulIntLit16) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0 << 8,
Instruction::MUL_INT_LIT16, 3,
Instruction::RETURN);
- TestCode(data, true, 12);
+ TestCode(GetParam(), data, true, 12);
}
-TEST(CodegenTest, MaterializedCondition1) {
+TEST_P(CodegenTest, MaterializedCondition1) {
// Check that condition are materialized correctly. A materialized condition
// should yield `1` if it evaluated to true, and `0` otherwise.
// We force the materialization of comparisons for different combinations of
@@ -689,11 +758,11 @@ TEST(CodegenTest, MaterializedCondition1) {
block->InsertInstructionBefore(move, block->GetLastInstruction());
};
- RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]);
+ RunCodeOptimized(GetParam(), graph, hook_before_codegen, true, lhs[i] < rhs[i]);
}
}
-TEST(CodegenTest, MaterializedCondition2) {
+TEST_P(CodegenTest, MaterializedCondition2) {
// Check that HIf correctly interprets a materialized condition.
// We force the materialization of comparisons for different combinations of
// inputs. An HIf takes the materialized combination as input and returns a
@@ -755,31 +824,35 @@ TEST(CodegenTest, MaterializedCondition2) {
block->InsertInstructionBefore(move, block->GetLastInstruction());
};
- RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]);
+ RunCodeOptimized(GetParam(), graph, hook_before_codegen, true, lhs[i] < rhs[i]);
}
}
-TEST(CodegenTest, ReturnDivIntLit8) {
+TEST_P(CodegenTest, ReturnDivIntLit8) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0 << 8,
Instruction::DIV_INT_LIT8, 3 << 8 | 0,
Instruction::RETURN);
- TestCode(data, true, 1);
+ TestCode(GetParam(), data, true, 1);
}
-TEST(CodegenTest, ReturnDivInt2Addr) {
+TEST_P(CodegenTest, ReturnDivInt2Addr) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0,
Instruction::CONST_4 | 2 << 12 | 1 << 8,
Instruction::DIV_INT_2ADDR | 1 << 12,
Instruction::RETURN);
- TestCode(data, true, 2);
+ TestCode(GetParam(), data, true, 2);
}
// Helper method.
-static void TestComparison(IfCondition condition, int64_t i, int64_t j, Primitive::Type type) {
+static void TestComparison(IfCondition condition,
+ int64_t i,
+ int64_t j,
+ Primitive::Type type,
+ const InstructionSet target_isa) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = CreateGraph(&allocator);
@@ -862,46 +935,78 @@ static void TestComparison(IfCondition condition, int64_t i, int64_t j, Primitiv
auto hook_before_codegen = [](HGraph*) {
};
- RunCodeOptimized(graph, hook_before_codegen, true, expected_result);
+ RunCodeOptimized(target_isa, graph, hook_before_codegen, true, expected_result);
}
-TEST(CodegenTest, ComparisonsInt) {
+TEST_P(CodegenTest, ComparisonsInt) {
+ const InstructionSet target_isa = GetParam();
for (int64_t i = -1; i <= 1; i++) {
for (int64_t j = -1; j <= 1; j++) {
- TestComparison(kCondEQ, i, j, Primitive::kPrimInt);
- TestComparison(kCondNE, i, j, Primitive::kPrimInt);
- TestComparison(kCondLT, i, j, Primitive::kPrimInt);
- TestComparison(kCondLE, i, j, Primitive::kPrimInt);
- TestComparison(kCondGT, i, j, Primitive::kPrimInt);
- TestComparison(kCondGE, i, j, Primitive::kPrimInt);
- TestComparison(kCondB, i, j, Primitive::kPrimInt);
- TestComparison(kCondBE, i, j, Primitive::kPrimInt);
- TestComparison(kCondA, i, j, Primitive::kPrimInt);
- TestComparison(kCondAE, i, j, Primitive::kPrimInt);
+ TestComparison(kCondEQ, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondNE, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondLT, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondLE, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondGT, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondGE, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondB, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondBE, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondA, i, j, Primitive::kPrimInt, target_isa);
+ TestComparison(kCondAE, i, j, Primitive::kPrimInt, target_isa);
}
}
}
-TEST(CodegenTest, ComparisonsLong) {
+TEST_P(CodegenTest, ComparisonsLong) {
// TODO: make MIPS work for long
if (kRuntimeISA == kMips || kRuntimeISA == kMips64) {
return;
}
+ const InstructionSet target_isa = GetParam();
+ if (target_isa == kMips || target_isa == kMips64) {
+ return;
+ }
+
for (int64_t i = -1; i <= 1; i++) {
for (int64_t j = -1; j <= 1; j++) {
- TestComparison(kCondEQ, i, j, Primitive::kPrimLong);
- TestComparison(kCondNE, i, j, Primitive::kPrimLong);
- TestComparison(kCondLT, i, j, Primitive::kPrimLong);
- TestComparison(kCondLE, i, j, Primitive::kPrimLong);
- TestComparison(kCondGT, i, j, Primitive::kPrimLong);
- TestComparison(kCondGE, i, j, Primitive::kPrimLong);
- TestComparison(kCondB, i, j, Primitive::kPrimLong);
- TestComparison(kCondBE, i, j, Primitive::kPrimLong);
- TestComparison(kCondA, i, j, Primitive::kPrimLong);
- TestComparison(kCondAE, i, j, Primitive::kPrimLong);
+ TestComparison(kCondEQ, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondNE, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondLT, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondLE, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondGT, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondGE, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondB, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondBE, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondA, i, j, Primitive::kPrimLong, target_isa);
+ TestComparison(kCondAE, i, j, Primitive::kPrimLong, target_isa);
}
}
}
+static ::std::vector<InstructionSet> GetTargetISAs() {
+ ::std::vector<InstructionSet> v;
+ // Add all ISAs that are executable on hardware or on simulator.
+ const ::std::vector<InstructionSet> executable_isa_candidates = {
+ kArm,
+ kArm64,
+ kThumb2,
+ kX86,
+ kX86_64,
+ kMips,
+ kMips64
+ };
+
+ for (auto target_isa : executable_isa_candidates) {
+ if (CanExecute(target_isa)) {
+ v.push_back(target_isa);
+ }
+ }
+
+ return v;
+}
+
+INSTANTIATE_TEST_CASE_P(MultipleTargets,
+ CodegenTest,
+ ::testing::ValuesIn(GetTargetISAs()));
+
} // namespace art
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index a8f65bf516..e469c8d6d0 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -32,7 +32,7 @@ namespace art {
/**
* Fixture class for the constant folding and dce tests.
*/
-class ConstantFoldingTest : public CommonCompilerTest {
+class ConstantFoldingTest : public testing::Test {
public:
ConstantFoldingTest() : pool_(), allocator_(&pool_) {
graph_ = CreateGraph(&allocator_);
@@ -56,7 +56,7 @@ class ConstantFoldingTest : public CommonCompilerTest {
const std::string& expected_after_dce,
std::function<void(HGraph*)> check_after_cf) {
ASSERT_NE(graph_, nullptr);
- TransformToSsa(graph_);
+ graph_->TryBuildingSsa();
StringPrettyPrinter printer_before(graph_);
printer_before.VisitInsertionOrder();
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index f0f98efadb..2c6a1ef63d 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -26,8 +26,6 @@
namespace art {
-class DeadCodeEliminationTest : public CommonCompilerTest {};
-
static void TestCode(const uint16_t* data,
const std::string& expected_before,
const std::string& expected_after) {
@@ -36,7 +34,7 @@ static void TestCode(const uint16_t* data,
HGraph* graph = CreateCFG(&allocator, data);
ASSERT_NE(graph, nullptr);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
StringPrettyPrinter printer_before(graph);
printer_before.VisitInsertionOrder();
@@ -57,6 +55,7 @@ static void TestCode(const uint16_t* data,
ASSERT_EQ(actual_after, expected_after);
}
+
/**
* Small three-register program.
*
@@ -70,7 +69,7 @@ static void TestCode(const uint16_t* data,
* L1: v2 <- v0 + v1 5. add-int v2, v0, v1
* return-void 7. return
*/
-TEST_F(DeadCodeEliminationTest, AdditionAndConditionalJump) {
+TEST(DeadCodeElimination, AdditionAndConditionalJump) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 1 << 8 | 1 << 12,
Instruction::CONST_4 | 0 << 8 | 0 << 12,
@@ -132,7 +131,7 @@ TEST_F(DeadCodeEliminationTest, AdditionAndConditionalJump) {
* L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4
* return 13. return-void
*/
-TEST_F(DeadCodeEliminationTest, AdditionsAndInconditionalJumps) {
+TEST(DeadCodeElimination, AdditionsAndInconditionalJumps) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 << 8 | 0 << 12,
Instruction::CONST_4 | 1 << 8 | 1 << 12,
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index f3c1dbe3f5..dfc363f9fd 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -24,7 +24,6 @@
#include "base/arena_containers.h"
#include "base/bit_vector-inl.h"
#include "base/stringprintf.h"
-#include "handle_scope-inl.h"
namespace art {
@@ -595,17 +594,6 @@ void SSAChecker::VisitInstruction(HInstruction* instruction) {
}
}
}
-
- // Ensure that reference type instructions have reference type info.
- if (instruction->GetType() == Primitive::kPrimNot) {
- ScopedObjectAccess soa(Thread::Current());
- if (!instruction->GetReferenceTypeInfo().IsValid()) {
- AddError(StringPrintf("Reference type instruction %s:%d does not have "
- "valid reference type information.",
- instruction->DebugName(),
- instruction->GetId()));
- }
- }
}
static Primitive::Type PrimitiveKind(Primitive::Type type) {
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index d10df4ce3f..fee56c7f9e 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -17,6 +17,8 @@
#include "graph_checker.h"
#include "optimizing_unit_test.h"
+#include "gtest/gtest.h"
+
namespace art {
/**
@@ -41,6 +43,7 @@ HGraph* CreateSimpleCFG(ArenaAllocator* allocator) {
return graph;
}
+
static void TestCode(const uint16_t* data) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
@@ -58,7 +61,8 @@ static void TestCodeSSA(const uint16_t* data) {
HGraph* graph = CreateCFG(&allocator, data);
ASSERT_NE(graph, nullptr);
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
+ graph->TransformToSsa();
SSAChecker ssa_checker(graph);
ssa_checker.Run();
@@ -141,9 +145,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) {
ASSERT_FALSE(graph_checker.IsValid());
}
-class SSACheckerTest : public CommonCompilerTest {};
-
-TEST_F(SSACheckerTest, SSAPhi) {
+TEST(SSAChecker, SSAPhi) {
// This code creates one Phi function during the conversion to SSA form.
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 5f1328f545..e9fdb84d1e 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -30,7 +30,6 @@
#include "optimization.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
-#include "ssa_builder.h"
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
@@ -506,7 +505,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
} else {
StartAttributeStream("loop") << "B" << info->GetHeader()->GetBlockId();
}
- } else if ((IsPass(SsaBuilder::kSsaBuilderPassName)
+ } else if ((IsPass(ReferenceTypePropagation::kReferenceTypePropagationPassName)
|| IsPass(HInliner::kInlinerPassName))
&& (instruction->GetType() == Primitive::kPrimNot)) {
ReferenceTypeInfo info = instruction->IsLoadClass()
@@ -520,15 +519,21 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
} else if (instruction->IsLoadClass()) {
StartAttributeStream("klass") << "unresolved";
- } else {
+ } else if (instruction->IsNullConstant()) {
// The NullConstant may be added to the graph during other passes that happen between
// ReferenceTypePropagation and Inliner (e.g. InstructionSimplifier). If the inliner
// doesn't run or doesn't inline anything, the NullConstant remains untyped.
// So we should check NullConstants for validity only after reference type propagation.
- DCHECK(graph_in_bad_state_ ||
- (!is_after_pass_ && IsPass(SsaBuilder::kSsaBuilderPassName)))
- << instruction->DebugName() << instruction->GetId() << " has invalid rti "
- << (is_after_pass_ ? "after" : "before") << " pass " << pass_name_;
+ //
+ // Note: The infrastructure to properly type NullConstants everywhere is to complex to add
+ // for the benefits.
+ StartAttributeStream("klass") << "not_set";
+ DCHECK(!is_after_pass_
+ || !IsPass(ReferenceTypePropagation::kReferenceTypePropagationPassName))
+ << " Expected a valid rti after reference type propagation";
+ } else {
+ DCHECK(!is_after_pass_)
+ << "Expected a valid rti after reference type propagation";
}
}
if (disasm_info_ != nullptr) {
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 9929696ded..de60cf21aa 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -21,11 +21,11 @@
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
-namespace art {
+#include "gtest/gtest.h"
-class GVNTest : public CommonCompilerTest {};
+namespace art {
-TEST_F(GVNTest, LocalFieldElimination) {
+TEST(GVNTest, LocalFieldElimination) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
NullHandle<mirror::DexCache> dex_cache;
@@ -100,7 +100,7 @@ TEST_F(GVNTest, LocalFieldElimination) {
ASSERT_EQ(different_offset->GetBlock(), block);
ASSERT_EQ(use_after_kill->GetBlock(), block);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
GVNOptimization(graph, side_effects).Run();
@@ -110,7 +110,7 @@ TEST_F(GVNTest, LocalFieldElimination) {
ASSERT_EQ(use_after_kill->GetBlock(), block);
}
-TEST_F(GVNTest, GlobalFieldElimination) {
+TEST(GVNTest, GlobalFieldElimination) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
NullHandle<mirror::DexCache> dex_cache;
@@ -182,7 +182,7 @@ TEST_F(GVNTest, GlobalFieldElimination) {
0));
join->AddInstruction(new (&allocator) HExit());
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
GVNOptimization(graph, side_effects).Run();
@@ -193,7 +193,7 @@ TEST_F(GVNTest, GlobalFieldElimination) {
ASSERT_TRUE(join->GetFirstInstruction()->IsExit());
}
-TEST_F(GVNTest, LoopFieldElimination) {
+TEST(GVNTest, LoopFieldElimination) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
NullHandle<mirror::DexCache> dex_cache;
@@ -288,7 +288,7 @@ TEST_F(GVNTest, LoopFieldElimination) {
ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
{
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
@@ -316,7 +316,7 @@ TEST_F(GVNTest, LoopFieldElimination) {
}
// Test that inner loops affect the side effects of the outer loop.
-TEST_F(GVNTest, LoopSideEffects) {
+TEST(GVNTest, LoopSideEffects) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
NullHandle<mirror::DexCache> dex_cache;
@@ -364,7 +364,7 @@ TEST_F(GVNTest, LoopSideEffects) {
inner_loop_exit->AddInstruction(new (&allocator) HGoto());
outer_loop_exit->AddInstruction(new (&allocator) HExit());
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
*outer_loop_header->GetLoopInformation()));
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 0b7fdf85ea..19e6cbd314 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -71,10 +71,10 @@ HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph)
}
void HInductionVarAnalysis::Run() {
- // Detects sequence variables (generalized induction variables) during an inner-loop-first
- // traversal of all loops using Gerlek's algorithm. The order is only relevant if outer
- // loops would use induction information of inner loops (not currently done).
- for (HPostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
+ // Detects sequence variables (generalized induction variables) during an outer to inner
+ // traversal of all loops using Gerlek's algorithm. The order is important to enable
+ // range analysis on outer loop while visiting inner loops.
+ for (HReversePostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
HBasicBlock* graph_block = it_graph.Current();
if (graph_block->IsLoopHeader()) {
VisitLoop(graph_block->GetLoopInformation());
@@ -745,8 +745,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
if (value == 1) {
return b;
} else if (value == -1) {
- op = kNeg;
- a = nullptr;
+ return CreateSimplifiedInvariant(kNeg, nullptr, b);
}
}
}
@@ -763,41 +762,27 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
if (value == 1) {
return a;
} else if (value == -1) {
- op = kNeg;
- b = a;
- a = nullptr;
+ return CreateSimplifiedInvariant(kNeg, nullptr, a);
}
}
} else if (b->operation == kNeg) {
// Simplify a + (-b) = a - b, a - (-b) = a + b, -(-b) = b.
if (op == kAdd) {
- op = kSub;
- b = b->op_b;
+ return CreateSimplifiedInvariant(kSub, a, b->op_b);
} else if (op == kSub) {
- op = kAdd;
- b = b->op_b;
+ return CreateSimplifiedInvariant(kAdd, a, b->op_b);
} else if (op == kNeg) {
return b->op_b;
}
+ } else if (b->operation == kSub) {
+ // Simplify - (a - b) = b - a.
+ if (op == kNeg) {
+ return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a);
+ }
}
return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
}
-bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
- InductionInfo* info2) {
- // Test structural equality only, without accounting for simplifications.
- if (info1 != nullptr && info2 != nullptr) {
- return
- info1->induction_class == info2->induction_class &&
- info1->operation == info2->operation &&
- info1->fetch == info2->fetch &&
- InductionEqual(info1->op_a, info2->op_a) &&
- InductionEqual(info1->op_b, info2->op_b);
- }
- // Otherwise only two nullptrs are considered equal.
- return info1 == info2;
-}
-
bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) {
if (info != nullptr && info->induction_class == kInvariant) {
// A direct constant fetch.
@@ -812,19 +797,35 @@ bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) {
}
}
// Use range analysis to resolve compound values.
- int32_t range_value;
- if (InductionVarRange::GetConstant(info, &range_value)) {
- *value = range_value;
+ InductionVarRange range(this);
+ int32_t min_val = 0;
+ int32_t max_val = 0;
+ if (range.IsConstantRange(info, &min_val, &max_val) && min_val == max_val) {
+ *value = min_val;
return true;
}
}
return false;
}
+bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
+ InductionInfo* info2) {
+ // Test structural equality only, without accounting for simplifications.
+ if (info1 != nullptr && info2 != nullptr) {
+ return
+ info1->induction_class == info2->induction_class &&
+ info1->operation == info2->operation &&
+ info1->fetch == info2->fetch &&
+ InductionEqual(info1->op_a, info2->op_a) &&
+ InductionEqual(info1->op_b, info2->op_b);
+ }
+ // Otherwise only two nullptrs are considered equal.
+ return info1 == info2;
+}
+
std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
if (info != nullptr) {
if (info->induction_class == kInvariant) {
- int64_t value = -1;
std::string inv = "(";
inv += InductionToString(info->op_a);
switch (info->operation) {
@@ -840,8 +841,10 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
case kGE: inv += " >= "; break;
case kFetch:
DCHECK(info->fetch);
- if (IsIntAndGet(info, &value)) {
- inv += std::to_string(value);
+ if (info->fetch->IsIntConstant()) {
+ inv += std::to_string(info->fetch->AsIntConstant()->GetValue());
+ } else if (info->fetch->IsLongConstant()) {
+ inv += std::to_string(info->fetch->AsLongConstant()->GetValue());
} else {
inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
}
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index cf354093f2..84d5d82568 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -188,9 +188,11 @@ class HInductionVarAnalysis : public HOptimization {
InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
+ // Constants.
+ bool IsIntAndGet(InductionInfo* info, int64_t* value);
+
// Helpers.
static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
- static bool IsIntAndGet(InductionInfo* info, int64_t* value);
static std::string InductionToString(InductionInfo* info);
// TODO: fine tune the following data structures, only keep relevant data.
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 776c115e9d..5de94f43c9 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -18,6 +18,7 @@
#include "base/arena_allocator.h"
#include "builder.h"
+#include "gtest/gtest.h"
#include "induction_var_analysis.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -27,7 +28,7 @@ namespace art {
/**
* Fixture class for the InductionVarAnalysis tests.
*/
-class InductionVarAnalysisTest : public CommonCompilerTest {
+class InductionVarAnalysisTest : public testing::Test {
public:
InductionVarAnalysisTest() : pool_(), allocator_(&pool_) {
graph_ = CreateGraph(&allocator_);
@@ -101,7 +102,6 @@ class InductionVarAnalysisTest : public CommonCompilerTest {
basic_[d] = new (&allocator_) HLocal(d);
entry_->AddInstruction(basic_[d]);
loop_preheader_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], constant0_));
- loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto());
HInstruction* load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
loop_header_[d]->AddInstruction(load);
HInstruction* compare = new (&allocator_) HLessThan(load, constant100_);
@@ -168,7 +168,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest {
// Performs InductionVarAnalysis (after proper set up).
void PerformInductionVarAnalysis() {
- TransformToSsa(graph_);
+ ASSERT_TRUE(graph_->TryBuildingSsa());
iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
iva_->Run();
}
@@ -212,7 +212,7 @@ TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
// ..
// }
BuildLoopNest(10);
- TransformToSsa(graph_);
+ ASSERT_TRUE(graph_->TryBuildingSsa());
ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
for (int d = 0; d < 1; d++) {
ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 9d0cde7c9f..ae15fcf381 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -119,7 +119,7 @@ void InductionVarRange::GetInductionRange(HInstruction* context,
}
}
-bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) {
+bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const {
Value v1 = RefineOuter(*min_val, /* is_min */ true);
Value v2 = RefineOuter(*max_val, /* is_min */ false);
if (v1.instruction != min_val->instruction || v2.instruction != max_val->instruction) {
@@ -167,7 +167,7 @@ void InductionVarRange::GenerateTakenTest(HInstruction* context,
// Private class methods.
//
-bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) {
+bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const {
if (info != nullptr) {
if (info->induction_class == HInductionVarAnalysis::kLinear) {
return true;
@@ -178,7 +178,7 @@ bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* inf
return false;
}
-bool InductionVarRange::IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) {
+bool InductionVarRange::IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
if (trip != nullptr) {
if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
return trip->operation == HInductionVarAnalysis::kTripCountInBody ||
@@ -188,7 +188,7 @@ bool InductionVarRange::IsBodyTripCount(HInductionVarAnalysis::InductionInfo* tr
return false;
}
-bool InductionVarRange::IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) {
+bool InductionVarRange::IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
if (trip != nullptr) {
if (trip->induction_class == HInductionVarAnalysis::kInvariant) {
return trip->operation == HInductionVarAnalysis::kTripCountInBodyUnsafe ||
@@ -198,10 +198,57 @@ bool InductionVarRange::IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo*
return false;
}
+InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const {
+ // Detect common situation where an offset inside the trip count cancels out during range
+ // analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
+ // min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
+ // with intermediate results that only incorporate single instructions.
+ if (trip != nullptr) {
+ HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
+ if (trip_expr->operation == HInductionVarAnalysis::kSub) {
+ int32_t min_value = 0;
+ int32_t stride_value = 0;
+ if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) {
+ if (!is_min && stride_value == 1) {
+ // Test original trip's negative operand (trip_expr->op_b) against
+ // the offset of the linear induction.
+ if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
+ // Analyze cancelled trip with just the positive operand (trip_expr->op_a).
+ HInductionVarAnalysis::InductionInfo cancelled_trip(
+ trip->induction_class, trip->operation, trip_expr->op_a, trip->op_b, nullptr);
+ return GetVal(&cancelled_trip, trip, in_body, is_min);
+ }
+ } else if (is_min && stride_value == -1) {
+ // Test original trip's positive operand (trip_expr->op_a) against
+ // the offset of the linear induction.
+ if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) {
+ // Analyze cancelled trip with just the negative operand (trip_expr->op_b).
+ HInductionVarAnalysis::InductionInfo neg(
+ HInductionVarAnalysis::kInvariant,
+ HInductionVarAnalysis::kNeg,
+ nullptr,
+ trip_expr->op_b,
+ nullptr);
+ HInductionVarAnalysis::InductionInfo cancelled_trip(
+ trip->induction_class, trip->operation, &neg, trip->op_b, nullptr);
+ return SubValue(Value(0), GetVal(&cancelled_trip, trip, in_body, !is_min));
+ }
+ }
+ }
+ }
+ }
+ // General rule of linear induction a * i + b, for normalized 0 <= i < TC.
+ return AddValue(GetMul(info->op_a, trip, trip, in_body, is_min),
+ GetVal(info->op_b, trip, in_body, is_min));
+}
+
InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
- bool is_min) {
+ bool is_min) const {
// Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
// more likely range analysis will compare the same instructions as terminal nodes.
int32_t value;
@@ -227,7 +274,7 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
- bool is_min) {
+ bool is_min) const {
if (info != nullptr) {
switch (info->induction_class) {
case HInductionVarAnalysis::kInvariant:
@@ -266,13 +313,11 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct
break;
}
break;
- case HInductionVarAnalysis::kLinear:
- // Linear induction a * i + b, for normalized 0 <= i < TC.
- return AddValue(GetMul(info->op_a, trip, trip, in_body, is_min),
- GetVal(info->op_b, trip, in_body, is_min));
+ case HInductionVarAnalysis::kLinear: {
+ return GetLinear(info, trip, in_body, is_min);
+ }
case HInductionVarAnalysis::kWrapAround:
case HInductionVarAnalysis::kPeriodic:
- // Merge values in the wrap-around/periodic.
return MergeVal(GetVal(info->op_a, trip, in_body, is_min),
GetVal(info->op_b, trip, in_body, is_min), is_min);
}
@@ -284,11 +329,17 @@ InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::Induct
HInductionVarAnalysis::InductionInfo* info2,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
- bool is_min) {
+ bool is_min) const {
Value v1_min = GetVal(info1, trip, in_body, /* is_min */ true);
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
+ // Try to refine certain failure.
+ if (v1_min.a_constant && v1_max.a_constant) {
+ v1_min = RefineOuter(v1_min, /* is_min */ true);
+ v1_max = RefineOuter(v1_max, /* is_min */ false);
+ }
+ // Positive or negative range?
if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
// Positive range vs. positive or negative range.
if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
@@ -298,7 +349,7 @@ InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::Induct
return is_min ? MulValue(v1_max, v2_min)
: MulValue(v1_min, v2_max);
}
- } else if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
+ } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) {
// Negative range vs. positive or negative range.
if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
return is_min ? MulValue(v1_min, v2_max)
@@ -315,11 +366,12 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct
HInductionVarAnalysis::InductionInfo* info2,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
- bool is_min) {
+ bool is_min) const {
Value v1_min = GetVal(info1, trip, in_body, /* is_min */ true);
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
+ // Positive or negative range?
if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
// Positive range vs. positive or negative range.
if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
@@ -329,7 +381,7 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct
return is_min ? DivValue(v1_max, v2_max)
: DivValue(v1_min, v2_min);
}
- } else if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
+ } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) {
// Negative range vs. positive or negative range.
if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
return is_min ? DivValue(v1_min, v2_min)
@@ -342,19 +394,23 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct
return Value();
}
-bool InductionVarRange::GetConstant(HInductionVarAnalysis::InductionInfo* info, int32_t *value) {
- Value v_min = GetVal(info, nullptr, false, /* is_min */ true);
- Value v_max = GetVal(info, nullptr, false, /* is_min */ false);
- if (v_min.is_known && v_max.is_known) {
- if (v_min.a_constant == 0 && v_max.a_constant == 0 && v_min.b_constant == v_max.b_constant) {
- *value = v_min.b_constant;
+bool InductionVarRange::IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
+ int32_t *min_value,
+ int32_t *max_value) const {
+ bool in_body = true; // no known trip count
+ Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true);
+ Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false);
+ do {
+ if (v_min.is_known && v_min.a_constant == 0 && v_max.is_known && v_max.a_constant == 0) {
+ *min_value = v_min.b_constant;
+ *max_value = v_max.b_constant;
return true;
}
- }
+ } while (RefineOuter(&v_min, &v_max));
return false;
}
-InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) {
+InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
const int32_t b = v1.b_constant + v2.b_constant;
if (v1.a_constant == 0) {
@@ -368,7 +424,7 @@ InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) {
return Value();
}
-InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) {
+InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
const int32_t b = v1.b_constant - v2.b_constant;
if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
@@ -382,7 +438,7 @@ InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) {
return Value();
}
-InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2) {
+InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known) {
if (v1.a_constant == 0) {
if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
@@ -397,7 +453,7 @@ InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2) {
return Value();
}
-InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2) {
+InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known && v1.a_constant == 0 && v2.a_constant == 0) {
if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
return Value(v1.b_constant / v2.b_constant);
@@ -406,7 +462,7 @@ InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2) {
return Value();
}
-InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is_min) {
+InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is_min) const {
if (v1.is_known && v2.is_known) {
if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
return Value(v1.instruction, v1.a_constant,
@@ -417,7 +473,7 @@ InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is
return Value();
}
-InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) {
+InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const {
if (v.instruction != nullptr) {
HLoopInformation* loop =
v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop
@@ -444,7 +500,7 @@ bool InductionVarRange::GenerateCode(HInstruction* context,
/*out*/HInstruction** upper,
/*out*/HInstruction** taken_test,
/*out*/bool* needs_finite_test,
- /*out*/bool* needs_taken_test) {
+ /*out*/bool* needs_taken_test) const {
HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
if (loop != nullptr) {
// Set up loop information.
@@ -492,7 +548,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
HBasicBlock* block,
/*out*/HInstruction** result,
bool in_body,
- bool is_min) {
+ bool is_min) const {
if (info != nullptr) {
// Handle current operation.
Primitive::Type type = Primitive::kPrimInt;
@@ -586,8 +642,9 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
case HInductionVarAnalysis::kLinear: {
// Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only
// to avoid arithmetic wrap-around situations that are hard to guard against.
+ int32_t min_value = 0;
int32_t stride_value = 0;
- if (GetConstant(info->op_a, &stride_value)) {
+ if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) {
if (stride_value == 1 || stride_value == -1) {
const bool is_min_a = stride_value == 1 ? is_min : !is_min;
if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) &&
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 71b0b1b4c3..974b8fba06 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -69,7 +69,7 @@ class InductionVarRange {
/*out*/bool* needs_finite_test);
/** Refines the values with induction of next outer loop. Returns true on change. */
- bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val);
+ bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const;
/**
* Returns true if range analysis is able to generate code for the lower and upper
@@ -116,46 +116,48 @@ class InductionVarRange {
/*out*/HInstruction** taken_test);
private:
- //
- // Private helper methods.
- //
-
- static bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info);
- static bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip);
- static bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip);
-
- static Value GetFetch(HInstruction* instruction,
- HInductionVarAnalysis::InductionInfo* trip,
- bool in_body,
- bool is_min);
- static Value GetVal(HInductionVarAnalysis::InductionInfo* info,
- HInductionVarAnalysis::InductionInfo* trip,
- bool in_body,
- bool is_min);
- static Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
- HInductionVarAnalysis::InductionInfo* info2,
- HInductionVarAnalysis::InductionInfo* trip,
- bool in_body,
- bool is_min);
- static Value GetDiv(HInductionVarAnalysis::InductionInfo* info1,
- HInductionVarAnalysis::InductionInfo* info2,
- HInductionVarAnalysis::InductionInfo* trip,
- bool in_body,
- bool is_min);
-
- static bool GetConstant(HInductionVarAnalysis::InductionInfo* info, int32_t *value);
-
- static Value AddValue(Value v1, Value v2);
- static Value SubValue(Value v1, Value v2);
- static Value MulValue(Value v1, Value v2);
- static Value DivValue(Value v1, Value v2);
- static Value MergeVal(Value v1, Value v2, bool is_min);
+ bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const;
+ bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
+ bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
+
+ Value GetLinear(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
+ Value GetFetch(HInstruction* instruction,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
+ Value GetVal(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
+ Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
+ Value GetDiv(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
+
+ bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
+ int32_t *min_value,
+ int32_t *max_value) const;
+
+ Value AddValue(Value v1, Value v2) const;
+ Value SubValue(Value v1, Value v2) const;
+ Value MulValue(Value v1, Value v2) const;
+ Value DivValue(Value v1, Value v2) const;
+ Value MergeVal(Value v1, Value v2, bool is_min) const;
/**
* Returns refined value using induction of next outer loop or the input value if no
* further refinement is possible.
*/
- Value RefineOuter(Value val, bool is_min);
+ Value RefineOuter(Value val, bool is_min) const;
/**
* Generates code for lower/upper/taken-test in the HIR. Returns true on success.
@@ -170,15 +172,15 @@ class InductionVarRange {
/*out*/HInstruction** upper,
/*out*/HInstruction** taken_test,
/*out*/bool* needs_finite_test,
- /*out*/bool* needs_taken_test);
-
- static bool GenerateCode(HInductionVarAnalysis::InductionInfo* info,
- HInductionVarAnalysis::InductionInfo* trip,
- HGraph* graph,
- HBasicBlock* block,
- /*out*/HInstruction** result,
- bool in_body,
- bool is_min);
+ /*out*/bool* needs_taken_test) const;
+
+ bool GenerateCode(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ HGraph* graph,
+ HBasicBlock* block,
+ /*out*/HInstruction** result,
+ bool in_body,
+ bool is_min) const;
/** Results of prior induction variable analysis. */
HInductionVarAnalysis *induction_analysis_;
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index a1c797a80a..5c0bdd7c4c 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -16,6 +16,7 @@
#include "base/arena_allocator.h"
#include "builder.h"
+#include "gtest/gtest.h"
#include "induction_var_analysis.h"
#include "induction_var_range.h"
#include "nodes.h"
@@ -28,11 +29,14 @@ using Value = InductionVarRange::Value;
/**
* Fixture class for the InductionVarRange tests.
*/
-class InductionVarRangeTest : public CommonCompilerTest {
+class InductionVarRangeTest : public testing::Test {
public:
- InductionVarRangeTest() : pool_(), allocator_(&pool_) {
- graph_ = CreateGraph(&allocator_);
- iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
+ InductionVarRangeTest()
+ : pool_(),
+ allocator_(&pool_),
+ graph_(CreateGraph(&allocator_)),
+ iva_(new (&allocator_) HInductionVarAnalysis(graph_)),
+ range_(iva_) {
BuildGraph();
}
@@ -58,6 +62,11 @@ class InductionVarRangeTest : public CommonCompilerTest {
graph_->AddBlock(exit_block_);
graph_->SetEntryBlock(entry_block_);
graph_->SetExitBlock(exit_block_);
+ // Two parameters.
+ x_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
+ entry_block_->AddInstruction(x_);
+ y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
+ entry_block_->AddInstruction(y_);
}
/** Constructs loop with given upper bound. */
@@ -102,9 +111,9 @@ class InductionVarRangeTest : public CommonCompilerTest {
exit_block_->AddInstruction(new (&allocator_) HExit());
}
- /** Performs induction variable analysis. */
+ /** Constructs SSA and performs induction variable analysis. */
void PerformInductionVarAnalysis() {
- TransformToSsa(graph_);
+ ASSERT_TRUE(graph_->TryBuildingSsa());
iva_->Run();
}
@@ -179,49 +188,51 @@ class InductionVarRangeTest : public CommonCompilerTest {
//
bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) {
- return InductionVarRange::NeedsTripCount(info);
+ return range_.NeedsTripCount(info);
}
bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) {
- return InductionVarRange::IsBodyTripCount(trip);
+ return range_.IsBodyTripCount(trip);
}
bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) {
- return InductionVarRange::IsUnsafeTripCount(trip);
+ return range_.IsUnsafeTripCount(trip);
}
Value GetMin(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* induc) {
- return InductionVarRange::GetVal(info, induc, /* in_body */ true, /* is_min */ true);
+ return range_.GetVal(info, induc, /* in_body */ true, /* is_min */ true);
}
Value GetMax(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* induc) {
- return InductionVarRange::GetVal(info, induc, /* in_body */ true, /* is_min */ false);
+ return range_.GetVal(info, induc, /* in_body */ true, /* is_min */ false);
}
Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
HInductionVarAnalysis::InductionInfo* info2,
bool is_min) {
- return InductionVarRange::GetMul(info1, info2, nullptr, /* in_body */ true, is_min);
+ return range_.GetMul(info1, info2, nullptr, /* in_body */ true, is_min);
}
Value GetDiv(HInductionVarAnalysis::InductionInfo* info1,
HInductionVarAnalysis::InductionInfo* info2,
bool is_min) {
- return InductionVarRange::GetDiv(info1, info2, nullptr, /* in_body */ true, is_min);
+ return range_.GetDiv(info1, info2, nullptr, /* in_body */ true, is_min);
}
- bool GetConstant(HInductionVarAnalysis::InductionInfo* info, int32_t* value) {
- return InductionVarRange::GetConstant(info, value);
+ bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
+ int32_t* min_value,
+ int32_t* max_value) {
+ return range_.IsConstantRange(info, min_value, max_value);
}
- Value AddValue(Value v1, Value v2) { return InductionVarRange::AddValue(v1, v2); }
- Value SubValue(Value v1, Value v2) { return InductionVarRange::SubValue(v1, v2); }
- Value MulValue(Value v1, Value v2) { return InductionVarRange::MulValue(v1, v2); }
- Value DivValue(Value v1, Value v2) { return InductionVarRange::DivValue(v1, v2); }
- Value MinValue(Value v1, Value v2) { return InductionVarRange::MergeVal(v1, v2, true); }
- Value MaxValue(Value v1, Value v2) { return InductionVarRange::MergeVal(v1, v2, false); }
+ Value AddValue(Value v1, Value v2) { return range_.AddValue(v1, v2); }
+ Value SubValue(Value v1, Value v2) { return range_.SubValue(v1, v2); }
+ Value MulValue(Value v1, Value v2) { return range_.MulValue(v1, v2); }
+ Value DivValue(Value v1, Value v2) { return range_.DivValue(v1, v2); }
+ Value MinValue(Value v1, Value v2) { return range_.MergeVal(v1, v2, true); }
+ Value MaxValue(Value v1, Value v2) { return range_.MergeVal(v1, v2, false); }
// General building fields.
ArenaPool pool_;
@@ -231,16 +242,17 @@ class InductionVarRangeTest : public CommonCompilerTest {
HBasicBlock* exit_block_;
HBasicBlock* loop_preheader_;
HInductionVarAnalysis* iva_;
+ InductionVarRange range_;
// Instructions.
HInstruction* condition_;
HInstruction* increment_;
- HReturnVoid x_;
- HReturnVoid y_;
+ HInstruction* x_;
+ HInstruction* y_;
};
//
-// Tests on static methods.
+// Tests on private methods.
//
TEST_F(InductionVarRangeTest, TripCountProperties) {
@@ -273,14 +285,14 @@ TEST_F(InductionVarRangeTest, GetMinMaxAdd) {
GetMin(CreateInvariant('+', CreateConst(2), CreateRange(10, 20)), nullptr));
ExpectEqual(Value(22),
GetMax(CreateInvariant('+', CreateConst(2), CreateRange(10, 20)), nullptr));
- ExpectEqual(Value(&x_, 1, -20),
- GetMin(CreateInvariant('+', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
- ExpectEqual(Value(&x_, 1, -10),
- GetMax(CreateInvariant('+', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
- ExpectEqual(Value(&x_, 1, 10),
- GetMin(CreateInvariant('+', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
- ExpectEqual(Value(&x_, 1, 20),
- GetMax(CreateInvariant('+', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(x_, 1, -20),
+ GetMin(CreateInvariant('+', CreateFetch(x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(x_, 1, -10),
+ GetMax(CreateInvariant('+', CreateFetch(x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(x_, 1, 10),
+ GetMin(CreateInvariant('+', CreateRange(10, 20), CreateFetch(x_)), nullptr));
+ ExpectEqual(Value(x_, 1, 20),
+ GetMax(CreateInvariant('+', CreateRange(10, 20), CreateFetch(x_)), nullptr));
ExpectEqual(Value(5),
GetMin(CreateInvariant('+', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
ExpectEqual(Value(19),
@@ -292,14 +304,14 @@ TEST_F(InductionVarRangeTest, GetMinMaxSub) {
GetMin(CreateInvariant('-', CreateConst(2), CreateRange(10, 20)), nullptr));
ExpectEqual(Value(-8),
GetMax(CreateInvariant('-', CreateConst(2), CreateRange(10, 20)), nullptr));
- ExpectEqual(Value(&x_, 1, 10),
- GetMin(CreateInvariant('-', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
- ExpectEqual(Value(&x_, 1, 20),
- GetMax(CreateInvariant('-', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
- ExpectEqual(Value(&x_, -1, 10),
- GetMin(CreateInvariant('-', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
- ExpectEqual(Value(&x_, -1, 20),
- GetMax(CreateInvariant('-', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(x_, 1, 10),
+ GetMin(CreateInvariant('-', CreateFetch(x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(x_, 1, 20),
+ GetMax(CreateInvariant('-', CreateFetch(x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(x_, -1, 10),
+ GetMin(CreateInvariant('-', CreateRange(10, 20), CreateFetch(x_)), nullptr));
+ ExpectEqual(Value(x_, -1, 20),
+ GetMax(CreateInvariant('-', CreateRange(10, 20), CreateFetch(x_)), nullptr));
ExpectEqual(Value(-25),
GetMin(CreateInvariant('-', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
ExpectEqual(Value(-11),
@@ -311,8 +323,8 @@ TEST_F(InductionVarRangeTest, GetMinMaxNeg) {
ExpectEqual(Value(-10), GetMax(CreateInvariant('n', nullptr, CreateRange(10, 20)), nullptr));
ExpectEqual(Value(10), GetMin(CreateInvariant('n', nullptr, CreateRange(-20, -10)), nullptr));
ExpectEqual(Value(20), GetMax(CreateInvariant('n', nullptr, CreateRange(-20, -10)), nullptr));
- ExpectEqual(Value(&x_, -1, 0), GetMin(CreateInvariant('n', nullptr, CreateFetch(&x_)), nullptr));
- ExpectEqual(Value(&x_, -1, 0), GetMax(CreateInvariant('n', nullptr, CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(x_, -1, 0), GetMin(CreateInvariant('n', nullptr, CreateFetch(x_)), nullptr));
+ ExpectEqual(Value(x_, -1, 0), GetMax(CreateInvariant('n', nullptr, CreateFetch(x_)), nullptr));
}
TEST_F(InductionVarRangeTest, GetMinMaxMul) {
@@ -335,8 +347,8 @@ TEST_F(InductionVarRangeTest, GetMinMaxConstant) {
}
TEST_F(InductionVarRangeTest, GetMinMaxFetch) {
- ExpectEqual(Value(&x_, 1, 0), GetMin(CreateFetch(&x_), nullptr));
- ExpectEqual(Value(&x_, 1, 0), GetMax(CreateFetch(&x_), nullptr));
+ ExpectEqual(Value(x_, 1, 0), GetMin(CreateFetch(x_), nullptr));
+ ExpectEqual(Value(x_, 1, 0), GetMax(CreateFetch(x_), nullptr));
}
TEST_F(InductionVarRangeTest, GetMinMaxLinear) {
@@ -363,45 +375,70 @@ TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) {
TEST_F(InductionVarRangeTest, GetMulMin) {
ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), true));
ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), true));
+ ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), true));
ExpectEqual(Value(-50), GetMul(CreateRange(-10, -2), CreateRange(3, 5), true));
ExpectEqual(Value(6), GetMul(CreateRange(-10, -2), CreateRange(-5, -3), true));
+ ExpectEqual(Value(), GetMul(CreateRange(-10, -2), CreateRange(-1, 1), true));
+ ExpectEqual(Value(), GetMul(CreateRange(-1, 1), CreateRange(2, 10), true));
+ ExpectEqual(Value(), GetMul(CreateRange(-1, 1), CreateRange(-10, -2), true));
+ ExpectEqual(Value(), GetMul(CreateRange(-1, 1), CreateRange(-1, 1), true));
}
TEST_F(InductionVarRangeTest, GetMulMax) {
ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), false));
ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), false));
+ ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), false));
ExpectEqual(Value(-6), GetMul(CreateRange(-10, -2), CreateRange(3, 5), false));
ExpectEqual(Value(50), GetMul(CreateRange(-10, -2), CreateRange(-5, -3), false));
+ ExpectEqual(Value(), GetMul(CreateRange(-10, -2), CreateRange(-1, 1), false));
+ ExpectEqual(Value(), GetMul(CreateRange(-1, 1), CreateRange(2, 10), false));
+ ExpectEqual(Value(), GetMul(CreateRange(-1, 1), CreateRange(-10, -2), false));
+ ExpectEqual(Value(), GetMul(CreateRange(-1, 1), CreateRange(-1, 1), false));
}
TEST_F(InductionVarRangeTest, GetDivMin) {
ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), true));
ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), true));
+ ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), true));
ExpectEqual(Value(-500), GetDiv(CreateRange(-1000, -40), CreateRange(2, 4), true));
ExpectEqual(Value(10), GetDiv(CreateRange(-1000, -40), CreateRange(-4, -2), true));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1000, -40), CreateRange(-1, 1), true));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(40, 1000), true));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1000, -40), true));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), true));
}
TEST_F(InductionVarRangeTest, GetDivMax) {
ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), false));
ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), false));
+ ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), false));
ExpectEqual(Value(-10), GetDiv(CreateRange(-1000, -40), CreateRange(2, 4), false));
ExpectEqual(Value(500), GetDiv(CreateRange(-1000, -40), CreateRange(-4, -2), false));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1000, -40), CreateRange(-1, 1), false));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(40, 1000), false));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1000, 40), false));
+ ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), false));
}
-TEST_F(InductionVarRangeTest, GetConstant) {
- int32_t value;
- ASSERT_TRUE(GetConstant(CreateConst(12345), &value));
- EXPECT_EQ(12345, value);
- EXPECT_FALSE(GetConstant(CreateRange(1, 2), &value));
+TEST_F(InductionVarRangeTest, IsConstantRange) {
+ int32_t min_value;
+ int32_t max_value;
+ ASSERT_TRUE(IsConstantRange(CreateConst(12345), &min_value, &max_value));
+ EXPECT_EQ(12345, min_value);
+ EXPECT_EQ(12345, max_value);
+ ASSERT_TRUE(IsConstantRange(CreateRange(1, 2), &min_value, &max_value));
+ EXPECT_EQ(1, min_value);
+ EXPECT_EQ(2, max_value);
+ EXPECT_FALSE(IsConstantRange(CreateFetch(x_), &min_value, &max_value));
}
TEST_F(InductionVarRangeTest, AddValue) {
ExpectEqual(Value(110), AddValue(Value(10), Value(100)));
- ExpectEqual(Value(-5), AddValue(Value(&x_, 1, -4), Value(&x_, -1, -1)));
- ExpectEqual(Value(&x_, 3, -5), AddValue(Value(&x_, 2, -4), Value(&x_, 1, -1)));
- ExpectEqual(Value(), AddValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
- ExpectEqual(Value(&x_, 1, 23), AddValue(Value(&x_, 1, 20), Value(3)));
- ExpectEqual(Value(&y_, 1, 5), AddValue(Value(55), Value(&y_, 1, -50)));
+ ExpectEqual(Value(-5), AddValue(Value(x_, 1, -4), Value(x_, -1, -1)));
+ ExpectEqual(Value(x_, 3, -5), AddValue(Value(x_, 2, -4), Value(x_, 1, -1)));
+ ExpectEqual(Value(), AddValue(Value(x_, 1, 5), Value(y_, 1, -7)));
+ ExpectEqual(Value(x_, 1, 23), AddValue(Value(x_, 1, 20), Value(3)));
+ ExpectEqual(Value(y_, 1, 5), AddValue(Value(55), Value(y_, 1, -50)));
const int32_t max_value = std::numeric_limits<int32_t>::max();
ExpectEqual(Value(max_value), AddValue(Value(max_value - 5), Value(5)));
ExpectEqual(Value(), AddValue(Value(max_value - 5), Value(6))); // unsafe
@@ -409,11 +446,11 @@ TEST_F(InductionVarRangeTest, AddValue) {
TEST_F(InductionVarRangeTest, SubValue) {
ExpectEqual(Value(-90), SubValue(Value(10), Value(100)));
- ExpectEqual(Value(-3), SubValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
- ExpectEqual(Value(&x_, 2, -3), SubValue(Value(&x_, 3, -4), Value(&x_, 1, -1)));
- ExpectEqual(Value(), SubValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
- ExpectEqual(Value(&x_, 1, 17), SubValue(Value(&x_, 1, 20), Value(3)));
- ExpectEqual(Value(&y_, -4, 105), SubValue(Value(55), Value(&y_, 4, -50)));
+ ExpectEqual(Value(-3), SubValue(Value(x_, 1, -4), Value(x_, 1, -1)));
+ ExpectEqual(Value(x_, 2, -3), SubValue(Value(x_, 3, -4), Value(x_, 1, -1)));
+ ExpectEqual(Value(), SubValue(Value(x_, 1, 5), Value(y_, 1, -7)));
+ ExpectEqual(Value(x_, 1, 17), SubValue(Value(x_, 1, 20), Value(3)));
+ ExpectEqual(Value(y_, -4, 105), SubValue(Value(55), Value(y_, 4, -50)));
const int32_t min_value = std::numeric_limits<int32_t>::min();
ExpectEqual(Value(min_value), SubValue(Value(min_value + 5), Value(5)));
ExpectEqual(Value(), SubValue(Value(min_value + 5), Value(6))); // unsafe
@@ -421,145 +458,140 @@ TEST_F(InductionVarRangeTest, SubValue) {
TEST_F(InductionVarRangeTest, MulValue) {
ExpectEqual(Value(1000), MulValue(Value(10), Value(100)));
- ExpectEqual(Value(), MulValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
- ExpectEqual(Value(), MulValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
- ExpectEqual(Value(&x_, 9, 60), MulValue(Value(&x_, 3, 20), Value(3)));
- ExpectEqual(Value(&y_, 55, -110), MulValue(Value(55), Value(&y_, 1, -2)));
+ ExpectEqual(Value(), MulValue(Value(x_, 1, -4), Value(x_, 1, -1)));
+ ExpectEqual(Value(), MulValue(Value(x_, 1, 5), Value(y_, 1, -7)));
+ ExpectEqual(Value(x_, 9, 60), MulValue(Value(x_, 3, 20), Value(3)));
+ ExpectEqual(Value(y_, 55, -110), MulValue(Value(55), Value(y_, 1, -2)));
ExpectEqual(Value(), MulValue(Value(90000), Value(-90000))); // unsafe
}
TEST_F(InductionVarRangeTest, DivValue) {
ExpectEqual(Value(25), DivValue(Value(100), Value(4)));
- ExpectEqual(Value(), DivValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
- ExpectEqual(Value(), DivValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
- ExpectEqual(Value(), DivValue(Value(&x_, 12, 24), Value(3)));
- ExpectEqual(Value(), DivValue(Value(55), Value(&y_, 1, -50)));
+ ExpectEqual(Value(), DivValue(Value(x_, 1, -4), Value(x_, 1, -1)));
+ ExpectEqual(Value(), DivValue(Value(x_, 1, 5), Value(y_, 1, -7)));
+ ExpectEqual(Value(), DivValue(Value(x_, 12, 24), Value(3)));
+ ExpectEqual(Value(), DivValue(Value(55), Value(y_, 1, -50)));
ExpectEqual(Value(), DivValue(Value(1), Value(0))); // unsafe
}
TEST_F(InductionVarRangeTest, MinValue) {
ExpectEqual(Value(10), MinValue(Value(10), Value(100)));
- ExpectEqual(Value(&x_, 1, -4), MinValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
- ExpectEqual(Value(&x_, 4, -4), MinValue(Value(&x_, 4, -4), Value(&x_, 4, -1)));
- ExpectEqual(Value(), MinValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
- ExpectEqual(Value(), MinValue(Value(&x_, 1, 20), Value(3)));
- ExpectEqual(Value(), MinValue(Value(55), Value(&y_, 1, -50)));
+ ExpectEqual(Value(x_, 1, -4), MinValue(Value(x_, 1, -4), Value(x_, 1, -1)));
+ ExpectEqual(Value(x_, 4, -4), MinValue(Value(x_, 4, -4), Value(x_, 4, -1)));
+ ExpectEqual(Value(), MinValue(Value(x_, 1, 5), Value(y_, 1, -7)));
+ ExpectEqual(Value(), MinValue(Value(x_, 1, 20), Value(3)));
+ ExpectEqual(Value(), MinValue(Value(55), Value(y_, 1, -50)));
}
TEST_F(InductionVarRangeTest, MaxValue) {
ExpectEqual(Value(100), MaxValue(Value(10), Value(100)));
- ExpectEqual(Value(&x_, 1, -1), MaxValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
- ExpectEqual(Value(&x_, 4, -1), MaxValue(Value(&x_, 4, -4), Value(&x_, 4, -1)));
- ExpectEqual(Value(), MaxValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
- ExpectEqual(Value(), MaxValue(Value(&x_, 1, 20), Value(3)));
- ExpectEqual(Value(), MaxValue(Value(55), Value(&y_, 1, -50)));
+ ExpectEqual(Value(x_, 1, -1), MaxValue(Value(x_, 1, -4), Value(x_, 1, -1)));
+ ExpectEqual(Value(x_, 4, -1), MaxValue(Value(x_, 4, -4), Value(x_, 4, -1)));
+ ExpectEqual(Value(), MaxValue(Value(x_, 1, 5), Value(y_, 1, -7)));
+ ExpectEqual(Value(), MaxValue(Value(x_, 1, 20), Value(3)));
+ ExpectEqual(Value(), MaxValue(Value(55), Value(y_, 1, -50)));
}
//
-// Tests on instance methods.
+// Tests on public methods.
//
TEST_F(InductionVarRangeTest, ConstantTripCountUp) {
BuildLoop(0, graph_->GetIntConstant(1000), 1);
PerformInductionVarAnalysis();
- InductionVarRange range(iva_);
Value v1, v2;
bool needs_finite_test = true;
// In context of header: known.
- range.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(999), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
- range.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
+ range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(1), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
}
TEST_F(InductionVarRangeTest, ConstantTripCountDown) {
BuildLoop(1000, graph_->GetIntConstant(0), -1);
PerformInductionVarAnalysis();
- InductionVarRange range(iva_);
Value v1, v2;
bool needs_finite_test = true;
// In context of header: known.
- range.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(1), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
- range.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
+ range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(999), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
}
TEST_F(InductionVarRangeTest, SymbolicTripCountUp) {
- HInstruction* parameter = new (&allocator_) HParameterValue(
- graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
- entry_block_->AddInstruction(parameter);
- BuildLoop(0, parameter, 1);
+ BuildLoop(0, x_, 1);
PerformInductionVarAnalysis();
- InductionVarRange range(iva_);
Value v1, v2;
bool needs_finite_test = true;
bool needs_taken_test = true;
// In context of header: upper unknown.
- range.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
- ExpectEqual(Value(parameter, 1, -1), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
- range.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ ExpectEqual(Value(x_, 1, -1), v2);
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
+ range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(1), v1);
- ExpectEqual(Value(parameter, 1, 0), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ ExpectEqual(Value(x_, 1, 0), v2);
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
HInstruction* lower = nullptr;
HInstruction* upper = nullptr;
HInstruction* taken = nullptr;
// Can generate code in context of loop-body only.
- EXPECT_FALSE(range.CanGenerateCode(
+ EXPECT_FALSE(range_.CanGenerateCode(
condition_, condition_->InputAt(0), &needs_finite_test, &needs_taken_test));
- ASSERT_TRUE(range.CanGenerateCode(
+ ASSERT_TRUE(range_.CanGenerateCode(
increment_, condition_->InputAt(0), &needs_finite_test, &needs_taken_test));
EXPECT_FALSE(needs_finite_test);
EXPECT_TRUE(needs_taken_test);
// Generates code.
- range.GenerateRangeCode(increment_, condition_->InputAt(0), graph_, loop_preheader_, &lower, &upper);
+ range_.GenerateRangeCode(
+ increment_, condition_->InputAt(0), graph_, loop_preheader_, &lower, &upper);
// Verify lower is 0+0.
ASSERT_TRUE(lower != nullptr);
@@ -580,7 +612,7 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountUp) {
EXPECT_EQ(0, upper->InputAt(1)->AsIntConstant()->GetValue());
// Verify taken-test is 0<V.
- range.GenerateTakenTest(increment_, graph_, loop_preheader_, &taken);
+ range_.GenerateTakenTest(increment_, graph_, loop_preheader_, &taken);
ASSERT_TRUE(taken != nullptr);
ASSERT_TRUE(taken->IsLessThan());
ASSERT_TRUE(taken->InputAt(0)->IsIntConstant());
@@ -589,52 +621,49 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountUp) {
}
TEST_F(InductionVarRangeTest, SymbolicTripCountDown) {
- HInstruction* parameter = new (&allocator_) HParameterValue(
- graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
- entry_block_->AddInstruction(parameter);
- BuildLoop(1000, parameter, -1);
+ BuildLoop(1000, x_, -1);
PerformInductionVarAnalysis();
- InductionVarRange range(iva_);
Value v1, v2;
bool needs_finite_test = true;
bool needs_taken_test = true;
// In context of header: lower unknown.
- range.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
- ExpectEqual(Value(parameter, 1, 1), v1);
+ ExpectEqual(Value(x_, 1, 1), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
- range.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
+ range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
- ExpectEqual(Value(parameter, 1, 0), v1);
+ ExpectEqual(Value(x_, 1, 0), v1);
ExpectEqual(Value(999), v2);
- EXPECT_FALSE(range.RefineOuter(&v1, &v2));
+ EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
HInstruction* lower = nullptr;
HInstruction* upper = nullptr;
HInstruction* taken = nullptr;
// Can generate code in context of loop-body only.
- EXPECT_FALSE(range.CanGenerateCode(
+ EXPECT_FALSE(range_.CanGenerateCode(
condition_, condition_->InputAt(0), &needs_finite_test, &needs_taken_test));
- ASSERT_TRUE(range.CanGenerateCode(
+ ASSERT_TRUE(range_.CanGenerateCode(
increment_, condition_->InputAt(0), &needs_finite_test, &needs_taken_test));
EXPECT_FALSE(needs_finite_test);
EXPECT_TRUE(needs_taken_test);
// Generates code.
- range.GenerateRangeCode(increment_, condition_->InputAt(0), graph_, loop_preheader_, &lower, &upper);
+ range_.GenerateRangeCode(
+ increment_, condition_->InputAt(0), graph_, loop_preheader_, &lower, &upper);
- // Verify lower is 1000-(-(V-1000)-1).
+ // Verify lower is 1000-((1000-V)-1).
ASSERT_TRUE(lower != nullptr);
ASSERT_TRUE(lower->IsSub());
ASSERT_TRUE(lower->InputAt(0)->IsIntConstant());
@@ -644,12 +673,10 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountDown) {
ASSERT_TRUE(lower->InputAt(1)->IsIntConstant());
EXPECT_EQ(1, lower->InputAt(1)->AsIntConstant()->GetValue());
lower = lower->InputAt(0);
- ASSERT_TRUE(lower->IsNeg());
- lower = lower->InputAt(0);
ASSERT_TRUE(lower->IsSub());
- EXPECT_TRUE(lower->InputAt(0)->IsParameterValue());
- ASSERT_TRUE(lower->InputAt(1)->IsIntConstant());
- EXPECT_EQ(1000, lower->InputAt(1)->AsIntConstant()->GetValue());
+ ASSERT_TRUE(lower->InputAt(0)->IsIntConstant());
+ EXPECT_EQ(1000, lower->InputAt(0)->AsIntConstant()->GetValue());
+ EXPECT_TRUE(lower->InputAt(1)->IsParameterValue());
// Verify upper is 1000-0.
ASSERT_TRUE(upper != nullptr);
@@ -660,7 +687,7 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountDown) {
EXPECT_EQ(0, upper->InputAt(1)->AsIntConstant()->GetValue());
// Verify taken-test is 1000>V.
- range.GenerateTakenTest(increment_, graph_, loop_preheader_, &taken);
+ range_.GenerateTakenTest(increment_, graph_, loop_preheader_, &taken);
ASSERT_TRUE(taken != nullptr);
ASSERT_TRUE(taken->IsGreaterThan());
ASSERT_TRUE(taken->InputAt(0)->IsIntConstant());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index db1170909f..a4dcb3aeba 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -33,7 +33,6 @@
#include "reference_type_propagation.h"
#include "register_allocator.h"
#include "sharpening.h"
-#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
@@ -515,7 +514,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
return false;
}
- if (callee_graph->TryBuildingSsa(handles_) != kBuildSsaSuccess) {
+ if (!callee_graph->TryBuildingSsa()) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be transformed to SSA";
return false;
@@ -550,12 +549,14 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
// Run simple optimizations on the graph.
HDeadCodeElimination dce(callee_graph, stats_);
HConstantFolding fold(callee_graph);
+ ReferenceTypePropagation type_propagation(callee_graph, handles_);
HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
InstructionSimplifier simplify(callee_graph, stats_);
IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_);
HOptimization* optimizations[] = {
&intrinsics,
+ &type_propagation,
&sharpening,
&simplify,
&fold,
@@ -676,36 +677,42 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
}
+ // When merging the graph we might create a new NullConstant in the caller graph which does
+ // not have the chance to be typed. We assign the correct type here so that we can keep the
+ // assertion that every reference has a valid type. This also simplifies checks along the way.
+ HNullConstant* null_constant = graph_->GetNullConstant();
+ if (!null_constant->GetReferenceTypeInfo().IsValid()) {
+ ReferenceTypeInfo::TypeHandle obj_handle =
+ handles_->NewHandle(class_linker->GetClassRoot(ClassLinker::kJavaLangObject));
+ null_constant->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(obj_handle, false /* is_exact */));
+ }
+
// Check the integrity of reference types and run another type propagation if needed.
- if (return_replacement != nullptr) {
- if (return_replacement->GetType() == Primitive::kPrimNot) {
- if (!return_replacement->GetReferenceTypeInfo().IsValid()) {
- // Make sure that we have a valid type for the return. We may get an invalid one when
- // we inline invokes with multiple branches and create a Phi for the result.
- // TODO: we could be more precise by merging the phi inputs but that requires
- // some functionality from the reference type propagation.
- DCHECK(return_replacement->IsPhi());
- size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- ReferenceTypeInfo::TypeHandle return_handle =
- handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size));
- return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
- return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */));
- }
+ if ((return_replacement != nullptr)
+ && (return_replacement->GetType() == Primitive::kPrimNot)) {
+ if (!return_replacement->GetReferenceTypeInfo().IsValid()) {
+ // Make sure that we have a valid type for the return. We may get an invalid one when
+ // we inline invokes with multiple branches and create a Phi for the result.
+ // TODO: we could be more precise by merging the phi inputs but that requires
+ // some functionality from the reference type propagation.
+ DCHECK(return_replacement->IsPhi());
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ ReferenceTypeInfo::TypeHandle return_handle =
+ handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size));
+ return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
+ return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */));
+ }
- if (do_rtp) {
- // If the return type is a refinement of the declared type run the type propagation again.
- ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo();
- ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo();
- if (invoke_rti.IsStrictSupertypeOf(return_rti)
- || (return_rti.IsExact() && !invoke_rti.IsExact())
- || !return_replacement->CanBeNull()) {
- ReferenceTypePropagation(graph_, handles_).Run();
- }
- }
- } else if (return_replacement->IsInstanceOf()) {
- if (do_rtp) {
- // Inlining InstanceOf into an If may put a tighter bound on reference types.
- ReferenceTypePropagation(graph_, handles_).Run();
+ if (do_rtp) {
+ // If the return type is a refinement of the declared type run the type propagation again.
+ ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo();
+ ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo();
+ if (invoke_rti.IsStrictSupertypeOf(return_rti)
+ || (return_rti.IsExact() && !invoke_rti.IsExact())
+ || !return_replacement->CanBeNull()) {
+ ReferenceTypePropagation rtp_fixup(graph_, handles_);
+ rtp_fixup.Run();
}
}
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index e1b13c5087..67097deaeb 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -778,9 +778,9 @@ void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
// Try to fold an HCompare into this HCondition.
// This simplification is currently supported on x86, x86_64, ARM and ARM64.
- // TODO: Implement it for MIPS and MIPS64.
+ // TODO: Implement it for MIPS64.
InstructionSet instruction_set = GetGraph()->GetInstructionSet();
- if (instruction_set == kMips || instruction_set == kMips64) {
+ if (instruction_set == kMips64) {
return;
}
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 6bbc751bee..6a34b13320 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -49,7 +49,6 @@ void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstructio
GetGraph()->GetIntConstant(mirror::Array::DataOffset(access_size).Uint32Value());
HArm64IntermediateAddress* address =
new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc);
- address->SetReferenceTypeInfo(array->GetReferenceTypeInfo());
access->GetBlock()->InsertInstructionBefore(address, access);
access->ReplaceInput(address, 0);
// Both instructions must depend on GC to prevent any instruction that can
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index ac9b24503c..ce737e3f7e 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1917,16 +1917,30 @@ static void GenUnsafeGet(HInvoke* invoke,
Location offset_loc = locations->InAt(2);
CpuRegister offset = offset_loc.AsRegister<CpuRegister>();
Location output_loc = locations->Out();
- CpuRegister output = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister output = output_loc.AsRegister<CpuRegister>();
switch (type) {
case Primitive::kPrimInt:
- case Primitive::kPrimNot:
__ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
- if (type == Primitive::kPrimNot) {
- codegen->MaybeGenerateReadBarrier(invoke, output_loc, output_loc, base_loc, 0U, offset_loc);
+ break;
+
+ case Primitive::kPrimNot: {
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ Location temp = locations->GetTemp(0);
+ codegen->GenerateArrayLoadWithBakerReadBarrier(
+ invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ } else {
+ __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
+ codegen->GenerateReadBarrierSlow(
+ invoke, output_loc, output_loc, base_loc, 0U, offset_loc);
+ }
+ } else {
+ __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
+ __ MaybeUnpoisonHeapReference(output);
}
break;
+ }
case Primitive::kPrimLong:
__ movq(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
@@ -1938,7 +1952,9 @@ static void GenUnsafeGet(HInvoke* invoke,
}
}
-static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
+ HInvoke* invoke,
+ Primitive::Type type) {
bool can_call = kEmitCompilerReadBarrier &&
(invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
@@ -1951,25 +1967,30 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
+ if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ // We need a temporary register for the read barrier marking slow
+ // path in InstructionCodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier.
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeGet(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
}
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 956de2cb8a..2bb769a430 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -16,6 +16,7 @@
#include "base/arena_allocator.h"
#include "builder.h"
+#include "gtest/gtest.h"
#include "licm.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -26,7 +27,7 @@ namespace art {
/**
* Fixture class for the LICM tests.
*/
-class LICMTest : public CommonCompilerTest {
+class LICMTest : public testing::Test {
public:
LICMTest() : pool_(), allocator_(&pool_) {
graph_ = CreateGraph(&allocator_);
@@ -69,16 +70,16 @@ class LICMTest : public CommonCompilerTest {
loop_preheader_->AddInstruction(new (&allocator_) HGoto());
loop_header_->AddInstruction(new (&allocator_) HIf(parameter_));
loop_body_->AddInstruction(new (&allocator_) HGoto());
- return_->AddInstruction(new (&allocator_) HReturnVoid());
exit_->AddInstruction(new (&allocator_) HExit());
}
// Performs LICM optimizations (after proper set up).
void PerformLICM() {
- TransformToSsa(graph_);
+ ASSERT_TRUE(graph_->TryBuildingSsa());
SideEffectsAnalysis side_effects(graph_);
side_effects.Run();
- LICM(graph_, side_effects).Run();
+ LICM licm(graph_, side_effects);
+ licm.Run();
}
// General building fields.
@@ -168,10 +169,10 @@ TEST_F(LICMTest, ArrayHoisting) {
// Populate the loop with instructions: set/get array with different types.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, constant_, Primitive::kPrimByte, 0);
+ parameter_, constant_, Primitive::kPrimLong, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, constant_, constant_, Primitive::kPrimShort, 0);
+ parameter_, constant_, constant_, Primitive::kPrimInt, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
@@ -186,10 +187,10 @@ TEST_F(LICMTest, NoArrayHoisting) {
// Populate the loop with instructions: set/get array with same types.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, constant_, Primitive::kPrimByte, 0);
+ parameter_, constant_, Primitive::kPrimLong, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, get_array, constant_, Primitive::kPrimByte, 0);
+ parameter_, get_array, constant_, Primitive::kPrimLong, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index ed275b1544..a059766e00 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -29,11 +29,12 @@
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "pretty_printer.h"
+#include "ssa_builder.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+#include "gtest/gtest.h"
-class LinearizeTest : public CommonCompilerTest {};
+namespace art {
template <size_t number_of_blocks>
static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[number_of_blocks]) {
@@ -45,7 +46,7 @@ static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[numb
bool graph_built = builder.BuildGraph(*item);
ASSERT_TRUE(graph_built);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
@@ -59,7 +60,7 @@ static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[numb
}
}
-TEST_F(LinearizeTest, CFG1) {
+TEST(LinearizeTest, CFG1) {
// Structure of this graph (+ are back edges)
// Block0
// |
@@ -84,7 +85,7 @@ TEST_F(LinearizeTest, CFG1) {
TestCode(data, blocks);
}
-TEST_F(LinearizeTest, CFG2) {
+TEST(LinearizeTest, CFG2) {
// Structure of this graph (+ are back edges)
// Block0
// |
@@ -109,7 +110,7 @@ TEST_F(LinearizeTest, CFG2) {
TestCode(data, blocks);
}
-TEST_F(LinearizeTest, CFG3) {
+TEST(LinearizeTest, CFG3) {
// Structure of this graph (+ are back edges)
// Block0
// |
@@ -136,7 +137,7 @@ TEST_F(LinearizeTest, CFG3) {
TestCode(data, blocks);
}
-TEST_F(LinearizeTest, CFG4) {
+TEST(LinearizeTest, CFG4) {
/* Structure of this graph (+ are back edges)
// Block0
// |
@@ -166,7 +167,7 @@ TEST_F(LinearizeTest, CFG4) {
TestCode(data, blocks);
}
-TEST_F(LinearizeTest, CFG5) {
+TEST(LinearizeTest, CFG5) {
/* Structure of this graph (+ are back edges)
// Block0
// |
@@ -196,7 +197,7 @@ TEST_F(LinearizeTest, CFG5) {
TestCode(data, blocks);
}
-TEST_F(LinearizeTest, CFG6) {
+TEST(LinearizeTest, CFG6) {
// Block0
// |
// Block1
@@ -222,7 +223,7 @@ TEST_F(LinearizeTest, CFG6) {
TestCode(data, blocks);
}
-TEST_F(LinearizeTest, CFG7) {
+TEST(LinearizeTest, CFG7) {
// Structure of this graph (+ are back edges)
// Block0
// |
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 926f9399a5..7f67560692 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -27,9 +27,9 @@
#include "prepare_for_register_allocation.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+#include "gtest/gtest.h"
-class LiveRangesTest : public CommonCompilerTest {};
+namespace art {
static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) {
HGraph* graph = CreateGraph(allocator);
@@ -39,13 +39,13 @@ static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) {
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
return graph;
}
-TEST_F(LiveRangesTest, CFG1) {
+TEST(LiveRangesTest, CFG1) {
/*
* Test the following snippet:
* return 0;
@@ -83,7 +83,7 @@ TEST_F(LiveRangesTest, CFG1) {
ASSERT_TRUE(range->GetNext() == nullptr);
}
-TEST_F(LiveRangesTest, CFG2) {
+TEST(LiveRangesTest, CFG2) {
/*
* Test the following snippet:
* var a = 0;
@@ -131,7 +131,7 @@ TEST_F(LiveRangesTest, CFG2) {
ASSERT_TRUE(range->GetNext() == nullptr);
}
-TEST_F(LiveRangesTest, CFG3) {
+TEST(LiveRangesTest, CFG3) {
/*
* Test the following snippet:
* var a = 0;
@@ -204,7 +204,7 @@ TEST_F(LiveRangesTest, CFG3) {
ASSERT_TRUE(range->GetNext() == nullptr);
}
-TEST_F(LiveRangesTest, Loop1) {
+TEST(LiveRangesTest, Loop1) {
/*
* Test the following snippet:
* var a = 0;
@@ -284,7 +284,7 @@ TEST_F(LiveRangesTest, Loop1) {
ASSERT_TRUE(range->GetNext() == nullptr);
}
-TEST_F(LiveRangesTest, Loop2) {
+TEST(LiveRangesTest, Loop2) {
/*
* Test the following snippet:
* var a = 0;
@@ -360,7 +360,7 @@ TEST_F(LiveRangesTest, Loop2) {
ASSERT_TRUE(range->GetNext() == nullptr);
}
-TEST_F(LiveRangesTest, CFG4) {
+TEST(LiveRangesTest, CFG4) {
/*
* Test the following snippet:
* var a = 0;
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 7736eedae1..9d7d0b6c67 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -27,9 +27,9 @@
#include "prepare_for_register_allocation.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+#include "gtest/gtest.h"
-class LivenessTest : public CommonCompilerTest {};
+namespace art {
static void DumpBitVector(BitVector* vector,
std::ostream& buffer,
@@ -51,7 +51,7 @@ static void TestCode(const uint16_t* data, const char* expected) {
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
bool graph_built = builder.BuildGraph(*item);
ASSERT_TRUE(graph_built);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
@@ -75,7 +75,7 @@ static void TestCode(const uint16_t* data, const char* expected) {
ASSERT_STREQ(expected, buffer.str().c_str());
}
-TEST_F(LivenessTest, CFG1) {
+TEST(LivenessTest, CFG1) {
const char* expected =
"Block 0\n"
" live in: (0)\n"
@@ -98,7 +98,7 @@ TEST_F(LivenessTest, CFG1) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, CFG2) {
+TEST(LivenessTest, CFG2) {
const char* expected =
"Block 0\n"
" live in: (0)\n"
@@ -120,7 +120,7 @@ TEST_F(LivenessTest, CFG2) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, CFG3) {
+TEST(LivenessTest, CFG3) {
const char* expected =
"Block 0\n" // entry block
" live in: (000)\n"
@@ -149,7 +149,7 @@ TEST_F(LivenessTest, CFG3) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, CFG4) {
+TEST(LivenessTest, CFG4) {
// var a;
// if (0 == 0) {
// a = 5;
@@ -197,7 +197,7 @@ TEST_F(LivenessTest, CFG4) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, CFG5) {
+TEST(LivenessTest, CFG5) {
// var a = 0;
// if (0 == 0) {
// } else {
@@ -242,7 +242,7 @@ TEST_F(LivenessTest, CFG5) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, Loop1) {
+TEST(LivenessTest, Loop1) {
// Simple loop with one preheader and one back edge.
// var a = 0;
// while (a == a) {
@@ -288,7 +288,7 @@ TEST_F(LivenessTest, Loop1) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, Loop3) {
+TEST(LivenessTest, Loop3) {
// Test that the returned value stays live in a preceding loop.
// var a = 0;
// while (a == a) {
@@ -335,7 +335,7 @@ TEST_F(LivenessTest, Loop3) {
}
-TEST_F(LivenessTest, Loop4) {
+TEST(LivenessTest, Loop4) {
// Make sure we support a preheader of a loop not being the first predecessor
// in the predecessor list of the header.
// var a = 0;
@@ -387,7 +387,7 @@ TEST_F(LivenessTest, Loop4) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, Loop5) {
+TEST(LivenessTest, Loop5) {
// Make sure we create a preheader of a loop when a header originally has two
// incoming blocks and one back edge.
// Bitsets are made of:
@@ -443,7 +443,7 @@ TEST_F(LivenessTest, Loop5) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, Loop6) {
+TEST(LivenessTest, Loop6) {
// Bitsets are made of:
// (constant0, constant4, constant5, phi in block 2)
const char* expected =
@@ -494,7 +494,7 @@ TEST_F(LivenessTest, Loop6) {
}
-TEST_F(LivenessTest, Loop7) {
+TEST(LivenessTest, Loop7) {
// Bitsets are made of:
// (constant0, constant4, constant5, phi in block 2, phi in block 6)
const char* expected =
@@ -548,7 +548,7 @@ TEST_F(LivenessTest, Loop7) {
TestCode(data, expected);
}
-TEST_F(LivenessTest, Loop8) {
+TEST(LivenessTest, Loop8) {
// var a = 0;
// while (a == a) {
// a = a + a;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index bb0b545c1e..926bc156cf 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -198,38 +198,10 @@ void HGraph::ComputeDominanceInformation() {
}
}
-BuildSsaResult HGraph::TryBuildingSsa(StackHandleScopeCollection* handles) {
- BuildDominatorTree();
-
- // The SSA builder requires loops to all be natural. Specifically, the dead phi
- // elimination phase checks the consistency of the graph when doing a post-order
- // visit for eliminating dead phis: a dead phi can only have loop header phi
- // users remaining when being visited.
- BuildSsaResult result = AnalyzeNaturalLoops();
- if (result != kBuildSsaSuccess) {
- return result;
- }
-
- // Precompute per-block try membership before entering the SSA builder,
- // which needs the information to build catch block phis from values of
- // locals at throwing instructions inside try blocks.
- ComputeTryBlockInformation();
-
- // Create the inexact Object reference type and store it in the HGraph.
- ScopedObjectAccess soa(Thread::Current());
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- inexact_object_rti_ = ReferenceTypeInfo::Create(
- handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)),
- /* is_exact */ false);
-
- // Tranforms graph to SSA form.
- result = SsaBuilder(this, handles).BuildSsa();
- if (result != kBuildSsaSuccess) {
- return result;
- }
-
- in_ssa_form_ = true;
- return kBuildSsaSuccess;
+void HGraph::TransformToSsa() {
+ DCHECK(!reverse_post_order_.empty());
+ SsaBuilder ssa_builder(this);
+ ssa_builder.BuildSsa();
}
HBasicBlock* HGraph::SplitEdge(HBasicBlock* block, HBasicBlock* successor) {
@@ -438,7 +410,7 @@ void HGraph::SimplifyCFG() {
}
}
-BuildSsaResult HGraph::AnalyzeNaturalLoops() const {
+bool HGraph::AnalyzeNaturalLoops() const {
// Order does not matter.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
@@ -446,16 +418,16 @@ BuildSsaResult HGraph::AnalyzeNaturalLoops() const {
if (block->IsCatchBlock()) {
// TODO: Dealing with exceptional back edges could be tricky because
// they only approximate the real control flow. Bail out for now.
- return kBuildSsaFailThrowCatchLoop;
+ return false;
}
HLoopInformation* info = block->GetLoopInformation();
if (!info->Populate()) {
// Abort if the loop is non natural. We currently bailout in such cases.
- return kBuildSsaFailNonNaturalLoop;
+ return false;
}
}
}
- return kBuildSsaSuccess;
+ return true;
}
void HGraph::InsertConstant(HConstant* constant) {
@@ -474,13 +446,8 @@ HNullConstant* HGraph::GetNullConstant(uint32_t dex_pc) {
// id and/or any invariants the graph is assuming when adding new instructions.
if ((cached_null_constant_ == nullptr) || (cached_null_constant_->GetBlock() == nullptr)) {
cached_null_constant_ = new (arena_) HNullConstant(dex_pc);
- cached_null_constant_->SetReferenceTypeInfo(inexact_object_rti_);
InsertConstant(cached_null_constant_);
}
- if (kIsDebugBuild) {
- ScopedObjectAccess soa(Thread::Current());
- DCHECK(cached_null_constant_->GetReferenceTypeInfo().IsValid());
- }
return cached_null_constant_;
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 55e436f0b7..1f8ef4717c 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -98,13 +98,6 @@ enum IfCondition {
kCondAE, // >=
};
-enum BuildSsaResult {
- kBuildSsaFailNonNaturalLoop,
- kBuildSsaFailThrowCatchLoop,
- kBuildSsaFailAmbiguousArrayGet,
- kBuildSsaSuccess,
-};
-
class HInstructionList : public ValueObject {
public:
HInstructionList() : first_instruction_(nullptr), last_instruction_(nullptr) {}
@@ -150,122 +143,6 @@ class HInstructionList : public ValueObject {
DISALLOW_COPY_AND_ASSIGN(HInstructionList);
};
-class ReferenceTypeInfo : ValueObject {
- public:
- typedef Handle<mirror::Class> TypeHandle;
-
- static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) {
- // The constructor will check that the type_handle is valid.
- return ReferenceTypeInfo(type_handle, is_exact);
- }
-
- static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); }
-
- static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) {
- return handle.GetReference() != nullptr;
- }
-
- bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) {
- return IsValidHandle(type_handle_);
- }
-
- bool IsExact() const { return is_exact_; }
-
- bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- return GetTypeHandle()->IsObjectClass();
- }
-
- bool IsStringClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- return GetTypeHandle()->IsStringClass();
- }
-
- bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- return IsArrayClass() && GetTypeHandle()->GetComponentType()->IsObjectClass();
- }
-
- bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- return GetTypeHandle()->IsInterface();
- }
-
- bool IsArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- return GetTypeHandle()->IsArrayClass();
- }
-
- bool IsPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- return GetTypeHandle()->IsPrimitiveArray();
- }
-
- bool IsNonPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- return GetTypeHandle()->IsArrayClass() && !GetTypeHandle()->IsPrimitiveArray();
- }
-
- bool CanArrayHold(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- if (!IsExact()) return false;
- if (!IsArrayClass()) return false;
- return GetTypeHandle()->GetComponentType()->IsAssignableFrom(rti.GetTypeHandle().Get());
- }
-
- bool CanArrayHoldValuesOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- if (!IsExact()) return false;
- if (!IsArrayClass()) return false;
- if (!rti.IsArrayClass()) return false;
- return GetTypeHandle()->GetComponentType()->IsAssignableFrom(
- rti.GetTypeHandle()->GetComponentType());
- }
-
- Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
-
- bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- DCHECK(rti.IsValid());
- return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
- }
-
- bool IsStrictSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(IsValid());
- DCHECK(rti.IsValid());
- return GetTypeHandle().Get() != rti.GetTypeHandle().Get() &&
- GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
- }
-
- // Returns true if the type information provide the same amount of details.
- // Note that it does not mean that the instructions have the same actual type
- // (because the type can be the result of a merge).
- bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) {
- if (!IsValid() && !rti.IsValid()) {
- // Invalid types are equal.
- return true;
- }
- if (!IsValid() || !rti.IsValid()) {
- // One is valid, the other not.
- return false;
- }
- return IsExact() == rti.IsExact()
- && GetTypeHandle().Get() == rti.GetTypeHandle().Get();
- }
-
- private:
- ReferenceTypeInfo();
- ReferenceTypeInfo(TypeHandle type_handle, bool is_exact);
-
- // The class of the object.
- TypeHandle type_handle_;
- // Whether or not the type is exact or a superclass of the actual type.
- // Whether or not we have any information about this type.
- bool is_exact_;
-};
-
-std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs);
-
// Control-flow graph of a method. Contains a list of basic blocks.
class HGraph : public ArenaObject<kArenaAllocGraph> {
public:
@@ -302,8 +179,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
cached_float_constants_(std::less<int32_t>(), arena->Adapter(kArenaAllocConstantsMap)),
cached_long_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)),
cached_double_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)),
- cached_current_method_(nullptr),
- inexact_object_rti_(ReferenceTypeInfo::CreateInvalid()) {
+ cached_current_method_(nullptr) {
blocks_.reserve(kDefaultNumberOfBlocks);
}
@@ -321,23 +197,36 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
void AddBlock(HBasicBlock* block);
- // Try building the SSA form of this graph, with dominance computation and
- // loop recognition. Returns a code specifying that it was successful or the
- // reason for failure.
- BuildSsaResult TryBuildingSsa(StackHandleScopeCollection* handles);
+ // Try building the SSA form of this graph, with dominance computation and loop
+ // recognition. Returns whether it was successful in doing all these steps.
+ bool TryBuildingSsa() {
+ BuildDominatorTree();
+ // The SSA builder requires loops to all be natural. Specifically, the dead phi
+ // elimination phase checks the consistency of the graph when doing a post-order
+ // visit for eliminating dead phis: a dead phi can only have loop header phi
+ // users remaining when being visited.
+ if (!AnalyzeNaturalLoops()) return false;
+ // Precompute per-block try membership before entering the SSA builder,
+ // which needs the information to build catch block phis from values of
+ // locals at throwing instructions inside try blocks.
+ ComputeTryBlockInformation();
+ TransformToSsa();
+ in_ssa_form_ = true;
+ return true;
+ }
void ComputeDominanceInformation();
void ClearDominanceInformation();
void BuildDominatorTree();
+ void TransformToSsa();
void SimplifyCFG();
void SimplifyCatchBlocks();
- // Analyze all natural loops in this graph. Returns a code specifying that it
- // was successful or the reason for failure. The method will fail if a loop
- // is not natural, that is the header does not dominate a back edge, or if it
- // is a throw-catch loop, i.e. the header is a catch block.
- BuildSsaResult AnalyzeNaturalLoops() const;
+ // Analyze all natural loops in this graph. Returns false if one
+ // loop is not natural, that is the header does not dominate the
+ // back edge.
+ bool AnalyzeNaturalLoops() const;
// Iterate over blocks to compute try block membership. Needs reverse post
// order and loop information.
@@ -598,10 +487,6 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
// (such as when the superclass could not be found).
ArtMethod* art_method_;
- // Keep the RTI of inexact Object to avoid having to pass stack handle
- // collection pointer to passes which may create NullConstant.
- ReferenceTypeInfo inexact_object_rti_;
-
friend class SsaBuilder; // For caching constants.
friend class SsaLivenessAnalysis; // For the linear order.
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
@@ -1789,6 +1674,122 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> {
DISALLOW_COPY_AND_ASSIGN(HEnvironment);
};
+class ReferenceTypeInfo : ValueObject {
+ public:
+ typedef Handle<mirror::Class> TypeHandle;
+
+ static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) {
+ // The constructor will check that the type_handle is valid.
+ return ReferenceTypeInfo(type_handle, is_exact);
+ }
+
+ static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); }
+
+ static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) {
+ return handle.GetReference() != nullptr;
+ }
+
+ bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return IsValidHandle(type_handle_);
+ }
+
+ bool IsExact() const { return is_exact_; }
+
+ bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsObjectClass();
+ }
+
+ bool IsStringClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsStringClass();
+ }
+
+ bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return IsArrayClass() && GetTypeHandle()->GetComponentType()->IsObjectClass();
+ }
+
+ bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsInterface();
+ }
+
+ bool IsArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsArrayClass();
+ }
+
+ bool IsPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsPrimitiveArray();
+ }
+
+ bool IsNonPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ return GetTypeHandle()->IsArrayClass() && !GetTypeHandle()->IsPrimitiveArray();
+ }
+
+ bool CanArrayHold(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ if (!IsExact()) return false;
+ if (!IsArrayClass()) return false;
+ return GetTypeHandle()->GetComponentType()->IsAssignableFrom(rti.GetTypeHandle().Get());
+ }
+
+ bool CanArrayHoldValuesOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ if (!IsExact()) return false;
+ if (!IsArrayClass()) return false;
+ if (!rti.IsArrayClass()) return false;
+ return GetTypeHandle()->GetComponentType()->IsAssignableFrom(
+ rti.GetTypeHandle()->GetComponentType());
+ }
+
+ Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
+
+ bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ DCHECK(rti.IsValid());
+ return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
+ }
+
+ bool IsStrictSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsValid());
+ DCHECK(rti.IsValid());
+ return GetTypeHandle().Get() != rti.GetTypeHandle().Get() &&
+ GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
+ }
+
+ // Returns true if the type information provide the same amount of details.
+ // Note that it does not mean that the instructions have the same actual type
+ // (because the type can be the result of a merge).
+ bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (!IsValid() && !rti.IsValid()) {
+ // Invalid types are equal.
+ return true;
+ }
+ if (!IsValid() || !rti.IsValid()) {
+ // One is valid, the other not.
+ return false;
+ }
+ return IsExact() == rti.IsExact()
+ && GetTypeHandle().Get() == rti.GetTypeHandle().Get();
+ }
+
+ private:
+ ReferenceTypeInfo();
+ ReferenceTypeInfo(TypeHandle type_handle, bool is_exact);
+
+ // The class of the object.
+ TypeHandle type_handle_;
+ // Whether or not the type is exact or a superclass of the actual type.
+ // Whether or not we have any information about this type.
+ bool is_exact_;
+};
+
+std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs);
+
class HInstruction : public ArenaObject<kArenaAllocInstruction> {
public:
HInstruction(SideEffects side_effects, uint32_t dex_pc)
@@ -4416,16 +4417,7 @@ class HPhi : public HInstruction {
void RemoveInputAt(size_t index);
Primitive::Type GetType() const OVERRIDE { return type_; }
- void SetType(Primitive::Type new_type) {
- // Make sure that only valid type changes occur. The following are allowed:
- // (1) int -> float/ref (primitive type propagation),
- // (2) long -> double (primitive type propagation).
- DCHECK(type_ == new_type ||
- (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
- (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
- (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
- type_ = new_type;
- }
+ void SetType(Primitive::Type type) { type_ = type; }
bool CanBeNull() const OVERRIDE { return can_be_null_; }
void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
@@ -4665,21 +4657,7 @@ class HArrayGet : public HExpression<2> {
return false;
}
- bool IsEquivalentOf(HArrayGet* other) const {
- bool result = (GetDexPc() == other->GetDexPc());
- if (kIsDebugBuild && result) {
- DCHECK_EQ(GetBlock(), other->GetBlock());
- DCHECK_EQ(GetArray(), other->GetArray());
- DCHECK_EQ(GetIndex(), other->GetIndex());
- if (Primitive::IsIntOrLongType(GetType())) {
- DCHECK(Primitive::IsFloatingPointType(other->GetType()));
- } else {
- DCHECK(Primitive::IsFloatingPointType(GetType()));
- DCHECK(Primitive::IsIntOrLongType(other->GetType()));
- }
- }
- return result;
- }
+ void SetType(Primitive::Type type) { type_ = type; }
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index ba435180e5..831b626c4f 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -501,8 +501,11 @@ static void RunOptimizations(HGraph* graph,
CompilerDriver* driver,
OptimizingCompilerStats* stats,
const DexCompilationUnit& dex_compilation_unit,
- PassObserver* pass_observer,
- StackHandleScopeCollection* handles) {
+ PassObserver* pass_observer) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScopeCollection handles(soa.Self());
+ ScopedThreadSuspension sts(soa.Self(), kNative);
+
ArenaAllocator* arena = graph->GetArena();
HDeadCodeElimination* dce1 = new (arena) HDeadCodeElimination(
graph, stats, HDeadCodeElimination::kInitialDeadCodeEliminationPassName);
@@ -519,23 +522,29 @@ static void RunOptimizations(HGraph* graph,
LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects);
HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects, induction);
+ ReferenceTypePropagation* type_propagation =
+ new (arena) ReferenceTypePropagation(graph, &handles);
HSharpening* sharpening = new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver);
InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier(
- graph, stats, "instruction_simplifier_after_bce");
+ graph, stats, "instruction_simplifier_after_types");
InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
+ graph, stats, "instruction_simplifier_after_bce");
+ InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_before_codegen");
IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver);
HOptimization* optimizations1[] = {
intrinsics,
- sharpening,
fold1,
simplify1,
+ type_propagation,
+ sharpening,
dce1,
+ simplify2
};
RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
- MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
+ MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, &handles);
HOptimization* optimizations2[] = {
// BooleanSimplifier depends on the InstructionSimplifier removing
@@ -548,13 +557,13 @@ static void RunOptimizations(HGraph* graph,
induction,
bce,
fold3, // evaluates code generated by dynamic bce
- simplify2,
+ simplify3,
lse,
dce2,
// The codegen has a few assumptions that only the instruction simplifier
// can satisfy. For example, the code generator does not expect to see a
// HTypeConversion from a type to the same type.
- simplify3,
+ simplify4,
};
RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
@@ -759,29 +768,14 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
}
VLOG(compiler) << "Optimizing " << pass_observer.GetMethodName();
-
if (run_optimizations_) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
- ScopedThreadSuspension sts(soa.Self(), kNative);
-
{
PassScope scope(SsaBuilder::kSsaBuilderPassName, &pass_observer);
- BuildSsaResult result = graph->TryBuildingSsa(&handles);
- if (result != kBuildSsaSuccess) {
- switch (result) {
- case kBuildSsaFailNonNaturalLoop:
- MaybeRecordStat(MethodCompilationStat::kNotCompiledNonNaturalLoop);
- break;
- case kBuildSsaFailThrowCatchLoop:
- MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop);
- break;
- case kBuildSsaFailAmbiguousArrayGet:
- MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayGet);
- break;
- case kBuildSsaSuccess:
- UNREACHABLE();
- }
+ if (!graph->TryBuildingSsa()) {
+ // We could not transform the graph to SSA, bailout.
+ LOG(INFO) << "Skipping compilation of " << pass_observer.GetMethodName()
+ << ": it contains a non natural loop";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledCannotBuildSSA);
pass_observer.SetGraphInBadState();
return nullptr;
}
@@ -792,8 +786,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
compiler_driver,
compilation_stats_.get(),
dex_compilation_unit,
- &pass_observer,
- &handles);
+ &pass_observer);
codegen->CompileOptimized(code_allocator);
} else {
codegen->CompileBaseline(code_allocator);
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 4713514bb2..6296eedfb0 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -38,9 +38,7 @@ enum MethodCompilationStat {
kRemovedDeadInstruction,
kRemovedNullCheck,
kNotCompiledBranchOutsideMethodCode,
- kNotCompiledNonNaturalLoop,
- kNotCompiledThrowCatchLoop,
- kNotCompiledAmbiguousArrayGet,
+ kNotCompiledCannotBuildSSA,
kNotCompiledHugeMethod,
kNotCompiledLargeMethodNoBranches,
kNotCompiledMalformedOpcode,
@@ -106,9 +104,7 @@ class OptimizingCompilerStats {
case kRemovedDeadInstruction: name = "RemovedDeadInstruction"; break;
case kRemovedNullCheck: name = "RemovedNullCheck"; break;
case kNotCompiledBranchOutsideMethodCode: name = "NotCompiledBranchOutsideMethodCode"; break;
- case kNotCompiledNonNaturalLoop : name = "NotCompiledNonNaturalLoop"; break;
- case kNotCompiledThrowCatchLoop : name = "NotCompiledThrowCatchLoop"; break;
- case kNotCompiledAmbiguousArrayGet : name = "NotCompiledAmbiguousArrayGet"; break;
+ case kNotCompiledCannotBuildSSA : name = "NotCompiledCannotBuildSSA"; break;
case kNotCompiledHugeMethod : name = "NotCompiledHugeMethod"; break;
case kNotCompiledLargeMethodNoBranches : name = "NotCompiledLargeMethodNoBranches"; break;
case kNotCompiledMalformedOpcode : name = "NotCompiledMalformedOpcode"; break;
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index af3a005304..350f0b14ab 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -19,13 +19,9 @@
#include "nodes.h"
#include "builder.h"
-#include "common_compiler_test.h"
#include "compiler/dex/pass_manager.h"
#include "dex_file.h"
#include "dex_instruction.h"
-#include "handle_scope-inl.h"
-#include "scoped_thread_state_change.h"
-#include "ssa_builder.h"
#include "ssa_liveness_analysis.h"
#include "gtest/gtest.h"
@@ -46,6 +42,7 @@ namespace art {
#define FIVE_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(5, __VA_ARGS__)
#define SIX_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(6, __VA_ARGS__)
+
LiveInterval* BuildInterval(const size_t ranges[][2],
size_t number_of_ranges,
ArenaAllocator* allocator,
@@ -114,12 +111,6 @@ inline bool IsRemoved(HInstruction* instruction) {
return instruction->GetBlock() == nullptr;
}
-inline void TransformToSsa(HGraph* graph) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
- EXPECT_EQ(graph->TryBuildingSsa(&handles), kBuildSsaSuccess);
-}
-
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index a385448104..b383f1e1ad 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -15,7 +15,6 @@
*/
#include "pc_relative_fixups_x86.h"
-#include "code_generator_x86.h"
namespace art {
namespace x86 {
@@ -80,10 +79,6 @@ class PCRelativeHandlerVisitor : public HGraphVisitor {
}
void VisitPackedSwitch(HPackedSwitch* switch_insn) OVERRIDE {
- if (switch_insn->GetNumEntries() <=
- InstructionCodeGeneratorX86::kPackedSwitchJumpTableThreshold) {
- return;
- }
// We need to replace the HPackedSwitch with a HX86PackedSwitch in order to
// address the constant area.
InitializePCRelativeBasePointer();
diff --git a/compiler/optimizing/primitive_type_propagation.cc b/compiler/optimizing/primitive_type_propagation.cc
new file mode 100644
index 0000000000..bde54ee977
--- /dev/null
+++ b/compiler/optimizing/primitive_type_propagation.cc
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#include "primitive_type_propagation.h"
+
+#include "nodes.h"
+#include "ssa_builder.h"
+
+namespace art {
+
+static Primitive::Type MergeTypes(Primitive::Type existing, Primitive::Type new_type) {
+ // We trust the verifier has already done the necessary checking.
+ switch (existing) {
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ case Primitive::kPrimNot:
+ return existing;
+ default:
+ // Phis are initialized with a void type, so if we are asked
+ // to merge with a void type, we should use the existing one.
+ return new_type == Primitive::kPrimVoid
+ ? existing
+ : HPhi::ToPhiType(new_type);
+ }
+}
+
+// Re-compute and update the type of the instruction. Returns
+// whether or not the type was changed.
+bool PrimitiveTypePropagation::UpdateType(HPhi* phi) {
+ DCHECK(phi->IsLive());
+ Primitive::Type existing = phi->GetType();
+
+ Primitive::Type new_type = existing;
+ for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+ Primitive::Type input_type = phi->InputAt(i)->GetType();
+ new_type = MergeTypes(new_type, input_type);
+ }
+ phi->SetType(new_type);
+
+ if (new_type == Primitive::kPrimDouble
+ || new_type == Primitive::kPrimFloat
+ || new_type == Primitive::kPrimNot) {
+ // If the phi is of floating point type, we need to update its inputs to that
+ // type. For inputs that are phis, we need to recompute their types.
+ for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+ HInstruction* input = phi->InputAt(i);
+ if (input->GetType() != new_type) {
+ HInstruction* equivalent = (new_type == Primitive::kPrimNot)
+ ? SsaBuilder::GetReferenceTypeEquivalent(input)
+ : SsaBuilder::GetFloatOrDoubleEquivalent(phi, input, new_type);
+ phi->ReplaceInput(equivalent, i);
+ if (equivalent->IsPhi()) {
+ AddToWorklist(equivalent->AsPhi());
+ } else if (equivalent == input) {
+ // The input has changed its type. It can be an input of other phis,
+ // so we need to put phi users in the work list.
+ AddDependentInstructionsToWorklist(equivalent);
+ }
+ }
+ }
+ }
+
+ return existing != new_type;
+}
+
+void PrimitiveTypePropagation::Run() {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+ ProcessWorklist();
+}
+
+void PrimitiveTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+ if (block->IsLoopHeader()) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ if (phi->IsLive()) {
+ AddToWorklist(phi);
+ }
+ }
+ } else {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ // Eagerly compute the type of the phi, for quicker convergence. Note
+ // that we don't need to add users to the worklist because we are
+ // doing a reverse post-order visit, therefore either the phi users are
+ // non-loop phi and will be visited later in the visit, or are loop-phis,
+ // and they are already in the work list.
+ HPhi* phi = it.Current()->AsPhi();
+ if (phi->IsLive()) {
+ UpdateType(phi);
+ }
+ }
+ }
+}
+
+void PrimitiveTypePropagation::ProcessWorklist() {
+ while (!worklist_.empty()) {
+ HPhi* instruction = worklist_.back();
+ worklist_.pop_back();
+ if (UpdateType(instruction)) {
+ AddDependentInstructionsToWorklist(instruction);
+ }
+ }
+}
+
+void PrimitiveTypePropagation::AddToWorklist(HPhi* instruction) {
+ DCHECK(instruction->IsLive());
+ worklist_.push_back(instruction);
+}
+
+void PrimitiveTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
+ for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->GetUser()->AsPhi();
+ if (phi != nullptr && phi->IsLive() && phi->GetType() != instruction->GetType()) {
+ AddToWorklist(phi);
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/primitive_type_propagation.h b/compiler/optimizing/primitive_type_propagation.h
new file mode 100644
index 0000000000..212fcfc69f
--- /dev/null
+++ b/compiler/optimizing/primitive_type_propagation.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
+
+#include "base/arena_containers.h"
+#include "nodes.h"
+
+namespace art {
+
+// Compute and propagate primitive types of phis in the graph.
+class PrimitiveTypePropagation : public ValueObject {
+ public:
+ explicit PrimitiveTypePropagation(HGraph* graph)
+ : graph_(graph), worklist_(graph->GetArena()->Adapter(kArenaAllocPrimitiveTypePropagation)) {
+ worklist_.reserve(kDefaultWorklistSize);
+ }
+
+ void Run();
+
+ private:
+ void VisitBasicBlock(HBasicBlock* block);
+ void ProcessWorklist();
+ void AddToWorklist(HPhi* phi);
+ void AddDependentInstructionsToWorklist(HInstruction* instruction);
+ bool UpdateType(HPhi* phi);
+
+ HGraph* const graph_;
+ ArenaVector<HPhi*> worklist_;
+
+ static constexpr size_t kDefaultWorklistSize = 8;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveTypePropagation);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 94a297c9e6..fea903d9cf 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -40,6 +40,7 @@ class RTPVisitor : public HGraphDelegateVisitor {
throwable_class_handle_(throwable_class_handle),
worklist_(worklist) {}
+ void VisitNullConstant(HNullConstant* null_constant) OVERRIDE;
void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
@@ -70,6 +71,8 @@ class RTPVisitor : public HGraphDelegateVisitor {
ReferenceTypeInfo::TypeHandle string_class_handle_;
ReferenceTypeInfo::TypeHandle throwable_class_handle_;
ArenaVector<HInstruction*>* worklist_;
+
+ static constexpr size_t kDefaultWorklistSize = 8;
};
ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
@@ -168,13 +171,9 @@ static void ForEachUntypedInstruction(HGraph* graph, Functor fn) {
ScopedObjectAccess soa(Thread::Current());
for (HReversePostOrderIterator block_it(*graph); !block_it.Done(); block_it.Advance()) {
for (HInstructionIterator it(block_it.Current()->GetPhis()); !it.Done(); it.Advance()) {
- HPhi* phi = it.Current()->AsPhi();
- // Note that the graph may contain dead phis when run from the SsaBuilder.
- // Skip those as they might have a type conflict and will be removed anyway.
- if (phi->IsLive() &&
- phi->GetType() == Primitive::kPrimNot &&
- !phi->GetReferenceTypeInfo().IsValid()) {
- fn(phi);
+ HInstruction* instr = it.Current();
+ if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) {
+ fn(instr);
}
}
for (HInstructionIterator it(block_it.Current()->GetInstructions()); !it.Done(); it.Advance()) {
@@ -377,75 +376,6 @@ void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) {
}
}
-// Returns true if one of the patterns below has been recognized. If so, the
-// InstanceOf instruction together with the true branch of `ifInstruction` will
-// be returned using the out parameters.
-// Recognized patterns:
-// (1) patterns equivalent to `if (obj instanceof X)`
-// (a) InstanceOf -> Equal to 1 -> If
-// (b) InstanceOf -> NotEqual to 0 -> If
-// (c) InstanceOf -> If
-// (2) patterns equivalent to `if (!(obj instanceof X))`
-// (a) InstanceOf -> Equal to 0 -> If
-// (b) InstanceOf -> NotEqual to 1 -> If
-// (c) InstanceOf -> BooleanNot -> If
-static bool MatchIfInstanceOf(HIf* ifInstruction,
- /* out */ HInstanceOf** instanceOf,
- /* out */ HBasicBlock** trueBranch) {
- HInstruction* input = ifInstruction->InputAt(0);
-
- if (input->IsEqual()) {
- HInstruction* rhs = input->AsEqual()->GetConstantRight();
- if (rhs != nullptr) {
- HInstruction* lhs = input->AsEqual()->GetLeastConstantLeft();
- if (lhs->IsInstanceOf() && rhs->IsIntConstant()) {
- if (rhs->AsIntConstant()->IsOne()) {
- // Case (1a)
- *trueBranch = ifInstruction->IfTrueSuccessor();
- } else {
- // Case (2a)
- DCHECK(rhs->AsIntConstant()->IsZero());
- *trueBranch = ifInstruction->IfFalseSuccessor();
- }
- *instanceOf = lhs->AsInstanceOf();
- return true;
- }
- }
- } else if (input->IsNotEqual()) {
- HInstruction* rhs = input->AsNotEqual()->GetConstantRight();
- if (rhs != nullptr) {
- HInstruction* lhs = input->AsNotEqual()->GetLeastConstantLeft();
- if (lhs->IsInstanceOf() && rhs->IsIntConstant()) {
- if (rhs->AsIntConstant()->IsZero()) {
- // Case (1b)
- *trueBranch = ifInstruction->IfTrueSuccessor();
- } else {
- // Case (2b)
- DCHECK(rhs->AsIntConstant()->IsOne());
- *trueBranch = ifInstruction->IfFalseSuccessor();
- }
- *instanceOf = lhs->AsInstanceOf();
- return true;
- }
- }
- } else if (input->IsInstanceOf()) {
- // Case (1c)
- *instanceOf = input->AsInstanceOf();
- *trueBranch = ifInstruction->IfTrueSuccessor();
- return true;
- } else if (input->IsBooleanNot()) {
- HInstruction* not_input = input->InputAt(0);
- if (not_input->IsInstanceOf()) {
- // Case (2c)
- *instanceOf = not_input->AsInstanceOf();
- *trueBranch = ifInstruction->IfFalseSuccessor();
- return true;
- }
- }
-
- return false;
-}
-
// Detects if `block` is the True block for the pattern
// `if (x instanceof ClassX) { }`
// If that's the case insert an HBoundType instruction to bound the type of `x`
@@ -455,11 +385,22 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) {
if (ifInstruction == nullptr) {
return;
}
-
- // Try to recognize common `if (instanceof)` and `if (!instanceof)` patterns.
- HInstanceOf* instanceOf = nullptr;
+ HInstruction* ifInput = ifInstruction->InputAt(0);
+ HInstruction* instanceOf = nullptr;
HBasicBlock* instanceOfTrueBlock = nullptr;
- if (!MatchIfInstanceOf(ifInstruction, &instanceOf, &instanceOfTrueBlock)) {
+
+ // The instruction simplifier has transformed:
+ // - `if (a instanceof A)` into an HIf with an HInstanceOf input
+ // - `if (!(a instanceof A)` into an HIf with an HBooleanNot input (which in turn
+ // has an HInstanceOf input)
+ // So we should not see the usual HEqual here.
+ if (ifInput->IsInstanceOf()) {
+ instanceOf = ifInput;
+ instanceOfTrueBlock = ifInstruction->IfTrueSuccessor();
+ } else if (ifInput->IsBooleanNot() && ifInput->InputAt(0)->IsInstanceOf()) {
+ instanceOf = ifInput->InputAt(0);
+ instanceOfTrueBlock = ifInstruction->IfFalseSuccessor();
+ } else {
return;
}
@@ -564,6 +505,13 @@ void RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr,
SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
}
+void RTPVisitor::VisitNullConstant(HNullConstant* instr) {
+ // TODO: The null constant could be bound contextually (e.g. based on return statements)
+ // to a more precise type.
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
+}
+
void RTPVisitor::VisitNewInstance(HNewInstance* instr) {
UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
}
@@ -575,11 +523,7 @@ void RTPVisitor::VisitNewArray(HNewArray* instr) {
static mirror::Class* GetClassFromDexCache(Thread* self, const DexFile& dex_file, uint16_t type_idx)
SHARED_REQUIRES(Locks::mutator_lock_) {
mirror::DexCache* dex_cache =
- Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file, /* allow_failure */ true);
- if (dex_cache == nullptr) {
- // Dex cache could not be found. This should only happen during gtests.
- return nullptr;
- }
+ Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file, false);
// Get type from dex cache assuming it was populated by the verifier.
return dex_cache->GetResolvedType(type_idx);
}
@@ -596,24 +540,17 @@ void RTPVisitor::VisitParameterValue(HParameterValue* instr) {
void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr,
const FieldInfo& info) {
- if (instr->GetType() != Primitive::kPrimNot) {
+ // The field index is unknown only during tests.
+ if (instr->GetType() != Primitive::kPrimNot || info.GetFieldIndex() == kUnknownFieldIndex) {
return;
}
ScopedObjectAccess soa(Thread::Current());
- mirror::Class* klass = nullptr;
-
- // The field index is unknown only during tests.
- if (info.GetFieldIndex() != kUnknownFieldIndex) {
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
- ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), info.GetDexCache().Get());
- // TODO: There are certain cases where we can't resolve the field.
- // b/21914925 is open to keep track of a repro case for this issue.
- if (field != nullptr) {
- klass = field->GetType<false>();
- }
- }
-
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), info.GetDexCache().Get());
+ // TODO: There are certain cases where we can't resolve the field.
+ // b/21914925 is open to keep track of a repro case for this issue.
+ mirror::Class* klass = (field == nullptr) ? nullptr : field->GetType<false>();
SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
@@ -729,7 +666,7 @@ void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
}
void ReferenceTypePropagation::VisitPhi(HPhi* phi) {
- if (phi->IsDead() || phi->GetType() != Primitive::kPrimNot) {
+ if (phi->GetType() != Primitive::kPrimNot) {
return;
}
@@ -887,8 +824,6 @@ void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) {
// NullConstant inputs are ignored during merging as they do not provide any useful information.
// If all the inputs are NullConstants then the type of the phi will be set to Object.
void ReferenceTypePropagation::UpdatePhi(HPhi* instr) {
- DCHECK(instr->IsLive());
-
size_t input_count = instr->InputCount();
size_t first_input_index_not_null = 0;
while (first_input_index_not_null < input_count &&
@@ -933,7 +868,7 @@ void ReferenceTypePropagation::UpdatePhi(HPhi* instr) {
// Re-computes and updates the nullability of the instruction. Returns whether or
// not the nullability was changed.
bool ReferenceTypePropagation::UpdateNullability(HInstruction* instr) {
- DCHECK((instr->IsPhi() && instr->AsPhi()->IsLive())
+ DCHECK(instr->IsPhi()
|| instr->IsBoundType()
|| instr->IsNullCheck()
|| instr->IsArrayGet());
@@ -981,7 +916,7 @@ void ReferenceTypePropagation::AddToWorklist(HInstruction* instruction) {
void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
HInstruction* user = it.Current()->GetUser();
- if ((user->IsPhi() && user->AsPhi()->IsLive())
+ if (user->IsPhi()
|| user->IsBoundType()
|| user->IsNullCheck()
|| (user->IsArrayGet() && (user->GetType() == Primitive::kPrimNot))) {
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index b900ed0966..080f970756 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -28,13 +28,13 @@
#include "ssa_liveness_analysis.h"
#include "ssa_phi_elimination.h"
+#include "gtest/gtest.h"
+
namespace art {
// Note: the register allocator tests rely on the fact that constants have live
// intervals and registers get allocated to them.
-class RegisterAllocatorTest : public CommonCompilerTest {};
-
static bool Check(const uint16_t* data) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
@@ -42,7 +42,7 @@ static bool Check(const uint16_t* data) {
HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
builder.BuildGraph(*item);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions());
@@ -57,7 +57,7 @@ static bool Check(const uint16_t* data) {
* Unit testing of RegisterAllocator::ValidateIntervals. Register allocator
* tests are based on this validation method.
*/
-TEST_F(RegisterAllocatorTest, ValidateIntervals) {
+TEST(RegisterAllocatorTest, ValidateIntervals) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = CreateGraph(&allocator);
@@ -146,7 +146,7 @@ TEST_F(RegisterAllocatorTest, ValidateIntervals) {
}
}
-TEST_F(RegisterAllocatorTest, CFG1) {
+TEST(RegisterAllocatorTest, CFG1) {
/*
* Test the following snippet:
* return 0;
@@ -166,7 +166,7 @@ TEST_F(RegisterAllocatorTest, CFG1) {
ASSERT_TRUE(Check(data));
}
-TEST_F(RegisterAllocatorTest, Loop1) {
+TEST(RegisterAllocatorTest, Loop1) {
/*
* Test the following snippet:
* int a = 0;
@@ -205,7 +205,7 @@ TEST_F(RegisterAllocatorTest, Loop1) {
ASSERT_TRUE(Check(data));
}
-TEST_F(RegisterAllocatorTest, Loop2) {
+TEST(RegisterAllocatorTest, Loop2) {
/*
* Test the following snippet:
* int a = 0;
@@ -259,11 +259,11 @@ static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) {
HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
builder.BuildGraph(*item);
- TransformToSsa(graph);
+ graph->TryBuildingSsa();
return graph;
}
-TEST_F(RegisterAllocatorTest, Loop3) {
+TEST(RegisterAllocatorTest, Loop3) {
/*
* Test the following snippet:
* int a = 0
@@ -326,7 +326,7 @@ TEST_F(RegisterAllocatorTest, Loop3) {
ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister());
}
-TEST_F(RegisterAllocatorTest, FirstRegisterUse) {
+TEST(RegisterAllocatorTest, FirstRegisterUse) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::XOR_INT_LIT8 | 1 << 8, 1 << 8,
@@ -366,7 +366,7 @@ TEST_F(RegisterAllocatorTest, FirstRegisterUse) {
ASSERT_EQ(new_interval->FirstRegisterUse(), last_xor->GetLifetimePosition());
}
-TEST_F(RegisterAllocatorTest, DeadPhi) {
+TEST(RegisterAllocatorTest, DeadPhi) {
/* Test for a dead loop phi taking as back-edge input a phi that also has
* this loop phi as input. Walking backwards in SsaDeadPhiElimination
* does not solve the problem because the loop phi will be visited last.
@@ -407,7 +407,7 @@ TEST_F(RegisterAllocatorTest, DeadPhi) {
* that share the same register. It should split the interval it is currently
* allocating for at the minimum lifetime position between the two inactive intervals.
*/
-TEST_F(RegisterAllocatorTest, FreeUntil) {
+TEST(RegisterAllocatorTest, FreeUntil) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::RETURN);
@@ -539,7 +539,7 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator,
return graph;
}
-TEST_F(RegisterAllocatorTest, PhiHint) {
+TEST(RegisterAllocatorTest, PhiHint) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HPhi *phi;
@@ -658,7 +658,7 @@ static HGraph* BuildFieldReturn(ArenaAllocator* allocator,
return graph;
}
-TEST_F(RegisterAllocatorTest, ExpectedInRegisterHint) {
+TEST(RegisterAllocatorTest, ExpectedInRegisterHint) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HInstruction *field, *ret;
@@ -726,7 +726,7 @@ static HGraph* BuildTwoSubs(ArenaAllocator* allocator,
return graph;
}
-TEST_F(RegisterAllocatorTest, SameAsFirstInputHint) {
+TEST(RegisterAllocatorTest, SameAsFirstInputHint) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HInstruction *first_sub, *second_sub;
@@ -795,7 +795,7 @@ static HGraph* BuildDiv(ArenaAllocator* allocator,
return graph;
}
-TEST_F(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) {
+TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
HInstruction *div;
@@ -819,7 +819,7 @@ TEST_F(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) {
// Test a bug in the register allocator, where allocating a blocked
// register would lead to spilling an inactive interval at the wrong
// position.
-TEST_F(RegisterAllocatorTest, SpillInactive) {
+TEST(RegisterAllocatorTest, SpillInactive) {
ArenaPool pool;
// Create a synthesized graph to please the register_allocator and
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 9e869e18e9..9e6cfbe653 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -17,11 +17,214 @@
#include "ssa_builder.h"
#include "nodes.h"
-#include "reference_type_propagation.h"
+#include "primitive_type_propagation.h"
#include "ssa_phi_elimination.h"
namespace art {
+// Returns whether this is a loop header phi which was eagerly created but later
+// found inconsistent due to the vreg being undefined in one of its predecessors.
+// Such phi is marked dead and should be ignored until its removal in SsaPhiElimination.
+static bool IsUndefinedLoopHeaderPhi(HPhi* phi) {
+ return phi->IsLoopHeaderPhi() && phi->InputCount() != phi->GetBlock()->GetPredecessors().size();
+}
+
+/**
+ * A debuggable application may require to reviving phis, to ensure their
+ * associated DEX register is available to a debugger. This class implements
+ * the logic for statement (c) of the SsaBuilder (see ssa_builder.h). It
+ * also makes sure that phis with incompatible input types are not revived
+ * (statement (b) of the SsaBuilder).
+ *
+ * This phase must be run after detecting dead phis through the
+ * DeadPhiElimination phase, and before deleting the dead phis.
+ */
+class DeadPhiHandling : public ValueObject {
+ public:
+ explicit DeadPhiHandling(HGraph* graph)
+ : graph_(graph), worklist_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) {
+ worklist_.reserve(kDefaultWorklistSize);
+ }
+
+ void Run();
+
+ private:
+ void VisitBasicBlock(HBasicBlock* block);
+ void ProcessWorklist();
+ void AddToWorklist(HPhi* phi);
+ void AddDependentInstructionsToWorklist(HPhi* phi);
+ bool UpdateType(HPhi* phi);
+
+ HGraph* const graph_;
+ ArenaVector<HPhi*> worklist_;
+
+ static constexpr size_t kDefaultWorklistSize = 8;
+
+ DISALLOW_COPY_AND_ASSIGN(DeadPhiHandling);
+};
+
+static bool HasConflictingEquivalent(HPhi* phi) {
+ if (phi->GetNext() == nullptr) {
+ return false;
+ }
+ HPhi* next = phi->GetNext()->AsPhi();
+ if (next->GetRegNumber() == phi->GetRegNumber()) {
+ if (next->GetType() == Primitive::kPrimVoid) {
+ // We only get a void type for an equivalent phi we processed and found out
+ // it was conflicting.
+ return true;
+ } else {
+ // Go to the next phi, in case it is also an equivalent.
+ return HasConflictingEquivalent(next);
+ }
+ }
+ return false;
+}
+
+bool DeadPhiHandling::UpdateType(HPhi* phi) {
+ if (phi->IsDead()) {
+ // Phi was rendered dead while waiting in the worklist because it was replaced
+ // with an equivalent.
+ return false;
+ }
+
+ Primitive::Type existing = phi->GetType();
+
+ bool conflict = false;
+ Primitive::Type new_type = existing;
+ for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+ HInstruction* input = phi->InputAt(i);
+ if (input->IsPhi() && input->AsPhi()->IsDead()) {
+ // We are doing a reverse post order visit of the graph, reviving
+ // phis that have environment uses and updating their types. If an
+ // input is a phi, and it is dead (because its input types are
+ // conflicting), this phi must be marked dead as well.
+ conflict = true;
+ break;
+ }
+ Primitive::Type input_type = HPhi::ToPhiType(input->GetType());
+
+ // The only acceptable transitions are:
+ // - From void to typed: first time we update the type of this phi.
+ // - From int to reference (or reference to int): the phi has to change
+ // to reference type. If the integer input cannot be converted to a
+ // reference input, the phi will remain dead.
+ if (new_type == Primitive::kPrimVoid) {
+ new_type = input_type;
+ } else if (new_type == Primitive::kPrimNot && input_type == Primitive::kPrimInt) {
+ if (input->IsPhi() && HasConflictingEquivalent(input->AsPhi())) {
+ // If we already asked for an equivalent of the input phi, but that equivalent
+ // ended up conflicting, make this phi conflicting too.
+ conflict = true;
+ break;
+ }
+ HInstruction* equivalent = SsaBuilder::GetReferenceTypeEquivalent(input);
+ if (equivalent == nullptr) {
+ conflict = true;
+ break;
+ }
+ phi->ReplaceInput(equivalent, i);
+ if (equivalent->IsPhi()) {
+ DCHECK_EQ(equivalent->GetType(), Primitive::kPrimNot);
+ // We created a new phi, but that phi has the same inputs as the old phi. We
+ // add it to the worklist to ensure its inputs can also be converted to reference.
+ // If not, it will remain dead, and the algorithm will make the current phi dead
+ // as well.
+ equivalent->AsPhi()->SetLive();
+ AddToWorklist(equivalent->AsPhi());
+ }
+ } else if (new_type == Primitive::kPrimInt && input_type == Primitive::kPrimNot) {
+ new_type = Primitive::kPrimNot;
+ // Start over, we may request reference equivalents for the inputs of the phi.
+ i = -1;
+ } else if (new_type != input_type) {
+ conflict = true;
+ break;
+ }
+ }
+
+ if (conflict) {
+ phi->SetType(Primitive::kPrimVoid);
+ phi->SetDead();
+ return true;
+ } else if (existing == new_type) {
+ return false;
+ }
+
+ DCHECK(phi->IsLive());
+ phi->SetType(new_type);
+
+ // There might exist a `new_type` equivalent of `phi` already. In that case,
+ // we replace the equivalent with the, now live, `phi`.
+ HPhi* equivalent = phi->GetNextEquivalentPhiWithSameType();
+ if (equivalent != nullptr) {
+ // There cannot be more than two equivalents with the same type.
+ DCHECK(equivalent->GetNextEquivalentPhiWithSameType() == nullptr);
+ // If doing fix-point iteration, the equivalent might be in `worklist_`.
+ // Setting it dead will make UpdateType skip it.
+ equivalent->SetDead();
+ equivalent->ReplaceWith(phi);
+ }
+
+ return true;
+}
+
+void DeadPhiHandling::VisitBasicBlock(HBasicBlock* block) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ if (IsUndefinedLoopHeaderPhi(phi)) {
+ DCHECK(phi->IsDead());
+ continue;
+ }
+ if (phi->IsDead() && phi->HasEnvironmentUses()) {
+ phi->SetLive();
+ if (block->IsLoopHeader()) {
+ // Loop phis must have a type to guarantee convergence of the algorithm.
+ DCHECK_NE(phi->GetType(), Primitive::kPrimVoid);
+ AddToWorklist(phi);
+ } else {
+ // Because we are doing a reverse post order visit, all inputs of
+ // this phi have been visited and therefore had their (initial) type set.
+ UpdateType(phi);
+ }
+ }
+ }
+}
+
+void DeadPhiHandling::ProcessWorklist() {
+ while (!worklist_.empty()) {
+ HPhi* instruction = worklist_.back();
+ worklist_.pop_back();
+ // Note that the same equivalent phi can be added multiple times in the work list, if
+ // used by multiple phis. The first call to `UpdateType` will know whether the phi is
+ // dead or live.
+ if (instruction->IsLive() && UpdateType(instruction)) {
+ AddDependentInstructionsToWorklist(instruction);
+ }
+ }
+}
+
+void DeadPhiHandling::AddToWorklist(HPhi* instruction) {
+ DCHECK(instruction->IsLive());
+ worklist_.push_back(instruction);
+}
+
+void DeadPhiHandling::AddDependentInstructionsToWorklist(HPhi* instruction) {
+ for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->GetUser()->AsPhi();
+ if (phi != nullptr && !phi->IsDead()) {
+ AddToWorklist(phi);
+ }
+ }
+}
+
+void DeadPhiHandling::Run() {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+ ProcessWorklist();
+}
+
void SsaBuilder::SetLoopHeaderPhiInputs() {
for (size_t i = loop_headers_.size(); i > 0; --i) {
HBasicBlock* block = loop_headers_[i - 1];
@@ -82,11 +285,10 @@ void SsaBuilder::EquivalentPhisCleanup() {
HPhi* phi = it.Current()->AsPhi();
HPhi* next = phi->GetNextEquivalentPhiWithSameType();
if (next != nullptr) {
- // Make sure we do not replace a live phi with a dead phi. A live phi
- // has been handled by the type propagation phase, unlike a dead phi.
+ // Make sure we do not replace a live phi with a dead phi. A live phi has been
+ // handled by the type propagation phase, unlike a dead phi.
if (next->IsLive()) {
phi->ReplaceWith(next);
- phi->SetDead();
} else {
next->ReplaceWith(phi);
}
@@ -98,7 +300,64 @@ void SsaBuilder::EquivalentPhisCleanup() {
}
}
-void SsaBuilder::FixEnvironmentPhis() {
+void SsaBuilder::BuildSsa() {
+ // 1) Visit in reverse post order. We need to have all predecessors of a block visited
+ // (with the exception of loops) in order to create the right environment for that
+ // block. For loops, we create phis whose inputs will be set in 2).
+ for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+
+ // 2) Set inputs of loop phis.
+ SetLoopHeaderPhiInputs();
+
+ // 3) Mark dead phis. This will mark phis that are only used by environments:
+ // at the DEX level, the type of these phis does not need to be consistent, but
+ // our code generator will complain if the inputs of a phi do not have the same
+ // type. The marking allows the type propagation to know which phis it needs
+ // to handle. We mark but do not eliminate: the elimination will be done in
+ // step 9).
+ SsaDeadPhiElimination dead_phis_for_type_propagation(GetGraph());
+ dead_phis_for_type_propagation.MarkDeadPhis();
+
+ // 4) Propagate types of phis. At this point, phis are typed void in the general
+ // case, or float/double/reference when we created an equivalent phi. So we
+ // need to propagate the types across phis to give them a correct type.
+ PrimitiveTypePropagation type_propagation(GetGraph());
+ type_propagation.Run();
+
+ // 5) When creating equivalent phis we copy the inputs of the original phi which
+ // may be improperly typed. This was fixed during the type propagation in 4) but
+ // as a result we may end up with two equivalent phis with the same type for
+ // the same dex register. This pass cleans them up.
+ EquivalentPhisCleanup();
+
+ // 6) Mark dead phis again. Step 4) may have introduced new phis.
+ // Step 5) might enable the death of new phis.
+ SsaDeadPhiElimination dead_phis(GetGraph());
+ dead_phis.MarkDeadPhis();
+
+ // 7) Now that the graph is correctly typed, we can get rid of redundant phis.
+ // Note that we cannot do this phase before type propagation, otherwise
+ // we could get rid of phi equivalents, whose presence is a requirement for the
+ // type propagation phase. Note that this is to satisfy statement (a) of the
+ // SsaBuilder (see ssa_builder.h).
+ SsaRedundantPhiElimination redundant_phi(GetGraph());
+ redundant_phi.Run();
+
+ // 8) Fix the type for null constants which are part of an equality comparison.
+ // We need to do this after redundant phi elimination, to ensure the only cases
+ // that we can see are reference comparison against 0. The redundant phi
+ // elimination ensures we do not see a phi taking two 0 constants in a HEqual
+ // or HNotEqual.
+ FixNullConstantType();
+
+ // 9) Make sure environments use the right phi "equivalent": a phi marked dead
+ // can have a phi equivalent that is not dead. We must therefore update
+ // all environment uses of the dead phi to use its equivalent. Note that there
+ // can be multiple phis for the same Dex register that are live (for example
+ // when merging constants), in which case it is OK for the environments
+ // to just reference one.
for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
for (HInstructionIterator it_phis(block->GetPhis()); !it_phis.Done(); it_phis.Advance()) {
@@ -119,345 +378,24 @@ void SsaBuilder::FixEnvironmentPhis() {
phi->ReplaceWith(next);
}
}
-}
-static void AddDependentInstructionsToWorklist(HInstruction* instruction,
- ArenaVector<HPhi*>* worklist) {
- // If `instruction` is a dead phi, type conflict was just identified. All its
- // live phi users, and transitively users of those users, therefore need to be
- // marked dead/conflicting too, so we add them to the worklist. Otherwise we
- // add users whose type does not match and needs to be updated.
- bool add_all_live_phis = instruction->IsPhi() && instruction->AsPhi()->IsDead();
- for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
- if (user->IsPhi() && user->AsPhi()->IsLive()) {
- if (add_all_live_phis || user->GetType() != instruction->GetType()) {
- worklist->push_back(user->AsPhi());
- }
- }
+ // 10) Deal with phis to guarantee liveness of phis in case of a debuggable
+ // application. This is for satisfying statement (c) of the SsaBuilder
+ // (see ssa_builder.h).
+ if (GetGraph()->IsDebuggable()) {
+ DeadPhiHandling dead_phi_handler(GetGraph());
+ dead_phi_handler.Run();
}
-}
-
-// Find a candidate primitive type for `phi` by merging the type of its inputs.
-// Return false if conflict is identified.
-static bool TypePhiFromInputs(HPhi* phi) {
- Primitive::Type common_type = phi->GetType();
- for (HInputIterator it(phi); !it.Done(); it.Advance()) {
- HInstruction* input = it.Current();
- if (input->IsPhi() && input->AsPhi()->IsDead()) {
- // Phis are constructed live so if an input is a dead phi, it must have
- // been made dead due to type conflict. Mark this phi conflicting too.
- return false;
- }
-
- Primitive::Type input_type = HPhi::ToPhiType(input->GetType());
- if (common_type == input_type) {
- // No change in type.
- } else if (Primitive::ComponentSize(common_type) != Primitive::ComponentSize(input_type)) {
- // Types are of different sizes, e.g. int vs. long. Must be a conflict.
- return false;
- } else if (Primitive::IsIntegralType(common_type)) {
- // Previous inputs were integral, this one is not but is of the same size.
- // This does not imply conflict since some bytecode instruction types are
- // ambiguous. TypeInputsOfPhi will either type them or detect a conflict.
- DCHECK(Primitive::IsFloatingPointType(input_type) || input_type == Primitive::kPrimNot);
- common_type = input_type;
- } else if (Primitive::IsIntegralType(input_type)) {
- // Input is integral, common type is not. Same as in the previous case, if
- // there is a conflict, it will be detected during TypeInputsOfPhi.
- DCHECK(Primitive::IsFloatingPointType(common_type) || common_type == Primitive::kPrimNot);
- } else {
- // Combining float and reference types. Clearly a conflict.
- DCHECK((common_type == Primitive::kPrimFloat && input_type == Primitive::kPrimNot) ||
- (common_type == Primitive::kPrimNot && input_type == Primitive::kPrimFloat));
- return false;
- }
- }
-
- // We have found a candidate type for the phi. Set it and return true. We may
- // still discover conflict whilst typing the individual inputs in TypeInputsOfPhi.
- phi->SetType(common_type);
- return true;
-}
-
-// Replace inputs of `phi` to match its type. Return false if conflict is identified.
-bool SsaBuilder::TypeInputsOfPhi(HPhi* phi, ArenaVector<HPhi*>* worklist) {
- Primitive::Type common_type = phi->GetType();
- if (common_type == Primitive::kPrimVoid || Primitive::IsIntegralType(common_type)) {
- // Phi either contains only other untyped phis (common_type == kPrimVoid),
- // or `common_type` is integral and we do not need to retype ambiguous inputs
- // because they are always constructed with the integral type candidate.
- if (kIsDebugBuild) {
- for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
- HInstruction* input = phi->InputAt(i);
- if (common_type == Primitive::kPrimVoid) {
- DCHECK(input->IsPhi() && input->GetType() == Primitive::kPrimVoid);
- } else {
- DCHECK((input->IsPhi() && input->GetType() == Primitive::kPrimVoid) ||
- HPhi::ToPhiType(input->GetType()) == common_type);
- }
- }
- }
- // Inputs did not need to be replaced, hence no conflict. Report success.
- return true;
- } else {
- DCHECK(common_type == Primitive::kPrimNot || Primitive::IsFloatingPointType(common_type));
- for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
- HInstruction* input = phi->InputAt(i);
- if (input->GetType() != common_type) {
- // Input type does not match phi's type. Try to retype the input or
- // generate a suitably typed equivalent.
- HInstruction* equivalent = (common_type == Primitive::kPrimNot)
- ? GetReferenceTypeEquivalent(input)
- : GetFloatOrDoubleEquivalent(input, common_type);
- if (equivalent == nullptr) {
- // Input could not be typed. Report conflict.
- return false;
- }
- // Make sure the input did not change its type and we do not need to
- // update its users.
- DCHECK_NE(input, equivalent);
-
- phi->ReplaceInput(equivalent, i);
- if (equivalent->IsPhi()) {
- worklist->push_back(equivalent->AsPhi());
- }
- }
- }
- // All inputs either matched the type of the phi or we successfully replaced
- // them with a suitable equivalent. Report success.
- return true;
- }
-}
-
-// Attempt to set the primitive type of `phi` to match its inputs. Return whether
-// it was changed by the algorithm or not.
-bool SsaBuilder::UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist) {
- DCHECK(phi->IsLive());
- Primitive::Type original_type = phi->GetType();
-
- // Try to type the phi in two stages:
- // (1) find a candidate type for the phi by merging types of all its inputs,
- // (2) try to type the phi's inputs to that candidate type.
- // Either of these stages may detect a type conflict and fail, in which case
- // we immediately abort.
- if (!TypePhiFromInputs(phi) || !TypeInputsOfPhi(phi, worklist)) {
- // Conflict detected. Mark the phi dead and return true because it changed.
- phi->SetDead();
- return true;
- }
-
- // Return true if the type of the phi has changed.
- return phi->GetType() != original_type;
-}
-
-void SsaBuilder::RunPrimitiveTypePropagation() {
- ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter());
-
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
- if (block->IsLoopHeader()) {
- for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- HPhi* phi = phi_it.Current()->AsPhi();
- if (phi->IsLive()) {
- worklist.push_back(phi);
- }
- }
- } else {
- for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- // Eagerly compute the type of the phi, for quicker convergence. Note
- // that we don't need to add users to the worklist because we are
- // doing a reverse post-order visit, therefore either the phi users are
- // non-loop phi and will be visited later in the visit, or are loop-phis,
- // and they are already in the work list.
- HPhi* phi = phi_it.Current()->AsPhi();
- if (phi->IsLive()) {
- UpdatePrimitiveType(phi, &worklist);
- }
- }
- }
- }
-
- ProcessPrimitiveTypePropagationWorklist(&worklist);
- EquivalentPhisCleanup();
-}
-
-void SsaBuilder::ProcessPrimitiveTypePropagationWorklist(ArenaVector<HPhi*>* worklist) {
- // Process worklist
- while (!worklist->empty()) {
- HPhi* phi = worklist->back();
- worklist->pop_back();
- // The phi could have been made dead as a result of conflicts while in the
- // worklist. If it is now dead, there is no point in updating its type.
- if (phi->IsLive() && UpdatePrimitiveType(phi, worklist)) {
- AddDependentInstructionsToWorklist(phi, worklist);
- }
- }
-}
-
-static HArrayGet* FindFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
- Primitive::Type type = aget->GetType();
- DCHECK(Primitive::IsIntOrLongType(type));
- HArrayGet* next = aget->GetNext()->AsArrayGet();
- return (next != nullptr && next->IsEquivalentOf(aget)) ? next : nullptr;
-}
-
-static HArrayGet* CreateFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
- Primitive::Type type = aget->GetType();
- DCHECK(Primitive::IsIntOrLongType(type));
- DCHECK(FindFloatOrDoubleEquivalentOfArrayGet(aget) == nullptr);
-
- HArrayGet* equivalent = new (aget->GetBlock()->GetGraph()->GetArena()) HArrayGet(
- aget->GetArray(),
- aget->GetIndex(),
- type == Primitive::kPrimInt ? Primitive::kPrimFloat : Primitive::kPrimDouble,
- aget->GetDexPc());
- aget->GetBlock()->InsertInstructionAfter(equivalent, aget);
- return equivalent;
-}
-
-// Returns true if the array input of `aget` is either of type int[] or long[].
-// Should only be called on ArrayGets with ambiguous type (int/float, long/double)
-// on arrays which were typed to an array class by RTP.
-static bool IsArrayGetOnIntegralArray(HArrayGet* aget) SHARED_REQUIRES(Locks::mutator_lock_) {
- ReferenceTypeInfo array_type = aget->GetArray()->GetReferenceTypeInfo();
- DCHECK(array_type.IsPrimitiveArrayClass());
- ReferenceTypeInfo::TypeHandle array_type_handle = array_type.GetTypeHandle();
-
- bool is_integral_type;
- if (Primitive::Is64BitType(aget->GetType())) {
- is_integral_type = array_type_handle->GetComponentType()->IsPrimitiveLong();
- DCHECK(is_integral_type || array_type_handle->GetComponentType()->IsPrimitiveDouble());
- } else {
- is_integral_type = array_type_handle->GetComponentType()->IsPrimitiveInt();
- DCHECK(is_integral_type || array_type_handle->GetComponentType()->IsPrimitiveFloat());
- }
- return is_integral_type;
-}
-
-bool SsaBuilder::FixAmbiguousArrayGets() {
- if (ambiguous_agets_.empty()) {
- return true;
- }
-
- // The wrong ArrayGet equivalent may still have Phi uses coming from ArraySet
- // uses (because they are untyped) and environment uses (if --debuggable).
- // After resolving all ambiguous ArrayGets, we will re-run primitive type
- // propagation on the Phis which need to be updated.
- ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter());
-
- {
- ScopedObjectAccess soa(Thread::Current());
-
- for (HArrayGet* aget_int : ambiguous_agets_) {
- if (!aget_int->GetArray()->GetReferenceTypeInfo().IsPrimitiveArrayClass()) {
- // RTP did not type the input array. Bail.
- return false;
- }
-
- HArrayGet* aget_float = FindFloatOrDoubleEquivalentOfArrayGet(aget_int);
- if (IsArrayGetOnIntegralArray(aget_int)) {
- if (aget_float != nullptr) {
- // There is a float/double equivalent. We must replace it and re-run
- // primitive type propagation on all dependent instructions.
- aget_float->ReplaceWith(aget_int);
- aget_float->GetBlock()->RemoveInstruction(aget_float);
- AddDependentInstructionsToWorklist(aget_int, &worklist);
- }
- } else {
- if (aget_float == nullptr) {
- // This is a float/double ArrayGet but there were no typed uses which
- // would create the typed equivalent. Create it now.
- aget_float = CreateFloatOrDoubleEquivalentOfArrayGet(aget_int);
- }
- // Replace the original int/long instruction. Note that it may have phi
- // uses, environment uses, as well as real uses (from untyped ArraySets).
- // We need to re-run primitive type propagation on its dependent instructions.
- aget_int->ReplaceWith(aget_float);
- aget_int->GetBlock()->RemoveInstruction(aget_int);
- AddDependentInstructionsToWorklist(aget_float, &worklist);
- }
- }
- }
-
- // Set a flag stating that types of ArrayGets have been resolved. This is used
- // by GetFloatOrDoubleEquivalentOfArrayGet to report conflict.
- agets_fixed_ = true;
-
- if (!worklist.empty()) {
- ProcessPrimitiveTypePropagationWorklist(&worklist);
- EquivalentPhisCleanup();
- }
-
- return true;
-}
-
-BuildSsaResult SsaBuilder::BuildSsa() {
- // 1) Visit in reverse post order. We need to have all predecessors of a block
- // visited (with the exception of loops) in order to create the right environment
- // for that block. For loops, we create phis whose inputs will be set in 2).
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
- VisitBasicBlock(it.Current());
- }
-
- // 2) Set inputs of loop header phis.
- SetLoopHeaderPhiInputs();
-
- // 3) Propagate types of phis. At this point, phis are typed void in the general
- // case, or float/double/reference if we created an equivalent phi. So we need
- // to propagate the types across phis to give them a correct type. If a type
- // conflict is detected in this stage, the phi is marked dead.
- RunPrimitiveTypePropagation();
-
- // 4) Now that the correct primitive types have been assigned, we can get rid
- // of redundant phis. Note that we cannot do this phase before type propagation,
- // otherwise we could get rid of phi equivalents, whose presence is a requirement
- // for the type propagation phase. Note that this is to satisfy statement (a)
- // of the SsaBuilder (see ssa_builder.h).
- SsaRedundantPhiElimination(GetGraph()).Run();
-
- // 5) Fix the type for null constants which are part of an equality comparison.
- // We need to do this after redundant phi elimination, to ensure the only cases
- // that we can see are reference comparison against 0. The redundant phi
- // elimination ensures we do not see a phi taking two 0 constants in a HEqual
- // or HNotEqual.
- FixNullConstantType();
-
- // 6) Compute type of reference type instructions. The pass assumes that
- // NullConstant has been fixed up.
- ReferenceTypePropagation(GetGraph(), handles_).Run();
-
- // 7) Step 1) duplicated ArrayGet instructions with ambiguous type (int/float
- // or long/double). Now that RTP computed the type of the array input, the
- // ambiguity can be resolved and the correct equivalent kept.
- if (!FixAmbiguousArrayGets()) {
- return kBuildSsaFailAmbiguousArrayGet;
- }
-
- // 8) Mark dead phis. This will mark phis which are not used by instructions
- // or other live phis. If compiling as debuggable code, phis will also be kept
- // live if they have an environment use.
- SsaDeadPhiElimination dead_phi_elimimation(GetGraph());
- dead_phi_elimimation.MarkDeadPhis();
-
- // 9) Make sure environments use the right phi equivalent: a phi marked dead
- // can have a phi equivalent that is not dead. In that case we have to replace
- // it with the live equivalent because deoptimization and try/catch rely on
- // environments containing values of all live vregs at that point. Note that
- // there can be multiple phis for the same Dex register that are live
- // (for example when merging constants), in which case it is okay for the
- // environments to just reference one.
- FixEnvironmentPhis();
-
- // 10) Now that the right phis are used for the environments, we can eliminate
- // phis we do not need. Regardless of the debuggable status, this phase is
- /// necessary for statement (b) of the SsaBuilder (see ssa_builder.h), as well
- // as for the code generation, which does not deal with phis of conflicting
+ // 11) Now that the right phis are used for the environments, and we
+ // have potentially revive dead phis in case of a debuggable application,
+ // we can eliminate phis we do not need. Regardless of the debuggable status,
+ // this phase is necessary for statement (b) of the SsaBuilder (see ssa_builder.h),
+ // as well as for the code generation, which does not deal with phis of conflicting
// input types.
- dead_phi_elimimation.EliminateDeadPhis();
+ dead_phis.EliminateDeadPhis();
- // 11) Clear locals.
+ // 12) Clear locals.
for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions());
!it.Done();
it.Advance()) {
@@ -466,8 +404,6 @@ BuildSsaResult SsaBuilder::BuildSsa() {
current->GetBlock()->RemoveInstruction(current);
}
}
-
- return kBuildSsaSuccess;
}
ArenaVector<HInstruction*>* SsaBuilder::GetLocalsFor(HBasicBlock* block) {
@@ -655,8 +591,6 @@ HDoubleConstant* SsaBuilder::GetDoubleEquivalent(HLongConstant* constant) {
* phi with a floating point / reference type.
*/
HPhi* SsaBuilder::GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type) {
- DCHECK(phi->IsLive()) << "Cannot get equivalent of a dead phi since it would create a live one.";
-
// We place the floating point /reference phi next to this phi.
HInstruction* next = phi->GetNext();
if (next != nullptr
@@ -672,50 +606,35 @@ HPhi* SsaBuilder::GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive:
ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena();
HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type);
for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
- // Copy the inputs. Note that the graph may not be correctly typed
- // by doing this copy, but the type propagation phase will fix it.
+ // Copy the inputs. Note that the graph may not be correctly typed by doing this copy,
+ // but the type propagation phase will fix it.
new_phi->SetRawInputAt(i, phi->InputAt(i));
}
phi->GetBlock()->InsertPhiAfter(new_phi, phi);
- DCHECK(new_phi->IsLive());
return new_phi;
} else {
- // An existing equivalent was found. If it is dead, conflict was previously
- // identified and we return nullptr instead.
HPhi* next_phi = next->AsPhi();
DCHECK_EQ(next_phi->GetType(), type);
- return next_phi->IsLive() ? next_phi : nullptr;
- }
-}
-
-HArrayGet* SsaBuilder::GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
- DCHECK(Primitive::IsIntegralType(aget->GetType()));
-
- if (!Primitive::IsIntOrLongType(aget->GetType())) {
- // Cannot type boolean, char, byte, short to float/double.
- return nullptr;
- }
-
- DCHECK(ContainsElement(ambiguous_agets_, aget));
- if (agets_fixed_) {
- // This used to be an ambiguous ArrayGet but its type has been resolved to
- // int/long. Requesting a float/double equivalent should lead to a conflict.
- if (kIsDebugBuild) {
- ScopedObjectAccess soa(Thread::Current());
- DCHECK(IsArrayGetOnIntegralArray(aget));
+ if (next_phi->IsDead()) {
+ // TODO(dbrazdil): Remove this SetLive (we should not need to revive phis)
+ // once we stop running MarkDeadPhis before PrimitiveTypePropagation. This
+ // cannot revive undefined loop header phis because they cannot have uses.
+ DCHECK(!IsUndefinedLoopHeaderPhi(next_phi));
+ next_phi->SetLive();
}
- return nullptr;
- } else {
- // This is an ambiguous ArrayGet which has not been resolved yet. Return an
- // equivalent float/double instruction to use until it is resolved.
- HArrayGet* equivalent = FindFloatOrDoubleEquivalentOfArrayGet(aget);
- return (equivalent == nullptr) ? CreateFloatOrDoubleEquivalentOfArrayGet(aget) : equivalent;
+ return next_phi;
}
}
-HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* value, Primitive::Type type) {
+HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* user,
+ HInstruction* value,
+ Primitive::Type type) {
if (value->IsArrayGet()) {
- return GetFloatOrDoubleEquivalentOfArrayGet(value->AsArrayGet());
+ // The verifier has checked that values in arrays cannot be used for both
+ // floating point and non-floating point operations. It is therefore safe to just
+ // change the type of the operation.
+ value->AsArrayGet()->SetType(type);
+ return value;
} else if (value->IsLongConstant()) {
return GetDoubleEquivalent(value->AsLongConstant());
} else if (value->IsIntConstant()) {
@@ -723,7 +642,12 @@ HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* value, Primit
} else if (value->IsPhi()) {
return GetFloatDoubleOrReferenceEquivalentOfPhi(value->AsPhi(), type);
} else {
- return nullptr;
+ // For other instructions, we assume the verifier has checked that the dex format is correctly
+ // typed and the value in a dex register will not be used for both floating point and
+ // non-floating point operations. So the only reason an instruction would want a floating
+ // point equivalent is for an unused phi that will be removed by the dead phi elimination phase.
+ DCHECK(user->IsPhi()) << "is actually " << user->DebugName() << " (" << user->GetId() << ")";
+ return value;
}
}
@@ -738,17 +662,15 @@ HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) {
}
void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {
- Primitive::Type load_type = load->GetType();
HInstruction* value = (*current_locals_)[load->GetLocal()->GetRegNumber()];
// If the operation requests a specific type, we make sure its input is of that type.
- if (load_type != value->GetType()) {
- if (load_type == Primitive::kPrimFloat || load_type == Primitive::kPrimDouble) {
- value = GetFloatOrDoubleEquivalent(value, load_type);
- } else if (load_type == Primitive::kPrimNot) {
+ if (load->GetType() != value->GetType()) {
+ if (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble) {
+ value = GetFloatOrDoubleEquivalent(load, value, load->GetType());
+ } else if (load->GetType() == Primitive::kPrimNot) {
value = GetReferenceTypeEquivalent(value);
}
}
-
load->ReplaceWith(value);
load->GetBlock()->RemoveInstruction(load);
}
@@ -838,13 +760,4 @@ void SsaBuilder::VisitTemporary(HTemporary* temp) {
temp->GetBlock()->RemoveInstruction(temp);
}
-void SsaBuilder::VisitArrayGet(HArrayGet* aget) {
- Primitive::Type type = aget->GetType();
- DCHECK(!Primitive::IsFloatingPointType(type));
- if (Primitive::IsIntOrLongType(type)) {
- ambiguous_agets_.push_back(aget);
- }
- VisitInstruction(aget);
-}
-
} // namespace art
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index ed6f5cab51..dcce5e4c2c 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -49,20 +49,17 @@ static constexpr int kDefaultNumberOfLoops = 2;
*/
class SsaBuilder : public HGraphVisitor {
public:
- explicit SsaBuilder(HGraph* graph, StackHandleScopeCollection* handles)
+ explicit SsaBuilder(HGraph* graph)
: HGraphVisitor(graph),
- handles_(handles),
- agets_fixed_(false),
current_locals_(nullptr),
loop_headers_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
- ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
locals_for_(graph->GetBlocks().size(),
ArenaVector<HInstruction*>(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) {
loop_headers_.reserve(kDefaultNumberOfLoops);
}
- BuildSsaResult BuildSsa();
+ void BuildSsa();
// Returns locals vector for `block`. If it is a catch block, the vector will be
// prepopulated with catch phis for vregs which are defined in `current_locals_`.
@@ -74,38 +71,23 @@ class SsaBuilder : public HGraphVisitor {
void VisitStoreLocal(HStoreLocal* store);
void VisitInstruction(HInstruction* instruction);
void VisitTemporary(HTemporary* instruction);
- void VisitArrayGet(HArrayGet* aget);
+
+ static HInstruction* GetFloatOrDoubleEquivalent(HInstruction* user,
+ HInstruction* instruction,
+ Primitive::Type type);
+
+ static HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction);
static constexpr const char* kSsaBuilderPassName = "ssa_builder";
private:
void SetLoopHeaderPhiInputs();
- void FixEnvironmentPhis();
void FixNullConstantType();
void EquivalentPhisCleanup();
- void RunPrimitiveTypePropagation();
-
- // Attempts to resolve types of aget and aget-wide instructions from reference
- // type information on the input array. Returns false if the type of the array
- // is unknown.
- bool FixAmbiguousArrayGets();
-
- bool TypeInputsOfPhi(HPhi* phi, ArenaVector<HPhi*>* worklist);
- bool UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist);
- void ProcessPrimitiveTypePropagationWorklist(ArenaVector<HPhi*>* worklist);
- HInstruction* GetFloatOrDoubleEquivalent(HInstruction* instruction, Primitive::Type type);
- HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction);
-
- HFloatConstant* GetFloatEquivalent(HIntConstant* constant);
- HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant);
- HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type);
- HArrayGet* GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget);
-
- StackHandleScopeCollection* const handles_;
-
- // True if types of ambiguous ArrayGets have been resolved.
- bool agets_fixed_;
+ static HFloatConstant* GetFloatEquivalent(HIntConstant* constant);
+ static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant);
+ static HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type);
// Locals for the current block being visited.
ArenaVector<HInstruction*>* current_locals_;
@@ -114,8 +96,6 @@ class SsaBuilder : public HGraphVisitor {
// over these blocks to set the inputs of their phis.
ArenaVector<HBasicBlock*> loop_headers_;
- ArenaVector<HArrayGet*> ambiguous_agets_;
-
// HEnvironment for each block.
ArenaVector<ArenaVector<HInstruction*>> locals_for_;
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 63aba88c2b..a3219dcc38 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -40,17 +40,15 @@ void SsaDeadPhiElimination::MarkDeadPhis() {
continue;
}
- bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
- if (!keep_alive) {
- for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
- if (!use_it.Current()->GetUser()->IsPhi()) {
- keep_alive = true;
- break;
- }
+ bool has_non_phi_use = false;
+ for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
+ if (!use_it.Current()->GetUser()->IsPhi()) {
+ has_non_phi_use = true;
+ break;
}
}
- if (keep_alive) {
+ if (has_non_phi_use) {
worklist_.push_back(phi);
} else {
phi->SetDead();
@@ -96,8 +94,8 @@ void SsaDeadPhiElimination::EliminateDeadPhis() {
for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done();
use_it.Advance()) {
HInstruction* user = use_it.Current()->GetUser();
- DCHECK(user->IsLoopHeaderPhi());
- DCHECK(user->AsPhi()->IsDead());
+ DCHECK(user->IsLoopHeaderPhi()) << user->GetId();
+ DCHECK(user->AsPhi()->IsDead()) << user->GetId();
}
}
// Remove the phi from use lists of its inputs.
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index d2885a8fd7..024278f4b2 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -28,8 +28,6 @@
namespace art {
-class SsaTest : public CommonCompilerTest {};
-
class SsaPrettyPrinter : public HPrettyPrinter {
public:
explicit SsaPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {}
@@ -85,10 +83,11 @@ static void TestCode(const uint16_t* data, const char* expected) {
bool graph_built = builder.BuildGraph(*item);
ASSERT_TRUE(graph_built);
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
+ graph->TransformToSsa();
ReNumberInstructions(graph);
// Test that phis had their type set.
@@ -104,7 +103,7 @@ static void TestCode(const uint16_t* data, const char* expected) {
ASSERT_STREQ(expected, printer.str().c_str());
}
-TEST_F(SsaTest, CFG1) {
+TEST(SsaTest, CFG1) {
// Test that we get rid of loads and stores.
const char* expected =
"BasicBlock 0, succ: 1\n"
@@ -132,7 +131,7 @@ TEST_F(SsaTest, CFG1) {
TestCode(data, expected);
}
-TEST_F(SsaTest, CFG2) {
+TEST(SsaTest, CFG2) {
// Test that we create a phi for the join block of an if control flow instruction
// when there is only code in the else branch.
const char* expected =
@@ -163,7 +162,7 @@ TEST_F(SsaTest, CFG2) {
TestCode(data, expected);
}
-TEST_F(SsaTest, CFG3) {
+TEST(SsaTest, CFG3) {
// Test that we create a phi for the join block of an if control flow instruction
// when both branches update a local.
const char* expected =
@@ -196,7 +195,7 @@ TEST_F(SsaTest, CFG3) {
TestCode(data, expected);
}
-TEST_F(SsaTest, Loop1) {
+TEST(SsaTest, Loop1) {
// Test that we create a phi for an initialized local at entry of a loop.
const char* expected =
"BasicBlock 0, succ: 1\n"
@@ -229,7 +228,7 @@ TEST_F(SsaTest, Loop1) {
TestCode(data, expected);
}
-TEST_F(SsaTest, Loop2) {
+TEST(SsaTest, Loop2) {
// Simple loop with one preheader and one back edge.
const char* expected =
"BasicBlock 0, succ: 1\n"
@@ -259,7 +258,7 @@ TEST_F(SsaTest, Loop2) {
TestCode(data, expected);
}
-TEST_F(SsaTest, Loop3) {
+TEST(SsaTest, Loop3) {
// Test that a local not yet defined at the entry of a loop is handled properly.
const char* expected =
"BasicBlock 0, succ: 1\n"
@@ -291,7 +290,7 @@ TEST_F(SsaTest, Loop3) {
TestCode(data, expected);
}
-TEST_F(SsaTest, Loop4) {
+TEST(SsaTest, Loop4) {
// Make sure we support a preheader of a loop not being the first predecessor
// in the predecessor list of the header.
const char* expected =
@@ -326,7 +325,7 @@ TEST_F(SsaTest, Loop4) {
TestCode(data, expected);
}
-TEST_F(SsaTest, Loop5) {
+TEST(SsaTest, Loop5) {
// Make sure we create a preheader of a loop when a header originally has two
// incoming blocks and one back edge.
const char* expected =
@@ -368,7 +367,7 @@ TEST_F(SsaTest, Loop5) {
TestCode(data, expected);
}
-TEST_F(SsaTest, Loop6) {
+TEST(SsaTest, Loop6) {
// Test a loop with one preheader and two back edges (e.g. continue).
const char* expected =
"BasicBlock 0, succ: 1\n"
@@ -407,7 +406,7 @@ TEST_F(SsaTest, Loop6) {
TestCode(data, expected);
}
-TEST_F(SsaTest, Loop7) {
+TEST(SsaTest, Loop7) {
// Test a loop with one preheader, one back edge, and two exit edges (e.g. break).
const char* expected =
"BasicBlock 0, succ: 1\n"
@@ -449,7 +448,7 @@ TEST_F(SsaTest, Loop7) {
TestCode(data, expected);
}
-TEST_F(SsaTest, DeadLocal) {
+TEST(SsaTest, DeadLocal) {
// Test that we correctly handle a local not being used.
const char* expected =
"BasicBlock 0, succ: 1\n"
@@ -467,7 +466,7 @@ TEST_F(SsaTest, DeadLocal) {
TestCode(data, expected);
}
-TEST_F(SsaTest, LocalInIf) {
+TEST(SsaTest, LocalInIf) {
// Test that we do not create a phi in the join block when one predecessor
// does not update the local.
const char* expected =
@@ -497,7 +496,7 @@ TEST_F(SsaTest, LocalInIf) {
TestCode(data, expected);
}
-TEST_F(SsaTest, MultiplePredecessors) {
+TEST(SsaTest, MultiplePredecessors) {
// Test that we do not create a phi when one predecessor
// does not update the local.
const char* expected =
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 9457da1c36..2579ddb52e 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -147,7 +147,7 @@ class AssemblerTest : public testing::Test {
std::string (AssemblerTest::*GetName2)(const Reg2&),
std::string fmt) {
std::string str;
- std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0);
+ std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
for (auto reg1 : reg1_registers) {
for (auto reg2 : reg2_registers) {
@@ -188,14 +188,66 @@ class AssemblerTest : public testing::Test {
return str;
}
- template <typename RegType, typename ImmType>
- std::string RepeatTemplatedRegisterImmBits(void (Ass::*f)(RegType, ImmType),
+ template <typename ImmType, typename Reg1, typename Reg2>
+ std::string RepeatTemplatedImmBitsRegisters(void (Ass::*f)(ImmType, Reg1, Reg2),
+ const std::vector<Reg1*> reg1_registers,
+ const std::vector<Reg2*> reg2_registers,
+ std::string (AssemblerTest::*GetName1)(const Reg1&),
+ std::string (AssemblerTest::*GetName2)(const Reg2&),
int imm_bits,
- const std::vector<Reg*> registers,
- std::string (AssemblerTest::*GetName)(const RegType&),
std::string fmt) {
+ std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
+
+ WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size());
+
std::string str;
- std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0);
+ for (auto reg1 : reg1_registers) {
+ for (auto reg2 : reg2_registers) {
+ for (int64_t imm : imms) {
+ ImmType new_imm = CreateImmediate(imm);
+ (assembler_.get()->*f)(new_imm, *reg1, *reg2);
+ std::string base = fmt;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+ base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+ }
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+ base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+ }
+
+ size_t imm_index = base.find(IMM_TOKEN);
+ if (imm_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << imm;
+ std::string imm_string = sreg.str();
+ base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
+ }
+
+ if (str.size() > 0) {
+ str += "\n";
+ }
+ str += base;
+ }
+ }
+ }
+ // Add a newline at the end.
+ str += "\n";
+ return str;
+ }
+
+ template <typename RegType, typename ImmType>
+ std::string RepeatTemplatedRegisterImmBits(void (Ass::*f)(RegType, ImmType),
+ int imm_bits,
+ const std::vector<Reg*> registers,
+ std::string (AssemblerTest::*GetName)(const RegType&),
+ std::string fmt) {
+ std::string str;
+ std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
for (auto reg : registers) {
for (int64_t imm : imms) {
@@ -291,6 +343,17 @@ class AssemblerTest : public testing::Test {
fmt);
}
+ template <typename ImmType>
+ std::string RepeatIbFF(void (Ass::*f)(ImmType, FPReg, FPReg), int imm_bits, std::string fmt) {
+ return RepeatTemplatedImmBitsRegisters<ImmType, FPReg, FPReg>(f,
+ GetFPRegisters(),
+ GetFPRegisters(),
+ &AssemblerTest::GetFPRegName,
+ &AssemblerTest::GetFPRegName,
+ imm_bits,
+ fmt);
+ }
+
std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) {
return RepeatTemplatedRegisters<FPReg, Reg>(f,
GetFPRegisters(),
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 733ad2cc38..afca8adcbb 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -503,6 +503,18 @@ void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
EmitI(0x7, rt, static_cast<Register>(0), imm16);
}
+void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16);
+}
+
+void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>((cc << 2) | 1), imm16);
+}
+
void MipsAssembler::J(uint32_t addr26) {
EmitI26(0x2, addr26);
}
@@ -637,7 +649,17 @@ void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
EmitI21(0x3E, rs, imm21);
}
-void MipsAssembler::EmitBcond(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
+void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
+ CHECK(IsR6());
+ EmitFI(0x11, 0x9, ft, imm16);
+}
+
+void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
+ CHECK(IsR6());
+ EmitFI(0x11, 0xD, ft, imm16);
+}
+
+void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
switch (cond) {
case kCondLTZ:
CHECK_EQ(rt, ZERO);
@@ -669,6 +691,14 @@ void MipsAssembler::EmitBcond(BranchCondition cond, Register rs, Register rt, ui
CHECK_EQ(rt, ZERO);
Bnez(rs, imm16);
break;
+ case kCondF:
+ CHECK_EQ(rt, ZERO);
+ Bc1f(static_cast<int>(rs), imm16);
+ break;
+ case kCondT:
+ CHECK_EQ(rt, ZERO);
+ Bc1t(static_cast<int>(rs), imm16);
+ break;
case kCondLT:
case kCondGE:
case kCondLE:
@@ -683,7 +713,7 @@ void MipsAssembler::EmitBcond(BranchCondition cond, Register rs, Register rt, ui
}
}
-void MipsAssembler::EmitBcondc(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
+void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
switch (cond) {
case kCondLT:
Bltc(rs, rt, imm16_21);
@@ -733,6 +763,14 @@ void MipsAssembler::EmitBcondc(BranchCondition cond, Register rs, Register rt, u
case kCondGEU:
Bgeuc(rs, rt, imm16_21);
break;
+ case kCondF:
+ CHECK_EQ(rt, ZERO);
+ Bc1eqz(static_cast<FRegister>(rs), imm16_21);
+ break;
+ case kCondT:
+ CHECK_EQ(rt, ZERO);
+ Bc1nez(static_cast<FRegister>(rs), imm16_21);
+ break;
case kUncond:
LOG(FATAL) << "Unexpected branch condition " << cond;
UNREACHABLE();
@@ -787,6 +825,202 @@ void MipsAssembler::NegD(FRegister fd, FRegister fs) {
EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7);
}
+void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
+}
+
+void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
+}
+
+void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
+}
+
+void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
+}
+
+void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
+}
+
+void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
+}
+
+void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37);
+}
+
+void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
+}
+
+void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
+}
+
+void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
+}
+
+void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
+}
+
+void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
+}
+
+void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
+}
+
+void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37);
+}
+
+void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x01);
+}
+
+void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x02);
+}
+
+void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x03);
+}
+
+void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x04);
+}
+
+void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x05);
+}
+
+void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x06);
+}
+
+void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x07);
+}
+
+void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x11);
+}
+
+void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x12);
+}
+
+void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x14, ft, fs, fd, 0x13);
+}
+
+void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x01);
+}
+
+void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x02);
+}
+
+void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x03);
+}
+
+void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x04);
+}
+
+void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x05);
+}
+
+void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x06);
+}
+
+void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x07);
+}
+
+void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x11);
+}
+
+void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x12);
+}
+
+void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x15, ft, fs, fd, 0x13);
+}
+
+void MipsAssembler::Movf(Register rd, Register rs, int cc) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01);
+}
+
+void MipsAssembler::Movt(Register rd, Register rs, int cc) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01);
+}
+
void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20);
}
@@ -1058,6 +1292,10 @@ MipsAssembler::Branch::Branch(bool is_r6,
CHECK_NE(lhs_reg, ZERO);
CHECK_EQ(rhs_reg, ZERO);
break;
+ case kCondF:
+ case kCondT:
+ CHECK_EQ(rhs_reg, ZERO);
+ break;
case kUncond:
UNREACHABLE();
}
@@ -1112,6 +1350,10 @@ MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
return kCondGEU;
case kCondGEU:
return kCondLTU;
+ case kCondF:
+ return kCondT;
+ case kCondT:
+ return kCondF;
case kUncond:
LOG(FATAL) << "Unexpected branch condition " << cond;
}
@@ -1514,7 +1756,7 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
break;
case Branch::kCondBranch:
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
- EmitBcond(condition, lhs, rhs, offset);
+ EmitBcondR2(condition, lhs, rhs, offset);
Nop(); // TODO: improve by filling the delay slot.
break;
case Branch::kCall:
@@ -1561,7 +1803,7 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
// Note: the opposite condition branch encodes 8 as the distance, which is equal to the
// number of instructions skipped:
// (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
- EmitBcond(Branch::OppositeCondition(condition), lhs, rhs, 8);
+ EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Push(RA);
Nal();
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
@@ -1589,8 +1831,8 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
break;
case Branch::kR6CondBranch:
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
- EmitBcondc(condition, lhs, rhs, offset);
- Nop(); // TODO: improve by filling the forbidden slot.
+ EmitBcondR6(condition, lhs, rhs, offset);
+ Nop(); // TODO: improve by filling the forbidden/delay slot.
break;
case Branch::kR6Call:
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
@@ -1606,7 +1848,7 @@ void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
Jic(AT, Low16Bits(offset));
break;
case Branch::kR6LongCondBranch:
- EmitBcondc(Branch::OppositeCondition(condition), lhs, rhs, 2);
+ EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Auipc(AT, High16Bits(offset));
@@ -1708,6 +1950,24 @@ void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
}
}
+void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
+ CHECK(IsUint<3>(cc)) << cc;
+ Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
+}
+
+void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
+ CHECK(IsUint<3>(cc)) << cc;
+ Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
+}
+
+void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
+ Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
+}
+
+void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
+ Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
+}
+
void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
int32_t offset) {
// IsInt<16> must be passed a signed value.
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 62366f6a8b..f569aa858c 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -72,8 +72,8 @@ class MipsExceptionSlowPath {
: scratch_(scratch), stack_adjust_(stack_adjust) {}
MipsExceptionSlowPath(MipsExceptionSlowPath&& src)
- : scratch_(std::move(src.scratch_)),
- stack_adjust_(std::move(src.stack_adjust_)),
+ : scratch_(src.scratch_),
+ stack_adjust_(src.stack_adjust_),
exception_entry_(std::move(src.exception_entry_)) {}
private:
@@ -185,6 +185,8 @@ class MipsAssembler FINAL : public Assembler {
void Bgez(Register rt, uint16_t imm16);
void Blez(Register rt, uint16_t imm16);
void Bgtz(Register rt, uint16_t imm16);
+ void Bc1f(int cc, uint16_t imm16); // R2
+ void Bc1t(int cc, uint16_t imm16); // R2
void J(uint32_t addr26);
void Jal(uint32_t addr26);
void Jalr(Register rd, Register rs);
@@ -208,6 +210,8 @@ class MipsAssembler FINAL : public Assembler {
void Bnec(Register rs, Register rt, uint16_t imm16); // R6
void Beqzc(Register rs, uint32_t imm21); // R6
void Bnezc(Register rs, uint32_t imm21); // R6
+ void Bc1eqz(FRegister ft, uint16_t imm16); // R6
+ void Bc1nez(FRegister ft, uint16_t imm16); // R6
void AddS(FRegister fd, FRegister fs, FRegister ft);
void SubS(FRegister fd, FRegister fs, FRegister ft);
@@ -222,6 +226,43 @@ class MipsAssembler FINAL : public Assembler {
void NegS(FRegister fd, FRegister fs);
void NegD(FRegister fd, FRegister fs);
+ void CunS(int cc, FRegister fs, FRegister ft); // R2
+ void CeqS(int cc, FRegister fs, FRegister ft); // R2
+ void CueqS(int cc, FRegister fs, FRegister ft); // R2
+ void ColtS(int cc, FRegister fs, FRegister ft); // R2
+ void CultS(int cc, FRegister fs, FRegister ft); // R2
+ void ColeS(int cc, FRegister fs, FRegister ft); // R2
+ void CuleS(int cc, FRegister fs, FRegister ft); // R2
+ void CunD(int cc, FRegister fs, FRegister ft); // R2
+ void CeqD(int cc, FRegister fs, FRegister ft); // R2
+ void CueqD(int cc, FRegister fs, FRegister ft); // R2
+ void ColtD(int cc, FRegister fs, FRegister ft); // R2
+ void CultD(int cc, FRegister fs, FRegister ft); // R2
+ void ColeD(int cc, FRegister fs, FRegister ft); // R2
+ void CuleD(int cc, FRegister fs, FRegister ft); // R2
+ void CmpUnS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpEqS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUeqS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpLtS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUltS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpLeS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUleS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpOrS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUneS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpNeS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUnD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpEqD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUeqD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpLtD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUltD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpLeD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUleD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpOrD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpUneD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void CmpNeD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void Movf(Register rd, Register rs, int cc); // R2
+ void Movt(Register rd, Register rs, int cc); // R2
+
void Cvtsw(FRegister fd, FRegister fs);
void Cvtdw(FRegister fd, FRegister fs);
void Cvtsd(FRegister fd, FRegister fs);
@@ -267,6 +308,10 @@ class MipsAssembler FINAL : public Assembler {
void Bge(Register rs, Register rt, MipsLabel* label);
void Bltu(Register rs, Register rt, MipsLabel* label);
void Bgeu(Register rs, Register rt, MipsLabel* label);
+ void Bc1f(int cc, MipsLabel* label); // R2
+ void Bc1t(int cc, MipsLabel* label); // R2
+ void Bc1eqz(FRegister ft, MipsLabel* label); // R6
+ void Bc1nez(FRegister ft, MipsLabel* label); // R6
void EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, size_t size);
void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset);
@@ -296,7 +341,8 @@ class MipsAssembler FINAL : public Assembler {
//
// Emit code that will create an activation on the stack.
- void BuildFrame(size_t frame_size, ManagedRegister method_reg,
+ void BuildFrame(size_t frame_size,
+ ManagedRegister method_reg,
const std::vector<ManagedRegister>& callee_save_regs,
const ManagedRegisterEntrySpills& entry_spills) OVERRIDE;
@@ -314,58 +360,85 @@ class MipsAssembler FINAL : public Assembler {
void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister mscratch) OVERRIDE;
- void StoreImmediateToThread32(ThreadOffset<4> dest, uint32_t imm, ManagedRegister mscratch)
- OVERRIDE;
+ void StoreImmediateToThread32(ThreadOffset<kMipsWordSize> dest,
+ uint32_t imm,
+ ManagedRegister mscratch) OVERRIDE;
- void StoreStackOffsetToThread32(ThreadOffset<4> thr_offs, FrameOffset fr_offs,
+ void StoreStackOffsetToThread32(ThreadOffset<kMipsWordSize> thr_offs,
+ FrameOffset fr_offs,
ManagedRegister mscratch) OVERRIDE;
- void StoreStackPointerToThread32(ThreadOffset<4> thr_offs) OVERRIDE;
+ void StoreStackPointerToThread32(ThreadOffset<kMipsWordSize> thr_offs) OVERRIDE;
- void StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off,
+ void StoreSpanning(FrameOffset dest,
+ ManagedRegister msrc,
+ FrameOffset in_off,
ManagedRegister mscratch) OVERRIDE;
// Load routines.
void Load(ManagedRegister mdest, FrameOffset src, size_t size) OVERRIDE;
- void LoadFromThread32(ManagedRegister mdest, ThreadOffset<4> src, size_t size) OVERRIDE;
+ void LoadFromThread32(ManagedRegister mdest,
+ ThreadOffset<kMipsWordSize> src,
+ size_t size) OVERRIDE;
void LoadRef(ManagedRegister dest, FrameOffset src) OVERRIDE;
- void LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
+ void LoadRef(ManagedRegister mdest,
+ ManagedRegister base,
+ MemberOffset offs,
bool unpoison_reference) OVERRIDE;
void LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) OVERRIDE;
- void LoadRawPtrFromThread32(ManagedRegister mdest, ThreadOffset<4> offs) OVERRIDE;
+ void LoadRawPtrFromThread32(ManagedRegister mdest, ThreadOffset<kMipsWordSize> offs) OVERRIDE;
// Copying routines.
void Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) OVERRIDE;
- void CopyRawPtrFromThread32(FrameOffset fr_offs, ThreadOffset<4> thr_offs,
+ void CopyRawPtrFromThread32(FrameOffset fr_offs,
+ ThreadOffset<kMipsWordSize> thr_offs,
ManagedRegister mscratch) OVERRIDE;
- void CopyRawPtrToThread32(ThreadOffset<4> thr_offs, FrameOffset fr_offs,
+ void CopyRawPtrToThread32(ThreadOffset<kMipsWordSize> thr_offs,
+ FrameOffset fr_offs,
ManagedRegister mscratch) OVERRIDE;
void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) OVERRIDE;
void Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) OVERRIDE;
- void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, ManagedRegister mscratch,
+ void Copy(FrameOffset dest,
+ ManagedRegister src_base,
+ Offset src_offset,
+ ManagedRegister mscratch,
size_t size) OVERRIDE;
- void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
- ManagedRegister mscratch, size_t size) OVERRIDE;
+ void Copy(ManagedRegister dest_base,
+ Offset dest_offset,
+ FrameOffset src,
+ ManagedRegister mscratch,
+ size_t size) OVERRIDE;
- void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, ManagedRegister mscratch,
+ void Copy(FrameOffset dest,
+ FrameOffset src_base,
+ Offset src_offset,
+ ManagedRegister mscratch,
size_t size) OVERRIDE;
- void Copy(ManagedRegister dest, Offset dest_offset, ManagedRegister src, Offset src_offset,
- ManagedRegister mscratch, size_t size) OVERRIDE;
+ void Copy(ManagedRegister dest,
+ Offset dest_offset,
+ ManagedRegister src,
+ Offset src_offset,
+ ManagedRegister mscratch,
+ size_t size) OVERRIDE;
- void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset,
- ManagedRegister mscratch, size_t size) OVERRIDE;
+ void Copy(FrameOffset dest,
+ Offset dest_offset,
+ FrameOffset src,
+ Offset src_offset,
+ ManagedRegister mscratch,
+ size_t size) OVERRIDE;
void MemoryBarrier(ManagedRegister) OVERRIDE;
@@ -383,13 +456,17 @@ class MipsAssembler FINAL : public Assembler {
// value is null and null_allowed. in_reg holds a possibly stale reference
// that can be used to avoid loading the handle scope entry to see if the value is
// null.
- void CreateHandleScopeEntry(ManagedRegister out_reg, FrameOffset handlescope_offset,
- ManagedRegister in_reg, bool null_allowed) OVERRIDE;
+ void CreateHandleScopeEntry(ManagedRegister out_reg,
+ FrameOffset handlescope_offset,
+ ManagedRegister in_reg,
+ bool null_allowed) OVERRIDE;
// Set up out_off to hold a Object** into the handle scope, or to be null if the
// value is null and null_allowed.
- void CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handlescope_offset,
- ManagedRegister mscratch, bool null_allowed) OVERRIDE;
+ void CreateHandleScopeEntry(FrameOffset out_off,
+ FrameOffset handlescope_offset,
+ ManagedRegister mscratch,
+ bool null_allowed) OVERRIDE;
// src holds a handle scope entry (Object**) load this into dst.
void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) OVERRIDE;
@@ -402,7 +479,7 @@ class MipsAssembler FINAL : public Assembler {
// Call to address held at [base+offset].
void Call(ManagedRegister base, Offset offset, ManagedRegister mscratch) OVERRIDE;
void Call(FrameOffset base, Offset offset, ManagedRegister mscratch) OVERRIDE;
- void CallFromThread32(ThreadOffset<4> offset, ManagedRegister mscratch) OVERRIDE;
+ void CallFromThread32(ThreadOffset<kMipsWordSize> offset, ManagedRegister mscratch) OVERRIDE;
// Generate code to check if Thread::Current()->exception_ is non-null
// and branch to a ExceptionSlowPath if it is.
@@ -437,6 +514,8 @@ class MipsAssembler FINAL : public Assembler {
kCondNEZ,
kCondLTU,
kCondGEU,
+ kCondF, // Floating-point predicate false.
+ kCondT, // Floating-point predicate true.
kUncond,
};
friend std::ostream& operator<<(std::ostream& os, const BranchCondition& rhs);
@@ -543,7 +622,22 @@ class MipsAssembler FINAL : public Assembler {
//
// Composite branches (made of several instructions) with longer reach have 32-bit
// offsets encoded as 2 16-bit "halves" in two instructions (high half goes first).
- // The composite branches cover the range of PC + +/-2GB.
+ // The composite branches cover the range of PC + +/-2GB on MIPS32 CPUs. However,
+ // the range is not end-to-end on MIPS64 (unless addresses are forced to zero- or
+ // sign-extend from 32 to 64 bits by the appropriate CPU configuration).
+ // Consider the following implementation of a long unconditional branch, for
+ // example:
+ //
+ // auipc at, offset_31_16 // at = pc + sign_extend(offset_31_16) << 16
+ // jic at, offset_15_0 // pc = at + sign_extend(offset_15_0)
+ //
+ // Both of the above instructions take 16-bit signed offsets as immediate operands.
+ // When bit 15 of offset_15_0 is 1, it effectively causes subtraction of 0x10000
+ // due to sign extension. This must be compensated for by incrementing offset_31_16
+ // by 1. offset_31_16 can only be incremented by 1 if it's not 0x7FFF. If it is
+ // 0x7FFF, adding 1 will overflow the positive offset into the negative range.
+ // Therefore, the long branch range is something like from PC - 0x80000000 to
+ // PC + 0x7FFF7FFF, IOW, shorter by 32KB on one side.
//
// The returned values are therefore: 18, 21, 23, 28 and 32. There's also a special
// case with the addiu instruction and a 16 bit offset.
@@ -580,17 +674,17 @@ class MipsAssembler FINAL : public Assembler {
// Helper for the above.
void InitShortOrLong(OffsetBits ofs_size, Type short_type, Type long_type);
- uint32_t old_location_; // Offset into assembler buffer in bytes.
- uint32_t location_; // Offset into assembler buffer in bytes.
- uint32_t target_; // Offset into assembler buffer in bytes.
+ uint32_t old_location_; // Offset into assembler buffer in bytes.
+ uint32_t location_; // Offset into assembler buffer in bytes.
+ uint32_t target_; // Offset into assembler buffer in bytes.
- uint32_t lhs_reg_ : 5; // Left-hand side register in conditional branches or
- // indirect call register.
- uint32_t rhs_reg_ : 5; // Right-hand side register in conditional branches.
- BranchCondition condition_ : 5; // Condition for conditional branches.
+ uint32_t lhs_reg_; // Left-hand side register in conditional branches or
+ // indirect call register.
+ uint32_t rhs_reg_; // Right-hand side register in conditional branches.
+ BranchCondition condition_; // Condition for conditional branches.
- Type type_ : 5; // Current type of the branch.
- Type old_type_ : 5; // Initial type of the branch.
+ Type type_; // Current type of the branch.
+ Type old_type_; // Initial type of the branch.
};
friend std::ostream& operator<<(std::ostream& os, const Branch::Type& rhs);
friend std::ostream& operator<<(std::ostream& os, const Branch::OffsetBits& rhs);
@@ -601,8 +695,8 @@ class MipsAssembler FINAL : public Assembler {
void EmitI26(int opcode, uint32_t imm26);
void EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd, int funct);
void EmitFI(int opcode, int fmt, FRegister rt, uint16_t imm);
- void EmitBcond(BranchCondition cond, Register rs, Register rt, uint16_t imm16);
- void EmitBcondc(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21); // R6
+ void EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16);
+ void EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21);
void Buncond(MipsLabel* label);
void Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs = ZERO);
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index 063d8bd825..6f8b3e8c57 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -21,6 +21,8 @@
#include "base/stl_util.h"
#include "utils/assembler_test.h"
+#define __ GetAssembler()->
+
namespace art {
struct MIPSCpuRegisterCompare {
@@ -184,6 +186,63 @@ class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler,
return result;
}
+ void BranchCondOneRegHelper(void (mips::MipsAssembler::*f)(mips::Register,
+ mips::MipsLabel*),
+ std::string instr_name) {
+ mips::MipsLabel label;
+ (Base::GetAssembler()->*f)(mips::A0, &label);
+ constexpr size_t kAdduCount1 = 63;
+ for (size_t i = 0; i != kAdduCount1; ++i) {
+ __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
+ }
+ __ Bind(&label);
+ constexpr size_t kAdduCount2 = 64;
+ for (size_t i = 0; i != kAdduCount2; ++i) {
+ __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
+ }
+ (Base::GetAssembler()->*f)(mips::A1, &label);
+
+ std::string expected =
+ ".set noreorder\n" +
+ instr_name + " $a0, 1f\n"
+ "nop\n" +
+ RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
+ "1:\n" +
+ RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
+ instr_name + " $a1, 1b\n"
+ "nop\n";
+ DriverStr(expected, instr_name);
+ }
+
+ void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register,
+ mips::Register,
+ mips::MipsLabel*),
+ std::string instr_name) {
+ mips::MipsLabel label;
+ (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label);
+ constexpr size_t kAdduCount1 = 63;
+ for (size_t i = 0; i != kAdduCount1; ++i) {
+ __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
+ }
+ __ Bind(&label);
+ constexpr size_t kAdduCount2 = 64;
+ for (size_t i = 0; i != kAdduCount2; ++i) {
+ __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
+ }
+ (Base::GetAssembler()->*f)(mips::A2, mips::A3, &label);
+
+ std::string expected =
+ ".set noreorder\n" +
+ instr_name + " $a0, $a1, 1f\n"
+ "nop\n" +
+ RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
+ "1:\n" +
+ RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
+ instr_name + " $a2, $a3, 1b\n"
+ "nop\n";
+ DriverStr(expected, instr_name);
+ }
+
private:
std::vector<mips::Register*> registers_;
std::map<mips::Register, std::string, MIPSCpuRegisterCompare> secondary_register_names_;
@@ -196,8 +255,6 @@ TEST_F(AssemblerMIPSTest, Toolchain) {
EXPECT_TRUE(CheckTools());
}
-#define __ GetAssembler()->
-
TEST_F(AssemblerMIPSTest, Addu) {
DriverStr(RepeatRRR(&mips::MipsAssembler::Addu, "addu ${reg1}, ${reg2}, ${reg3}"), "Addu");
}
@@ -418,6 +475,84 @@ TEST_F(AssemblerMIPSTest, NegD) {
DriverStr(RepeatFF(&mips::MipsAssembler::NegD, "neg.d ${reg1}, ${reg2}"), "NegD");
}
+TEST_F(AssemblerMIPSTest, CunS) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CunS, 3, "c.un.s $fcc{imm}, ${reg1}, ${reg2}"),
+ "CunS");
+}
+
+TEST_F(AssemblerMIPSTest, CeqS) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CeqS, 3, "c.eq.s $fcc{imm}, ${reg1}, ${reg2}"),
+ "CeqS");
+}
+
+TEST_F(AssemblerMIPSTest, CueqS) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CueqS, 3, "c.ueq.s $fcc{imm}, ${reg1}, ${reg2}"),
+ "CueqS");
+}
+
+TEST_F(AssemblerMIPSTest, ColtS) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::ColtS, 3, "c.olt.s $fcc{imm}, ${reg1}, ${reg2}"),
+ "ColtS");
+}
+
+TEST_F(AssemblerMIPSTest, CultS) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CultS, 3, "c.ult.s $fcc{imm}, ${reg1}, ${reg2}"),
+ "CultS");
+}
+
+TEST_F(AssemblerMIPSTest, ColeS) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::ColeS, 3, "c.ole.s $fcc{imm}, ${reg1}, ${reg2}"),
+ "ColeS");
+}
+
+TEST_F(AssemblerMIPSTest, CuleS) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CuleS, 3, "c.ule.s $fcc{imm}, ${reg1}, ${reg2}"),
+ "CuleS");
+}
+
+TEST_F(AssemblerMIPSTest, CunD) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CunD, 3, "c.un.d $fcc{imm}, ${reg1}, ${reg2}"),
+ "CunD");
+}
+
+TEST_F(AssemblerMIPSTest, CeqD) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CeqD, 3, "c.eq.d $fcc{imm}, ${reg1}, ${reg2}"),
+ "CeqD");
+}
+
+TEST_F(AssemblerMIPSTest, CueqD) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CueqD, 3, "c.ueq.d $fcc{imm}, ${reg1}, ${reg2}"),
+ "CueqD");
+}
+
+TEST_F(AssemblerMIPSTest, ColtD) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::ColtD, 3, "c.olt.d $fcc{imm}, ${reg1}, ${reg2}"),
+ "ColtD");
+}
+
+TEST_F(AssemblerMIPSTest, CultD) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CultD, 3, "c.ult.d $fcc{imm}, ${reg1}, ${reg2}"),
+ "CultD");
+}
+
+TEST_F(AssemblerMIPSTest, ColeD) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::ColeD, 3, "c.ole.d $fcc{imm}, ${reg1}, ${reg2}"),
+ "ColeD");
+}
+
+TEST_F(AssemblerMIPSTest, CuleD) {
+ DriverStr(RepeatIbFF(&mips::MipsAssembler::CuleD, 3, "c.ule.d $fcc{imm}, ${reg1}, ${reg2}"),
+ "CuleD");
+}
+
+TEST_F(AssemblerMIPSTest, Movf) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::Movf, 3, "movf ${reg1}, ${reg2}, $fcc{imm}"), "Movf");
+}
+
+TEST_F(AssemblerMIPSTest, Movt) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::Movt, 3, "movt ${reg1}, ${reg2}, $fcc{imm}"), "Movt");
+}
+
TEST_F(AssemblerMIPSTest, CvtSW) {
DriverStr(RepeatFF(&mips::MipsAssembler::Cvtsw, "cvt.s.w ${reg1}, ${reg2}"), "CvtSW");
}
@@ -1000,55 +1135,11 @@ TEST_F(AssemblerMIPSTest, B) {
}
TEST_F(AssemblerMIPSTest, Beq) {
- mips::MipsLabel label;
- __ Beq(mips::A0, mips::A1, &label);
- constexpr size_t kAdduCount1 = 63;
- for (size_t i = 0; i != kAdduCount1; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Bind(&label);
- constexpr size_t kAdduCount2 = 64;
- for (size_t i = 0; i != kAdduCount2; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Beq(mips::A2, mips::A3, &label);
-
- std::string expected =
- ".set noreorder\n"
- "beq $a0, $a1, 1f\n"
- "nop\n" +
- RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
- "1:\n" +
- RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "beq $a2, $a3, 1b\n"
- "nop\n";
- DriverStr(expected, "Beq");
+ BranchCondTwoRegsHelper(&mips::MipsAssembler::Beq, "Beq");
}
TEST_F(AssemblerMIPSTest, Bne) {
- mips::MipsLabel label;
- __ Bne(mips::A0, mips::A1, &label);
- constexpr size_t kAdduCount1 = 63;
- for (size_t i = 0; i != kAdduCount1; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Bind(&label);
- constexpr size_t kAdduCount2 = 64;
- for (size_t i = 0; i != kAdduCount2; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Bne(mips::A2, mips::A3, &label);
-
- std::string expected =
- ".set noreorder\n"
- "bne $a0, $a1, 1f\n"
- "nop\n" +
- RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
- "1:\n" +
- RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "bne $a2, $a3, 1b\n"
- "nop\n";
- DriverStr(expected, "Bne");
+ BranchCondTwoRegsHelper(&mips::MipsAssembler::Bne, "Bne");
}
TEST_F(AssemblerMIPSTest, Beqz) {
@@ -1104,60 +1195,24 @@ TEST_F(AssemblerMIPSTest, Bnez) {
}
TEST_F(AssemblerMIPSTest, Bltz) {
- mips::MipsLabel label;
- __ Bltz(mips::A0, &label);
- constexpr size_t kAdduCount1 = 63;
- for (size_t i = 0; i != kAdduCount1; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Bind(&label);
- constexpr size_t kAdduCount2 = 64;
- for (size_t i = 0; i != kAdduCount2; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Bltz(mips::A1, &label);
-
- std::string expected =
- ".set noreorder\n"
- "bltz $a0, 1f\n"
- "nop\n" +
- RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
- "1:\n" +
- RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "bltz $a1, 1b\n"
- "nop\n";
- DriverStr(expected, "Bltz");
+ BranchCondOneRegHelper(&mips::MipsAssembler::Bltz, "Bltz");
}
TEST_F(AssemblerMIPSTest, Bgez) {
- mips::MipsLabel label;
- __ Bgez(mips::A0, &label);
- constexpr size_t kAdduCount1 = 63;
- for (size_t i = 0; i != kAdduCount1; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Bind(&label);
- constexpr size_t kAdduCount2 = 64;
- for (size_t i = 0; i != kAdduCount2; ++i) {
- __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
- }
- __ Bgez(mips::A1, &label);
-
- std::string expected =
- ".set noreorder\n"
- "bgez $a0, 1f\n"
- "nop\n" +
- RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
- "1:\n" +
- RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "bgez $a1, 1b\n"
- "nop\n";
- DriverStr(expected, "Bgez");
+ BranchCondOneRegHelper(&mips::MipsAssembler::Bgez, "Bgez");
}
TEST_F(AssemblerMIPSTest, Blez) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Blez, "Blez");
+}
+
+TEST_F(AssemblerMIPSTest, Bgtz) {
+ BranchCondOneRegHelper(&mips::MipsAssembler::Bgtz, "Bgtz");
+}
+
+TEST_F(AssemblerMIPSTest, Blt) {
mips::MipsLabel label;
- __ Blez(mips::A0, &label);
+ __ Blt(mips::A0, mips::A1, &label);
constexpr size_t kAdduCount1 = 63;
for (size_t i = 0; i != kAdduCount1; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
@@ -1167,23 +1222,25 @@ TEST_F(AssemblerMIPSTest, Blez) {
for (size_t i = 0; i != kAdduCount2; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
}
- __ Blez(mips::A1, &label);
+ __ Blt(mips::A2, mips::A3, &label);
std::string expected =
".set noreorder\n"
- "blez $a0, 1f\n"
+ "slt $at, $a0, $a1\n"
+ "bne $zero, $at, 1f\n"
"nop\n" +
RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
"1:\n" +
RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "blez $a1, 1b\n"
+ "slt $at, $a2, $a3\n"
+ "bne $zero, $at, 1b\n"
"nop\n";
- DriverStr(expected, "Blez");
+ DriverStr(expected, "Blt");
}
-TEST_F(AssemblerMIPSTest, Bgtz) {
+TEST_F(AssemblerMIPSTest, Bge) {
mips::MipsLabel label;
- __ Bgtz(mips::A0, &label);
+ __ Bge(mips::A0, mips::A1, &label);
constexpr size_t kAdduCount1 = 63;
for (size_t i = 0; i != kAdduCount1; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
@@ -1193,23 +1250,25 @@ TEST_F(AssemblerMIPSTest, Bgtz) {
for (size_t i = 0; i != kAdduCount2; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
}
- __ Bgtz(mips::A1, &label);
+ __ Bge(mips::A2, mips::A3, &label);
std::string expected =
".set noreorder\n"
- "bgtz $a0, 1f\n"
+ "slt $at, $a0, $a1\n"
+ "beq $zero, $at, 1f\n"
"nop\n" +
RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
"1:\n" +
RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "bgtz $a1, 1b\n"
+ "slt $at, $a2, $a3\n"
+ "beq $zero, $at, 1b\n"
"nop\n";
- DriverStr(expected, "Bgtz");
+ DriverStr(expected, "Bge");
}
-TEST_F(AssemblerMIPSTest, Blt) {
+TEST_F(AssemblerMIPSTest, Bltu) {
mips::MipsLabel label;
- __ Blt(mips::A0, mips::A1, &label);
+ __ Bltu(mips::A0, mips::A1, &label);
constexpr size_t kAdduCount1 = 63;
for (size_t i = 0; i != kAdduCount1; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
@@ -1219,25 +1278,25 @@ TEST_F(AssemblerMIPSTest, Blt) {
for (size_t i = 0; i != kAdduCount2; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
}
- __ Blt(mips::A2, mips::A3, &label);
+ __ Bltu(mips::A2, mips::A3, &label);
std::string expected =
".set noreorder\n"
- "slt $at, $a0, $a1\n"
+ "sltu $at, $a0, $a1\n"
"bne $zero, $at, 1f\n"
"nop\n" +
RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
"1:\n" +
RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "slt $at, $a2, $a3\n"
+ "sltu $at, $a2, $a3\n"
"bne $zero, $at, 1b\n"
"nop\n";
- DriverStr(expected, "Blt");
+ DriverStr(expected, "Bltu");
}
-TEST_F(AssemblerMIPSTest, Bge) {
+TEST_F(AssemblerMIPSTest, Bgeu) {
mips::MipsLabel label;
- __ Bge(mips::A0, mips::A1, &label);
+ __ Bgeu(mips::A0, mips::A1, &label);
constexpr size_t kAdduCount1 = 63;
for (size_t i = 0; i != kAdduCount1; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
@@ -1247,25 +1306,25 @@ TEST_F(AssemblerMIPSTest, Bge) {
for (size_t i = 0; i != kAdduCount2; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
}
- __ Bge(mips::A2, mips::A3, &label);
+ __ Bgeu(mips::A2, mips::A3, &label);
std::string expected =
".set noreorder\n"
- "slt $at, $a0, $a1\n"
+ "sltu $at, $a0, $a1\n"
"beq $zero, $at, 1f\n"
"nop\n" +
RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
"1:\n" +
RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "slt $at, $a2, $a3\n"
+ "sltu $at, $a2, $a3\n"
"beq $zero, $at, 1b\n"
"nop\n";
- DriverStr(expected, "Bge");
+ DriverStr(expected, "Bgeu");
}
-TEST_F(AssemblerMIPSTest, Bltu) {
+TEST_F(AssemblerMIPSTest, Bc1f) {
mips::MipsLabel label;
- __ Bltu(mips::A0, mips::A1, &label);
+ __ Bc1f(0, &label);
constexpr size_t kAdduCount1 = 63;
for (size_t i = 0; i != kAdduCount1; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
@@ -1275,25 +1334,23 @@ TEST_F(AssemblerMIPSTest, Bltu) {
for (size_t i = 0; i != kAdduCount2; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
}
- __ Bltu(mips::A2, mips::A3, &label);
+ __ Bc1f(7, &label);
std::string expected =
".set noreorder\n"
- "sltu $at, $a0, $a1\n"
- "bne $zero, $at, 1f\n"
+ "bc1f $fcc0, 1f\n"
"nop\n" +
RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
"1:\n" +
RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "sltu $at, $a2, $a3\n"
- "bne $zero, $at, 1b\n"
+ "bc1f $fcc7, 1b\n"
"nop\n";
- DriverStr(expected, "Bltu");
+ DriverStr(expected, "Bc1f");
}
-TEST_F(AssemblerMIPSTest, Bgeu) {
+TEST_F(AssemblerMIPSTest, Bc1t) {
mips::MipsLabel label;
- __ Bgeu(mips::A0, mips::A1, &label);
+ __ Bc1t(0, &label);
constexpr size_t kAdduCount1 = 63;
for (size_t i = 0; i != kAdduCount1; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
@@ -1303,20 +1360,18 @@ TEST_F(AssemblerMIPSTest, Bgeu) {
for (size_t i = 0; i != kAdduCount2; ++i) {
__ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
}
- __ Bgeu(mips::A2, mips::A3, &label);
+ __ Bc1t(7, &label);
std::string expected =
".set noreorder\n"
- "sltu $at, $a0, $a1\n"
- "beq $zero, $at, 1f\n"
+ "bc1t $fcc0, 1f\n"
"nop\n" +
RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
"1:\n" +
RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
- "sltu $at, $a2, $a3\n"
- "beq $zero, $at, 1b\n"
+ "bc1t $fcc7, 1b\n"
"nop\n";
- DriverStr(expected, "Bgeu");
+ DriverStr(expected, "Bc1t");
}
#undef __
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 9a18635066..1a2f2c2bc3 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -471,18 +471,19 @@ static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode)
/*
* Callback for dumping each positions table entry.
*/
-static bool dumpPositionsCb(void* /*context*/, u4 address, u4 lineNum) {
- fprintf(gOutFile, " 0x%04x line=%d\n", address, lineNum);
+static bool dumpPositionsCb(void* /*context*/, const DexFile::PositionInfo& entry) {
+ fprintf(gOutFile, " 0x%04x line=%d\n", entry.address_, entry.line_);
return false;
}
/*
* Callback for dumping locals table entry.
*/
-static void dumpLocalsCb(void* /*context*/, u2 slot, u4 startAddress, u4 endAddress,
- const char* name, const char* descriptor, const char* signature) {
+static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) {
+ const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
fprintf(gOutFile, " 0x%04x - 0x%04x reg=%d %s %s %s\n",
- startAddress, endAddress, slot, name, descriptor, signature);
+ entry.start_address_, entry.end_address_, entry.reg_,
+ entry.name_, entry.descriptor_, signature);
}
/*
@@ -900,11 +901,9 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags,
// Positions and locals table in the debug info.
bool is_static = (flags & kAccStatic) != 0;
fprintf(gOutFile, " positions : \n");
- pDexFile->DecodeDebugInfo(
- pCode, is_static, idx, dumpPositionsCb, nullptr, nullptr);
+ pDexFile->DecodeDebugPositionInfo(pCode, dumpPositionsCb, nullptr);
fprintf(gOutFile, " locals : \n");
- pDexFile->DecodeDebugInfo(
- pCode, is_static, idx, nullptr, dumpLocalsCb, nullptr);
+ pDexFile->DecodeDebugLocalInfo(pCode, is_static, idx, dumpLocalsCb, nullptr);
}
/*
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index 1d0f75ea92..d20c16919a 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -80,10 +80,10 @@ static char* descriptorToDot(const char* str) {
* first line in the method, which *should* correspond to the first
* entry from the table. (Could also use "min" here.)
*/
-static bool positionsCb(void* context, u4 /*address*/, u4 lineNum) {
+static bool positionsCb(void* context, const DexFile::PositionInfo& entry) {
int* pFirstLine = reinterpret_cast<int *>(context);
if (*pFirstLine == -1) {
- *pFirstLine = lineNum;
+ *pFirstLine = entry.line_;
}
return 0;
}
@@ -92,7 +92,7 @@ static bool positionsCb(void* context, u4 /*address*/, u4 lineNum) {
* Dumps a method.
*/
static void dumpMethod(const DexFile* pDexFile,
- const char* fileName, u4 idx, u4 flags,
+ const char* fileName, u4 idx, u4 flags ATTRIBUTE_UNUSED,
const DexFile::CodeItem* pCode, u4 codeOffset) {
// Abstract and native methods don't get listed.
if (pCode == nullptr || codeOffset == 0) {
@@ -121,9 +121,7 @@ static void dumpMethod(const DexFile* pDexFile,
// Find the first line.
int firstLine = -1;
- bool is_static = (flags & kAccStatic) != 0;
- pDexFile->DecodeDebugInfo(
- pCode, is_static, idx, positionsCb, nullptr, &firstLine);
+ pDexFile->DecodeDebugPositionInfo(pCode, positionsCb, &firstLine);
// Method signature.
const Signature signature = pDexFile->GetMethodSignature(pMethodId);
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 2d15f6f41e..cd64a4f926 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -111,6 +111,8 @@ static const MipsInstruction gMipsInstructions[] = {
{ kRTypeMask | (0x1f << 21), 63, "dsra32", "DTA", },
// SPECIAL0
+ { kSpecial0Mask | 0x307ff, 1, "movf", "DSc" },
+ { kSpecial0Mask | 0x307ff, 0x10001, "movt", "DSc" },
{ kSpecial0Mask | 0x7ff, (2 << 6) | 24, "mul", "DST" },
{ kSpecial0Mask | 0x7ff, (3 << 6) | 24, "muh", "DST" },
{ kSpecial0Mask | 0x7ff, (2 << 6) | 25, "mulu", "DST" },
@@ -216,6 +218,11 @@ static const MipsInstruction gMipsInstructions[] = {
{ kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "TI", },
{ kITypeMask, 15 << kOpcodeShift, "aui", "TSI", },
+ { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21), "bc1f", "cB" },
+ { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21) | (1 << 16), "bc1t", "cB" },
+ { kITypeMask | (0x1f << 21), (17 << kOpcodeShift) | (9 << 21), "bc1eqz", "tB" },
+ { kITypeMask | (0x1f << 21), (17 << kOpcodeShift) | (13 << 21), "bc1nez", "tB" },
+
{ kITypeMask | (0x1f << 21), 22 << kOpcodeShift, "blezc", "TB" },
// TODO: de-dup
@@ -333,6 +340,26 @@ static const MipsInstruction gMipsInstructions[] = {
{ kFpMask | (0x1f << 21), kCop1 | (0x04 << 21), "mtc1", "Td" },
{ kFpMask | (0x1f << 21), kCop1 | (0x05 << 21), "dmtc1", "Td" },
{ kFpMask | (0x1f << 21), kCop1 | (0x07 << 21), "mthc1", "Td" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 1, "cmp.un.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 2, "cmp.eq.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 3, "cmp.ueq.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 4, "cmp.lt.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 5, "cmp.ult.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 6, "cmp.le.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 7, "cmp.ule.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 17, "cmp.or.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 18, "cmp.une.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x14 << 21) | 19, "cmp.ne.s", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 1, "cmp.un.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 2, "cmp.eq.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 3, "cmp.ueq.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 4, "cmp.lt.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 5, "cmp.ult.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 6, "cmp.le.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 7, "cmp.ule.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 17, "cmp.or.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 18, "cmp.une.d", "adt" },
+ { kFpMask | (0x1f << 21), kCop1 | (0x15 << 21) | 19, "cmp.ne.d", "adt" },
{ kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 0, "add", "fadt" },
{ kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 1, "sub", "fadt" },
{ kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 2, "mul", "fadt" },
@@ -356,6 +383,13 @@ static const MipsInstruction gMipsInstructions[] = {
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 36, "cvt.w", "fad" },
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 37, "cvt.l", "fad" },
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 38, "cvt.ps", "fad" },
+ { kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 49, "c.un", "fCdt" },
+ { kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 50, "c.eq", "fCdt" },
+ { kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 51, "c.ueq", "fCdt" },
+ { kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 52, "c.olt", "fCdt" },
+ { kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 53, "c.ult", "fCdt" },
+ { kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 54, "c.ole", "fCdt" },
+ { kFpMask | (0x10 << 21), kCop1 | (0x10 << 21) | 55, "c.ule", "fCdt" },
{ kFpMask, kCop1 | 0x10, "sel", "fadt" },
{ kFpMask, kCop1 | 0x1e, "max", "fadt" },
{ kFpMask, kCop1 | 0x1c, "min", "fadt" },
@@ -408,6 +442,12 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) {
<< StringPrintf(" ; %+d", offset);
}
break;
+ case 'C': // Floating-point condition code flag in c.<cond>.fmt.
+ args << "cc" << (sa >> 2);
+ break;
+ case 'c': // Floating-point condition code flag in bc1f/bc1t and movf/movt.
+ args << "cc" << (rt >> 2);
+ break;
case 'D': args << 'r' << rd; break;
case 'd': args << 'f' << rd; break;
case 'a': args << 'f' << sa; break;
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 993f37f3f7..40961179d3 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -42,6 +42,7 @@ LIBART_COMMON_SRC_FILES := \
check_jni.cc \
class_linker.cc \
class_table.cc \
+ code_simulator_container.cc \
common_throws.cc \
debugger.cc \
dex_file.cc \
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 2cd1a4de9f..115c26073d 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -48,6 +48,7 @@ struct LogVerbosity {
bool oat;
bool profiler;
bool signals;
+ bool simulator;
bool startup;
bool third_party_jni; // Enabled with "-verbose:third-party-jni".
bool threads;
diff --git a/runtime/code_simulator_container.cc b/runtime/code_simulator_container.cc
new file mode 100644
index 0000000000..d884c58782
--- /dev/null
+++ b/runtime/code_simulator_container.cc
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+
+#include "code_simulator_container.h"
+#include "globals.h"
+
+namespace art {
+
+CodeSimulatorContainer::CodeSimulatorContainer(InstructionSet target_isa)
+ : libart_simulator_handle_(nullptr),
+ simulator_(nullptr) {
+ const char* libart_simulator_so_name =
+ kIsDebugBuild ? "libartd-simulator.so" : "libart-simulator.so";
+ libart_simulator_handle_ = dlopen(libart_simulator_so_name, RTLD_NOW);
+ // It is not a real error when libart-simulator does not exist, e.g., on target.
+ if (libart_simulator_handle_ == nullptr) {
+ VLOG(simulator) << "Could not load " << libart_simulator_so_name << ": " << dlerror();
+ } else {
+ typedef CodeSimulator* (*create_code_simulator_ptr_)(InstructionSet target_isa);
+ create_code_simulator_ptr_ create_code_simulator_ =
+ reinterpret_cast<create_code_simulator_ptr_>(
+ dlsym(libart_simulator_handle_, "CreateCodeSimulator"));
+ DCHECK(create_code_simulator_ != nullptr) << "Fail to find symbol of CreateCodeSimulator: "
+ << dlerror();
+ simulator_ = create_code_simulator_(target_isa);
+ }
+}
+
+CodeSimulatorContainer::~CodeSimulatorContainer() {
+ // Free simulator object before closing libart-simulator because destructor of
+ // CodeSimulator lives in it.
+ if (simulator_ != nullptr) {
+ delete simulator_;
+ }
+ if (libart_simulator_handle_ != nullptr) {
+ dlclose(libart_simulator_handle_);
+ }
+}
+
+} // namespace art
diff --git a/runtime/code_simulator_container.h b/runtime/code_simulator_container.h
new file mode 100644
index 0000000000..655a2472f4
--- /dev/null
+++ b/runtime/code_simulator_container.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
+#define ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
+
+#include "arch/instruction_set.h"
+#include "simulator/code_simulator.h"
+
+namespace art {
+
+// This container dynamically opens and closes libart-simulator.
+class CodeSimulatorContainer {
+ public:
+ explicit CodeSimulatorContainer(InstructionSet target_isa);
+ ~CodeSimulatorContainer();
+
+ bool CanSimulate() const {
+ return simulator_ != nullptr;
+ }
+
+ CodeSimulator* Get() {
+ DCHECK(CanSimulate());
+ return simulator_;
+ }
+
+ const CodeSimulator* Get() const {
+ DCHECK(CanSimulate());
+ return simulator_;
+ }
+
+ private:
+ void* libart_simulator_handle_;
+ CodeSimulator* simulator_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeSimulatorContainer);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 51f57c3cf7..e5d648bd19 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1536,10 +1536,10 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan
int numItems;
JDWP::ExpandBuf* pReply;
- static bool Callback(void* context, uint32_t address, uint32_t line_number) {
+ static bool Callback(void* context, const DexFile::PositionInfo& entry) {
DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
- expandBufAdd8BE(pContext->pReply, address);
- expandBufAdd4BE(pContext->pReply, line_number);
+ expandBufAdd8BE(pContext->pReply, entry.address_);
+ expandBufAdd4BE(pContext->pReply, entry.line_);
pContext->numItems++;
return false;
}
@@ -1569,8 +1569,7 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan
context.pReply = pReply;
if (code_item != nullptr) {
- m->GetDexFile()->DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(),
- DebugCallbackContext::Callback, nullptr, &context);
+ m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context);
}
JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
@@ -1584,25 +1583,26 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi
size_t variable_count;
bool with_generic;
- static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress,
- const char* name, const char* descriptor, const char* signature)
+ static void Callback(void* context, const DexFile::LocalInfo& entry)
SHARED_REQUIRES(Locks::mutator_lock_) {
DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
+ uint16_t slot = entry.reg_;
VLOG(jdwp) << StringPrintf(" %2zd: %d(%d) '%s' '%s' '%s' actual slot=%d mangled slot=%d",
- pContext->variable_count, startAddress, endAddress - startAddress,
- name, descriptor, signature, slot,
+ pContext->variable_count, entry.start_address_,
+ entry.end_address_ - entry.start_address_,
+ entry.name_, entry.descriptor_, entry.signature_, slot,
MangleSlot(slot, pContext->method));
slot = MangleSlot(slot, pContext->method);
- expandBufAdd8BE(pContext->pReply, startAddress);
- expandBufAddUtf8String(pContext->pReply, name);
- expandBufAddUtf8String(pContext->pReply, descriptor);
+ expandBufAdd8BE(pContext->pReply, entry.start_address_);
+ expandBufAddUtf8String(pContext->pReply, entry.name_);
+ expandBufAddUtf8String(pContext->pReply, entry.descriptor_);
if (pContext->with_generic) {
- expandBufAddUtf8String(pContext->pReply, signature);
+ expandBufAddUtf8String(pContext->pReply, entry.signature_);
}
- expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
+ expandBufAdd4BE(pContext->pReply, entry.end_address_- entry.start_address_);
expandBufAdd4BE(pContext->pReply, slot);
++pContext->variable_count;
@@ -1627,8 +1627,8 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi
const DexFile::CodeItem* code_item = m->GetCodeItem();
if (code_item != nullptr) {
- m->GetDexFile()->DecodeDebugInfo(
- code_item, m->IsStatic(), m->GetDexMethodIndex(), nullptr, DebugCallbackContext::Callback,
+ m->GetDexFile()->DecodeDebugLocalInfo(
+ code_item, m->IsStatic(), m->GetDexMethodIndex(), DebugCallbackContext::Callback,
&context);
}
@@ -3716,19 +3716,19 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize
code_item_(code_item), last_pc_valid(false), last_pc(0) {
}
- static bool Callback(void* raw_context, uint32_t address, uint32_t line_number_cb) {
+ static bool Callback(void* raw_context, const DexFile::PositionInfo& entry) {
DebugCallbackContext* context = reinterpret_cast<DebugCallbackContext*>(raw_context);
- if (static_cast<int32_t>(line_number_cb) == context->line_number_) {
+ if (static_cast<int32_t>(entry.line_) == context->line_number_) {
if (!context->last_pc_valid) {
// Everything from this address until the next line change is ours.
- context->last_pc = address;
+ context->last_pc = entry.address_;
context->last_pc_valid = true;
}
// Otherwise, if we're already in a valid range for this line,
// just keep going (shouldn't really happen)...
} else if (context->last_pc_valid) { // and the line number is new
// Add everything from the last entry up until here to the set
- for (uint32_t dex_pc = context->last_pc; dex_pc < address; ++dex_pc) {
+ for (uint32_t dex_pc = context->last_pc; dex_pc < entry.address_; ++dex_pc) {
context->single_step_control_->AddDexPc(dex_pc);
}
context->last_pc_valid = false;
@@ -3769,8 +3769,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize
if (m != nullptr && !m->IsNative()) {
const DexFile::CodeItem* const code_item = m->GetCodeItem();
DebugCallbackContext context(single_step_control, line_number, code_item);
- m->GetDexFile()->DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(),
- DebugCallbackContext::Callback, nullptr, &context);
+ m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context);
}
// Activate single-step in the thread.
@@ -4756,12 +4755,7 @@ void Dbg::DdmSendHeapSegments(bool native) {
// Send a series of heap segment chunks.
HeapChunkContext context(what == HPSG_WHAT_MERGED_OBJECTS, native);
if (native) {
-#if defined(__ANDROID__) && defined(USE_DLMALLOC)
- dlmalloc_inspect_all(HeapChunkContext::HeapChunkNativeCallback, &context);
- HeapChunkContext::HeapChunkNativeCallback(nullptr, nullptr, 0, &context); // Indicate end of a space.
-#else
- UNIMPLEMENTED(WARNING) << "Native heap inspection is only supported with dlmalloc";
-#endif
+ UNIMPLEMENTED(WARNING) << "Native heap inspection is not supported";
} else {
gc::Heap* heap = Runtime::Current()->GetHeap();
for (const auto& space : heap->GetContinuousSpaces()) {
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index b1a05384f8..880d3e0dea 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -767,8 +767,7 @@ int32_t DexFile::GetLineNumFromPC(ArtMethod* method, uint32_t rel_pc) const {
// A method with no line number info should return -1
LineNumFromPcContext context(rel_pc, -1);
- DecodeDebugInfo(code_item, method->IsStatic(), method->GetDexMethodIndex(), LineNumForPcCb,
- nullptr, &context);
+ DecodeDebugPositionInfo(code_item, LineNumForPcCb, &context);
return context.line_num_;
}
@@ -805,45 +804,48 @@ int32_t DexFile::FindCatchHandlerOffset(const CodeItem &code_item, uint32_t addr
}
}
-void DexFile::DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32_t method_idx,
- DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
- void* context, const uint8_t* stream, LocalInfo* local_in_reg)
- const {
- uint32_t line = DecodeUnsignedLeb128(&stream);
- uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
- uint16_t arg_reg = code_item->registers_size_ - code_item->ins_size_;
- uint32_t address = 0;
- bool need_locals = (local_cb != nullptr);
+bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, uint32_t method_idx,
+ DexDebugNewLocalCb local_cb, void* context) const {
+ DCHECK(local_cb != nullptr);
+ if (code_item == nullptr) {
+ return false;
+ }
+ const uint8_t* stream = GetDebugInfoStream(code_item);
+ if (stream == nullptr) {
+ return false;
+ }
+ std::vector<LocalInfo> local_in_reg(code_item->registers_size_);
+ uint16_t arg_reg = code_item->registers_size_ - code_item->ins_size_;
if (!is_static) {
- if (need_locals) {
- const char* descriptor = GetMethodDeclaringClassDescriptor(GetMethodId(method_idx));
- local_in_reg[arg_reg].name_ = "this";
- local_in_reg[arg_reg].descriptor_ = descriptor;
- local_in_reg[arg_reg].signature_ = nullptr;
- local_in_reg[arg_reg].start_address_ = 0;
- local_in_reg[arg_reg].is_live_ = true;
- }
+ const char* descriptor = GetMethodDeclaringClassDescriptor(GetMethodId(method_idx));
+ local_in_reg[arg_reg].name_ = "this";
+ local_in_reg[arg_reg].descriptor_ = descriptor;
+ local_in_reg[arg_reg].signature_ = nullptr;
+ local_in_reg[arg_reg].start_address_ = 0;
+ local_in_reg[arg_reg].reg_ = arg_reg;
+ local_in_reg[arg_reg].is_live_ = true;
arg_reg++;
}
DexFileParameterIterator it(*this, GetMethodPrototype(GetMethodId(method_idx)));
- for (uint32_t i = 0; i < parameters_size && it.HasNext(); ++i, it.Next()) {
+ DecodeUnsignedLeb128(&stream); // Line.
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ uint32_t i;
+ for (i = 0; i < parameters_size && it.HasNext(); ++i, it.Next()) {
if (arg_reg >= code_item->registers_size_) {
LOG(ERROR) << "invalid stream - arg reg >= reg size (" << arg_reg
<< " >= " << code_item->registers_size_ << ") in " << GetLocation();
- return;
+ return false;
}
- uint32_t id = DecodeUnsignedLeb128P1(&stream);
+ uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
const char* descriptor = it.GetDescriptor();
- if (need_locals && id != kDexNoIndex) {
- const char* name = StringDataByIdx(id);
- local_in_reg[arg_reg].name_ = name;
- local_in_reg[arg_reg].descriptor_ = descriptor;
- local_in_reg[arg_reg].signature_ = nullptr;
- local_in_reg[arg_reg].start_address_ = address;
- local_in_reg[arg_reg].is_live_ = true;
- }
+ local_in_reg[arg_reg].name_ = StringDataByIdx(name_idx);
+ local_in_reg[arg_reg].descriptor_ = descriptor;
+ local_in_reg[arg_reg].signature_ = nullptr;
+ local_in_reg[arg_reg].start_address_ = 0;
+ local_in_reg[arg_reg].reg_ = arg_reg;
+ local_in_reg[arg_reg].is_live_ = true;
switch (*descriptor) {
case 'D':
case 'J':
@@ -854,152 +856,188 @@ void DexFile::DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32
break;
}
}
-
- if (it.HasNext()) {
+ if (i != parameters_size || it.HasNext()) {
LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation()
<< " for method " << PrettyMethod(method_idx, *this);
- return;
+ return false;
}
+ uint32_t address = 0;
for (;;) {
uint8_t opcode = *stream++;
- uint16_t reg;
- uint32_t name_idx;
- uint32_t descriptor_idx;
- uint32_t signature_idx = 0;
-
switch (opcode) {
case DBG_END_SEQUENCE:
- return;
-
+ // Emit all variables which are still alive at the end of the method.
+ for (uint16_t reg = 0; reg < code_item->registers_size_; reg++) {
+ if (local_in_reg[reg].is_live_) {
+ local_in_reg[reg].end_address_ = code_item->insns_size_in_code_units_;
+ local_cb(context, local_in_reg[reg]);
+ }
+ }
+ return true;
case DBG_ADVANCE_PC:
address += DecodeUnsignedLeb128(&stream);
break;
-
case DBG_ADVANCE_LINE:
- line += DecodeSignedLeb128(&stream);
+ DecodeSignedLeb128(&stream); // Line.
break;
-
case DBG_START_LOCAL:
- case DBG_START_LOCAL_EXTENDED:
- reg = DecodeUnsignedLeb128(&stream);
- if (reg > code_item->registers_size_) {
- LOG(ERROR) << "invalid stream - reg > reg size (" << reg << " > "
+ case DBG_START_LOCAL_EXTENDED: {
+ uint16_t reg = DecodeUnsignedLeb128(&stream);
+ if (reg >= code_item->registers_size_) {
+ LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
<< code_item->registers_size_ << ") in " << GetLocation();
- return;
+ return false;
}
- name_idx = DecodeUnsignedLeb128P1(&stream);
- descriptor_idx = DecodeUnsignedLeb128P1(&stream);
+ uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+ uint32_t descriptor_idx = DecodeUnsignedLeb128P1(&stream);
+ uint32_t signature_idx = kDexNoIndex;
if (opcode == DBG_START_LOCAL_EXTENDED) {
signature_idx = DecodeUnsignedLeb128P1(&stream);
}
// Emit what was previously there, if anything
- if (need_locals) {
- InvokeLocalCbIfLive(context, reg, address, local_in_reg, local_cb);
-
- local_in_reg[reg].name_ = StringDataByIdx(name_idx);
- local_in_reg[reg].descriptor_ = StringByTypeIdx(descriptor_idx);
- local_in_reg[reg].signature_ =
- (opcode == DBG_START_LOCAL_EXTENDED) ? StringDataByIdx(signature_idx)
- : nullptr;
- local_in_reg[reg].start_address_ = address;
- local_in_reg[reg].is_live_ = true;
+ if (local_in_reg[reg].is_live_) {
+ local_in_reg[reg].end_address_ = address;
+ local_cb(context, local_in_reg[reg]);
}
- break;
- case DBG_END_LOCAL:
- reg = DecodeUnsignedLeb128(&stream);
- if (reg > code_item->registers_size_) {
- LOG(ERROR) << "invalid stream - reg > reg size (" << reg << " > "
+ local_in_reg[reg].name_ = StringDataByIdx(name_idx);
+ local_in_reg[reg].descriptor_ = StringByTypeIdx(descriptor_idx);
+ local_in_reg[reg].signature_ = StringDataByIdx(signature_idx);
+ local_in_reg[reg].start_address_ = address;
+ local_in_reg[reg].reg_ = reg;
+ local_in_reg[reg].is_live_ = true;
+ break;
+ }
+ case DBG_END_LOCAL: {
+ uint16_t reg = DecodeUnsignedLeb128(&stream);
+ if (reg >= code_item->registers_size_) {
+ LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
<< code_item->registers_size_ << ") in " << GetLocation();
- return;
+ return false;
}
-
- if (need_locals) {
- InvokeLocalCbIfLive(context, reg, address, local_in_reg, local_cb);
- local_in_reg[reg].is_live_ = false;
+ if (!local_in_reg[reg].is_live_) {
+ LOG(ERROR) << "invalid stream - end without start in " << GetLocation();
+ return false;
}
+ local_in_reg[reg].end_address_ = address;
+ local_cb(context, local_in_reg[reg]);
+ local_in_reg[reg].is_live_ = false;
break;
-
- case DBG_RESTART_LOCAL:
- reg = DecodeUnsignedLeb128(&stream);
- if (reg > code_item->registers_size_) {
- LOG(ERROR) << "invalid stream - reg > reg size (" << reg << " > "
+ }
+ case DBG_RESTART_LOCAL: {
+ uint16_t reg = DecodeUnsignedLeb128(&stream);
+ if (reg >= code_item->registers_size_) {
+ LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
<< code_item->registers_size_ << ") in " << GetLocation();
- return;
+ return false;
}
-
- if (need_locals) {
- if (local_in_reg[reg].name_ == nullptr || local_in_reg[reg].descriptor_ == nullptr) {
- LOG(ERROR) << "invalid stream - no name or descriptor in " << GetLocation();
- return;
- }
-
- // If the register is live, the "restart" is superfluous,
- // and we don't want to mess with the existing start address.
- if (!local_in_reg[reg].is_live_) {
- local_in_reg[reg].start_address_ = address;
- local_in_reg[reg].is_live_ = true;
- }
+ // If the register is live, the "restart" is superfluous,
+ // and we don't want to mess with the existing start address.
+ if (!local_in_reg[reg].is_live_) {
+ local_in_reg[reg].start_address_ = address;
+ local_in_reg[reg].is_live_ = true;
}
break;
-
+ }
case DBG_SET_PROLOGUE_END:
case DBG_SET_EPILOGUE_BEGIN:
+ break;
case DBG_SET_FILE:
+ DecodeUnsignedLeb128P1(&stream); // name.
break;
-
- default: {
- int adjopcode = opcode - DBG_FIRST_SPECIAL;
-
- address += adjopcode / DBG_LINE_RANGE;
- line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
-
- if (position_cb != nullptr) {
- if (position_cb(context, address, line)) {
- // early exit
- return;
- }
- }
+ default:
+ address += (opcode - DBG_FIRST_SPECIAL) / DBG_LINE_RANGE;
break;
- }
}
}
}
-void DexFile::DecodeDebugInfo(const CodeItem* code_item, bool is_static, uint32_t method_idx,
- DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
- void* context) const {
- DCHECK(code_item != nullptr);
+bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, DexDebugNewPositionCb position_cb,
+ void* context) const {
+ DCHECK(position_cb != nullptr);
+ if (code_item == nullptr) {
+ return false;
+ }
const uint8_t* stream = GetDebugInfoStream(code_item);
- std::unique_ptr<LocalInfo[]> local_in_reg(local_cb != nullptr ?
- new LocalInfo[code_item->registers_size_] :
- nullptr);
- if (stream != nullptr) {
- DecodeDebugInfo0(code_item, is_static, method_idx, position_cb, local_cb, context, stream,
- &local_in_reg[0]);
+ if (stream == nullptr) {
+ return false;
+ }
+
+ PositionInfo entry = PositionInfo();
+ entry.line_ = DecodeUnsignedLeb128(&stream);
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ for (uint32_t i = 0; i < parameters_size; ++i) {
+ DecodeUnsignedLeb128P1(&stream); // Parameter name.
}
- for (int reg = 0; reg < code_item->registers_size_; reg++) {
- InvokeLocalCbIfLive(context, reg, code_item->insns_size_in_code_units_, &local_in_reg[0],
- local_cb);
+
+ for (;;) {
+ uint8_t opcode = *stream++;
+ switch (opcode) {
+ case DBG_END_SEQUENCE:
+ return true; // end of stream.
+ case DBG_ADVANCE_PC:
+ entry.address_ += DecodeUnsignedLeb128(&stream);
+ break;
+ case DBG_ADVANCE_LINE:
+ entry.line_ += DecodeSignedLeb128(&stream);
+ break;
+ case DBG_START_LOCAL:
+ DecodeUnsignedLeb128(&stream); // reg.
+ DecodeUnsignedLeb128P1(&stream); // name.
+ DecodeUnsignedLeb128P1(&stream); // descriptor.
+ break;
+ case DBG_START_LOCAL_EXTENDED:
+ DecodeUnsignedLeb128(&stream); // reg.
+ DecodeUnsignedLeb128P1(&stream); // name.
+ DecodeUnsignedLeb128P1(&stream); // descriptor.
+ DecodeUnsignedLeb128P1(&stream); // signature.
+ break;
+ case DBG_END_LOCAL:
+ case DBG_RESTART_LOCAL:
+ DecodeUnsignedLeb128(&stream); // reg.
+ break;
+ case DBG_SET_PROLOGUE_END:
+ entry.prologue_end_ = true;
+ break;
+ case DBG_SET_EPILOGUE_BEGIN:
+ entry.epilogue_begin_ = true;
+ break;
+ case DBG_SET_FILE: {
+ uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+ entry.source_file_ = StringDataByIdx(name_idx);
+ break;
+ }
+ default: {
+ int adjopcode = opcode - DBG_FIRST_SPECIAL;
+ entry.address_ += adjopcode / DBG_LINE_RANGE;
+ entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+ if (position_cb(context, entry)) {
+ return true; // early exit.
+ }
+ entry.prologue_end_ = false;
+ entry.epilogue_begin_ = false;
+ break;
+ }
+ }
}
}
-bool DexFile::LineNumForPcCb(void* raw_context, uint32_t address, uint32_t line_num) {
+bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) {
LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context);
// We know that this callback will be called in
// ascending address order, so keep going until we find
// a match or we've just gone past it.
- if (address > context->address_) {
+ if (entry.address_ > context->address_) {
// The line number from the previous positions callback
// wil be the final result.
return true;
} else {
- context->line_num_ = line_num;
- return address == context->address_;
+ context->line_num_ = entry.line_;
+ return entry.address_ == context->address_;
}
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index ed1597b82a..8a3db6ccf3 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -819,20 +819,50 @@ class DexFile {
}
}
+ struct PositionInfo {
+ PositionInfo()
+ : address_(0),
+ line_(0),
+ source_file_(nullptr),
+ prologue_end_(false),
+ epilogue_begin_(false) {
+ }
+
+ uint32_t address_; // In 16-bit code units.
+ uint32_t line_; // Source code line number starting at 1.
+ const char* source_file_; // nullptr if the file from ClassDef still applies.
+ bool prologue_end_;
+ bool epilogue_begin_;
+ };
+
// Callback for "new position table entry".
// Returning true causes the decoder to stop early.
- typedef bool (*DexDebugNewPositionCb)(void* context, uint32_t address, uint32_t line_num);
+ typedef bool (*DexDebugNewPositionCb)(void* context, const PositionInfo& entry);
+
+ struct LocalInfo {
+ LocalInfo()
+ : name_(nullptr),
+ descriptor_(nullptr),
+ signature_(nullptr),
+ start_address_(0),
+ end_address_(0),
+ reg_(0),
+ is_live_(false) {
+ }
- // Callback for "new locals table entry". "signature" is an empty string
- // if no signature is available for an entry.
- typedef void (*DexDebugNewLocalCb)(void* context, uint16_t reg,
- uint32_t start_address,
- uint32_t end_address,
- const char* name,
- const char* descriptor,
- const char* signature);
+ const char* name_; // E.g., list. It can be nullptr if unknown.
+ const char* descriptor_; // E.g., Ljava/util/LinkedList;
+ const char* signature_; // E.g., java.util.LinkedList<java.lang.Integer>
+ uint32_t start_address_; // PC location where the local is first defined.
+ uint32_t end_address_; // PC location where the local is no longer defined.
+ uint16_t reg_; // Dex register which stores the values.
+ bool is_live_; // Is the local defined and live.
+ };
+
+ // Callback for "new locals table entry".
+ typedef void (*DexDebugNewLocalCb)(void* context, const LocalInfo& entry);
- static bool LineNumForPcCb(void* context, uint32_t address, uint32_t line_num);
+ static bool LineNumForPcCb(void* context, const PositionInfo& entry);
const AnnotationsDirectoryItem* GetAnnotationsDirectory(const ClassDef& class_def) const {
if (class_def.annotations_off_ == 0) {
@@ -1044,21 +1074,6 @@ class DexFile {
DBG_LINE_RANGE = 15,
};
- struct LocalInfo {
- LocalInfo()
- : name_(nullptr), descriptor_(nullptr), signature_(nullptr), start_address_(0),
- is_live_(false) {}
-
- const char* name_; // E.g., list
- const char* descriptor_; // E.g., Ljava/util/LinkedList;
- const char* signature_; // E.g., java.util.LinkedList<java.lang.Integer>
- uint16_t start_address_; // PC location where the local is first defined.
- bool is_live_; // Is the local defined and live.
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LocalInfo);
- };
-
struct LineNumFromPcContext {
LineNumFromPcContext(uint32_t address, uint32_t line_num)
: address_(address), line_num_(line_num) {}
@@ -1068,15 +1083,6 @@ class DexFile {
DISALLOW_COPY_AND_ASSIGN(LineNumFromPcContext);
};
- void InvokeLocalCbIfLive(void* context, int reg, uint32_t end_address,
- LocalInfo* local_in_reg, DexDebugNewLocalCb local_cb) const {
- if (local_cb != nullptr && local_in_reg[reg].is_live_) {
- local_cb(context, reg, local_in_reg[reg].start_address_, end_address,
- local_in_reg[reg].name_, local_in_reg[reg].descriptor_,
- local_in_reg[reg].signature_ != nullptr ? local_in_reg[reg].signature_ : "");
- }
- }
-
// Determine the source file line number based on the program counter.
// "pc" is an offset, in 16-bit units, from the start of the method's code.
//
@@ -1088,9 +1094,13 @@ class DexFile {
int32_t GetLineNumFromPC(ArtMethod* method, uint32_t rel_pc) const
SHARED_REQUIRES(Locks::mutator_lock_);
- void DecodeDebugInfo(const CodeItem* code_item, bool is_static, uint32_t method_idx,
- DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
- void* context) const;
+ // Returns false if there is no debugging information or if it can not be decoded.
+ bool DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, uint32_t method_idx,
+ DexDebugNewLocalCb local_cb, void* context) const;
+
+ // Returns false if there is no debugging information or if it can not be decoded.
+ bool DecodeDebugPositionInfo(const CodeItem* code_item, DexDebugNewPositionCb position_cb,
+ void* context) const;
const char* GetSourceFile(const ClassDef& class_def) const {
if (class_def.source_file_idx_ == 0xffffffff) {
@@ -1200,10 +1210,6 @@ class DexFile {
// Returns true if the header magic and version numbers are of the expected values.
bool CheckMagicAndVersion(std::string* error_msg) const;
- void DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32_t method_idx,
- DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
- void* context, const uint8_t* stream, LocalInfo* local_in_reg) const;
-
// Check whether a location denotes a multidex dex file. This is a very simple check: returns
// whether the string contains the separator character.
static bool IsMultiDexLocation(const char* location);
@@ -1275,6 +1281,7 @@ class DexFileParameterIterator {
}
}
bool HasNext() const { return pos_ < size_; }
+ size_t Size() const { return size_; }
void Next() { ++pos_; }
uint16_t GetTypeIdx() {
return type_list_->GetTypeItem(pos_).type_idx_;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index da9a79e1a2..6d72f3142e 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1352,28 +1352,10 @@ void Heap::TrimSpaces(Thread* self) {
uint64_t gc_heap_end_ns = NanoTime();
// We never move things in the native heap, so we can finish the GC at this point.
FinishGC(self, collector::kGcTypeNone);
- size_t native_reclaimed = 0;
-#ifdef __ANDROID__
- // Only trim the native heap if we don't care about pauses.
- if (!CareAboutPauseTimes()) {
-#if defined(USE_DLMALLOC)
- // Trim the native heap.
- dlmalloc_trim(0);
- dlmalloc_inspect_all(DlmallocMadviseCallback, &native_reclaimed);
-#elif defined(USE_JEMALLOC)
- // Jemalloc does it's own internal trimming.
-#else
- UNIMPLEMENTED(WARNING) << "Add trimming support";
-#endif
- }
-#endif // __ANDROID__
- uint64_t end_ns = NanoTime();
VLOG(heap) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns)
- << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration="
- << PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed)
- << ") heaps. Managed heap utilization of " << static_cast<int>(100 * managed_utilization)
- << "%.";
+ << ", advised=" << PrettySize(managed_reclaimed) << ") heap. Managed heap utilization of "
+ << static_cast<int>(100 * managed_utilization) << "%.";
ATRACE_END();
}
diff --git a/runtime/jdwp/jdwp_expand_buf.cc b/runtime/jdwp/jdwp_expand_buf.cc
index e492d7eb26..961dd369c8 100644
--- a/runtime/jdwp/jdwp_expand_buf.cc
+++ b/runtime/jdwp/jdwp_expand_buf.cc
@@ -164,7 +164,7 @@ static void SetUtf8String(uint8_t* buf, const char* str, size_t strLen) {
* have stored null bytes in a multi-byte encoding).
*/
void expandBufAddUtf8String(ExpandBuf* pBuf, const char* s) {
- int strLen = strlen(s);
+ int strLen = (s != nullptr ? strlen(s) : 0);
ensureSpace(pBuf, sizeof(uint32_t) + strLen);
SetUtf8String(pBuf->storage + pBuf->curLen, s, strLen);
pBuf->curLen += sizeof(uint32_t) + strLen;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 4032c7b832..4e829168bc 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -23,7 +23,6 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "gc/accounting/bitmap.h"
-#include "gc/allocator/dlmalloc.h"
#include "gc_root.h"
#include "jni.h"
#include "oat_file.h"
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 5b1061087d..2b92303fe2 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -399,6 +399,7 @@ static void MaybeOverrideVerbosity() {
// gLogVerbosity.oat = true; // TODO: don't check this in!
// gLogVerbosity.profiler = true; // TODO: don't check this in!
// gLogVerbosity.signals = true; // TODO: don't check this in!
+ // gLogVerbosity.simulator = true; // TODO: don't check this in!
// gLogVerbosity.startup = true; // TODO: don't check this in!
// gLogVerbosity.third_party_jni = true; // TODO: don't check this in!
// gLogVerbosity.threads = true; // TODO: don't check this in!
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index fad00c73d8..06b40fd925 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -87,6 +87,8 @@ TEST_F(ParsedOptionsTest, ParsedOptions) {
EXPECT_FALSE(VLOG_IS_ON(jdwp));
EXPECT_TRUE(VLOG_IS_ON(jni));
EXPECT_FALSE(VLOG_IS_ON(monitor));
+ EXPECT_FALSE(VLOG_IS_ON(signals));
+ EXPECT_FALSE(VLOG_IS_ON(simulator));
EXPECT_FALSE(VLOG_IS_ON(startup));
EXPECT_FALSE(VLOG_IS_ON(third_party_jni));
EXPECT_FALSE(VLOG_IS_ON(threads));
diff --git a/runtime/simulator/Android.mk b/runtime/simulator/Android.mk
new file mode 100644
index 0000000000..c154eb6346
--- /dev/null
+++ b/runtime/simulator/Android.mk
@@ -0,0 +1,105 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.common_build.mk
+
+LIBART_SIMULATOR_SRC_FILES := \
+ code_simulator.cc \
+ code_simulator_arm64.cc
+
+# $(1): target or host
+# $(2): ndebug or debug
+define build-libart-simulator
+ ifneq ($(1),target)
+ ifneq ($(1),host)
+ $$(error expected target or host for argument 1, received $(1))
+ endif
+ endif
+ ifneq ($(2),ndebug)
+ ifneq ($(2),debug)
+ $$(error expected ndebug or debug for argument 2, received $(2))
+ endif
+ endif
+
+ art_target_or_host := $(1)
+ art_ndebug_or_debug := $(2)
+
+ include $(CLEAR_VARS)
+ ifeq ($$(art_target_or_host),host)
+ LOCAL_IS_HOST_MODULE := true
+ endif
+ LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+ ifeq ($$(art_ndebug_or_debug),ndebug)
+ LOCAL_MODULE := libart-simulator
+ else # debug
+ LOCAL_MODULE := libartd-simulator
+ endif
+
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+ LOCAL_SRC_FILES := $$(LIBART_SIMULATOR_SRC_FILES)
+
+ ifeq ($$(art_target_or_host),target)
+ $(call set-target-local-clang-vars)
+ $(call set-target-local-cflags-vars,$(2))
+ else # host
+ LOCAL_CLANG := $(ART_HOST_CLANG)
+ LOCAL_LDLIBS := $(ART_HOST_LDLIBS)
+ LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
+ ifeq ($$(art_ndebug_or_debug),debug)
+ LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+ else
+ LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+ endif
+ endif
+
+ LOCAL_SHARED_LIBRARIES += liblog
+ ifeq ($$(art_ndebug_or_debug),debug)
+ LOCAL_SHARED_LIBRARIES += libartd
+ else
+ LOCAL_SHARED_LIBRARIES += libart
+ endif
+
+ LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+ LOCAL_MULTILIB := both
+
+ LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+ # For simulator_arm64.
+ ifeq ($$(art_ndebug_or_debug),debug)
+ LOCAL_SHARED_LIBRARIES += libvixld
+ else
+ LOCAL_SHARED_LIBRARIES += libvixl
+ endif
+ ifeq ($$(art_target_or_host),target)
+ include $(BUILD_SHARED_LIBRARY)
+ else # host
+ include $(BUILD_HOST_SHARED_LIBRARY)
+ endif
+endef
+
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
+ $(eval $(call build-libart-simulator,host,ndebug))
+endif
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
+ $(eval $(call build-libart-simulator,host,debug))
+endif
diff --git a/runtime/simulator/code_simulator.cc b/runtime/simulator/code_simulator.cc
new file mode 100644
index 0000000000..1a1116050e
--- /dev/null
+++ b/runtime/simulator/code_simulator.cc
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#include "simulator/code_simulator.h"
+#include "simulator/code_simulator_arm64.h"
+
+namespace art {
+
+CodeSimulator* CodeSimulator::CreateCodeSimulator(InstructionSet target_isa) {
+ switch (target_isa) {
+ case kArm64:
+ return arm64::CodeSimulatorArm64::CreateCodeSimulatorArm64();
+ default:
+ return nullptr;
+ }
+}
+
+CodeSimulator* CreateCodeSimulator(InstructionSet target_isa) {
+ return CodeSimulator::CreateCodeSimulator(target_isa);
+}
+
+} // namespace art
diff --git a/runtime/simulator/code_simulator.h b/runtime/simulator/code_simulator.h
new file mode 100644
index 0000000000..bd48909e41
--- /dev/null
+++ b/runtime/simulator/code_simulator.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
+#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
+
+#include "arch/instruction_set.h"
+
+namespace art {
+
+class CodeSimulator {
+ public:
+ CodeSimulator() {}
+ virtual ~CodeSimulator() {}
+ // Returns a null pointer if a simulator cannot be found for target_isa.
+ static CodeSimulator* CreateCodeSimulator(InstructionSet target_isa);
+
+ virtual void RunFrom(intptr_t code_buffer) = 0;
+
+ // Get return value according to C ABI.
+ virtual bool GetCReturnBool() const = 0;
+ virtual int32_t GetCReturnInt32() const = 0;
+ virtual int64_t GetCReturnInt64() const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CodeSimulator);
+};
+
+extern "C" CodeSimulator* CreateCodeSimulator(InstructionSet target_isa);
+
+} // namespace art
+
+#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_H_
diff --git a/runtime/simulator/code_simulator_arm64.cc b/runtime/simulator/code_simulator_arm64.cc
new file mode 100644
index 0000000000..39dfa6dafb
--- /dev/null
+++ b/runtime/simulator/code_simulator_arm64.cc
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#include "simulator/code_simulator_arm64.h"
+
+namespace art {
+namespace arm64 {
+
+// VIXL has not been tested on 32bit architectures, so vixl::Simulator is not always
+// available. To avoid linker error on these architectures, we check if we can simulate
+// in the beginning of following methods, with compile time constant `kCanSimulate`.
+// TODO: when vixl::Simulator is always available, remove the these checks.
+
+CodeSimulatorArm64* CodeSimulatorArm64::CreateCodeSimulatorArm64() {
+ if (kCanSimulate) {
+ return new CodeSimulatorArm64();
+ } else {
+ return nullptr;
+ }
+}
+
+CodeSimulatorArm64::CodeSimulatorArm64()
+ : CodeSimulator(), decoder_(nullptr), simulator_(nullptr) {
+ DCHECK(kCanSimulate);
+ decoder_ = new vixl::Decoder();
+ simulator_ = new vixl::Simulator(decoder_);
+}
+
+CodeSimulatorArm64::~CodeSimulatorArm64() {
+ DCHECK(kCanSimulate);
+ delete simulator_;
+ delete decoder_;
+}
+
+void CodeSimulatorArm64::RunFrom(intptr_t code_buffer) {
+ DCHECK(kCanSimulate);
+ simulator_->RunFrom(reinterpret_cast<const vixl::Instruction*>(code_buffer));
+}
+
+bool CodeSimulatorArm64::GetCReturnBool() const {
+ DCHECK(kCanSimulate);
+ return simulator_->wreg(0);
+}
+
+int32_t CodeSimulatorArm64::GetCReturnInt32() const {
+ DCHECK(kCanSimulate);
+ return simulator_->wreg(0);
+}
+
+int64_t CodeSimulatorArm64::GetCReturnInt64() const {
+ DCHECK(kCanSimulate);
+ return simulator_->xreg(0);
+}
+
+} // namespace arm64
+} // namespace art
diff --git a/runtime/simulator/code_simulator_arm64.h b/runtime/simulator/code_simulator_arm64.h
new file mode 100644
index 0000000000..10fceb98f7
--- /dev/null
+++ b/runtime/simulator/code_simulator_arm64.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+#define ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
+
+#include "memory"
+#include "simulator/code_simulator.h"
+// TODO: make vixl clean wrt -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "vixl/a64/simulator-a64.h"
+#pragma GCC diagnostic pop
+
+namespace art {
+namespace arm64 {
+
+class CodeSimulatorArm64 : public CodeSimulator {
+ public:
+ static CodeSimulatorArm64* CreateCodeSimulatorArm64();
+ virtual ~CodeSimulatorArm64();
+
+ void RunFrom(intptr_t code_buffer) OVERRIDE;
+
+ bool GetCReturnBool() const OVERRIDE;
+ int32_t GetCReturnInt32() const OVERRIDE;
+ int64_t GetCReturnInt64() const OVERRIDE;
+
+ private:
+ CodeSimulatorArm64();
+
+ vixl::Decoder* decoder_;
+ vixl::Simulator* simulator_;
+
+ // TODO: Enable CodeSimulatorArm64 for more host ISAs once vixl::Simulator supports them.
+ static constexpr bool kCanSimulate = (kRuntimeISA == kX86_64);
+
+ DISALLOW_COPY_AND_ASSIGN(CodeSimulatorArm64);
+};
+
+} // namespace arm64
+} // namespace art
+
+#endif // ART_RUNTIME_SIMULATOR_CODE_SIMULATOR_ARM64_H_
diff --git a/test/008-exceptions/src/Main.java b/test/008-exceptions/src/Main.java
index 9e3477a47b..b8231f12bd 100644
--- a/test/008-exceptions/src/Main.java
+++ b/test/008-exceptions/src/Main.java
@@ -60,7 +60,7 @@ public class Main {
} catch (NullPointerException npe) {
System.out.print("Got an NPE: ");
System.out.println(npe.getMessage());
- npe.printStackTrace();
+ npe.printStackTrace(System.out);
}
}
public static void main (String args[]) {
@@ -103,7 +103,7 @@ public class Main {
System.out.println(e.getCause());
}
} catch (Exception error) {
- error.printStackTrace();
+ error.printStackTrace(System.out);
}
}
@@ -126,7 +126,7 @@ public class Main {
System.out.println(e.getCause());
}
} catch (Exception error) {
- error.printStackTrace();
+ error.printStackTrace(System.out);
}
}
}
diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java
index 865355ce97..32122e4dcd 100644
--- a/test/444-checker-nce/src/Main.java
+++ b/test/444-checker-nce/src/Main.java
@@ -16,11 +16,11 @@
public class Main {
- /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier (before)
+ /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier (after)
+ /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (after)
/// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
public Main keepTest(Main m) {
@@ -31,7 +31,7 @@ public class Main {
/// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: Main Main.thisTest() instruction_simplifier (after)
+ /// CHECK-START: Main Main.thisTest() instruction_simplifier_after_types (after)
/// CHECK-NOT: NullCheck
/// CHECK: InvokeStaticOrDirect
public Main thisTest() {
@@ -45,7 +45,7 @@ public class Main {
/// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after)
+ /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier_after_types (after)
/// CHECK-NOT: NullCheck
public Main newInstanceRemoveTest() {
Main m = new Main();
@@ -57,7 +57,7 @@ public class Main {
/// CHECK: NullCheck
/// CHECK: ArrayGet
- /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after)
+ /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier_after_types (after)
/// CHECK: NewArray
/// CHECK-NOT: NullCheck
/// CHECK: ArrayGet
@@ -66,11 +66,11 @@ public class Main {
return ms[0];
}
- /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier (before)
+ /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (before)
/// CHECK: NewInstance
/// CHECK: NullCheck
- /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier (after)
+ /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (after)
/// CHECK: NewInstance
/// CHECK-NOT: NullCheck
public Main ifRemoveTest(boolean flag) {
@@ -83,11 +83,11 @@ public class Main {
return m.g();
}
- /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier (before)
+ /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (before)
/// CHECK: NewInstance
/// CHECK: NullCheck
- /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier (after)
+ /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (after)
/// CHECK: NewInstance
/// CHECK: NullCheck
public Main ifKeepTest(boolean flag) {
@@ -98,10 +98,10 @@ public class Main {
return m.g();
}
- /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier (before)
+ /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier (after)
+ /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: NullCheck
public Main forRemoveTest(int count) {
Main a = new Main();
@@ -114,10 +114,10 @@ public class Main {
return m.g();
}
- /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier (before)
+ /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier (after)
+ /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (after)
/// CHECK: NullCheck
public Main forKeepTest(int count) {
Main a = new Main();
@@ -132,10 +132,10 @@ public class Main {
return m.g();
}
- /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier (before)
+ /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier (after)
+ /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: NullCheck
public Main phiFlowRemoveTest(int count) {
Main a = new Main();
@@ -154,10 +154,10 @@ public class Main {
return n.g();
}
- /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier (before)
+ /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier (after)
+ /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (after)
/// CHECK: NullCheck
public Main phiFlowKeepTest(int count) {
Main a = new Main();
@@ -181,7 +181,7 @@ public class Main {
/// CHECK-START: Main Main.scopeRemoveTest(int, Main) ssa_builder (after)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after)
+ /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier_after_types (after)
/// CHECK-NOT: NullCheck
public Main scopeRemoveTest(int count, Main a) {
Main m = null;
@@ -196,10 +196,10 @@ public class Main {
return m;
}
- /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier (before)
+ /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier (after)
+ /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (after)
/// CHECK: NullCheck
public Main scopeKeepTest(int count, Main a) {
Main m = new Main();
@@ -214,10 +214,10 @@ public class Main {
return m;
}
- /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier (before)
+ /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier (after)
+ /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (after)
/// CHECK-NOT: NullCheck
public Main scopeIfNotNullRemove(Main m) {
if (m != null) {
@@ -226,10 +226,10 @@ public class Main {
return m;
}
- /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier (before)
+ /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
- /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier (after)
+ /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (after)
/// CHECK: NullCheck
public Main scopeIfKeep(Main m) {
if (m == null) {
@@ -258,11 +258,11 @@ public class Main {
class ListElement {
private ListElement next;
- /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier (before)
+ /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (before)
/// CHECK: NullCheck
/// CHECK: NullCheck
- /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier (after)
+ /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (after)
/// CHECK-NOT: NullCheck
static boolean isShorter(ListElement x, ListElement y) {
ListElement xTail = x;
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index fd4dd5ecbf..f1f80caff0 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -72,49 +72,49 @@ final class FinalException extends Exception {}
public class Main {
- /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier (before)
+ /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier (after)
+ /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testSimpleRemove() {
Super s = new SubclassA();
((SubclassA)s).$noinline$g();
}
- /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier (before)
+ /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier (after)
+ /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (after)
/// CHECK: CheckCast
public void testSimpleKeep(Super s) {
((SubclassA)s).$noinline$f();
}
- /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier (before)
+ /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier (after)
+ /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public String testClassRemove() {
Object s = SubclassA.class;
return ((Class)s).getName();
}
- /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier (before)
+ /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier (after)
+ /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (after)
/// CHECK: CheckCast
public String testClassKeep() {
Object s = SubclassA.class;
return ((SubclassA)s).$noinline$h();
}
- /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testIfRemove(int x) {
Super s;
@@ -126,10 +126,10 @@ public class Main {
((SubclassA)s).$noinline$g();
}
- /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (after)
/// CHECK: CheckCast
public void testIfKeep(int x) {
Super s;
@@ -141,10 +141,10 @@ public class Main {
((SubclassA)s).$noinline$g();
}
- /// CHECK-START: void Main.testForRemove(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testForRemove(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testForRemove(int x) {
Super s = new SubclassA();
@@ -156,10 +156,10 @@ public class Main {
((SubclassA)s).$noinline$g();
}
- /// CHECK-START: void Main.testForKeep(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testForKeep(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (after)
/// CHECK: CheckCast
public void testForKeep(int x) {
Super s = new SubclassA();
@@ -171,10 +171,10 @@ public class Main {
((SubclassC)s).$noinline$g();
}
- /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (after)
/// CHECK: CheckCast
public void testPhiFromCall(int i) {
Object x;
@@ -186,12 +186,11 @@ public class Main {
((SubclassC)x).$noinline$g();
}
- /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
/// CHECK: CheckCast
- /// CHECK-NOT: CheckCast
- /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOf(Object o) {
if (o instanceof SubclassC) {
@@ -202,101 +201,11 @@ public class Main {
}
}
- public static boolean $inline$InstanceofSubclassB(Object o) { return o instanceof SubclassB; }
- public static boolean $inline$InstanceofSubclassC(Object o) { return o instanceof SubclassC; }
-
- /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) ssa_builder (after)
- /// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<IOf1:z\d+>> InstanceOf
- /// CHECK-DAG: NotEqual [<<IOf1>>,<<Cst1>>]
- /// CHECK-DAG: <<IOf2:z\d+>> InstanceOf
- /// CHECK-DAG: Equal [<<IOf2>>,<<Cst0>>]
-
- /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before)
- /// CHECK: CheckCast
- /// CHECK: CheckCast
- /// CHECK-NOT: CheckCast
-
- /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after)
- /// CHECK-NOT: CheckCast
- public void testInstanceOf_NotInlined(Object o) {
- if ((o instanceof SubclassC) == true) {
- ((SubclassC)o).$noinline$g();
- }
- if ((o instanceof SubclassB) != false) {
- ((SubclassB)o).$noinline$g();
- }
- }
-
- /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) ssa_builder (after)
- /// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<IOf1:z\d+>> InstanceOf
- /// CHECK-DAG: Equal [<<IOf1>>,<<Cst1>>]
- /// CHECK-DAG: <<IOf2:z\d+>> InstanceOf
- /// CHECK-DAG: NotEqual [<<IOf2>>,<<Cst0>>]
-
- /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before)
- /// CHECK: CheckCast
- /// CHECK: CheckCast
- /// CHECK-NOT: CheckCast
-
- /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after)
- /// CHECK-NOT: CheckCast
- public void testNotInstanceOf_NotInlined(Object o) {
- if ((o instanceof SubclassC) != true) {
- // Empty branch to flip the condition.
- } else {
- ((SubclassC)o).$noinline$g();
- }
- if ((o instanceof SubclassB) == false) {
- // Empty branch to flip the condition.
- } else {
- ((SubclassB)o).$noinline$g();
- }
- }
-
- /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) inliner (after)
- /// CHECK-DAG: <<IOf:z\d+>> InstanceOf
- /// CHECK-DAG: If [<<IOf>>]
-
- /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (before)
- /// CHECK: CheckCast
- /// CHECK-NOT: CheckCast
-
- /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (after)
- /// CHECK-NOT: CheckCast
- public void testInstanceOf_Inlined(Object o) {
- if (!$inline$InstanceofSubclassC(o)) {
- // Empty branch to flip the condition.
- } else {
- ((SubclassC)o).$noinline$g();
- }
- }
-
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) inliner (after)
- /// CHECK-DAG: <<IOf:z\d+>> InstanceOf
- /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<IOf>>]
- /// CHECK-DAG: If [<<Not>>]
-
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (before)
- /// CHECK: CheckCast
- /// CHECK-NOT: CheckCast
-
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (after)
- /// CHECK-NOT: CheckCast
- public void testNotInstanceOf_Inlined(Object o) {
- if ($inline$InstanceofSubclassC(o)) {
- ((SubclassC)o).$noinline$g();
- }
- }
-
- /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (after)
/// CHECK: CheckCast
/// CHECK: CheckCast
public void testInstanceOfKeep(Object o) {
@@ -308,11 +217,11 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfNested(Object o) {
if (o instanceof SubclassC) {
@@ -324,10 +233,10 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfWithPhi(int i) {
Object o;
@@ -342,10 +251,10 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfInFor(int n) {
Object o = new SubclassA();
@@ -359,10 +268,10 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfSubclass() {
Object o = new SubclassA();
@@ -371,10 +280,10 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfWithPhiSubclass(int i) {
Object o;
@@ -389,10 +298,10 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfWithPhiTop(int i) {
Object o;
@@ -407,10 +316,10 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfSubclassInFor(int n) {
Object o = new SubclassA();
@@ -424,10 +333,10 @@ public class Main {
}
}
- /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceOfTopInFor(int n) {
Object o = new SubclassA();
@@ -452,10 +361,10 @@ public class Main {
public SubclassA a = new SubclassA();
public static SubclassA b = new SubclassA();
- /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier (before)
+ /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier (after)
+ /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInstanceFieldGetSimpleRemove() {
Main m = new Main();
@@ -463,10 +372,10 @@ public class Main {
((SubclassA)a).$noinline$g();
}
- /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier (before)
+ /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier (after)
+ /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testStaticFieldGetSimpleRemove() {
Super b = Main.b;
@@ -475,36 +384,36 @@ public class Main {
public SubclassA $noinline$getSubclass() { throw new RuntimeException(); }
- /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (before)
+ /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier (after)
+ /// CHECK-START: void Main.testArraySimpleRemove() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testArraySimpleRemove() {
Super[] b = new SubclassA[10];
SubclassA[] c = (SubclassA[])b;
}
- /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier (before)
+ /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier (after)
+ /// CHECK-START: void Main.testInvokeSimpleRemove() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testInvokeSimpleRemove() {
Super b = $noinline$getSubclass();
((SubclassA)b).$noinline$g();
}
- /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier (before)
+ /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier_after_types (before)
/// CHECK: CheckCast
- /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier (after)
+ /// CHECK-START: void Main.testArrayGetSimpleRemove() instruction_simplifier_after_types (after)
/// CHECK-NOT: CheckCast
public void testArrayGetSimpleRemove() {
Super[] a = new SubclassA[10];
((SubclassA)a[0]).$noinline$g();
}
- /// CHECK-START: int Main.testLoadExceptionInCatchNonExact(int, int) ssa_builder (after)
+ /// CHECK-START: int Main.testLoadExceptionInCatchNonExact(int, int) reference_type_propagation (after)
/// CHECK: LoadException klass:java.lang.ArithmeticException can_be_null:false exact:false
public int testLoadExceptionInCatchNonExact(int x, int y) {
try {
@@ -514,7 +423,7 @@ public class Main {
}
}
- /// CHECK-START: int Main.testLoadExceptionInCatchExact(int) ssa_builder (after)
+ /// CHECK-START: int Main.testLoadExceptionInCatchExact(int) reference_type_propagation (after)
/// CHECK: LoadException klass:FinalException can_be_null:false exact:true
public int testLoadExceptionInCatchExact(int x) {
try {
@@ -528,7 +437,7 @@ public class Main {
}
}
- /// CHECK-START: int Main.testLoadExceptionInCatchAll(int, int) ssa_builder (after)
+ /// CHECK-START: int Main.testLoadExceptionInCatchAll(int, int) reference_type_propagation (after)
/// CHECK: LoadException klass:java.lang.Throwable can_be_null:false exact:false
public int testLoadExceptionInCatchAll(int x, int y) {
try {
@@ -549,7 +458,7 @@ public class Main {
return genericFinal.get();
}
- /// CHECK-START: SubclassC Main.inlineGenerics() ssa_builder (after)
+ /// CHECK-START: SubclassC Main.inlineGenerics() reference_type_propagation (after)
/// CHECK: <<Invoke:l\d+>> InvokeStaticOrDirect klass:SubclassC exact:false
/// CHECK-NEXT: Return [<<Invoke>>]
@@ -561,7 +470,7 @@ public class Main {
return c;
}
- /// CHECK-START: Final Main.inlineGenericsFinal() ssa_builder (after)
+ /// CHECK-START: Final Main.inlineGenericsFinal() reference_type_propagation (after)
/// CHECK: <<Invoke:l\d+>> InvokeStaticOrDirect klass:Final exact:true
/// CHECK-NEXT: Return [<<Invoke>>]
@@ -603,7 +512,7 @@ public class Main {
return new SubclassA();
}
- /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) ssa_builder (after)
+ /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:Super
/// CHECK: NullCheck [<<Phi>>] klass:Super
@@ -625,7 +534,7 @@ public class Main {
/// CHECK: CheckCast [<<Param>>,<<Clazz>>]
/// CHECK: BoundType [<<Param>>] can_be_null:true
- /// CHECK-START: java.lang.String Main.checkcastPreserveNullCheck(java.lang.Object) instruction_simplifier (after)
+ /// CHECK-START: java.lang.String Main.checkcastPreserveNullCheck(java.lang.Object) instruction_simplifier_after_types (after)
/// CHECK: <<This:l\d+>> ParameterValue
/// CHECK: <<Param:l\d+>> ParameterValue
/// CHECK: <<Clazz:l\d+>> LoadClass
@@ -637,7 +546,7 @@ public class Main {
}
- /// CHECK-START: void Main.argumentCheck(Super, double, SubclassA, Final) ssa_builder (after)
+ /// CHECK-START: void Main.argumentCheck(Super, double, SubclassA, Final) reference_type_propagation (after)
/// CHECK: ParameterValue klass:Main can_be_null:false exact:false
/// CHECK: ParameterValue klass:Super can_be_null:true exact:false
/// CHECK: ParameterValue
@@ -653,7 +562,7 @@ public class Main {
private int mainField = 0;
- /// CHECK-START: SuperInterface Main.getWiderType(boolean, Interface, OtherInterface) ssa_builder (after)
+ /// CHECK-START: SuperInterface Main.getWiderType(boolean, Interface, OtherInterface) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private SuperInterface getWiderType(boolean cond, Interface a, OtherInterface b) {
@@ -709,7 +618,7 @@ public class Main {
getSuper();
}
- /// CHECK-START: void Main.testLoopPhiWithNullFirstInput(boolean) ssa_builder (after)
+ /// CHECK-START: void Main.testLoopPhiWithNullFirstInput(boolean) reference_type_propagation (after)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Main:l\d+>> NewInstance klass:Main exact:true
/// CHECK-DAG: <<LoopPhi:l\d+>> Phi [<<Null>>,<<LoopPhi>>,<<Main>>] klass:Main exact:true
@@ -722,7 +631,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.testLoopPhisWithNullAndCrossUses(boolean) ssa_builder (after)
+ /// CHECK-START: void Main.testLoopPhisWithNullAndCrossUses(boolean) reference_type_propagation (after)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<PhiA:l\d+>> Phi [<<Null>>,<<PhiB:l\d+>>,<<PhiA>>] klass:java.lang.Object exact:false
/// CHECK-DAG: <<PhiB>> Phi [<<Null>>,<<PhiB>>,<<PhiA>>] klass:java.lang.Object exact:false
@@ -738,7 +647,7 @@ public class Main {
}
}
- /// CHECK-START: java.lang.Object[] Main.testInstructionsWithUntypedParent() ssa_builder (after)
+ /// CHECK-START: java.lang.Object[] Main.testInstructionsWithUntypedParent() reference_type_propagation (after)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<LoopPhi:l\d+>> Phi [<<Null>>,<<Phi:l\d+>>] klass:java.lang.Object[] exact:true
/// CHECK-DAG: <<Array:l\d+>> NewArray klass:java.lang.Object[] exact:true
diff --git a/test/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java
index 0f65e44678..c873702408 100644
--- a/test/477-checker-bound-type/src/Main.java
+++ b/test/477-checker-bound-type/src/Main.java
@@ -17,7 +17,7 @@
public class Main {
- /// CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) reference_type_propagation (after)
/// CHECK: BoundType
public static Object boundTypeForIf(Object a) {
if (a != null) {
@@ -27,7 +27,7 @@ public class Main {
}
}
- /// CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after)
/// CHECK: BoundType
public static Object boundTypeForInstanceOf(Object a) {
if (a instanceof Main) {
@@ -37,7 +37,7 @@ public class Main {
}
}
- /// CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) reference_type_propagation (after)
/// CHECK-NOT: BoundType
public static Object noBoundTypeForIf(Object a) {
if (a == null) {
@@ -47,7 +47,7 @@ public class Main {
}
}
- /// CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after)
/// CHECK-NOT: BoundType
public static Object noBoundTypeForInstanceOf(Object a) {
if (a instanceof Main) {
diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java
index 3f6e48bbae..e827b1ed78 100644
--- a/test/530-checker-loops/src/Main.java
+++ b/test/530-checker-loops/src/Main.java
@@ -27,6 +27,7 @@ public class Main {
/// CHECK-START: int Main.linear(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linear(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -40,6 +41,7 @@ public class Main {
/// CHECK-START: int Main.linearDown(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearDown(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -53,6 +55,7 @@ public class Main {
/// CHECK-START: int Main.linearObscure(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearObscure(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -67,6 +70,7 @@ public class Main {
/// CHECK-START: int Main.linearVeryObscure(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearVeryObscure(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -79,8 +83,26 @@ public class Main {
return result;
}
+ /// CHECK-START: int Main.hiddenStride(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.hiddenStride(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ static int hiddenStride(int[] a) {
+ int result = 0;
+ for (int i = 1; i <= 1; i++) {
+ // Obscured unit stride.
+ for (int j = 0; j < a.length; j += i) {
+ result += a[j];
+ }
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.linearWhile(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearWhile(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -95,6 +117,7 @@ public class Main {
/// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -112,6 +135,7 @@ public class Main {
/// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -133,6 +157,7 @@ public class Main {
/// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -149,6 +174,7 @@ public class Main {
/// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -169,6 +195,7 @@ public class Main {
/// CHECK-START: int[] Main.linearWithParameter(int) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int[] Main.linearWithParameter(int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -182,6 +209,7 @@ public class Main {
/// CHECK-START: int[] Main.linearCopy(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int[] Main.linearCopy(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -197,6 +225,7 @@ public class Main {
/// CHECK-START: int Main.linearByTwo(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearByTwo(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -213,6 +242,7 @@ public class Main {
/// CHECK-START: int Main.linearByTwoSkip1(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearByTwoSkip1(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -226,6 +256,7 @@ public class Main {
/// CHECK-START: int Main.linearByTwoSkip2(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearByTwoSkip2(int[]) BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -240,6 +271,7 @@ public class Main {
/// CHECK-START: int Main.linearWithCompoundStride() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearWithCompoundStride() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -256,6 +288,7 @@ public class Main {
/// CHECK-START: int Main.linearWithLargePositiveStride() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearWithLargePositiveStride() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -273,6 +306,7 @@ public class Main {
/// CHECK-START: int Main.linearWithVeryLargePositiveStride() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearWithVeryLargePositiveStride() BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -290,6 +324,7 @@ public class Main {
/// CHECK-START: int Main.linearWithLargeNegativeStride() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearWithLargeNegativeStride() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -307,6 +342,7 @@ public class Main {
/// CHECK-START: int Main.linearWithVeryLargeNegativeStride() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearWithVeryLargeNegativeStride() BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -324,6 +360,7 @@ public class Main {
/// CHECK-START: int Main.linearForNEUp() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearForNEUp() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -338,6 +375,7 @@ public class Main {
/// CHECK-START: int Main.linearForNEDown() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearForNEDown() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -352,6 +390,7 @@ public class Main {
/// CHECK-START: int Main.linearDoWhileUp() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearDoWhileUp() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -367,6 +406,7 @@ public class Main {
/// CHECK-START: int Main.linearDoWhileDown() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearDoWhileDown() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -382,6 +422,7 @@ public class Main {
/// CHECK-START: int Main.linearShort() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.linearShort() BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -397,6 +438,7 @@ public class Main {
/// CHECK-START: int Main.invariantFromPreLoop(int[], int) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.invariantFromPreLoop(int[], int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -422,6 +464,7 @@ public class Main {
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
/// CHECK-DAG: ArraySet
+ //
/// CHECK-START: void Main.linearTriangularOnTwoArrayLengths(int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-DAG: ArrayGet
@@ -451,6 +494,7 @@ public class Main {
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
/// CHECK-DAG: ArraySet
+ //
/// CHECK-START: void Main.linearTriangularOnOneArrayLength(int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-DAG: ArrayGet
@@ -480,6 +524,7 @@ public class Main {
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
/// CHECK-DAG: ArraySet
+ //
/// CHECK-START: void Main.linearTriangularOnParameter(int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-DAG: ArrayGet
@@ -502,7 +547,54 @@ public class Main {
}
}
- // Verifier for triangular methods.
+ /// CHECK-START: void Main.linearTriangularVariations(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ //
+ /// CHECK-START: void Main.linearTriangularVariations(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: Deoptimize
+ private static void linearTriangularVariations(int n) {
+ int[] a = new int[n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < i; j++) {
+ a[j] += 1;
+ }
+ for (int j = i - 1; j >= 0; j--) {
+ a[j] += 1;
+ }
+ for (int j = i; j < n; j++) {
+ a[j] += 1;
+ }
+ for (int j = n - 1; j > i - 1; j--) {
+ a[j] += 1;
+ }
+ }
+ verifyTriangular(a);
+ }
+
+ // Verifier for triangular loops.
private static void verifyTriangular(int[] a, int[] b, int m, int n) {
expectEquals(n, a.length);
for (int i = 0, k = m; i < n; i++) {
@@ -515,6 +607,14 @@ public class Main {
}
}
+ // Verifier for triangular loops.
+ private static void verifyTriangular(int[] a) {
+ int n = a.length;
+ for (int i = 0; i < n; i++) {
+ expectEquals(a[i], n + n);
+ }
+ }
+
/// CHECK-START: void Main.bubble(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
@@ -523,6 +623,7 @@ public class Main {
/// CHECK-DAG: If
/// CHECK-DAG: ArraySet
/// CHECK-DAG: ArraySet
+ //
/// CHECK-START: void Main.bubble(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-DAG: ArrayGet
@@ -546,6 +647,7 @@ public class Main {
/// CHECK-START: int Main.periodicIdiom(int) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.periodicIdiom(int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -563,6 +665,7 @@ public class Main {
/// CHECK-START: int Main.periodicSequence2(int) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.periodicSequence2(int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -586,6 +689,7 @@ public class Main {
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.periodicSequence4(int) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -610,6 +714,7 @@ public class Main {
/// CHECK-START: int Main.justRightUp1() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justRightUp1() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -624,6 +729,7 @@ public class Main {
/// CHECK-START: int Main.justRightUp2() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justRightUp2() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -638,6 +744,7 @@ public class Main {
/// CHECK-START: int Main.justRightUp3() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justRightUp3() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -652,6 +759,7 @@ public class Main {
/// CHECK-START: int Main.justOOBUp() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justOOBUp() BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -667,6 +775,7 @@ public class Main {
/// CHECK-START: int Main.justRightDown1() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justRightDown1() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -681,6 +790,7 @@ public class Main {
/// CHECK-START: int Main.justRightDown2() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justRightDown2() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -695,6 +805,7 @@ public class Main {
/// CHECK-START: int Main.justRightDown3() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justRightDown3() BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -709,6 +820,7 @@ public class Main {
/// CHECK-START: int Main.justOOBDown() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.justOOBDown() BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -724,6 +836,7 @@ public class Main {
/// CHECK-START: void Main.lowerOOB(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -735,6 +848,7 @@ public class Main {
/// CHECK-START: void Main.upperOOB(int[]) BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: void Main.upperOOB(int[]) BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -746,6 +860,7 @@ public class Main {
/// CHECK-START: void Main.doWhileUpOOB() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: void Main.doWhileUpOOB() BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -759,6 +874,7 @@ public class Main {
/// CHECK-START: void Main.doWhileDownOOB() BCE (before)
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: void Main.doWhileDownOOB() BCE (after)
/// CHECK-DAG: BoundsCheck
/// CHECK-NOT: Deoptimize
@@ -770,6 +886,55 @@ public class Main {
} while (-1 <= i);
}
+ /// CHECK-START: int[] Main.multiply1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ //
+ /// CHECK-START: int[] Main.multiply1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ /// CHECK-NOT: Deoptimize
+ private static int[] multiply1() {
+ int[] a = new int[10];
+ try {
+ for (int i = 0; i <= 3; i++) {
+ for (int j = 0; j <= 3; j++) {
+ // Range [0,9]: safe.
+ a[i * j] += 1;
+ }
+ }
+ } catch (Exception e) {
+ a[0] += 1000;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int[] Main.multiply2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ //
+ /// CHECK-START: int[] Main.multiply2() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: ArrayGet
+ /// CHECK-DAG: ArraySet
+ static int[] multiply2() {
+ int[] a = new int[10];
+ try {
+ for (int i = -3; i <= 3; i++) {
+ for (int j = -3; j <= 3; j++) {
+ // Range [-9,9]: unsafe.
+ a[i * j] += 1;
+ }
+ }
+ } catch (Exception e) {
+ a[0] += 1000;
+ }
+ return a;
+ }
+
/// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before)
/// CHECK-DAG: StaticFieldGet
/// CHECK-DAG: NullCheck
@@ -777,6 +942,7 @@ public class Main {
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
/// CHECK-DAG: StaticFieldSet
+ //
/// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
/// CHECK-DAG: StaticFieldGet
/// CHECK-NOT: NullCheck
@@ -803,6 +969,7 @@ public class Main {
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
/// CHECK-DAG: StaticFieldSet
+ //
/// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
/// CHECK-DAG: StaticFieldGet
/// CHECK-NOT: NullCheck
@@ -827,6 +994,7 @@ public class Main {
/// CHECK-DAG: ArrayLength
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
+ //
/// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
/// CHECK-DAG: Deoptimize
/// CHECK-DAG: Deoptimize
@@ -850,6 +1018,7 @@ public class Main {
/// CHECK-DAG: ArrayLength
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
+ //
/// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
/// CHECK-DAG: Deoptimize
/// CHECK-DAG: Deoptimize
@@ -873,6 +1042,7 @@ public class Main {
/// CHECK-DAG: ArrayLength
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
+ //
/// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
/// CHECK-NOT: NullCheck
/// CHECK-NOT: ArrayLength
@@ -897,6 +1067,7 @@ public class Main {
/// CHECK-DAG: ArrayLength
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
+ //
/// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
/// CHECK-DAG: NullCheck
/// CHECK-DAG: ArrayLength
@@ -918,6 +1089,7 @@ public class Main {
/// CHECK-DAG: ArrayLength
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
+ //
/// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
/// CHECK-DAG: NullCheck
/// CHECK-DAG: ArrayLength
@@ -951,6 +1123,7 @@ public class Main {
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
+ //
/// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
/// CHECK-DAG: NullCheck
/// CHECK-DAG: ArrayLength
@@ -1015,6 +1188,7 @@ public class Main {
/// CHECK-DAG: ArrayGet
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: ArrayGet
+ //
/// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], java.lang.Integer[], int, int) BCE (after)
/// CHECK-DAG: If
/// CHECK-NOT: BoundsCheck
@@ -1087,6 +1261,8 @@ public class Main {
expectEquals(55, linearObscure(x));
expectEquals(0, linearVeryObscure(empty));
expectEquals(55, linearVeryObscure(x));
+ expectEquals(0, hiddenStride(empty));
+ expectEquals(55, hiddenStride(x));
expectEquals(0, linearWhile(empty));
expectEquals(55, linearWhile(x));
expectEquals(0, linearThreeWayPhi(empty));
@@ -1144,6 +1320,7 @@ public class Main {
linearTriangularOnTwoArrayLengths(10);
linearTriangularOnOneArrayLength(10);
linearTriangularOnParameter(10);
+ linearTriangularVariations(10);
// Sorting.
int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
@@ -1234,6 +1411,20 @@ public class Main {
}
expectEquals(1055, sResult);
+ // Multiplication.
+ {
+ int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 };
+ int[] a1 = multiply1();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a1[i], e1[i]);
+ }
+ int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 };
+ int[] a2 = multiply2();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a2[i], e2[i]);
+ }
+ }
+
// Dynamic BCE.
sResult = 0;
try {
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index ced3e50d41..98251e4af3 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -453,14 +453,16 @@ public class Main {
}
/// CHECK-START: float Main.test19(float[], float[]) load_store_elimination (before)
- /// CHECK: {{f\d+}} ArrayGet
- /// CHECK: {{f\d+}} ArrayGet
+ /// CHECK: <<IntTypeValue:i\d+>> ArrayGet
+ /// CHECK: ArraySet
+ /// CHECK: <<FloatTypeValue:f\d+>> ArrayGet
/// CHECK-START: float Main.test19(float[], float[]) load_store_elimination (after)
- /// CHECK: {{f\d+}} ArrayGet
- /// CHECK-NOT: {{f\d+}} ArrayGet
+ /// CHECK: <<IntTypeValue:i\d+>> ArrayGet
+ /// CHECK: ArraySet
+ /// CHECK: <<FloatTypeValue:f\d+>> ArrayGet
- // I/F, J/D aliasing should not happen any more and LSE should eliminate the load.
+ // I/F, J/D aliasing should keep the load/store.
static float test19(float[] fa1, float[] fa2) {
fa1[0] = fa2[0];
return fa1[0];
diff --git a/test/540-checker-rtp-bug/src/Main.java b/test/540-checker-rtp-bug/src/Main.java
index 9a9f0b6048..e9f16c04d9 100644
--- a/test/540-checker-rtp-bug/src/Main.java
+++ b/test/540-checker-rtp-bug/src/Main.java
@@ -21,14 +21,14 @@ final class Final {
}
public class Main {
- /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) ssa_builder (after)
+ /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: <<Class:l\d+>> LoadClass
/// CHECK: CheckCast [<<Phi>>,<<Class>>]
/// CHECK: <<Ret:l\d+>> BoundType [<<Phi>>] klass:Final
/// CHECK: Return [<<Ret>>]
- /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) instruction_simplifier (after)
+ /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) instruction_simplifier_after_types (after)
/// CHECK: <<Phi:l\d+>> Phi
/// CHECK: <<Class:l\d+>> LoadClass
/// CHECK: CheckCast [<<Phi>>,<<Class>>]
@@ -43,7 +43,7 @@ public class Main {
return (Final) x;
}
- /// CHECK-START: void Main.testKeepInstanceOf(java.lang.Object, boolean) ssa_builder (after)
+ /// CHECK-START: void Main.testKeepInstanceOf(java.lang.Object, boolean) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: <<Class:l\d+>> LoadClass
/// CHECK: InstanceOf [<<Phi>>,<<Class>>]
@@ -65,7 +65,7 @@ public class Main {
}
}
- /// CHECK-START: java.lang.String Main.testNoInline(java.lang.Object, boolean) ssa_builder (after)
+ /// CHECK-START: java.lang.String Main.testNoInline(java.lang.Object, boolean) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: <<NC:l\d+>> NullCheck [<<Phi>>]
/// CHECK: <<Ret:l\d+>> InvokeVirtual [<<NC>>] method_name:java.lang.Object.toString
diff --git a/test/549-checker-types-merge/src/Main.java b/test/549-checker-types-merge/src/Main.java
index 917073b1c9..dc27f10427 100644
--- a/test/549-checker-types-merge/src/Main.java
+++ b/test/549-checker-types-merge/src/Main.java
@@ -38,14 +38,14 @@ class ClassImplementsInterfaceA extends ClassSuper implements InterfaceA {}
public class Main {
- /// CHECK-START: java.lang.Object Main.testMergeNullContant(boolean) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeNullContant(boolean) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:Main
/// CHECK: Return [<<Phi>>]
private Object testMergeNullContant(boolean cond) {
return cond ? null : new Main();
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassExtendsB) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassExtendsB) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassExtendsA a, ClassExtendsB b) {
@@ -53,7 +53,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassExtendsA a, ClassSuper b) {
@@ -61,7 +61,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassSuper, ClassSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassSuper, ClassSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassSuper a, ClassSuper b) {
@@ -69,7 +69,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassOtherSuper, ClassSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassOtherSuper, ClassSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassOtherSuper a, ClassSuper b) {
@@ -77,7 +77,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassImplementsInterfaceA, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassImplementsInterfaceA, InterfaceSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClassWithInterface(boolean cond, ClassImplementsInterfaceA a, InterfaceSuper b) {
@@ -85,7 +85,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassSuper, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassSuper, InterfaceSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeClassWithInterface(boolean cond, ClassSuper a, InterfaceSuper b) {
@@ -93,7 +93,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceExtendsA a, InterfaceSuper b) {
@@ -101,7 +101,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceSuper a, InterfaceSuper b) {
@@ -109,7 +109,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceExtendsB) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceExtendsB) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceExtendsA a, InterfaceExtendsB b) {
@@ -117,7 +117,7 @@ public class Main {
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceOtherSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceOtherSuper) reference_type_propagation (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceSuper a, InterfaceOtherSuper b) {
diff --git a/test/552-checker-primitive-typeprop/info.txt b/test/552-checker-primitive-typeprop/info.txt
deleted file mode 100644
index 9d69056915..0000000000
--- a/test/552-checker-primitive-typeprop/info.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Test that phis with environment uses which can be properly typed are kept
-in --debuggable mode. \ No newline at end of file
diff --git a/test/552-checker-primitive-typeprop/smali/ArrayGet.smali b/test/552-checker-primitive-typeprop/smali/ArrayGet.smali
deleted file mode 100644
index 042fa0c80c..0000000000
--- a/test/552-checker-primitive-typeprop/smali/ArrayGet.smali
+++ /dev/null
@@ -1,245 +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.
-
-.class public LArrayGet;
-.super Ljava/lang/Object;
-
-
-# Test phi with fixed-type ArrayGet as an input and a matching second input.
-# The phi should be typed accordingly.
-
-## CHECK-START: void ArrayGet.matchingFixedType(float[], float) ssa_builder (after)
-## CHECK-NOT: Phi
-
-## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFixedType(float[], float) ssa_builder (after)
-## CHECK-DAG: <<Arg1:f\d+>> ParameterValue
-## CHECK-DAG: <<Aget:f\d+>> ArrayGet
-## CHECK-DAG: {{f\d+}} Phi [<<Aget>>,<<Arg1>>] reg:0
-.method public static matchingFixedType([FF)V
- .registers 8
-
- const v0, 0x0
- const v1, 0x1
-
- aget v0, p0, v0 # read value
- add-float v2, v0, v1 # float use fixes type
-
- float-to-int v2, p1
- if-eqz v2, :after
- move v0, p1
- :after
- # v0 = Phi [ArrayGet, Arg1] => float
-
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-
-# Test phi with fixed-type ArrayGet as an input and a conflicting second input.
-# The phi should be eliminated due to the conflict.
-
-## CHECK-START: void ArrayGet.conflictingFixedType(float[], int) ssa_builder (after)
-## CHECK-NOT: Phi
-
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType(float[], int) ssa_builder (after)
-## CHECK-NOT: Phi
-.method public static conflictingFixedType([FI)V
- .registers 8
-
- const v0, 0x0
- const v1, 0x1
-
- aget v0, p0, v0 # read value
- add-float v2, v0, v1 # float use fixes type
-
- if-eqz p1, :after
- move v0, p1
- :after
- # v0 = Phi [ArrayGet, Arg1] => conflict
-
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-
-# Same test as the one above, only this time tests that type of ArrayGet is not
-# changed.
-
-## CHECK-START: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after)
-## CHECK-NOT: Phi
-
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after)
-## CHECK-NOT: Phi
-
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after)
-## CHECK: {{i\d+}} ArrayGet
-.method public static conflictingFixedType2([IF)V
- .registers 8
-
- const v0, 0x0
- const v1, 0x1
-
- aget v0, p0, v0 # read value
- add-int v2, v0, v1 # int use fixes type
-
- float-to-int v2, p1
- if-eqz v2, :after
- move v0, p1
- :after
- # v0 = Phi [ArrayGet, Arg1] => conflict
-
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-
-# Test phi with free-type ArrayGet as an input and a matching second input.
-# The phi should be typed accordingly.
-
-## CHECK-START: void ArrayGet.matchingFreeType(float[], float) ssa_builder (after)
-## CHECK-NOT: Phi
-
-## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFreeType(float[], float) ssa_builder (after)
-## CHECK-DAG: <<Arg1:f\d+>> ParameterValue
-## CHECK-DAG: <<Aget:f\d+>> ArrayGet
-## CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Aget>>]
-## CHECK-DAG: {{f\d+}} Phi [<<Aget>>,<<Arg1>>] reg:0
-.method public static matchingFreeType([FF)V
- .registers 8
-
- const v0, 0x0
- const v1, 0x1
-
- aget v0, p0, v0 # read value, should be float but has no typed use
- aput v0, p0, v1 # aput does not disambiguate the type
-
- float-to-int v2, p1
- if-eqz v2, :after
- move v0, p1
- :after
- # v0 = Phi [ArrayGet, Arg1] => float
-
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-
-# Test phi with free-type ArrayGet as an input and a conflicting second input.
-# The phi will be kept and typed according to the second input despite the
-# conflict.
-
-## CHECK-START: void ArrayGet.conflictingFreeType(int[], float) ssa_builder (after)
-## CHECK-NOT: Phi
-
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFreeType(int[], float) ssa_builder (after)
-## CHECK-NOT: Phi
-
-.method public static conflictingFreeType([IF)V
- .registers 8
-
- const v0, 0x0
- const v1, 0x1
-
- aget v0, p0, v0 # read value, should be int but has no typed use
- aput v0, p0, v1
-
- float-to-int v2, p1
- if-eqz v2, :after
- move v0, p1
- :after
- # v0 = Phi [ArrayGet, Arg1] => float
-
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-
-# Test that real use of ArrayGet is propagated through phis. The following test
-# case uses ArrayGet indirectly through two phis. It also creates an unused
-# conflicting phi which should not be preserved.
-
-## CHECK-START: void ArrayGet.conflictingPhiUses(int[], float, boolean, boolean, boolean) ssa_builder (after)
-## CHECK: InvokeStaticOrDirect env:[[{{i\d+}},{{i\d+}},_,{{i\d+}},{{.*}}
-
-.method public static conflictingPhiUses([IFZZZ)V
- .registers 10
-
- const v0, 0x0
-
- # Create v1 = Phi [0x0, int ArrayGet]
- move v1, v0
- if-eqz p2, :else1
- aget v1, p0, v0
- :else1
-
- # Create v2 = Phi [v1, float]
- move v2, v1
- if-eqz p3, :else2
- move v2, p1
- :else2
-
- # Create v3 = Phi [v1, int]
- move v3, v1
- if-eqz p4, :else3
- move v3, v0
- :else3
-
- # Use v3 as int.
- add-int/lit8 v4, v3, 0x2a
-
- # Create env uses.
- invoke-static {}, Ljava/lang/System;->nanoTime()J
-
- return-void
-.end method
-
-# Test that the right ArrayGet equivalent is always selected. The following test
-# case uses ArrayGet as float through one phi and as an indeterminate type through
-# another. The situation needs to be resolved so that only one instruction
-# remains.
-
-## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) ssa_builder (after)
-## CHECK: {{f\d+}} ArrayGet
-
-## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) ssa_builder (after)
-## CHECK-NOT: {{i\d+}} ArrayGet
-
-.method public static typedVsUntypedPhiUse([FFZZ)V
- .registers 10
-
- const v0, 0x0
-
- # v1 = float ArrayGet
- aget v1, p0, v0
-
- # Create v2 = Phi [v1, 0.0f]
- move v2, v1
- if-eqz p2, :else1
- move v2, v0
- :else1
-
- # Use v2 as float
- cmpl-float v2, v2, p1
-
- # Create v3 = Phi [v1, 0.0f]
- move v3, v1
- if-eqz p3, :else2
- move v3, v0
- :else2
-
- # Use v3 without a determinate type.
- aput v3, p0, v0
-
- return-void
-.end method
diff --git a/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali b/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali
deleted file mode 100644
index 395feaaf61..0000000000
--- a/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali
+++ /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.
-
-.class public LSsaBuilder;
-.super Ljava/lang/Object;
-
-# Check that a dead phi with a live equivalent is replaced in an environment. The
-# following test case throws an exception and uses v0 afterwards. However, v0
-# contains a phi that is interpreted as int for the environment, and as float for
-# instruction use. SsaBuilder must substitute the int variant before removing it,
-# otherwise running the code with an array short enough to throw will crash at
-# runtime because v0 is undefined.
-
-## CHECK-START: int SsaBuilder.environmentPhi(boolean, int[]) ssa_builder (after)
-## CHECK-DAG: <<Cst0:f\d+>> FloatConstant 0
-## CHECK-DAG: <<Cst2:f\d+>> FloatConstant 2
-## CHECK-DAG: <<Phi:f\d+>> Phi [<<Cst0>>,<<Cst2>>]
-## CHECK-DAG: BoundsCheck env:[[<<Phi>>,{{i\d+}},{{z\d+}},{{l\d+}}]]
-
-.method public static environmentPhi(Z[I)I
- .registers 4
-
- const v0, 0x0
- if-eqz p0, :else
- const v0, 0x40000000
- :else
- # v0 = phi that can be both int and float
-
- :try_start
- const v1, 0x3
- aput v1, p1, v1
- const v0, 0x1 # generate catch phi for v0
- const v1, 0x4
- aput v1, p1, v1
- :try_end
- .catchall {:try_start .. :try_end} :use_as_float
-
- :use_as_float
- float-to-int v0, v0
- return v0
-.end method \ No newline at end of file
diff --git a/test/552-checker-primitive-typeprop/smali/TypePropagation.smali b/test/552-checker-primitive-typeprop/smali/TypePropagation.smali
deleted file mode 100644
index 58682a1923..0000000000
--- a/test/552-checker-primitive-typeprop/smali/TypePropagation.smali
+++ /dev/null
@@ -1,136 +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.
-
-.class public LTypePropagation;
-.super Ljava/lang/Object;
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDeadPhi(boolean, boolean, int, float, float) ssa_builder (after)
-## CHECK-NOT: Phi
-.method public static mergeDeadPhi(ZZIFF)V
- .registers 8
-
- if-eqz p0, :after1
- move p2, p3
- :after1
- # p2 = merge(int,float) = conflict
-
- if-eqz p1, :after2
- move p2, p4
- :after2
- # p2 = merge(conflict,float) = conflict
-
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeSameType(boolean, int, int) ssa_builder (after)
-## CHECK: {{i\d+}} Phi
-## CHECK-NOT: Phi
-.method public static mergeSameType(ZII)V
- .registers 8
- if-eqz p0, :after
- move p1, p2
- :after
- # p1 = merge(int,int) = int
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeVoidInput(boolean, boolean, int, int) ssa_builder (after)
-## CHECK: {{i\d+}} Phi
-## CHECK: {{i\d+}} Phi
-## CHECK-NOT: Phi
-.method public static mergeVoidInput(ZZII)V
- .registers 8
- :loop
- # p2 = void (loop phi) => p2 = merge(int,int) = int
- if-eqz p0, :after
- move p2, p3
- :after
- # p2 = merge(void,int) = int
- if-eqz p1, :loop
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDifferentSize(boolean, int, long) ssa_builder (after)
-## CHECK-NOT: Phi
-.method public static mergeDifferentSize(ZIJ)V
- .registers 8
- if-eqz p0, :after
- move-wide p1, p2
- :after
- # p1 = merge(int,long) = conflict
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeRefFloat(boolean, float, java.lang.Object) ssa_builder (after)
-## CHECK-NOT: Phi
-.method public static mergeRefFloat(ZFLjava/lang/Object;)V
- .registers 8
- if-eqz p0, :after
- move-object p1, p2
- :after
- # p1 = merge(float,reference) = conflict
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Success(boolean, float) ssa_builder (after)
-## CHECK: {{f\d+}} Phi
-## CHECK-NOT: Phi
-.method public static mergeIntFloat_Success(ZF)V
- .registers 8
- if-eqz p0, :after
- const/4 p1, 0x0
- :after
- # p1 = merge(float,0x0) = float
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Fail(boolean, int, float) ssa_builder (after)
-## CHECK-NOT: Phi
-.method public static mergeIntFloat_Fail(ZIF)V
- .registers 8
- if-eqz p0, :after
- move p1, p2
- :after
- # p1 = merge(int,float) = conflict
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
-
-## CHECK-START-DEBUGGABLE: void TypePropagation.updateAllUsersOnConflict(boolean, boolean, int, float, int) ssa_builder (after)
-## CHECK-NOT: Phi
-.method public static updateAllUsersOnConflict(ZZIFI)V
- .registers 8
-
- :loop1
- # loop phis for all args
- # p2 = merge(int,float) = float? => conflict
- move p2, p3
- if-eqz p0, :loop1
-
- :loop2
- # loop phis for all args
- # requests float equivalent of p4 phi in loop1 => conflict
- # propagates conflict to loop2's phis
- move p2, p4
- if-eqz p1, :loop2
-
- invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use
- return-void
-.end method
diff --git a/test/552-checker-primitive-typeprop/src/Main.java b/test/552-checker-primitive-typeprop/src/Main.java
deleted file mode 100644
index fe2343e48a..0000000000
--- a/test/552-checker-primitive-typeprop/src/Main.java
+++ /dev/null
@@ -1,43 +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.
- */
-
-import java.lang.reflect.Method;
-
-public class Main {
-
- // Workaround for b/18051191.
- class InnerClass {}
-
- private static void assertEquals(int expected, int actual) {
- if (expected != actual) {
- throw new Error("Wrong result, expected=" + expected + ", actual=" + actual);
- }
- }
-
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("SsaBuilder");
- Method m = c.getMethod("environmentPhi", new Class[] { boolean.class, int[].class });
-
- int[] array = new int[3];
- int result;
-
- result = (Integer) m.invoke(null, new Object[] { true, array } );
- assertEquals(2, result);
-
- result = (Integer) m.invoke(null, new Object[] { false, array } );
- assertEquals(0, result);
- }
-}
diff --git a/test/554-jit-profile-file/expected.txt b/test/554-jit-profile-file/expected.txt
deleted file mode 100644
index cde211e1c0..0000000000
--- a/test/554-jit-profile-file/expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-JNI_OnLoad called
-ProfileInfo:
-:classes.dex
- java.lang.String Main.hotMethod()
- void Main.main(java.lang.String[])
-:classes2.dex
- java.lang.String OtherDex.hotMethod()
diff --git a/test/554-jit-profile-file/info.txt b/test/554-jit-profile-file/info.txt
deleted file mode 100644
index b1bfe81eb8..0000000000
--- a/test/554-jit-profile-file/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Check that saving and restoring profile files works correctly in a JIT environment.
diff --git a/test/554-jit-profile-file/offline_profile.cc b/test/554-jit-profile-file/offline_profile.cc
deleted file mode 100644
index 75e441fc87..0000000000
--- a/test/554-jit-profile-file/offline_profile.cc
+++ /dev/null
@@ -1,59 +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.
- */
-
-#include "dex_file.h"
-
-#include "jit/offline_profiling_info.h"
-#include "jni.h"
-#include "mirror/class-inl.h"
-#include "oat_file_assistant.h"
-#include "oat_file_manager.h"
-#include "scoped_thread_state_change.h"
-#include "thread.h"
-
-namespace art {
-namespace {
-
-extern "C" JNIEXPORT jstring JNICALL Java_Main_getProfileInfoDump(
- JNIEnv* env, jclass cls, jstring filename) {
- std::string dex_location;
- {
- ScopedObjectAccess soa(Thread::Current());
- dex_location = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetDexFile()->GetLocation();
- }
- const OatFile* oat_file = Runtime::Current()->GetOatFileManager().GetPrimaryOatFile();
- std::vector<std::unique_ptr<const DexFile>> dex_files =
- OatFileAssistant::LoadDexFiles(*oat_file, dex_location.c_str());
- const char* filename_chars = env->GetStringUTFChars(filename, nullptr);
-
- std::vector<const DexFile*> dex_files_raw;
- for (size_t i = 0; i < dex_files.size(); i++) {
- dex_files_raw.push_back(dex_files[i].get());
- }
-
- ProfileCompilationInfo info(filename_chars);
-
- std::string result = info.Load(dex_files_raw)
- ? info.DumpInfo(/*print_full_dex_location*/false)
- : "Could not load profile info";
-
- env->ReleaseStringUTFChars(filename, filename_chars);
- // Return the dump of the profile info. It will be compared against a golden value.
- return env->NewStringUTF(result.c_str());
-}
-
-} // namespace
-} // namespace art
diff --git a/test/554-jit-profile-file/run b/test/554-jit-profile-file/run
deleted file mode 100644
index f93b32f5c7..0000000000
--- a/test/554-jit-profile-file/run
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-exec ${RUN} \
- -Xcompiler-option --compiler-filter=interpret-only \
- --runtime-option -Xjitsaveprofilinginfo \
- --runtime-option -Xusejit:true \
- --runtime-option -Xjitwarmupthreshold:2 \
- --runtime-option -Xjitthreshold:4 \
- "${@}"
diff --git a/test/554-jit-profile-file/src/Main.java b/test/554-jit-profile-file/src/Main.java
deleted file mode 100644
index 98297ed8ed..0000000000
--- a/test/554-jit-profile-file/src/Main.java
+++ /dev/null
@@ -1,145 +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.
- */
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-
-public class Main {
-
- public void coldMethod() {
- hotMethod();
- }
-
- public String hotMethod() {
- HashMap<String, String> map = new HashMap<String, String>();
- for (int i = 0; i < 10; i++) {
- map.put("" + i, "" + i + 1);
- }
- return map.get("1");
- }
-
- private static final String PKG_NAME = "test.package";
- private static final String APP_DIR_PREFIX = "app_dir_";
- private static final String CODE_CACHE = "code_cache";
- private static final String PROFILE_FILE = PKG_NAME + ".prof";
- private static final String TEMP_FILE_NAME_PREFIX = "dummy";
- private static final String TEMP_FILE_NAME_SUFFIX = "-file";
- private static final int JIT_INVOCATION_COUNT = 200;
-
- /* needs to match Runtime:: kProfileBackground */
- private static final int PROFILE_BACKGROUND = 1;
-
- public static void main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
-
- File file = null;
- File appDir = null;
- File profileDir = null;
- File profileFile = null;
- try {
- // We don't know where we have rights to create the code_cache. So create
- // a dummy temporary file and get its parent directory. That will serve as
- // the app directory.
- file = createTempFile();
- appDir = new File(file.getParent(), APP_DIR_PREFIX + file.getName());
- appDir.mkdir();
- profileDir = new File(appDir, CODE_CACHE);
- profileDir.mkdir();
-
- // Registering the app info will set the profile file name.
- VMRuntime.registerAppInfo(PKG_NAME, appDir.getPath());
-
- // Make sure the hot methods are jitted.
- Main m = new Main();
- OtherDex o = new OtherDex();
- for (int i = 0; i < JIT_INVOCATION_COUNT; i++) {
- m.hotMethod();
- o.hotMethod();
- }
-
- // Sleep for 2 second to make sure that the methods had a chance to get compiled.
- Thread.sleep(2000);
- // Updating the process state to BACKGROUND will trigger profile saving.
- VMRuntime.updateProcessState(PROFILE_BACKGROUND);
-
- // Check that the profile file exists.
- profileFile = new File(profileDir, PROFILE_FILE);
- if (!profileFile.exists()) {
- throw new RuntimeException("No profile file found");
- }
- // Dump the profile file.
- // We know what methods are hot and we compare with the golden `expected` output.
- System.out.println(getProfileInfoDump(profileFile.getPath()));
- } finally {
- if (file != null) {
- file.delete();
- }
- if (profileFile != null) {
- profileFile.delete();
- }
- if (profileDir != null) {
- profileDir.delete();
- }
- if (appDir != null) {
- appDir.delete();
- }
- }
- }
-
- private static class VMRuntime {
- private static final Method registerAppInfoMethod;
- private static final Method updateProcessStateMethod;
- private static final Method getRuntimeMethod;
- static {
- try {
- Class c = Class.forName("dalvik.system.VMRuntime");
- registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
- String.class, String.class, String.class);
- updateProcessStateMethod = c.getDeclaredMethod("updateProcessState", Integer.TYPE);
- getRuntimeMethod = c.getDeclaredMethod("getRuntime");
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static void registerAppInfo(String pkgName, String appDir) throws Exception {
- registerAppInfoMethod.invoke(null, pkgName, appDir, null);
- }
- public static void updateProcessState(int state) throws Exception {
- Object runtime = getRuntimeMethod.invoke(null);
- updateProcessStateMethod.invoke(runtime, state);
- }
- }
-
- static native String getProfileInfoDump(
- String filename);
-
- private static File createTempFile() throws Exception {
- try {
- return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
- } catch (IOException e) {
- System.setProperty("java.io.tmpdir", "/data/local/tmp");
- try {
- return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
- } catch (IOException e2) {
- System.setProperty("java.io.tmpdir", "/sdcard");
- return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
- }
- }
- }
-}
diff --git a/test/552-checker-primitive-typeprop/expected.txt b/test/558-switch/expected.txt
index e69de29bb2..e69de29bb2 100644
--- a/test/552-checker-primitive-typeprop/expected.txt
+++ b/test/558-switch/expected.txt
diff --git a/test/558-switch/info.txt b/test/558-switch/info.txt
new file mode 100644
index 0000000000..07283ffcd9
--- /dev/null
+++ b/test/558-switch/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing that used to generate invalid
+code for arm.
diff --git a/test/554-jit-profile-file/src-multidex/OtherDex.java b/test/558-switch/src/Main.java
index 51644db5aa..f44231e436 100644
--- a/test/554-jit-profile-file/src-multidex/OtherDex.java
+++ b/test/558-switch/src/Main.java
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-import java.util.HashMap;
-
-public class OtherDex {
- public void coldMethod() {
- hotMethod();
- }
+public class Main {
+ public static boolean testMethod(int statusCode) {
+ switch (statusCode) {
+ case 303:
+ case 301:
+ case 302:
+ case 307:
+ return true;
+ default:
+ return false;
+ } //end of switch
+ }
- public String hotMethod() {
- HashMap<String, String> map = new HashMap<String, String>();
- for (int i = 0; i < 10; i++) {
- map.put("" + i, "" + i + 1);
+ public static void main(String[] args) {
+ if (!testMethod(301)) {
+ throw new Error("Unexpected result");
}
- return map.get("1");
}
}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index f84dfe671e..f74a516486 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -38,8 +38,7 @@ LIBARTTEST_COMMON_SRC_FILES := \
461-get-reference-vreg/get_reference_vreg_jni.cc \
466-get-live-vreg/get_live_vreg_jni.cc \
497-inlining-and-class-loader/clear_dex_cache.cc \
- 543-env-long-ref/env_long_ref.cc \
- 554-jit-profile-file/offline_profile.cc
+ 543-env-long-ref/env_long_ref.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 54ceb753fe..7589f8f7cc 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -511,43 +511,54 @@ endif
TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
-# Tests that should fail in the read barrier configuration.
-# 055: Exceeds run time limits due to read barrier instrumentation.
-# 137: Read barrier forces interpreter. Cannot run this with the interpreter.
-# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
-# more parallel moves (at least on x86), thus some Checker assertions may fail.
-# 537: Expects an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet
-# handled in the read barrier configuration.
-# 554: Cannot run in interpreter mode and this rule covers both: the compiler and the interpreter.
-TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \
- 055-enum-performance \
- 137-cfi \
- 484-checker-register-hints \
- 537-checker-arraycopy \
+
+# Tests that should fail in the read barrier configuration with the default (Quick) compiler.
+# 137: Quick has no support for read barriers and punts to the
+# interpreter, but CFI unwinding expects managed frames.
+# 554: Quick does not support JIT profiling.
+TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := \
+ 137-cfi \
554-jit-profile-file
+# Tests that should fail in the read barrier configuration with the Optimizing compiler.
+# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
+# more parallel moves on x86, thus some Checker assertions may fail.
+# 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are
+# not yet handled in the read barrier configuration.
+TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
+ 484-checker-register-hints \
+ 537-checker-arraycopy
+
ifeq ($(ART_USE_READ_BARRIER),true)
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \
- $(PREBUILD_TYPES),$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \
- $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
- $(TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+ ifneq (,$(filter default,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \
+ $(PREBUILD_TYPES),default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \
+ $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
+ $(TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+ endif
+
+ ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \
+ $(PREBUILD_TYPES),optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \
+ $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
+ $(TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+ endif
endif
-TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
+TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS :=
+TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
# Tests that should fail in the heap poisoning configuration with the default (Quick) compiler.
-# 137: Quick punts to the interpreter, and this test cannot run this with the interpreter.
+# 137: Quick has no support for read barriers and punts to the
+# interpreter, but CFI unwinding expects managed frames.
+# 554: Quick does not support JIT profiling.
TEST_ART_BROKEN_DEFAULT_HEAP_POISONING_RUN_TESTS := \
- 137-cfi
+ 137-cfi \
+ 554-jit-profile-file
# Tests that should fail in the heap poisoning configuration with the Optimizing compiler.
-# 055-enum-performance: Exceeds run time limits due to heap poisoning instrumentation.
+# 055: Exceeds run time limits due to heap poisoning instrumentation (on ARM and ARM64 devices).
TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := \
055-enum-performance
-# Tests that should fail in the heap poisoning configuration with the interpreter.
-# 137: Cannot run this with the interpreter.
-TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS := \
- 137-cfi \
- 554-jit-profile-file
ifeq ($(ART_HEAP_POISONING),true)
ifneq (,$(filter default,$(COMPILER_TYPES)))
@@ -563,18 +574,10 @@ ifeq ($(ART_HEAP_POISONING),true)
$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
$(TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
endif
-
- ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \
- $(PREBUILD_TYPES),interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
- $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \
- $(TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
- endif
endif
-TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS :=
-TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS :=
TEST_ART_BROKEN_DEFAULT_HEAP_POISONING_RUN_TESTS :=
+TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS :=
# Clear variables ahead of appending to them when defining tests.
$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
diff --git a/test/run-test b/test/run-test
index 6e13b8a976..60e008c8bb 100755
--- a/test/run-test
+++ b/test/run-test
@@ -41,7 +41,7 @@ else
fi
checker="${progdir}/../tools/checker/checker.py"
export JAVA="java"
-export JAVAC="javac -g"
+export JAVAC="javac -g -source 1.7 -target 1.7 -Xlint:-options"
export RUN="${progdir}/etc/run-test-jar"
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index a5476f7c41..a2d2f239d6 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -170,5 +170,12 @@
result: EXEC_FAILED,
names: ["org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet"],
bug: 25437292
+},
+{
+ description: "JSR166TestCase.waitForThreadToEnterWaitState seems to time out; needs investigation.",
+ result: EXEC_FAILED,
+ names: ["jsr166.LinkedTransferQueueTest#testTransfer2",
+ "jsr166.LinkedTransferQueueTest#testWaitingConsumer"],
+ bug: 25883050
}
]