summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/Android.mk1
-rw-r--r--compiler/common_compiler_test.h60
-rw-r--r--compiler/dex/compiler_enums.h8
-rw-r--r--compiler/dex/frontend.cc11
-rw-r--r--compiler/dex/mir_optimization.cc5
-rw-r--r--compiler/dex/quick/arm/call_arm.cc28
-rw-r--r--compiler/dex/quick/arm/codegen_arm.h4
-rw-r--r--compiler/dex/quick/arm/int_arm.cc103
-rw-r--r--compiler/dex/quick/arm/target_arm.cc6
-rw-r--r--compiler/dex/quick/arm/utility_arm.cc61
-rw-r--r--compiler/dex/quick/codegen_util.cc17
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.cc12
-rw-r--r--compiler/dex/quick/gen_common.cc279
-rw-r--r--compiler/dex/quick/gen_invoke.cc136
-rw-r--r--compiler/dex/quick/gen_loadstore.cc23
-rw-r--r--compiler/dex/quick/mips/call_mips.cc15
-rw-r--r--compiler/dex/quick/mips/codegen_mips.h4
-rw-r--r--compiler/dex/quick/mips/int_mips.cc83
-rw-r--r--compiler/dex/quick/mips/target_mips.cc8
-rw-r--r--compiler/dex/quick/mips/utility_mips.cc36
-rw-r--r--compiler/dex/quick/mir_to_lir.cc47
-rw-r--r--compiler/dex/quick/mir_to_lir.h54
-rw-r--r--compiler/dex/quick/x86/call_x86.cc5
-rw-r--r--compiler/dex/quick/x86/codegen_x86.h8
-rw-r--r--compiler/dex/quick/x86/fp_x86.cc2
-rw-r--r--compiler/dex/quick/x86/int_x86.cc252
-rw-r--r--compiler/dex/quick/x86/target_x86.cc19
-rw-r--r--compiler/dex/quick/x86/utility_x86.cc29
-rw-r--r--compiler/elf_writer_test.cc4
-rw-r--r--compiler/image_test.cc2
-rw-r--r--compiler/image_writer.cc24
-rw-r--r--compiler/oat_test.cc13
-rw-r--r--compiler/oat_writer.cc1271
-rw-r--r--compiler/oat_writer.h120
-rw-r--r--compiler/optimizing/code_generator.cc102
-rw-r--r--compiler/optimizing/code_generator.h114
-rw-r--r--compiler/optimizing/code_generator_arm.cc163
-rw-r--r--compiler/optimizing/code_generator_arm.h11
-rw-r--r--compiler/optimizing/code_generator_x86.cc226
-rw-r--r--compiler/optimizing/code_generator_x86.h11
-rw-r--r--compiler/optimizing/nodes.cc167
-rw-r--r--compiler/optimizing/nodes.h223
-rw-r--r--compiler/optimizing/optimizing_compiler.cc4
-rw-r--r--compiler/optimizing/pretty_printer.h14
-rw-r--r--compiler/optimizing/ssa_builder.cc134
-rw-r--r--compiler/optimizing/ssa_builder.h71
-rw-r--r--compiler/optimizing/ssa_test.cc444
-rw-r--r--compiler/utils/growable_array.h1
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc5
49 files changed, 3070 insertions, 1370 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 6d656e63f1..e3201e7f8b 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -78,6 +78,7 @@ LIBART_COMPILER_SRC_FILES := \
optimizing/code_generator_x86.cc \
optimizing/nodes.cc \
optimizing/optimizing_compiler.cc \
+ optimizing/ssa_builder.cc \
trampolines/trampoline_compiler.cc \
utils/arena_allocator.cc \
utils/arena_bit_vector.cc \
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index fdf09a50a6..8bba84a9c1 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -132,34 +132,24 @@ static inline InstructionSetFeatures ParseFeatureList(std::string str) {
class CommonCompilerTest : public CommonRuntimeTest {
public:
- static void MakeExecutable(const std::vector<uint8_t>& code) {
- CHECK_NE(code.size(), 0U);
- MakeExecutable(&code[0], code.size());
- }
-
// Create an OatMethod based on pointers (for unit tests).
OatFile::OatMethod CreateOatMethod(const void* code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- const uint8_t* mapping_table,
- const uint8_t* vmap_table,
const uint8_t* gc_map) {
+ CHECK(code != nullptr);
const byte* base;
- uint32_t code_offset, mapping_table_offset, vmap_table_offset, gc_map_offset;
- if (mapping_table == nullptr && vmap_table == nullptr && gc_map == nullptr) {
+ uint32_t code_offset, gc_map_offset;
+ if (gc_map == nullptr) {
base = reinterpret_cast<const byte*>(code); // Base of data points at code.
base -= kPointerSize; // Move backward so that code_offset != 0.
code_offset = kPointerSize;
- mapping_table_offset = 0;
- vmap_table_offset = 0;
gc_map_offset = 0;
} else {
// TODO: 64bit support.
base = nullptr; // Base of data in oat file, ie 0.
code_offset = PointerToLowMemUInt32(code);
- mapping_table_offset = PointerToLowMemUInt32(mapping_table);
- vmap_table_offset = PointerToLowMemUInt32(vmap_table);
gc_map_offset = PointerToLowMemUInt32(gc_map);
}
return OatFile::OatMethod(base,
@@ -167,8 +157,6 @@ class CommonCompilerTest : public CommonRuntimeTest {
frame_size_in_bytes,
core_spill_mask,
fp_spill_mask,
- mapping_table_offset,
- vmap_table_offset,
gc_map_offset);
}
@@ -185,19 +173,44 @@ class CommonCompilerTest : public CommonRuntimeTest {
}
if (compiled_method != nullptr) {
const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
- if (code == nullptr) {
+ const void* code_ptr;
+ if (code != nullptr) {
+ uint32_t code_size = code->size();
+ CHECK_NE(0u, code_size);
+ const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+ uint32_t vmap_table_offset = vmap_table.empty() ? 0u
+ : sizeof(OatMethodHeader) + vmap_table.size();
+ const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
+ uint32_t mapping_table_offset = mapping_table.empty() ? 0u
+ : sizeof(OatMethodHeader) + vmap_table.size() + mapping_table.size();
+ OatMethodHeader method_header(vmap_table_offset, mapping_table_offset, code_size);
+
+ header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
+ std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
+ size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size();
+ size_t code_offset = compiled_method->AlignCode(size - code_size);
+ size_t padding = code_offset - (size - code_size);
+ chunk->reserve(padding + size);
+ chunk->resize(sizeof(method_header));
+ memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
+ chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
+ chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
+ chunk->insert(chunk->begin(), padding, 0);
+ chunk->insert(chunk->end(), code->begin(), code->end());
+ CHECK_EQ(padding + size, chunk->size());
+ code_ptr = &(*chunk)[code_offset];
+ } else {
code = compiled_method->GetPortableCode();
+ code_ptr = &(*code)[0];
}
- MakeExecutable(*code);
- const void* method_code = CompiledMethod::CodePointer(&(*code)[0],
+ MakeExecutable(code_ptr, code->size());
+ const void* method_code = CompiledMethod::CodePointer(code_ptr,
compiled_method->GetInstructionSet());
LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
OatFile::OatMethod oat_method = CreateOatMethod(method_code,
compiled_method->GetFrameSizeInBytes(),
compiled_method->GetCoreSpillMask(),
compiled_method->GetFpSpillMask(),
- &compiled_method->GetMappingTable()[0],
- &compiled_method->GetVmapTable()[0],
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -211,8 +224,6 @@ class CommonCompilerTest : public CommonRuntimeTest {
kStackAlignment,
0,
0,
- nullptr,
- nullptr,
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -230,8 +241,6 @@ class CommonCompilerTest : public CommonRuntimeTest {
sirt_size,
callee_save_method->GetCoreSpillMask(),
callee_save_method->GetFpSpillMask(),
- nullptr,
- nullptr,
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -436,6 +445,9 @@ class CommonCompilerTest : public CommonRuntimeTest {
private:
UniquePtr<MemMap> image_reservation_;
+
+ // Chunks must not move their storage after being created - use the node-based std::list.
+ std::list<std::vector<uint8_t> > header_code_and_maps_chunks_;
};
} // namespace art
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 8a88d618cc..ba4b5c356a 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -159,8 +159,10 @@ enum AssemblerStatus {
};
enum OpSize {
- kWord,
- kLong,
+ kWord, // Natural word size of target (32/64).
+ k32,
+ k64,
+ kReference, // Object reference; compressed on 64-bit targets.
kSingle,
kDouble,
kUnsignedHalf,
@@ -323,8 +325,6 @@ enum X86ConditionCode {
std::ostream& operator<<(std::ostream& os, const X86ConditionCode& kind);
enum ThrowKind {
- kThrowArrayBounds,
- kThrowConstantArrayBounds,
kThrowNoSuchMethod,
};
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 1bf5fce989..ed2ecace36 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -181,6 +181,17 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver,
(cu.enable_debug & (1 << kDebugVerbose));
}
+ if (gVerboseMethods.size() != 0) {
+ cu.verbose = false;
+ for (size_t i = 0; i < gVerboseMethods.size(); ++i) {
+ if (PrettyMethod(method_idx, dex_file).find(gVerboseMethods[i])
+ != std::string::npos) {
+ cu.verbose = true;
+ break;
+ }
+ }
+ }
+
/*
* TODO: rework handling of optimization and debug flags. Should we split out
* MIR and backend flags? Need command-line setting as well.
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 51419f4586..937e2585ef 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -42,6 +42,11 @@ void MIRGraph::DoConstantPropagation(BasicBlock* bb) {
MIR* mir;
for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ // Skip pass if BB has MIR without SSA representation.
+ if (mir->ssa_rep == NULL) {
+ return;
+ }
+
uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
DecodedInstruction *d_insn = &mir->dalvikInsn;
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index d0d0e6b3a7..b374ed861e 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -128,7 +128,7 @@ void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
// Load the displacement from the switch table
RegStorage disp_reg = AllocTemp();
- LoadBaseIndexed(table_base, keyReg, disp_reg, 2, kWord);
+ LoadBaseIndexed(table_base, keyReg, disp_reg, 2, k32);
// ..and go! NOTE: No instruction set switch here - must stay Thumb2
LIR* switch_branch = NewLIR1(kThumb2AddPCR, disp_reg.GetReg());
@@ -180,6 +180,7 @@ void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
*/
void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
FlushAllRegs();
+ // FIXME: need separate LoadValues for object references.
LoadValueDirectFixed(rl_src, rs_r0); // Get obj
LockCallTemps(); // Prepare for explicit register usage
constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15.
@@ -193,7 +194,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL);
}
}
- LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
+ Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
MarkPossibleNullPointerException(opt_flags);
LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_r1, 0, NULL);
@@ -219,7 +220,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
} else {
// Explicit null-check as slow-path is entered using an IT.
GenNullCheck(rs_r0, opt_flags);
- LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
+ Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
MarkPossibleNullPointerException(opt_flags);
OpRegImm(kOpCmp, rs_r1, 0);
@@ -248,7 +249,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
LoadValueDirectFixed(rl_src, rs_r0); // Get obj
LockCallTemps(); // Prepare for explicit register usage
LIR* null_check_branch = nullptr;
- LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
+ Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15.
if (kArchVariantHasGoodBranchPredictor) {
if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) {
@@ -259,11 +260,11 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL);
}
}
- LoadWordDisp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1);
+ Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1);
MarkPossibleNullPointerException(opt_flags);
LoadConstantNoClobber(rs_r3, 0);
LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_r1, rs_r2, NULL);
- StoreWordDisp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
+ Store32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
LIR* unlock_success_branch = OpUnconditionalBranch(NULL);
LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
@@ -284,14 +285,14 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
} else {
// Explicit null-check as slow-path is entered using an IT.
GenNullCheck(rs_r0, opt_flags);
- LoadWordDisp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); // Get lock
+ Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); // Get lock
MarkPossibleNullPointerException(opt_flags);
- LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
+ Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
LoadConstantNoClobber(rs_r3, 0);
// Is lock unheld on lock or held by us (==thread_id) on unlock?
OpRegReg(kOpCmp, rs_r1, rs_r2);
LIR* it = OpIT(kCondEq, "EE");
- StoreWordDisp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
+ Store32Disp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
// Go expensive route - UnlockObjectFromCode(obj);
LoadWordDisp/*ne*/(rs_rARM_SELF, QUICK_ENTRYPOINT_OFFSET(4, pUnlockObject).Int32Value(),
rs_rARM_LR);
@@ -307,9 +308,9 @@ void ArmMir2Lir::GenMoveException(RegLocation rl_dest) {
int ex_offset = Thread::ExceptionOffset<4>().Int32Value();
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
RegStorage reset_reg = AllocTemp();
- LoadWordDisp(rs_rARM_SELF, ex_offset, rl_result.reg);
+ Load32Disp(rs_rARM_SELF, ex_offset, rl_result.reg);
LoadConstant(reset_reg, 0);
- StoreWordDisp(rs_rARM_SELF, ex_offset, reset_reg);
+ Store32Disp(rs_rARM_SELF, ex_offset, reset_reg);
FreeTemp(reset_reg);
StoreValue(rl_dest, rl_result);
}
@@ -354,7 +355,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
if (!skip_overflow_check) {
if (Runtime::Current()->ExplicitStackOverflowChecks()) {
/* Load stack limit */
- LoadWordDisp(rs_rARM_SELF, Thread::StackEndOffset<4>().Int32Value(), rs_r12);
+ Load32Disp(rs_rARM_SELF, Thread::StackEndOffset<4>().Int32Value(), rs_r12);
}
}
/* Spill core callee saves */
@@ -391,6 +392,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
ThreadOffset<4> func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowStackOverflow);
// Load the entrypoint directly into the pc instead of doing a load + branch. Assumes
// codegen and target are in thumb2 mode.
+ // NOTE: native pointer.
m2l_->LoadWordDisp(rs_rARM_SELF, func_offset.Int32Value(), rs_rARM_PC);
}
@@ -421,7 +423,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
// a sub instruction. Otherwise we will get a temp allocation and the
// code size will increase.
OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, Thread::kStackOverflowReservedBytes);
- LoadWordDisp(rs_r12, 0, rs_r12);
+ Load32Disp(rs_r12, 0, rs_r12);
MarkPossibleStackOverflowException();
OpRegImm(kOpSub, rs_rARM_SP, frame_size_without_spills);
}
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index b0bc11d458..646859c03b 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -126,8 +126,6 @@ class ArmMir2Lir FINAL : public Mir2Lir {
RegLocation rl_src2);
void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2);
- LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, int offset,
- ThrowKind kind);
RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div);
RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div);
void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -162,7 +160,7 @@ class ArmMir2Lir FINAL : public Mir2Lir {
LIR* OpMem(OpKind op, RegStorage r_base, int disp);
LIR* OpPcRelLoad(RegStorage reg, LIR* target);
LIR* OpReg(OpKind op, RegStorage r_dest_src);
- LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src);
+ void OpRegCopy(RegStorage r_dest, RegStorage r_src);
LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src);
LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value);
LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset);
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index c876b3ac69..a2d6373622 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -314,11 +314,11 @@ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_va
/*
* A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit
* compare-and-branch if zero is ideal if it will reach. However, because null checks
- * branch forward to a launch pad, they will frequently not reach - and thus have to
+ * branch forward to a slow path, they will frequently not reach - and thus have to
* be converted to a long form during assembly (which will trigger another assembly
* pass). Here we estimate the branch distance for checks, and if large directly
* generate the long form in an attempt to avoid an extra assembly pass.
- * TODO: consider interspersing launchpads in code following unconditional branches.
+ * TODO: consider interspersing slowpaths in code following unconditional branches.
*/
bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget));
skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64);
@@ -361,37 +361,40 @@ LIR* ArmMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
return res;
}
-LIR* ArmMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
- LIR* res = OpRegCopyNoInsert(r_dest, r_src);
- AppendLIR(res);
- return res;
+void ArmMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
+ if (r_dest != r_src) {
+ LIR* res = OpRegCopyNoInsert(r_dest, r_src);
+ AppendLIR(res);
+ }
}
void ArmMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
- bool dest_fp = ARM_FPREG(r_dest.GetLowReg());
- bool src_fp = ARM_FPREG(r_src.GetLowReg());
- if (dest_fp) {
- if (src_fp) {
- // FIXME: handle 64-bit solo's here.
- OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())),
- RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg())));
- } else {
- NewLIR3(kThumb2Fmdrr, S2d(r_dest.GetLowReg(), r_dest.GetHighReg()),
- r_src.GetLowReg(), r_src.GetHighReg());
- }
- } else {
- if (src_fp) {
- NewLIR3(kThumb2Fmrrd, r_dest.GetLowReg(), r_dest.GetHighReg(), S2d(r_src.GetLowReg(),
- r_src.GetHighReg()));
+ if (r_dest != r_src) {
+ bool dest_fp = ARM_FPREG(r_dest.GetLowReg());
+ bool src_fp = ARM_FPREG(r_src.GetLowReg());
+ if (dest_fp) {
+ if (src_fp) {
+ // FIXME: handle 64-bit solo's here.
+ OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())),
+ RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg())));
+ } else {
+ NewLIR3(kThumb2Fmdrr, S2d(r_dest.GetLowReg(), r_dest.GetHighReg()),
+ r_src.GetLowReg(), r_src.GetHighReg());
+ }
} else {
- // Handle overlap
- if (r_src.GetHighReg() == r_dest.GetLowReg()) {
- DCHECK_NE(r_src.GetLowReg(), r_dest.GetHighReg());
- OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
- OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ if (src_fp) {
+ NewLIR3(kThumb2Fmrrd, r_dest.GetLowReg(), r_dest.GetHighReg(), S2d(r_src.GetLowReg(),
+ r_src.GetHighReg()));
} else {
- OpRegCopy(r_dest.GetLow(), r_src.GetLow());
- OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ // Handle overlap
+ if (r_src.GetHighReg() == r_dest.GetLowReg()) {
+ DCHECK_NE(r_src.GetLowReg(), r_dest.GetHighReg());
+ OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ } else {
+ OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ }
}
}
}
@@ -608,12 +611,6 @@ bool ArmMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit)
return true;
}
-LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base,
- int offset, ThrowKind kind) {
- LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm";
- return NULL;
-}
-
RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2, bool is_div, bool check_zero) {
LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
@@ -684,18 +681,18 @@ bool ArmMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
RegLocation rl_dest = InlineTarget(info);
RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- if (size == kLong) {
+ if (size == k64) {
// Fake unaligned LDRD by two unaligned LDR instructions on ARMv7 with SCTLR.A set to 0.
if (rl_address.reg.GetReg() != rl_result.reg.GetLowReg()) {
- LoadWordDisp(rl_address.reg, 0, rl_result.reg.GetLow());
- LoadWordDisp(rl_address.reg, 4, rl_result.reg.GetHigh());
+ Load32Disp(rl_address.reg, 0, rl_result.reg.GetLow());
+ Load32Disp(rl_address.reg, 4, rl_result.reg.GetHigh());
} else {
- LoadWordDisp(rl_address.reg, 4, rl_result.reg.GetHigh());
- LoadWordDisp(rl_address.reg, 0, rl_result.reg.GetLow());
+ Load32Disp(rl_address.reg, 4, rl_result.reg.GetHigh());
+ Load32Disp(rl_address.reg, 0, rl_result.reg.GetLow());
}
StoreValueWide(rl_dest, rl_result);
} else {
- DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
// Unaligned load with LDR and LDRSH is allowed on ARMv7 with SCTLR.A set to 0.
LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, INVALID_SREG);
StoreValue(rl_dest, rl_result);
@@ -708,13 +705,13 @@ bool ArmMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1]
RegLocation rl_src_value = info->args[2]; // [size] value
RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
- if (size == kLong) {
+ if (size == k64) {
// Fake unaligned STRD by two unaligned STR instructions on ARMv7 with SCTLR.A set to 0.
RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
- StoreBaseDisp(rl_address.reg, 0, rl_value.reg.GetLow(), kWord);
- StoreBaseDisp(rl_address.reg, 4, rl_value.reg.GetHigh(), kWord);
+ StoreBaseDisp(rl_address.reg, 0, rl_value.reg.GetLow(), k32);
+ StoreBaseDisp(rl_address.reg, 4, rl_value.reg.GetHigh(), k32);
} else {
- DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
// Unaligned store with STR and STRSH is allowed on ARMv7 with SCTLR.A set to 0.
RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size);
@@ -1148,7 +1145,7 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
if (needs_range_check) {
reg_len = AllocTemp();
/* Get len */
- LoadWordDisp(rl_array.reg, len_offset, reg_len);
+ Load32Disp(rl_array.reg, len_offset, reg_len);
MarkPossibleNullPointerException(opt_flags);
} else {
ForceImplicitNullCheck(rl_array.reg, opt_flags);
@@ -1167,9 +1164,9 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
if (needs_range_check) {
if (constant_index) {
- GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+ GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
} else {
- GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
}
FreeTemp(reg_len);
}
@@ -1196,7 +1193,7 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
rl_result = EvalLoc(rl_dest, reg_class, true);
if (needs_range_check) {
- GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
@@ -1217,7 +1214,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
bool constant_index = rl_index.is_const;
int data_offset;
- if (size == kLong || size == kDouble) {
+ if (size == k64 || size == kDouble) {
data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
} else {
data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
@@ -1254,7 +1251,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
reg_len = AllocTemp();
// NOTE: max live temps(4) here.
/* Get len */
- LoadWordDisp(rl_array.reg, len_offset, reg_len);
+ Load32Disp(rl_array.reg, len_offset, reg_len);
MarkPossibleNullPointerException(opt_flags);
} else {
ForceImplicitNullCheck(rl_array.reg, opt_flags);
@@ -1271,9 +1268,9 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
}
if (needs_range_check) {
if (constant_index) {
- GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+ GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len);
} else {
- GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
}
FreeTemp(reg_len);
}
@@ -1289,7 +1286,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
rl_src = LoadValue(rl_src, reg_class);
if (needs_range_check) {
- GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 1053a8fc41..305e89ba92 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -656,7 +656,7 @@ void ArmMir2Lir::FlushReg(RegStorage reg) {
if (info->live && info->dirty) {
info->dirty = false;
int v_reg = mir_graph_->SRegToVReg(info->s_reg);
- StoreBaseDisp(rs_rARM_SP, VRegOffset(v_reg), reg, kWord);
+ StoreBaseDisp(rs_rARM_SP, VRegOffset(v_reg), reg, k32);
}
}
@@ -738,8 +738,8 @@ RegStorage ArmMir2Lir::LoadHelper(ThreadOffset<4> offset) {
LIR* ArmMir2Lir::CheckSuspendUsingLoad() {
RegStorage tmp = rs_r0;
- LoadWordDisp(rs_rARM_SELF, Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp);
- LIR* load2 = LoadWordDisp(tmp, 0, tmp);
+ Load32Disp(rs_rARM_SELF, Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp);
+ LIR* load2 = Load32Disp(tmp, 0, tmp);
return load2;
}
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index 70cbdd2e31..2e64f74235 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -699,23 +699,24 @@ LIR* ArmMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStora
if (ARM_FPREG(r_dest.GetReg())) {
if (ARM_SINGLEREG(r_dest.GetReg())) {
- DCHECK((size == kWord) || (size == kSingle));
+ DCHECK((size == k32) || (size == kSingle) || (size == kReference));
opcode = kThumb2Vldrs;
size = kSingle;
} else {
DCHECK(ARM_DOUBLEREG(r_dest.GetReg()));
- DCHECK((size == kLong) || (size == kDouble));
+ DCHECK((size == k64) || (size == kDouble));
DCHECK_EQ((r_dest.GetReg() & 0x1), 0);
opcode = kThumb2Vldrd;
size = kDouble;
}
} else {
if (size == kSingle)
- size = kWord;
+ size = k32;
}
switch (size) {
case kDouble: // fall-through
+ // Intentional fall-though.
case kSingle:
reg_ptr = AllocTemp();
if (scale) {
@@ -727,7 +728,9 @@ LIR* ArmMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStora
load = NewLIR3(opcode, r_dest.GetReg(), reg_ptr.GetReg(), 0);
FreeTemp(reg_ptr);
return load;
- case kWord:
+ case k32:
+ // Intentional fall-though.
+ case kReference:
opcode = (thumb_form) ? kThumbLdrRRR : kThumb2LdrRRR;
break;
case kUnsignedHalf:
@@ -764,23 +767,24 @@ LIR* ArmMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor
if (ARM_FPREG(r_src.GetReg())) {
if (ARM_SINGLEREG(r_src.GetReg())) {
- DCHECK((size == kWord) || (size == kSingle));
+ DCHECK((size == k32) || (size == kSingle) || (size == kReference));
opcode = kThumb2Vstrs;
size = kSingle;
} else {
DCHECK(ARM_DOUBLEREG(r_src.GetReg()));
- DCHECK((size == kLong) || (size == kDouble));
+ DCHECK((size == k64) || (size == kDouble));
DCHECK_EQ((r_src.GetReg() & 0x1), 0);
opcode = kThumb2Vstrd;
size = kDouble;
}
} else {
if (size == kSingle)
- size = kWord;
+ size = k32;
}
switch (size) {
case kDouble: // fall-through
+ // Intentional fall-though.
case kSingle:
reg_ptr = AllocTemp();
if (scale) {
@@ -792,14 +796,18 @@ LIR* ArmMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor
store = NewLIR3(opcode, r_src.GetReg(), reg_ptr.GetReg(), 0);
FreeTemp(reg_ptr);
return store;
- case kWord:
+ case k32:
+ // Intentional fall-though.
+ case kReference:
opcode = (thumb_form) ? kThumbStrRRR : kThumb2StrRRR;
break;
case kUnsignedHalf:
+ // Intentional fall-though.
case kSignedHalf:
opcode = (thumb_form) ? kThumbStrhRRR : kThumb2StrhRRR;
break;
case kUnsignedByte:
+ // Intentional fall-though.
case kSignedByte:
opcode = (thumb_form) ? kThumbStrbRRR : kThumb2StrbRRR;
break;
@@ -832,7 +840,8 @@ LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorag
bool null_pointer_safepoint = false;
switch (size) {
case kDouble:
- case kLong:
+ // Intentional fall-though.
+ case k64:
if (ARM_FPREG(dest_low_reg)) {
// Note: following change to avoid using pairs for doubles, replace conversion w/ DCHECK.
if (r_dest.IsPair()) {
@@ -849,15 +858,18 @@ LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorag
load = NewLIR4(kThumb2LdrdI8, r_dest.GetLowReg(), r_dest.GetHighReg(), r_base.GetReg(),
displacement >> 2);
} else {
- load = LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), kWord, s_reg);
+ load = LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), k32, s_reg);
null_pointer_safepoint = true;
- LoadBaseDispBody(r_base, displacement + 4, r_dest.GetHigh(), kWord, INVALID_SREG);
+ LoadBaseDispBody(r_base, displacement + 4, r_dest.GetHigh(), k32, INVALID_SREG);
}
already_generated = true;
}
break;
case kSingle:
- case kWord:
+ // Intentional fall-though.
+ case k32:
+ // Intentional fall-though.
+ case kReference:
if (ARM_FPREG(r_dest.GetReg())) {
opcode = kThumb2Vldrs;
if (displacement <= 1020) {
@@ -953,13 +965,17 @@ LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorag
LIR* ArmMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size,
int s_reg) {
- DCHECK(!((size == kLong) || (size == kDouble)));
+ DCHECK(!((size == k64) || (size == kDouble)));
+ // TODO: base this on target.
+ if (size == kWord) {
+ size = k32;
+ }
return LoadBaseDispBody(r_base, displacement, r_dest, size, s_reg);
}
LIR* ArmMir2Lir::LoadBaseDispWide(RegStorage r_base, int displacement, RegStorage r_dest,
int s_reg) {
- return LoadBaseDispBody(r_base, displacement, r_dest, kLong, s_reg);
+ return LoadBaseDispBody(r_base, displacement, r_dest, k64, s_reg);
}
@@ -975,16 +991,16 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora
int src_low_reg = r_src.IsPair() ? r_src.GetLowReg() : r_src.GetReg();
bool null_pointer_safepoint = false;
switch (size) {
- case kLong:
+ case k64:
case kDouble:
if (!ARM_FPREG(src_low_reg)) {
if (displacement <= 1020) {
store = NewLIR4(kThumb2StrdI8, r_src.GetLowReg(), r_src.GetHighReg(), r_base.GetReg(),
displacement >> 2);
} else {
- store = StoreBaseDispBody(r_base, displacement, r_src.GetLow(), kWord);
+ store = StoreBaseDispBody(r_base, displacement, r_src.GetLow(), k32);
null_pointer_safepoint = true;
- StoreBaseDispBody(r_base, displacement + 4, r_src.GetHigh(), kWord);
+ StoreBaseDispBody(r_base, displacement + 4, r_src.GetHigh(), k32);
}
already_generated = true;
} else {
@@ -1001,7 +1017,8 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora
}
break;
case kSingle:
- case kWord:
+ case k32:
+ case kReference:
if (ARM_FPREG(r_src.GetReg())) {
DCHECK(ARM_SINGLEREG(r_src.GetReg()));
opcode = kThumb2Vstrs;
@@ -1082,12 +1099,16 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora
LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src,
OpSize size) {
- DCHECK(!((size == kLong) || (size == kDouble)));
+ // TODO: base this on target.
+ if (size == kWord) {
+ size = k32;
+ }
+ DCHECK(!((size == k64) || (size == kDouble)));
return StoreBaseDispBody(r_base, displacement, r_src, size);
}
LIR* ArmMir2Lir::StoreBaseDispWide(RegStorage r_base, int displacement, RegStorage r_src) {
- return StoreBaseDispBody(r_base, displacement, r_src, kLong);
+ return StoreBaseDispBody(r_base, displacement, r_src, k64);
}
LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 6e6b8f0a30..501e4e204b 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -274,6 +274,19 @@ void Mir2Lir::DumpPromotionMap() {
}
}
+void Mir2Lir::UpdateLIROffsets() {
+ // Only used for code listings.
+ size_t offset = 0;
+ for (LIR* lir = first_lir_insn_; lir != nullptr; lir = lir->next) {
+ lir->offset = offset;
+ if (!lir->flags.is_nop && !IsPseudoLirOp(lir->opcode)) {
+ offset += GetInsnSize(lir);
+ } else if (lir->opcode == kPseudoPseudoAlign4) {
+ offset += (offset & 0x2);
+ }
+ }
+}
+
/* Dump instructions and constant pool contents */
void Mir2Lir::CodegenDump() {
LOG(INFO) << "Dumping LIR insns for "
@@ -293,6 +306,7 @@ void Mir2Lir::CodegenDump() {
LOG(INFO) << "expansion factor: "
<< static_cast<float>(total_size_) / static_cast<float>(insns_size * 2);
DumpPromotionMap();
+ UpdateLIROffsets();
for (lir_insn = first_lir_insn_; lir_insn != NULL; lir_insn = lir_insn->next) {
DumpLIRInsn(lir_insn, 0);
}
@@ -926,7 +940,6 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena
mir_graph_(mir_graph),
switch_tables_(arena, 4, kGrowableArraySwitchTables),
fill_array_data_(arena, 4, kGrowableArrayFillArrayData),
- throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads),
suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads),
tempreg_info_(arena, 20, kGrowableArrayMisc),
reginfo_map_(arena, 64, kGrowableArrayMisc),
@@ -1118,7 +1131,7 @@ bool Mir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) {
LIR *Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg,
int offset, int check_value, LIR* target) {
// Handle this for architectures that can't compare to memory.
- LoadWordDisp(base_reg, offset, temp_reg);
+ Load32Disp(base_reg, offset, temp_reg);
LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target);
return branch;
}
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 8806e68b93..3ec31ba7d9 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -212,8 +212,8 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods
INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0),
INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0),
- INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord),
- INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong),
+ INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32),
+ INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64),
INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf),
INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0),
@@ -241,12 +241,12 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods
INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0),
INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte),
- INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord),
- INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong),
+ INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, k32),
+ INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, k64),
INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf),
INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte),
- INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord),
- INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong),
+ INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, k32),
+ INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, k64),
INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf),
INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas,
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 055f60c1c8..313174d218 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -19,12 +19,17 @@
#include "dex/quick/mir_to_lir-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/array.h"
+#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
#include "verifier/method_verifier.h"
#include <functional>
namespace art {
+// Shortcuts to repeatedly used long types.
+typedef mirror::ObjectArray<mirror::Object> ObjArray;
+typedef mirror::ObjectArray<mirror::Class> ClassArray;
+
/*
* This source files contains "gen" codegen routines that should
* be applicable to most targets. Only mid-level support utilities
@@ -42,22 +47,6 @@ void Mir2Lir::GenBarrier() {
barrier->u.m.def_mask = ENCODE_ALL;
}
-LIR* Mir2Lir::GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind) {
- LIR* tgt;
- LIR* branch;
- if (c_code == kCondAl) {
- tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, RegStorage::kInvalidRegVal,
- imm_val);
- branch = OpUnconditionalBranch(tgt);
- } else {
- tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, reg.GetReg(), imm_val);
- branch = OpCmpImmBranch(c_code, reg, imm_val, tgt);
- }
- // Remember branch target - will process later
- throw_launchpads_.Insert(tgt);
- return branch;
-}
-
void Mir2Lir::GenDivZeroException() {
LIR* branch = OpUnconditionalBranch(nullptr);
AddDivZeroCheckSlowPath(branch);
@@ -91,6 +80,59 @@ void Mir2Lir::AddDivZeroCheckSlowPath(LIR* branch) {
AddSlowPath(new (arena_) DivZeroCheckSlowPath(this, branch));
}
+void Mir2Lir::GenArrayBoundsCheck(RegStorage index, RegStorage length) {
+ class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+ public:
+ ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, RegStorage index, RegStorage length)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+ index_(index), length_(length) {
+ }
+
+ void Compile() OVERRIDE {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+ m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+ index_, length_, true);
+ }
+
+ private:
+ const RegStorage index_;
+ const RegStorage length_;
+ };
+
+ LIR* branch = OpCmpBranch(kCondUge, index, length, nullptr);
+ AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length));
+}
+
+void Mir2Lir::GenArrayBoundsCheck(int index, RegStorage length) {
+ class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+ public:
+ ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, int index, RegStorage length)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+ index_(index), length_(length) {
+ }
+
+ void Compile() OVERRIDE {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+
+ m2l_->OpRegCopy(m2l_->TargetReg(kArg1), length_);
+ m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_);
+ m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+ m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true);
+ }
+
+ private:
+ const int32_t index_;
+ const RegStorage length_;
+ };
+
+ LIR* branch = OpCmpImmBranch(kCondLs, length, index, nullptr);
+ AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length));
+}
+
LIR* Mir2Lir::GenNullCheck(RegStorage reg) {
class NullCheckSlowPath : public Mir2Lir::LIRSlowPath {
public:
@@ -151,23 +193,12 @@ void Mir2Lir::ForceImplicitNullCheck(RegStorage reg, int opt_flags) {
// register with offset 0. This will cause a signal if the register contains 0 (null).
RegStorage tmp = AllocTemp();
// TODO: for Mips, would be best to use rZERO as the bogus register target.
- LIR* load = LoadWordDisp(reg, 0, tmp);
+ LIR* load = Load32Disp(reg, 0, tmp);
FreeTemp(tmp);
MarkSafepointPC(load);
}
}
-/* Perform check on two registers */
-LIR* Mir2Lir::GenRegRegCheck(ConditionCode c_code, RegStorage reg1, RegStorage reg2,
- ThrowKind kind) {
- LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, reg1.GetReg(),
- reg2.GetReg());
- LIR* branch = OpCmpBranch(c_code, reg1, reg2, tgt);
- // Remember branch target - will process later
- throw_launchpads_.Insert(tgt);
- return branch;
-}
-
void Mir2Lir::GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1,
RegLocation rl_src2, LIR* taken,
LIR* fall_through) {
@@ -373,7 +404,7 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) {
for (int i = 0; i < elems; i++) {
RegLocation loc = UpdateLoc(info->args[i]);
if (loc.location == kLocPhysReg) {
- StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kWord);
+ Store32Disp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg);
}
}
/*
@@ -410,8 +441,8 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) {
// Generate the copy loop. Going backwards for convenience
LIR* target = NewLIR0(kPseudoTargetLabel);
// Copy next element
- LoadBaseIndexed(r_src, r_idx, r_val, 2, kWord);
- StoreBaseIndexed(r_dst, r_idx, r_val, 2, kWord);
+ LoadBaseIndexed(r_src, r_idx, r_val, 2, k32);
+ StoreBaseIndexed(r_dst, r_idx, r_val, 2, k32);
FreeTemp(r_val);
OpDecAndBranch(kCondGe, r_idx, target);
if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) {
@@ -423,9 +454,8 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) {
// TUNING: interleave
for (int i = 0; i < elems; i++) {
RegLocation rl_arg = LoadValue(info->args[i], kCoreReg);
- StoreBaseDisp(TargetReg(kRet0),
- mirror::Array::DataOffset(component_size).Int32Value() + i * 4,
- rl_arg.reg, kWord);
+ Store32Disp(TargetReg(kRet0),
+ mirror::Array::DataOffset(component_size).Int32Value() + i * 4, rl_arg.reg);
// If the LoadValue caused a temp to be allocated, free it
if (IsTemp(rl_arg.reg)) {
FreeTemp(rl_arg.reg);
@@ -476,7 +506,7 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double,
// Fast path, static storage base is this method's class
RegLocation rl_method = LoadCurrMethod();
r_base = AllocTemp();
- LoadWordDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base);
+ LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base);
if (IsTemp(rl_method.reg)) {
FreeTemp(rl_method.reg);
}
@@ -493,9 +523,9 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double,
LoadCurrMethodDirect(r_method);
r_base = TargetReg(kArg0);
LockTemp(r_base);
- LoadWordDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base);
- LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- sizeof(int32_t*) * field_info.StorageIndex(), r_base);
+ LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base);
+ int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
+ LoadRefDisp(r_base, offset_of_field, r_base);
// r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
if (!field_info.IsInitialized() &&
(mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
@@ -535,8 +565,10 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double,
}
if (is_long_or_double) {
StoreBaseDispWide(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg);
+ } else if (rl_src.ref) {
+ StoreRefDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg);
} else {
- StoreWordDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg);
+ Store32Disp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg);
}
if (field_info.IsVolatile()) {
// A load might follow the volatile store so insert a StoreLoad barrier.
@@ -567,7 +599,7 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest,
// Fast path, static storage base is this method's class
RegLocation rl_method = LoadCurrMethod();
r_base = AllocTemp();
- LoadWordDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base);
+ LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base);
} else {
// Medium path, static storage base in a different class which requires checks that the other
// class is initialized
@@ -580,9 +612,9 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest,
LoadCurrMethodDirect(r_method);
r_base = TargetReg(kArg0);
LockTemp(r_base);
- LoadWordDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base);
- LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- sizeof(int32_t*) * field_info.StorageIndex(), r_base);
+ LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base);
+ int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
+ LoadRefDisp(r_base, offset_of_field, r_base);
// r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
if (!field_info.IsInitialized() &&
(mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
@@ -615,8 +647,10 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest,
if (is_long_or_double) {
LoadBaseDispWide(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg, INVALID_SREG);
+ } else if (rl_result.ref) {
+ LoadRefDisp(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg);
} else {
- LoadWordDisp(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg);
+ Load32Disp(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg);
}
FreeTemp(r_base);
@@ -675,82 +709,6 @@ void Mir2Lir::HandleSuspendLaunchPads() {
}
}
-void Mir2Lir::HandleThrowLaunchPads() {
- int num_elems = throw_launchpads_.Size();
- for (int i = 0; i < num_elems; i++) {
- ResetRegPool();
- ResetDefTracking();
- LIR* lab = throw_launchpads_.Get(i);
- current_dalvik_offset_ = lab->operands[1];
- AppendLIR(lab);
- ThreadOffset<4> func_offset(-1);
- int v1 = lab->operands[2];
- int v2 = lab->operands[3];
- const bool target_x86 = cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64;
- switch (lab->operands[0]) {
- case kThrowConstantArrayBounds: // v1 is length reg (for Arm/Mips), v2 constant index
- // v1 holds the constant array index. Mips/Arm uses v2 for length, x86 reloads.
- if (target_x86) {
- OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v1),
- mirror::Array::LengthOffset().Int32Value());
- } else {
- OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v1));
- }
- // Make sure the following LoadConstant doesn't mess with kArg1.
- LockTemp(TargetReg(kArg1));
- LoadConstant(TargetReg(kArg0), v2);
- func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds);
- break;
- case kThrowArrayBounds:
- // Move v1 (array index) to kArg0 and v2 (array length) to kArg1
- if (v2 != TargetReg(kArg0).GetReg()) {
- OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1));
- if (target_x86) {
- // x86 leaves the array pointer in v2, so load the array length that the handler expects
- OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2),
- mirror::Array::LengthOffset().Int32Value());
- } else {
- OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2));
- }
- } else {
- if (v1 == TargetReg(kArg1).GetReg()) {
- // Swap v1 and v2, using kArg2 as a temp
- OpRegCopy(TargetReg(kArg2), RegStorage::Solo32(v1));
- if (target_x86) {
- // x86 leaves the array pointer in v2; load the array length that the handler expects
- OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2),
- mirror::Array::LengthOffset().Int32Value());
- } else {
- OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2));
- }
- OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));
- } else {
- if (target_x86) {
- // x86 leaves the array pointer in v2; load the array length that the handler expects
- OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2),
- mirror::Array::LengthOffset().Int32Value());
- } else {
- OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2));
- }
- OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1));
- }
- }
- func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds);
- break;
- case kThrowNoSuchMethod:
- OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1));
- func_offset =
- QUICK_ENTRYPOINT_OFFSET(4, pThrowNoSuchMethod);
- break;
- default:
- LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0];
- }
- ClobberCallerSave();
- RegStorage r_tgt = CallHelperSetup(func_offset);
- CallHelper(r_tgt, func_offset, true /* MarkSafepointPC */, true /* UseLink */);
- }
-}
-
void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size,
RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double,
bool is_object) {
@@ -798,7 +756,7 @@ void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size,
} else {
rl_result = EvalLoc(rl_dest, reg_class, true);
GenNullCheck(rl_obj.reg, opt_flags);
- LoadBaseDisp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_result.reg, kWord,
+ LoadBaseDisp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_result.reg, k32,
rl_obj.s_reg_low);
MarkPossibleNullPointerException(opt_flags);
if (field_info.IsVolatile()) {
@@ -862,7 +820,7 @@ void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size,
// There might have been a store before this volatile one so insert StoreStore barrier.
GenMemBarrier(kStoreStore);
}
- StoreBaseDisp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_src.reg, kWord);
+ Store32Disp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_src.reg);
MarkPossibleNullPointerException(opt_flags);
if (field_info.IsVolatile()) {
// A load might follow the volatile store so insert a StoreLoad barrier.
@@ -911,11 +869,9 @@ void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) {
// We're don't need access checks, load type from dex cache
int32_t dex_cache_offset =
mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
- LoadWordDisp(rl_method.reg, dex_cache_offset, res_reg);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
- * type_idx);
- LoadWordDisp(res_reg, offset_of_type, rl_result.reg);
+ Load32Disp(rl_method.reg, dex_cache_offset, res_reg);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+ Load32Disp(res_reg, offset_of_type, rl_result.reg);
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
type_idx) || SLOW_TYPE_PATH) {
// Slow path, at runtime test if type is null and if so initialize
@@ -961,8 +917,8 @@ void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) {
void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
/* NOTE: Most strings should be available at compile time */
- int32_t offset_of_string = mirror::Array::DataOffset(sizeof(mirror::String*)).Int32Value() +
- (sizeof(mirror::String*) * string_idx);
+ int32_t offset_of_string = mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx).
+ Int32Value();
if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
*cu_->dex_file, string_idx) || SLOW_STRING_PATH) {
// slow path, resolve string if not in dex cache
@@ -980,11 +936,11 @@ void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
r_method = TargetReg(kArg2);
LoadCurrMethodDirect(r_method);
}
- LoadWordDisp(r_method, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(),
- TargetReg(kArg0));
+ LoadRefDisp(r_method, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(),
+ TargetReg(kArg0));
// Might call out to helper, which will return resolved string in kRet0
- LoadWordDisp(TargetReg(kArg0), offset_of_string, TargetReg(kRet0));
+ Load32Disp(TargetReg(kArg0), offset_of_string, TargetReg(kRet0));
if (cu_->instruction_set == kThumb2 ||
cu_->instruction_set == kMips) {
// OpRegImm(kOpCmp, TargetReg(kRet0), 0); // Is resolved?
@@ -1034,8 +990,8 @@ void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
RegLocation rl_method = LoadCurrMethod();
RegStorage res_reg = AllocTemp();
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- LoadWordDisp(rl_method.reg, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), res_reg);
- LoadWordDisp(res_reg, offset_of_string, rl_result.reg);
+ LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), res_reg);
+ Load32Disp(res_reg, offset_of_string, rl_result.reg);
StoreValue(rl_dest, rl_result);
}
}
@@ -1120,19 +1076,18 @@ void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, Re
LoadCurrMethodDirect(check_class);
if (use_declaring_class) {
- LoadWordDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class);
- LoadWordDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class);
+ LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class);
+ LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class);
} else {
- LoadWordDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- check_class);
- LoadWordDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
- (sizeof(mirror::Class*) * type_idx);
- LoadWordDisp(check_class, offset_of_type, check_class);
+ LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ check_class);
+ LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+ LoadRefDisp(check_class, offset_of_type, check_class);
}
LIR* ne_branchover = NULL;
+ // FIXME: what should we be comparing here? compressed or decompressed references?
if (cu_->instruction_set == kThumb2) {
OpRegReg(kOpCmp, check_class, object_class); // Same?
LIR* it = OpIT(kCondEq, ""); // if-convert the test
@@ -1178,17 +1133,15 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know
LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
} else if (use_declaring_class) {
LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
- LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+ LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
class_reg);
} else {
// Load dex cache entry into class_reg (kArg2)
LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
- LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- class_reg);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
- * type_idx);
- LoadWordDisp(class_reg, offset_of_type, class_reg);
+ LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ class_reg);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+ LoadRefDisp(class_reg, offset_of_type, class_reg);
if (!can_assume_type_is_in_dex_cache) {
// Need to test presence of type in dex cache at runtime
LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
@@ -1212,7 +1165,7 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know
/* load object->klass_ */
DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
- LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
+ LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
/* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */
LIR* branchover = NULL;
if (type_known_final) {
@@ -1315,16 +1268,14 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_
type_idx, TargetReg(kArg1), true);
OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path
} else if (use_declaring_class) {
- LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
- class_reg);
+ LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+ class_reg);
} else {
// Load dex cache entry into class_reg (kArg2)
- LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- class_reg);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
- (sizeof(mirror::Class*) * type_idx);
- LoadWordDisp(class_reg, offset_of_type, class_reg);
+ LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ class_reg);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+ LoadRefDisp(class_reg, offset_of_type, class_reg);
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
// Need to test presence of type in dex cache at runtime
LIR* hop_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL);
@@ -1372,8 +1323,8 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_
GenerateTargetLabel();
if (load_) {
- m2l_->LoadWordDisp(m2l_->TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(),
- m2l_->TargetReg(kArg1));
+ m2l_->LoadRefDisp(m2l_->TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(),
+ m2l_->TargetReg(kArg1));
}
m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pCheckCast), m2l_->TargetReg(kArg2),
m2l_->TargetReg(kArg1), true);
@@ -1399,7 +1350,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_
LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL);
/* load object->klass_ */
DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
- LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
+ LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
LIR* branch2 = OpCmpBranch(kCondNe, TargetReg(kArg1), class_reg, NULL);
LIR* cont = NewLIR0(kPseudoTargetLabel);
@@ -1586,7 +1537,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
rl_src1 = LoadValue(rl_src1, kCoreReg);
rl_src2 = LoadValue(rl_src2, kCoreReg);
if (check_zero) {
- GenDivZeroCheck(rl_src2.reg);
+ GenDivZeroCheck(rl_src2.reg);
}
rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
done = true;
@@ -1597,7 +1548,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
rl_src1 = LoadValue(rl_src1, kCoreReg);
rl_src2 = LoadValue(rl_src2, kCoreReg);
if (check_zero) {
- GenDivZeroCheck(rl_src2.reg);
+ GenDivZeroCheck(rl_src2.reg);
}
rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
done = true;
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 4aae16d103..53b6ed420e 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -34,10 +34,10 @@ namespace art {
* and "op" calls may be used here.
*/
-void Mir2Lir::AddIntrinsicLaunchpad(CallInfo* info, LIR* branch, LIR* resume) {
- class IntrinsicLaunchpadPath : public Mir2Lir::LIRSlowPath {
+void Mir2Lir::AddIntrinsicSlowPath(CallInfo* info, LIR* branch, LIR* resume) {
+ class IntrinsicSlowPathPath : public Mir2Lir::LIRSlowPath {
public:
- IntrinsicLaunchpadPath(Mir2Lir* m2l, CallInfo* info, LIR* branch, LIR* resume = nullptr)
+ IntrinsicSlowPathPath(Mir2Lir* m2l, CallInfo* info, LIR* branch, LIR* resume = nullptr)
: LIRSlowPath(m2l, info->offset, branch, resume), info_(info) {
}
@@ -57,7 +57,7 @@ void Mir2Lir::AddIntrinsicLaunchpad(CallInfo* info, LIR* branch, LIR* resume) {
CallInfo* const info_;
};
- AddSlowPath(new (arena_) IntrinsicLaunchpadPath(this, info, branch, resume));
+ AddSlowPath(new (arena_) IntrinsicSlowPathPath(this, info, branch, resume));
}
/*
@@ -255,12 +255,27 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset<4> helper_off
CallHelper(r_tgt, helper_offset, safepoint_pc);
}
+void Mir2Lir::CopyToArgumentRegs(RegStorage arg0, RegStorage arg1) {
+ if (arg1.GetReg() == TargetReg(kArg0).GetReg()) {
+ if (arg0.GetReg() == TargetReg(kArg1).GetReg()) {
+ // Swap kArg0 and kArg1 with kArg2 as temp.
+ OpRegCopy(TargetReg(kArg2), arg1);
+ OpRegCopy(TargetReg(kArg0), arg0);
+ OpRegCopy(TargetReg(kArg1), TargetReg(kArg2));
+ } else {
+ OpRegCopy(TargetReg(kArg1), arg1);
+ OpRegCopy(TargetReg(kArg0), arg0);
+ }
+ } else {
+ OpRegCopy(TargetReg(kArg0), arg0);
+ OpRegCopy(TargetReg(kArg1), arg1);
+ }
+}
+
void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage arg0,
RegStorage arg1, bool safepoint_pc) {
RegStorage r_tgt = CallHelperSetup(helper_offset);
- DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg()); // check copy into arg0 won't clobber arg1
- OpRegCopy(TargetReg(kArg0), arg0);
- OpRegCopy(TargetReg(kArg1), arg1);
+ CopyToArgumentRegs(arg0, arg1);
ClobberCallerSave();
CallHelper(r_tgt, helper_offset, safepoint_pc);
}
@@ -268,9 +283,7 @@ void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage
void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset<4> helper_offset, RegStorage arg0,
RegStorage arg1, int arg2, bool safepoint_pc) {
RegStorage r_tgt = CallHelperSetup(helper_offset);
- DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg()); // check copy into arg0 won't clobber arg1
- OpRegCopy(TargetReg(kArg0), arg0);
- OpRegCopy(TargetReg(kArg1), arg1);
+ CopyToArgumentRegs(arg0, arg1);
LoadConstant(TargetReg(kArg2), arg2);
ClobberCallerSave();
CallHelper(r_tgt, helper_offset, safepoint_pc);
@@ -347,7 +360,11 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) {
rl_src.reg = TargetReg(kArg0);
rl_src.home = false;
MarkLive(rl_src.reg, rl_src.s_reg_low);
- StoreValue(rl_method, rl_src);
+ if (rl_method.wide) {
+ StoreValueWide(rl_method, rl_src);
+ } else {
+ StoreValue(rl_method, rl_src);
+ }
// If Method* has been promoted, explicitly flush
if (rl_method.location == kLocPhysReg) {
StoreWordDisp(TargetReg(kSp), 0, TargetReg(kArg0));
@@ -412,16 +429,15 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) {
}
}
if (need_flush) {
- StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, kWord);
+ Store32Disp(TargetReg(kSp), SRegOffset(start_vreg + i), reg);
}
} else {
// If arriving in frame & promoted
if (v_map->core_location == kLocPhysReg) {
- LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i),
- RegStorage::Solo32(v_map->core_reg));
+ Load32Disp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->core_reg));
}
if (v_map->fp_location == kLocPhysReg) {
- LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->FpReg));
+ Load32Disp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->FpReg));
}
}
}
@@ -463,9 +479,9 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
cg->LoadCurrMethodDirect(cg->TargetReg(kArg0));
break;
case 1: // Get method->dex_cache_resolved_methods_
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- cg->TargetReg(kArg0));
+ cg->LoadRefDisp(cg->TargetReg(kArg0),
+ mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ cg->TargetReg(kArg0));
// Set up direct code if known.
if (direct_code != 0) {
if (direct_code != static_cast<unsigned int>(-1)) {
@@ -478,9 +494,9 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
break;
case 2: // Grab target method*
CHECK_EQ(cu->dex_file, target_method.dex_file);
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- (target_method.dex_method_index * 4), cg->TargetReg(kArg0));
+ cg->LoadRefDisp(cg->TargetReg(kArg0),
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
+ (target_method.dex_method_index * 4), cg->TargetReg(kArg0));
break;
case 3: // Grab the code from the method*
if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
@@ -524,18 +540,18 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info,
case 1: // Is "this" null? [use kArg1]
cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags);
// get this->klass_ [use kArg1, set kInvokeTgt]
- cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
- cg->TargetReg(kInvokeTgt));
+ cg->LoadRefDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
cg->MarkPossibleNullPointerException(info->opt_flags);
break;
case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt]
- cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(),
- cg->TargetReg(kInvokeTgt));
+ cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
break;
case 3: // Get target method [use kInvokeTgt, set kArg0]
- cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) +
- mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(),
- cg->TargetReg(kArg0));
+ cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) +
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(),
+ cg->TargetReg(kArg0));
break;
case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt]
if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
@@ -579,15 +595,17 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
case 2: // Is "this" null? [use kArg1]
cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags);
// Get this->klass_ [use kArg1, set kInvokeTgt]
- cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
- cg->TargetReg(kInvokeTgt));
+ cg->LoadRefDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
cg->MarkPossibleNullPointerException(info->opt_flags);
break;
case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt]
- cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(),
- cg->TargetReg(kInvokeTgt));
+ // NOTE: native pointer.
+ cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
break;
case 4: // Get target method [use kInvokeTgt, set kArg0]
+ // NOTE: native pointer.
cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) +
mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(),
cg->TargetReg(kArg0));
@@ -740,11 +758,11 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
} else {
// kArg2 & rArg3 can safely be used here
reg = TargetReg(kArg3);
- LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg);
+ Load32Disp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg);
call_state = next_call_insn(cu_, info, call_state, target_method,
vtable_idx, direct_code, direct_method, type);
}
- StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord);
+ Store32Disp(TargetReg(kSp), (next_use + 1) * 4, reg);
call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
direct_code, direct_method, type);
next_use++;
@@ -778,7 +796,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
StoreBaseDispWide(TargetReg(kSp), outs_offset, RegStorage::MakeRegPair(low_reg, high_reg));
next_use += 2;
} else {
- StoreWordDisp(TargetReg(kSp), outs_offset, low_reg);
+ Store32Disp(TargetReg(kSp), outs_offset, low_reg);
next_use++;
}
call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
@@ -798,7 +816,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
// In lieu of generating a check for kArg1 being null, we need to
// perform a load when doing implicit checks.
RegStorage tmp = AllocTemp();
- LoadWordDisp(TargetReg(kArg1), 0, tmp);
+ Load32Disp(TargetReg(kArg1), 0, tmp);
MarkPossibleNullPointerException(info->opt_flags);
FreeTemp(tmp);
}
@@ -849,7 +867,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
} else {
loc = UpdateLoc(loc);
if ((next_arg >= 3) && (loc.location == kLocPhysReg)) {
- StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kWord);
+ Store32Disp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg);
}
next_arg++;
}
@@ -984,8 +1002,8 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
RegStorage temp = TargetReg(kArg3);
// Now load the argument VR and store to the outs.
- LoadWordDisp(TargetReg(kSp), current_src_offset, temp);
- StoreWordDisp(TargetReg(kSp), current_dest_offset, temp);
+ Load32Disp(TargetReg(kSp), current_src_offset, temp);
+ Store32Disp(TargetReg(kSp), current_dest_offset, temp);
}
current_src_offset += bytes_to_move;
@@ -1014,7 +1032,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
// In lieu of generating a check for kArg1 being null, we need to
// perform a load when doing implicit checks.
RegStorage tmp = AllocTemp();
- LoadWordDisp(TargetReg(kArg1), 0, tmp);
+ Load32Disp(TargetReg(kArg1), 0, tmp);
MarkPossibleNullPointerException(info->opt_flags);
FreeTemp(tmp);
}
@@ -1074,14 +1092,14 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
reg_ptr = AllocTemp();
if (range_check) {
reg_max = AllocTemp();
- LoadWordDisp(rl_obj.reg, count_offset, reg_max);
+ Load32Disp(rl_obj.reg, count_offset, reg_max);
MarkPossibleNullPointerException(info->opt_flags);
}
- LoadWordDisp(rl_obj.reg, offset_offset, reg_off);
+ Load32Disp(rl_obj.reg, offset_offset, reg_off);
MarkPossibleNullPointerException(info->opt_flags);
- LoadWordDisp(rl_obj.reg, value_offset, reg_ptr);
+ Load32Disp(rl_obj.reg, value_offset, reg_ptr);
if (range_check) {
- // Set up a launch pad to allow retry in case of bounds violation */
+ // Set up a slow path to allow retry in case of bounds violation */
OpRegReg(kOpCmp, rl_idx.reg, reg_max);
FreeTemp(reg_max);
range_check_branch = OpCondBranch(kCondUge, nullptr);
@@ -1102,8 +1120,8 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
}
reg_off = AllocTemp();
reg_ptr = AllocTemp();
- LoadWordDisp(rl_obj.reg, offset_offset, reg_off);
- LoadWordDisp(rl_obj.reg, value_offset, reg_ptr);
+ Load32Disp(rl_obj.reg, offset_offset, reg_off);
+ Load32Disp(rl_obj.reg, value_offset, reg_ptr);
}
if (rl_idx.is_const) {
OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg));
@@ -1128,7 +1146,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
if (range_check) {
DCHECK(range_check_branch != nullptr);
info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've already null checked.
- AddIntrinsicLaunchpad(info, range_check_branch);
+ AddIntrinsicSlowPath(info, range_check_branch);
}
return true;
}
@@ -1145,7 +1163,7 @@ bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) {
RegLocation rl_dest = InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
GenNullCheck(rl_obj.reg, info->opt_flags);
- LoadWordDisp(rl_obj.reg, mirror::String::CountOffset().Int32Value(), rl_result.reg);
+ Load32Disp(rl_obj.reg, mirror::String::CountOffset().Int32Value(), rl_result.reg);
MarkPossibleNullPointerException(info->opt_flags);
if (is_empty) {
// dst = (dst == 0);
@@ -1169,9 +1187,9 @@ bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) {
return false;
}
RegLocation rl_src_i = info->args[0];
- RegLocation rl_dest = (size == kLong) ? InlineTargetWide(info) : InlineTarget(info); // result reg
+ RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info); // result reg
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- if (size == kLong) {
+ if (size == k64) {
RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg);
RegStorage r_i_low = rl_i.reg.GetLow();
if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
@@ -1186,8 +1204,8 @@ bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) {
}
StoreValueWide(rl_dest, rl_result);
} else {
- DCHECK(size == kWord || size == kSignedHalf);
- OpKind op = (size == kWord) ? kOpRev : kOpRevsh;
+ DCHECK(size == k32 || size == kSignedHalf);
+ OpKind op = (size == k32) ? kOpRev : kOpRevsh;
RegLocation rl_i = LoadValue(rl_src_i, kCoreReg);
OpRegReg(op, rl_result.reg, rl_i.reg);
StoreValue(rl_dest, rl_result);
@@ -1339,7 +1357,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
DCHECK(high_code_point_branch != nullptr);
LIR* resume_tgt = NewLIR0(kPseudoTargetLabel);
info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked.
- AddIntrinsicLaunchpad(info, high_code_point_branch, resume_tgt);
+ AddIntrinsicSlowPath(info, high_code_point_branch, resume_tgt);
} else {
DCHECK_EQ(mir_graph_->ConstantValue(rl_char) & ~0xFFFF, 0);
DCHECK(high_code_point_branch == nullptr);
@@ -1371,7 +1389,7 @@ bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) {
info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked.
// TUNING: check if rl_cmp.s_reg_low is already null checked
LIR* cmp_null_check_branch = OpCmpImmBranch(kCondEq, reg_cmp, 0, nullptr);
- AddIntrinsicLaunchpad(info, cmp_null_check_branch);
+ AddIntrinsicSlowPath(info, cmp_null_check_branch);
// NOTE: not a safepoint
if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) {
OpReg(kOpBlx, r_tgt);
@@ -1389,7 +1407,7 @@ bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) {
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
ThreadOffset<4> offset = Thread::PeerOffset<4>();
if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) {
- LoadWordDisp(TargetReg(kSelf), offset.Int32Value(), rl_result.reg);
+ Load32Disp(TargetReg(kSelf), offset.Int32Value(), rl_result.reg);
} else {
CHECK(cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64);
reinterpret_cast<X86Mir2Lir*>(this)->OpRegThreadMem(kOpMov, rl_result.reg.GetReg(), offset);
@@ -1416,7 +1434,7 @@ bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info,
if (is_long) {
if (cu_->instruction_set == kX86) {
LoadBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_result.reg.GetLow(),
- rl_result.reg.GetHigh(), kLong, INVALID_SREG);
+ rl_result.reg.GetHigh(), k64, INVALID_SREG);
} else {
RegStorage rl_temp_offset = AllocTemp();
OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg);
@@ -1424,7 +1442,7 @@ bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info,
FreeTemp(rl_temp_offset.GetReg());
}
} else {
- LoadBaseIndexed(rl_object.reg, rl_offset.reg, rl_result.reg, 0, kWord);
+ LoadBaseIndexed(rl_object.reg, rl_offset.reg, rl_result.reg, 0, k32);
}
if (is_volatile) {
@@ -1464,7 +1482,7 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long,
rl_value = LoadValueWide(rl_src_value, kCoreReg);
if (cu_->instruction_set == kX86) {
StoreBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_value.reg.GetLow(),
- rl_value.reg.GetHigh(), kLong, INVALID_SREG);
+ rl_value.reg.GetHigh(), k64, INVALID_SREG);
} else {
RegStorage rl_temp_offset = AllocTemp();
OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg);
@@ -1473,7 +1491,7 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long,
}
} else {
rl_value = LoadValue(rl_src_value, kCoreReg);
- StoreBaseIndexed(rl_object.reg, rl_offset.reg, rl_value.reg, 0, kWord);
+ StoreBaseIndexed(rl_object.reg, rl_offset.reg, rl_value.reg, 0, k32);
}
// Free up the temp early, to ensure x86 doesn't run out of temporaries in MarkGCCard.
diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc
index 208eadde12..9808f7f36f 100644
--- a/compiler/dex/quick/gen_loadstore.cc
+++ b/compiler/dex/quick/gen_loadstore.cc
@@ -65,7 +65,7 @@ void Mir2Lir::Workaround7250540(RegLocation rl_dest, RegStorage zero_reg) {
OpRegCopy(RegStorage::Solo32(promotion_map_[pmap_index].core_reg), temp_reg);
} else {
// Lives in the frame, need to store.
- StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, kWord);
+ StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, k32);
}
if (!zero_reg.Valid()) {
FreeTemp(temp_reg);
@@ -74,15 +74,6 @@ void Mir2Lir::Workaround7250540(RegLocation rl_dest, RegStorage zero_reg) {
}
}
-/* Load a word at base + displacement. Displacement must be word multiple */
-LIR* Mir2Lir::LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
- return LoadBaseDisp(r_base, displacement, r_dest, kWord, INVALID_SREG);
-}
-
-LIR* Mir2Lir::StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) {
- return StoreBaseDisp(r_base, displacement, r_src, kWord);
-}
-
/*
* Load a Dalvik register into a physical register. Take care when
* using this routine, as it doesn't perform any bookkeeping regarding
@@ -93,11 +84,17 @@ void Mir2Lir::LoadValueDirect(RegLocation rl_src, RegStorage r_dest) {
if (rl_src.location == kLocPhysReg) {
OpRegCopy(r_dest, rl_src.reg);
} else if (IsInexpensiveConstant(rl_src)) {
+ // On 64-bit targets, will sign extend. Make sure constant reference is always NULL.
+ DCHECK(!rl_src.ref || (mir_graph_->ConstantValue(rl_src) == 0));
LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src));
} else {
DCHECK((rl_src.location == kLocDalvikFrame) ||
(rl_src.location == kLocCompilerTemp));
- LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
+ if (rl_src.ref) {
+ LoadRefDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
+ } else {
+ Load32Disp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
+ }
}
}
@@ -194,7 +191,7 @@ void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) {
ResetDefLoc(rl_dest);
if (IsDirty(rl_dest.reg) && oat_live_out(rl_dest.s_reg_low)) {
def_start = last_lir_insn_;
- StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord);
+ Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
MarkClean(rl_dest);
def_end = last_lir_insn_;
if (!rl_dest.ref) {
@@ -306,7 +303,7 @@ void Mir2Lir::StoreFinalValue(RegLocation rl_dest, RegLocation rl_src) {
if (IsDirty(rl_dest.reg) &&
oat_live_out(rl_dest.s_reg_low)) {
LIR *def_start = last_lir_insn_;
- StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord);
+ Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
MarkClean(rl_dest);
LIR *def_end = last_lir_insn_;
if (!rl_dest.ref) {
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index a938478b3d..a237ac76b0 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -112,11 +112,11 @@ void MipsMir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegStorage r_key = AllocTemp();
LIR* loop_label = NewLIR0(kPseudoTargetLabel);
LIR* exit_branch = OpCmpBranch(kCondEq, r_base, r_end, NULL);
- LoadWordDisp(r_base, 0, r_key);
+ Load32Disp(r_base, 0, r_key);
OpRegImm(kOpAdd, r_base, 8);
OpCmpBranch(kCondNe, rl_src.reg, r_key, loop_label);
RegStorage r_disp = AllocTemp();
- LoadWordDisp(r_base, -4, r_disp);
+ Load32Disp(r_base, -4, r_disp);
OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp);
OpReg(kOpBx, rs_rRA);
@@ -200,7 +200,7 @@ void MipsMir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset,
// Load the displacement from the switch table
RegStorage r_disp = AllocTemp();
- LoadBaseIndexed(r_base, r_key, r_disp, 2, kWord);
+ LoadBaseIndexed(r_base, r_key, r_disp, 2, k32);
// Add to rAP and go
OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp);
@@ -263,9 +263,9 @@ void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
int ex_offset = Thread::ExceptionOffset<4>().Int32Value();
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
RegStorage reset_reg = AllocTemp();
- LoadWordDisp(rs_rMIPS_SELF, ex_offset, rl_result.reg);
+ Load32Disp(rs_rMIPS_SELF, ex_offset, rl_result.reg);
LoadConstant(reset_reg, 0);
- StoreWordDisp(rs_rMIPS_SELF, ex_offset, reset_reg);
+ Store32Disp(rs_rMIPS_SELF, ex_offset, reset_reg);
FreeTemp(reset_reg);
StoreValue(rl_dest, rl_result);
}
@@ -277,6 +277,7 @@ void MipsMir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) {
RegStorage reg_card_base = AllocTemp();
RegStorage reg_card_no = AllocTemp();
LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
+ // NOTE: native pointer.
LoadWordDisp(rs_rMIPS_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base);
OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte);
@@ -310,7 +311,7 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
RegStorage new_sp = AllocTemp();
if (!skip_overflow_check) {
/* Load stack limit */
- LoadWordDisp(rs_rMIPS_SELF, Thread::StackEndOffset<4>().Int32Value(), check_reg);
+ Load32Disp(rs_rMIPS_SELF, Thread::StackEndOffset<4>().Int32Value(), check_reg);
}
/* Spill core callee saves */
SpillCoreRegs();
@@ -328,7 +329,7 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
m2l_->ResetDefTracking();
GenerateTargetLabel();
// LR is offset 0 since we push in reverse order.
- m2l_->LoadWordDisp(rs_rMIPS_SP, 0, rs_rRA);
+ m2l_->Load32Disp(rs_rMIPS_SP, 0, rs_rRA);
m2l_->OpRegImm(kOpAdd, rs_rMIPS_SP, sp_displace_);
m2l_->ClobberCallerSave();
ThreadOffset<4> func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowStackOverflow);
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 40641d670d..81d6782288 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -126,8 +126,6 @@ class MipsMir2Lir FINAL : public Mir2Lir {
RegLocation rl_src2);
void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2);
- LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, int offset,
- ThrowKind kind);
RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div);
RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div);
void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -161,7 +159,7 @@ class MipsMir2Lir FINAL : public Mir2Lir {
LIR* OpMem(OpKind op, RegStorage r_base, int disp);
LIR* OpPcRelLoad(RegStorage reg, LIR* target);
LIR* OpReg(OpKind op, RegStorage r_dest_src);
- LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src);
+ void OpRegCopy(RegStorage r_dest, RegStorage r_src);
LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src);
LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value);
LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index ac0847f22d..7c0becd41a 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -177,37 +177,40 @@ LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
return res;
}
-LIR* MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
- LIR *res = OpRegCopyNoInsert(r_dest, r_src);
- AppendLIR(res);
- return res;
+void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
+ if (r_dest != r_src) {
+ LIR *res = OpRegCopyNoInsert(r_dest, r_src);
+ AppendLIR(res);
+ }
}
void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
- bool dest_fp = MIPS_FPREG(r_dest.GetLowReg());
- bool src_fp = MIPS_FPREG(r_src.GetLowReg());
- if (dest_fp) {
- if (src_fp) {
- // FIXME: handle this here - reserve OpRegCopy for 32-bit copies.
- OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())),
- RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg())));
- } else {
- /* note the operands are swapped for the mtc1 instr */
- NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
- NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
- }
- } else {
- if (src_fp) {
- NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg());
- NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg());
+ if (r_dest != r_src) {
+ bool dest_fp = MIPS_FPREG(r_dest.GetLowReg());
+ bool src_fp = MIPS_FPREG(r_src.GetLowReg());
+ if (dest_fp) {
+ if (src_fp) {
+ // FIXME: handle this here - reserve OpRegCopy for 32-bit copies.
+ OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())),
+ RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg())));
+ } else {
+ /* note the operands are swapped for the mtc1 instr */
+ NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
+ NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
+ }
} else {
- // Handle overlap
- if (r_src.GetHighReg() == r_dest.GetLowReg()) {
- OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
- OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ if (src_fp) {
+ NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg());
+ NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg());
} else {
- OpRegCopy(r_dest.GetLow(), r_src.GetLow());
- OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ // Handle overlap
+ if (r_src.GetHighReg() == r_dest.GetLowReg()) {
+ OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ } else {
+ OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ }
}
}
}
@@ -221,12 +224,6 @@ void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch";
}
-LIR* MipsMir2Lir::GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base,
- int offset, ThrowKind kind) {
- LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm";
- return NULL;
-}
-
RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2,
bool is_div) {
NewLIR2(kMipsDiv, reg1.GetReg(), reg2.GetReg());
@@ -346,7 +343,7 @@ void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) {
DCHECK(reg.IsPair()); // TODO: support k64BitSolo.
RegStorage t_reg = AllocTemp();
OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh());
- GenDivZeroCheck(kCondEq);
+ GenDivZeroCheck(t_reg);
FreeTemp(t_reg);
}
@@ -480,7 +477,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
rl_array = LoadValue(rl_array, kCoreReg);
rl_index = LoadValue(rl_index, kCoreReg);
- if (size == kLong || size == kDouble) {
+ if (size == k64 || size == kDouble) {
data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
} else {
data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
@@ -495,12 +492,12 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
if (needs_range_check) {
reg_len = AllocTemp();
/* Get len */
- LoadWordDisp(rl_array.reg, len_offset, reg_len);
+ Load32Disp(rl_array.reg, len_offset, reg_len);
}
/* reg_ptr -> array data */
OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset);
FreeTemp(rl_array.reg.GetReg());
- if ((size == kLong) || (size == kDouble)) {
+ if ((size == k64) || (size == kDouble)) {
if (scale) {
RegStorage r_new_index = AllocTemp();
OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale);
@@ -513,7 +510,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
rl_result = EvalLoc(rl_dest, reg_class, true);
if (needs_range_check) {
- GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
LoadBaseDispWide(reg_ptr, 0, rl_result.reg, INVALID_SREG);
@@ -524,7 +521,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
rl_result = EvalLoc(rl_dest, reg_class, true);
if (needs_range_check) {
- GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size);
@@ -544,7 +541,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
- if (size == kLong || size == kDouble) {
+ if (size == k64 || size == kDouble) {
data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
} else {
data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
@@ -572,12 +569,12 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
reg_len = AllocTemp();
// NOTE: max live temps(4) here.
/* Get len */
- LoadWordDisp(rl_array.reg, len_offset, reg_len);
+ Load32Disp(rl_array.reg, len_offset, reg_len);
}
/* reg_ptr -> array data */
OpRegImm(kOpAdd, reg_ptr, data_offset);
/* at this point, reg_ptr points to array, 2 live temps */
- if ((size == kLong) || (size == kDouble)) {
+ if ((size == k64) || (size == kDouble)) {
// TUNING: specific wide routine that can handle fp regs
if (scale) {
RegStorage r_new_index = AllocTemp();
@@ -590,7 +587,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
rl_src = LoadValueWide(rl_src, reg_class);
if (needs_range_check) {
- GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
@@ -598,7 +595,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
} else {
rl_src = LoadValue(rl_src, reg_class);
if (needs_range_check) {
- GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index 3e02faed55..7f4cd5e242 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -369,7 +369,7 @@ void MipsMir2Lir::FlushReg(RegStorage reg) {
if (info->live && info->dirty) {
info->dirty = false;
int v_reg = mir_graph_->SRegToVReg(info->s_reg);
- StoreBaseDisp(rs_rMIPS_SP, VRegOffset(v_reg), reg, kWord);
+ Store32Disp(rs_rMIPS_SP, VRegOffset(v_reg), reg);
}
}
@@ -531,12 +531,14 @@ void MipsMir2Lir::FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free) {
* there is a trap in the shadow. Allocate a temp register.
*/
RegStorage MipsMir2Lir::LoadHelper(ThreadOffset<4> offset) {
+ // NOTE: native pointer.
LoadWordDisp(rs_rMIPS_SELF, offset.Int32Value(), rs_rT9);
return rs_rT9;
}
LIR* MipsMir2Lir::CheckSuspendUsingLoad() {
RegStorage tmp = AllocTemp();
+ // NOTE: native pointer.
LoadWordDisp(rs_rMIPS_SELF, Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp);
LIR *inst = LoadWordDisp(tmp, 0, tmp);
FreeTemp(tmp);
@@ -553,7 +555,7 @@ void MipsMir2Lir::SpillCoreRegs() {
for (int reg = 0; mask; mask >>= 1, reg++) {
if (mask & 0x1) {
offset -= 4;
- StoreWordDisp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg));
+ Store32Disp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg));
}
}
}
@@ -567,7 +569,7 @@ void MipsMir2Lir::UnSpillCoreRegs() {
for (int reg = 0; mask; mask >>= 1, reg++) {
if (mask & 0x1) {
offset -= 4;
- LoadWordDisp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg));
+ Load32Disp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg));
}
}
OpRegImm(kOpAdd, rs_rSP, frame_size_);
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index c959510025..a865430d6c 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -357,11 +357,11 @@ LIR* MipsMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor
if (MIPS_FPREG(r_dest.GetReg())) {
DCHECK(MIPS_SINGLEREG(r_dest.GetReg()));
- DCHECK((size == kWord) || (size == kSingle));
+ DCHECK((size == k32) || (size == kSingle) || (size == kReference));
size = kSingle;
} else {
if (size == kSingle)
- size = kWord;
+ size = k32;
}
if (!scale) {
@@ -375,7 +375,8 @@ LIR* MipsMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor
case kSingle:
opcode = kMipsFlwc1;
break;
- case kWord:
+ case k32:
+ case kReference:
opcode = kMipsLw;
break;
case kUnsignedHalf:
@@ -408,11 +409,11 @@ LIR* MipsMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto
if (MIPS_FPREG(r_src.GetReg())) {
DCHECK(MIPS_SINGLEREG(r_src.GetReg()));
- DCHECK((size == kWord) || (size == kSingle));
+ DCHECK((size == k32) || (size == kSingle) || (size == kReference));
size = kSingle;
} else {
if (size == kSingle)
- size = kWord;
+ size = k32;
}
if (!scale) {
@@ -426,7 +427,8 @@ LIR* MipsMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto
case kSingle:
opcode = kMipsFswc1;
break;
- case kWord:
+ case k32:
+ case kReference:
opcode = kMipsSw;
break;
case kUnsignedHalf:
@@ -463,7 +465,7 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora
bool pair = false;
switch (size) {
- case kLong:
+ case k64:
case kDouble:
pair = true;
opcode = kMipsLw;
@@ -481,8 +483,9 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora
short_form = IS_SIMM16_2WORD(displacement);
DCHECK_EQ((displacement & 0x3), 0);
break;
- case kWord:
+ case k32:
case kSingle:
+ case kReference:
opcode = kMipsLw;
if (MIPS_FPREG(r_dest.GetReg())) {
opcode = kMipsFlwc1;
@@ -544,13 +547,17 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora
LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
OpSize size, int s_reg) {
+ // TODO: base this on target.
+ if (size == kWord) {
+ size = k32;
+ }
return LoadBaseDispBody(r_base, displacement, r_dest, RegStorage::InvalidReg(), size,
s_reg);
}
LIR* MipsMir2Lir::LoadBaseDispWide(RegStorage r_base, int displacement, RegStorage r_dest,
int s_reg) {
- return LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), r_dest.GetHigh(), kLong, s_reg);
+ return LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), r_dest.GetHigh(), k64, s_reg);
}
LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement,
@@ -563,7 +570,7 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement,
bool pair = false;
switch (size) {
- case kLong:
+ case k64:
case kDouble:
pair = true;
opcode = kMipsSw;
@@ -580,8 +587,9 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement,
short_form = IS_SIMM16_2WORD(displacement);
DCHECK_EQ((displacement & 0x3), 0);
break;
- case kWord:
+ case k32:
case kSingle:
+ case kReference:
opcode = kMipsSw;
if (MIPS_FPREG(r_src.GetReg())) {
opcode = kMipsFswc1;
@@ -635,11 +643,15 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement,
LIR* MipsMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src,
OpSize size) {
+ // TODO: base this on target.
+ if (size == kWord) {
+ size = k32;
+ }
return StoreBaseDispBody(r_base, displacement, r_src, RegStorage::InvalidReg(), size);
}
LIR* MipsMir2Lir::StoreBaseDispWide(RegStorage r_base, int displacement, RegStorage r_src) {
- return StoreBaseDispBody(r_base, displacement, r_src.GetLow(), r_src.GetHigh(), kLong);
+ return StoreBaseDispBody(r_base, displacement, r_src.GetLow(), r_src.GetHigh(), k64);
}
LIR* MipsMir2Lir::OpThreadMem(OpKind op, ThreadOffset<4> thread_offset) {
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 6fcdf70b12..b8ab609f31 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -63,14 +63,14 @@ RegStorage Mir2Lir::LoadArg(int in_position, bool wide) {
} else {
reg_arg_high = AllocTemp();
int offset_high = offset + sizeof(uint32_t);
- LoadWordDisp(TargetReg(kSp), offset_high, reg_arg_high);
+ Load32Disp(TargetReg(kSp), offset_high, reg_arg_high);
}
}
// If the low part is not in a register yet, we need to load it.
if (!reg_arg_low.Valid()) {
reg_arg_low = AllocTemp();
- LoadWordDisp(TargetReg(kSp), offset, reg_arg_low);
+ Load32Disp(TargetReg(kSp), offset, reg_arg_low);
}
if (wide) {
@@ -96,7 +96,7 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) {
if (reg.Valid()) {
OpRegCopy(rl_dest.reg, reg);
} else {
- LoadWordDisp(TargetReg(kSp), offset, rl_dest.reg);
+ Load32Disp(TargetReg(kSp), offset, rl_dest.reg);
}
} else {
RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
@@ -107,10 +107,10 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) {
} else if (reg_arg_low.Valid() && !reg_arg_high.Valid()) {
OpRegCopy(rl_dest.reg, reg_arg_low);
int offset_high = offset + sizeof(uint32_t);
- LoadWordDisp(TargetReg(kSp), offset_high, rl_dest.reg.GetHigh());
+ Load32Disp(TargetReg(kSp), offset_high, rl_dest.reg.GetHigh());
} else if (!reg_arg_low.Valid() && reg_arg_high.Valid()) {
OpRegCopy(rl_dest.reg.GetHigh(), reg_arg_high);
- LoadWordDisp(TargetReg(kSp), offset, rl_dest.reg.GetLow());
+ Load32Disp(TargetReg(kSp), offset, rl_dest.reg.GetLow());
} else {
LoadBaseDispWide(TargetReg(kSp), offset, rl_dest.reg, INVALID_SREG);
}
@@ -137,7 +137,7 @@ bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) {
if (wide) {
LoadBaseDispWide(reg_obj, data.field_offset, rl_dest.reg, INVALID_SREG);
} else {
- LoadWordDisp(reg_obj, data.field_offset, rl_dest.reg);
+ Load32Disp(reg_obj, data.field_offset, rl_dest.reg);
}
if (data.is_volatile) {
// Without context sensitive analysis, we must issue the most conservative barriers.
@@ -175,7 +175,7 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) {
if (wide) {
StoreBaseDispWide(reg_obj, data.field_offset, reg_src);
} else {
- StoreBaseDisp(reg_obj, data.field_offset, reg_src, kWord);
+ Store32Disp(reg_obj, data.field_offset, reg_src);
}
if (data.is_volatile) {
// A load might follow the volatile store so insert a StoreLoad barrier.
@@ -449,7 +449,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
rl_src[0] = LoadValue(rl_src[0], kCoreReg);
GenNullCheck(rl_src[0].reg, opt_flags);
rl_result = EvalLoc(rl_dest, kCoreReg, true);
- LoadWordDisp(rl_src[0].reg, len_offset, rl_result.reg);
+ Load32Disp(rl_src[0].reg, len_offset, rl_result.reg);
MarkPossibleNullPointerException(opt_flags);
StoreValue(rl_dest, rl_result);
break;
@@ -562,11 +562,13 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
}
case Instruction::AGET_WIDE:
- GenArrayGet(opt_flags, kLong, rl_src[0], rl_src[1], rl_dest, 3);
+ GenArrayGet(opt_flags, k64, rl_src[0], rl_src[1], rl_dest, 3);
break;
- case Instruction::AGET:
case Instruction::AGET_OBJECT:
- GenArrayGet(opt_flags, kWord, rl_src[0], rl_src[1], rl_dest, 2);
+ GenArrayGet(opt_flags, kReference, rl_src[0], rl_src[1], rl_dest, 2);
+ break;
+ case Instruction::AGET:
+ GenArrayGet(opt_flags, k32, rl_src[0], rl_src[1], rl_dest, 2);
break;
case Instruction::AGET_BOOLEAN:
GenArrayGet(opt_flags, kUnsignedByte, rl_src[0], rl_src[1], rl_dest, 0);
@@ -581,10 +583,10 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1);
break;
case Instruction::APUT_WIDE:
- GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3, false);
+ GenArrayPut(opt_flags, k64, rl_src[1], rl_src[2], rl_src[0], 3, false);
break;
case Instruction::APUT:
- GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, false);
+ GenArrayPut(opt_flags, k32, rl_src[1], rl_src[2], rl_src[0], 2, false);
break;
case Instruction::APUT_OBJECT: {
bool is_null = mir_graph_->IsConstantNullRef(rl_src[0]);
@@ -597,7 +599,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
if (is_null || is_safe) {
// Store of constant null doesn't require an assignability test and can be generated inline
// without fixed register usage or a card mark.
- GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, !is_null);
+ GenArrayPut(opt_flags, kReference, rl_src[1], rl_src[2], rl_src[0], 2, !is_null);
} else {
GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0]);
}
@@ -613,15 +615,15 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
break;
case Instruction::IGET_OBJECT:
- GenIGet(mir, opt_flags, kWord, rl_dest, rl_src[0], false, true);
+ GenIGet(mir, opt_flags, kReference, rl_dest, rl_src[0], false, true);
break;
case Instruction::IGET_WIDE:
- GenIGet(mir, opt_flags, kLong, rl_dest, rl_src[0], true, false);
+ GenIGet(mir, opt_flags, k64, rl_dest, rl_src[0], true, false);
break;
case Instruction::IGET:
- GenIGet(mir, opt_flags, kWord, rl_dest, rl_src[0], false, false);
+ GenIGet(mir, opt_flags, k32, rl_dest, rl_src[0], false, false);
break;
case Instruction::IGET_CHAR:
@@ -638,15 +640,15 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
break;
case Instruction::IPUT_WIDE:
- GenIPut(mir, opt_flags, kLong, rl_src[0], rl_src[1], true, false);
+ GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1], true, false);
break;
case Instruction::IPUT_OBJECT:
- GenIPut(mir, opt_flags, kWord, rl_src[0], rl_src[1], false, true);
+ GenIPut(mir, opt_flags, kReference, rl_src[0], rl_src[1], false, true);
break;
case Instruction::IPUT:
- GenIPut(mir, opt_flags, kWord, rl_src[0], rl_src[1], false, false);
+ GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1], false, false);
break;
case Instruction::IPUT_BOOLEAN:
@@ -988,6 +990,9 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) {
ResetRegPool();
if (cu_->disable_opt & (1 << kTrackLiveTemps)) {
ClobberAllRegs();
+ // Reset temp allocation to minimize differences when A/B testing.
+ reg_pool_->next_core_reg = 0;
+ reg_pool_->next_fp_reg = 0;
}
if (cu_->disable_opt & (1 << kSuppressLoads)) {
@@ -1097,8 +1102,6 @@ void Mir2Lir::MethodMIR2LIR() {
cu_->NewTimingSplit("Launchpads");
HandleSuspendLaunchPads();
-
- HandleThrowLaunchPads();
}
//
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 65910e9eb8..2b6d78b35a 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -109,6 +109,11 @@ typedef uint32_t CodeOffset; // Native code offset in bytes.
#define REG_USE23 (REG_USE2 | REG_USE3)
#define REG_USE123 (REG_USE1 | REG_USE2 | REG_USE3)
+// TODO: #includes need a cleanup
+#ifndef INVALID_SREG
+#define INVALID_SREG (-1)
+#endif
+
struct BasicBlock;
struct CallInfo;
struct CompilationUnit;
@@ -554,12 +559,11 @@ class Mir2Lir : public Backend {
RegisterInfo* GetRegInfo(int reg);
// Shared by all targets - implemented in gen_common.cc.
- void AddIntrinsicLaunchpad(CallInfo* info, LIR* branch, LIR* resume = nullptr);
+ void AddIntrinsicSlowPath(CallInfo* info, LIR* branch, LIR* resume = nullptr);
bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
RegLocation rl_src, RegLocation rl_dest, int lit);
bool HandleEasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit);
void HandleSuspendLaunchPads();
- void HandleThrowLaunchPads();
void HandleSlowPaths();
void GenBarrier();
void GenDivZeroException();
@@ -567,6 +571,8 @@ class Mir2Lir : public Backend {
void GenDivZeroCheck(ConditionCode c_code);
// reg holds divisor.
void GenDivZeroCheck(RegStorage reg);
+ void GenArrayBoundsCheck(RegStorage index, RegStorage length);
+ void GenArrayBoundsCheck(int32_t index, RegStorage length);
LIR* GenNullCheck(RegStorage reg);
void MarkPossibleNullPointerException(int opt_flags);
void MarkPossibleStackOverflowException();
@@ -574,7 +580,6 @@ class Mir2Lir : public Backend {
LIR* GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind);
LIR* GenNullCheck(RegStorage m_reg, int opt_flags);
LIR* GenExplicitNullCheck(RegStorage m_reg, int opt_flags);
- LIR* GenRegRegCheck(ConditionCode c_code, RegStorage reg1, RegStorage reg2, ThrowKind kind);
void GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1,
RegLocation rl_src2, LIR* taken, LIR* fall_through);
void GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src,
@@ -723,14 +728,42 @@ class Mir2Lir : public Backend {
RegLocation LoadCurrMethod();
void LoadCurrMethodDirect(RegStorage r_tgt);
LIR* LoadConstant(RegStorage r_dest, int value);
- LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest);
+ // Natural word size.
+ LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
+ return LoadBaseDisp(r_base, displacement, r_dest, kWord, INVALID_SREG);
+ }
+ // Load 32 bits, regardless of target.
+ LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
+ return LoadBaseDisp(r_base, displacement, r_dest, k32, INVALID_SREG);
+ }
+ // Load a reference at base + displacement and decompress into register.
+ LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
+ return LoadBaseDisp(r_base, displacement, r_dest, kReference, INVALID_SREG);
+ }
+ // Load Dalvik value with 32-bit memory storage. If compressed object reference, decompress.
RegLocation LoadValue(RegLocation rl_src, RegisterClass op_kind);
+ // Load Dalvik value with 64-bit memory storage.
RegLocation LoadValueWide(RegLocation rl_src, RegisterClass op_kind);
+ // Load Dalvik value with 32-bit memory storage. If compressed object reference, decompress.
void LoadValueDirect(RegLocation rl_src, RegStorage r_dest);
+ // Load Dalvik value with 32-bit memory storage. If compressed object reference, decompress.
void LoadValueDirectFixed(RegLocation rl_src, RegStorage r_dest);
+ // Load Dalvik value with 64-bit memory storage.
void LoadValueDirectWide(RegLocation rl_src, RegStorage r_dest);
+ // Load Dalvik value with 64-bit memory storage.
void LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest);
- LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src);
+ // Store an item of natural word size.
+ LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) {
+ return StoreBaseDisp(r_base, displacement, r_src, kWord);
+ }
+ // Store an uncompressed reference into a compressed 32-bit container.
+ LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src) {
+ return StoreBaseDisp(r_base, displacement, r_src, kReference);
+ }
+ // Store 32 bits, regardless of target.
+ LIR* Store32Disp(RegStorage r_base, int displacement, RegStorage r_src) {
+ return StoreBaseDisp(r_base, displacement, r_src, k32);
+ }
/**
* @brief Used to do the final store in the destination as per bytecode semantics.
@@ -778,6 +811,8 @@ class Mir2Lir : public Backend {
bool MethodBlockCodeGen(BasicBlock* bb);
bool SpecialMIR2LIR(const InlineMethod& special);
void MethodMIR2LIR();
+ // Update LIR for verbose listings.
+ void UpdateLIROffsets();
/*
* @brief Load the address of the dex method into the register.
@@ -933,8 +968,6 @@ class Mir2Lir : public Backend {
RegLocation rl_src2) = 0;
virtual void GenXorLong(Instruction::Code, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2) = 0;
- virtual LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base,
- int offset, ThrowKind kind) = 0;
virtual RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi,
bool is_div) = 0;
virtual RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit,
@@ -1019,7 +1052,7 @@ class Mir2Lir : public Backend {
virtual LIR* OpMem(OpKind op, RegStorage r_base, int disp) = 0;
virtual LIR* OpPcRelLoad(RegStorage reg, LIR* target) = 0;
virtual LIR* OpReg(OpKind op, RegStorage r_dest_src) = 0;
- virtual LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src) = 0;
+ virtual void OpRegCopy(RegStorage r_dest, RegStorage r_src) = 0;
virtual LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) = 0;
virtual LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value) = 0;
virtual LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset) = 0;
@@ -1227,6 +1260,10 @@ class Mir2Lir : public Backend {
void AddDivZeroCheckSlowPath(LIR* branch);
+ // Copy arg0 and arg1 to kArg0 and kArg1 safely, possibly using
+ // kArg2 as temp.
+ void CopyToArgumentRegs(RegStorage arg0, RegStorage arg1);
+
public:
// TODO: add accessors for these.
LIR* literal_list_; // Constants.
@@ -1240,7 +1277,6 @@ class Mir2Lir : public Backend {
MIRGraph* const mir_graph_;
GrowableArray<SwitchTable*> switch_tables_;
GrowableArray<FillArrayData*> fill_array_data_;
- GrowableArray<LIR*> throw_launchpads_;
GrowableArray<LIR*> suspend_launchpads_;
GrowableArray<RegisterInfo*> tempreg_info_;
GrowableArray<RegisterInfo*> reginfo_map_;
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 729b30d621..00831099fc 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -239,7 +239,7 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
// mov esp, ebp
// in case a signal comes in that's not using an alternate signal stack and the large frame may
// have moved us outside of the reserved area at the end of the stack.
- // cmp rX86_SP, fs:[stack_end_]; jcc throw_launchpad
+ // cmp rX86_SP, fs:[stack_end_]; jcc throw_slowpath
OpRegThreadMem(kOpCmp, rX86_SP, Thread::StackEndOffset<4>());
LIR* branch = OpCondBranch(kCondUlt, nullptr);
AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, frame_size_ - 4));
@@ -251,7 +251,8 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
// We have been asked to save the address of the method start for later use.
setup_method_address_[0] = NewLIR1(kX86StartOfMethod, rX86_ARG0);
int displacement = SRegOffset(base_of_code_->s_reg_low);
- setup_method_address_[1] = StoreBaseDisp(rs_rX86_SP, displacement, rs_rX86_ARG0, kWord);
+ // Native pointer - must be natural word size.
+ setup_method_address_[1] = StoreWordDisp(rs_rX86_SP, displacement, rs_rX86_ARG0);
}
FreeTemp(rX86_ARG0);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 0b9823d667..760290cabe 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -127,15 +127,13 @@ class X86Mir2Lir FINAL : public Mir2Lir {
RegLocation rl_src2);
void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2);
- LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, int offset,
- ThrowKind kind);
- LIR* GenMemImmedCheck(ConditionCode c_code, RegStorage base, int offset, int check_value,
- ThrowKind kind);
// TODO: collapse reg_lo, reg_hi
RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div);
RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div);
void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenDivZeroCheckWide(RegStorage reg);
+ void GenArrayBoundsCheck(RegStorage index, RegStorage array_base, int32_t len_offset);
+ void GenArrayBoundsCheck(int32_t index, RegStorage array_base, int32_t len_offset);
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
void GenExitSequence();
void GenSpecialExitSequence();
@@ -234,7 +232,7 @@ class X86Mir2Lir FINAL : public Mir2Lir {
LIR* OpMem(OpKind op, RegStorage r_base, int disp);
LIR* OpPcRelLoad(RegStorage reg, LIR* target);
LIR* OpReg(OpKind op, RegStorage r_dest_src);
- LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src);
+ void OpRegCopy(RegStorage r_dest, RegStorage r_src);
LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src);
LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value);
LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset);
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index ee5387f050..f7b0c9d892 100644
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -193,7 +193,7 @@ void X86Mir2Lir::GenLongToFP(RegLocation rl_dest, RegLocation rl_src, bool is_do
} else {
rl_result = EvalLoc(rl_dest, kFPReg, true);
- LoadWordDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg);
+ Load32Disp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg);
StoreFinalValue(rl_dest, rl_result);
}
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 4ffb9a4c38..3bff4976bd 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -24,34 +24,6 @@
namespace art {
/*
- * Perform register memory operation.
- */
-LIR* X86Mir2Lir::GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base,
- int offset, ThrowKind kind) {
- LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
- current_dalvik_offset_, reg1.GetReg(), base.GetReg(), offset);
- OpRegMem(kOpCmp, reg1, base, offset);
- LIR* branch = OpCondBranch(c_code, tgt);
- // Remember branch target - will process later
- throw_launchpads_.Insert(tgt);
- return branch;
-}
-
-/*
- * Perform a compare of memory to immediate value
- */
-LIR* X86Mir2Lir::GenMemImmedCheck(ConditionCode c_code, RegStorage base, int offset,
- int check_value, ThrowKind kind) {
- LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
- current_dalvik_offset_, base.GetReg(), check_value, 0);
- NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base.GetReg(), offset, check_value);
- LIR* branch = OpCondBranch(c_code, tgt);
- // Remember branch target - will process later
- throw_launchpads_.Insert(tgt);
- return branch;
-}
-
-/*
* Compare two 64-bit values
* x = y return 0
* x < y return -1
@@ -144,52 +116,55 @@ LIR* X86Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
return res;
}
-LIR* X86Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
- LIR *res = OpRegCopyNoInsert(r_dest, r_src);
- AppendLIR(res);
- return res;
+void X86Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) {
+ if (r_dest != r_src) {
+ LIR *res = OpRegCopyNoInsert(r_dest, r_src);
+ AppendLIR(res);
+ }
}
void X86Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
- // FIXME: handle k64BitSolo when we start using them.
- DCHECK(r_dest.IsPair());
- DCHECK(r_src.IsPair());
- bool dest_fp = X86_FPREG(r_dest.GetLowReg());
- bool src_fp = X86_FPREG(r_src.GetLowReg());
- if (dest_fp) {
- if (src_fp) {
- // TODO: we ought to handle this case here - reserve OpRegCopy for 32-bit copies.
- OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())),
- RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg())));
- } else {
- // TODO: Prevent this from happening in the code. The result is often
- // unused or could have been loaded more easily from memory.
- NewLIR2(kX86MovdxrRR, r_dest.GetLowReg(), r_src.GetLowReg());
- RegStorage r_tmp = AllocTempDouble();
- NewLIR2(kX86MovdxrRR, r_tmp.GetLowReg(), r_src.GetHighReg());
- NewLIR2(kX86PunpckldqRR, r_dest.GetLowReg(), r_tmp.GetLowReg());
- FreeTemp(r_tmp);
- }
- } else {
- if (src_fp) {
- NewLIR2(kX86MovdrxRR, r_dest.GetLowReg(), r_src.GetLowReg());
- NewLIR2(kX86PsrlqRI, r_src.GetLowReg(), 32);
- NewLIR2(kX86MovdrxRR, r_dest.GetHighReg(), r_src.GetLowReg());
+ if (r_dest != r_src) {
+ // FIXME: handle k64BitSolo when we start using them.
+ DCHECK(r_dest.IsPair());
+ DCHECK(r_src.IsPair());
+ bool dest_fp = X86_FPREG(r_dest.GetLowReg());
+ bool src_fp = X86_FPREG(r_src.GetLowReg());
+ if (dest_fp) {
+ if (src_fp) {
+ // TODO: we ought to handle this case here - reserve OpRegCopy for 32-bit copies.
+ OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())),
+ RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg())));
+ } else {
+ // TODO: Prevent this from happening in the code. The result is often
+ // unused or could have been loaded more easily from memory.
+ NewLIR2(kX86MovdxrRR, r_dest.GetLowReg(), r_src.GetLowReg());
+ RegStorage r_tmp = AllocTempDouble();
+ NewLIR2(kX86MovdxrRR, r_tmp.GetLowReg(), r_src.GetHighReg());
+ NewLIR2(kX86PunpckldqRR, r_dest.GetLowReg(), r_tmp.GetLowReg());
+ FreeTemp(r_tmp);
+ }
} else {
- // Handle overlap
- if (r_src.GetHighReg() == r_dest.GetLowReg() && r_src.GetLowReg() == r_dest.GetHighReg()) {
- // Deal with cycles.
- RegStorage temp_reg = AllocTemp();
- OpRegCopy(temp_reg, r_dest.GetHigh());
- OpRegCopy(r_dest.GetHigh(), r_dest.GetLow());
- OpRegCopy(r_dest.GetLow(), temp_reg);
- FreeTemp(temp_reg);
- } else if (r_src.GetHighReg() == r_dest.GetLowReg()) {
- OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
- OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ if (src_fp) {
+ NewLIR2(kX86MovdrxRR, r_dest.GetLowReg(), r_src.GetLowReg());
+ NewLIR2(kX86PsrlqRI, r_src.GetLowReg(), 32);
+ NewLIR2(kX86MovdrxRR, r_dest.GetHighReg(), r_src.GetLowReg());
} else {
- OpRegCopy(r_dest.GetLow(), r_src.GetLow());
- OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ // Handle overlap
+ if (r_src.GetHighReg() == r_dest.GetLowReg() && r_src.GetLowReg() == r_dest.GetHighReg()) {
+ // Deal with cycles.
+ RegStorage temp_reg = AllocTemp();
+ OpRegCopy(temp_reg, r_dest.GetHigh());
+ OpRegCopy(r_dest.GetHigh(), r_dest.GetLow());
+ OpRegCopy(r_dest.GetLow(), temp_reg);
+ FreeTemp(temp_reg);
+ } else if (r_src.GetHighReg() == r_dest.GetLowReg()) {
+ OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ } else {
+ OpRegCopy(r_dest.GetLow(), r_src.GetLow());
+ OpRegCopy(r_dest.GetHigh(), r_src.GetHigh());
+ }
}
}
}
@@ -704,15 +679,15 @@ bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
RegLocation rl_src_address = info->args[0]; // long address
rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1]
- RegLocation rl_dest = size == kLong ? InlineTargetWide(info) : InlineTarget(info);
+ RegLocation rl_dest = size == k64 ? InlineTargetWide(info) : InlineTarget(info);
RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- if (size == kLong) {
+ if (size == k64) {
// Unaligned access is allowed on x86.
LoadBaseDispWide(rl_address.reg, 0, rl_result.reg, INVALID_SREG);
StoreValueWide(rl_dest, rl_result);
} else {
- DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
// Unaligned access is allowed on x86.
LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, INVALID_SREG);
StoreValue(rl_dest, rl_result);
@@ -725,12 +700,12 @@ bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1]
RegLocation rl_src_value = info->args[2]; // [size] value
RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
- if (size == kLong) {
+ if (size == k64) {
// Unaligned access is allowed on x86.
RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
StoreBaseDispWide(rl_address.reg, 0, rl_value.reg);
} else {
- DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == k32);
// Unaligned access is allowed on x86.
RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size);
@@ -780,6 +755,7 @@ bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
int srcObjSp = IsInReg(this, rl_src_obj, rs_rSI) ? 0
: (IsInReg(this, rl_src_obj, rs_rDI) ? 4
: (SRegOffset(rl_src_obj.s_reg_low) + push_offset));
+ // FIXME: needs 64-bit update.
LoadWordDisp(TargetReg(kSp), srcObjSp, rs_rDI);
int srcOffsetSp = IsInReg(this, rl_src_offset, rs_rSI) ? 0
: (IsInReg(this, rl_src_offset, rs_rDI) ? 4
@@ -891,6 +867,86 @@ void X86Mir2Lir::GenDivZeroCheckWide(RegStorage reg) {
FreeTemp(t_reg);
}
+void X86Mir2Lir::GenArrayBoundsCheck(RegStorage index,
+ RegStorage array_base,
+ int len_offset) {
+ class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+ public:
+ ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch,
+ RegStorage index, RegStorage array_base, int32_t len_offset)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+ index_(index), array_base_(array_base), len_offset_(len_offset) {
+ }
+
+ void Compile() OVERRIDE {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+
+ RegStorage new_index = index_;
+ // Move index out of kArg1, either directly to kArg0, or to kArg2.
+ if (index_.GetReg() == m2l_->TargetReg(kArg1).GetReg()) {
+ if (array_base_.GetReg() == m2l_->TargetReg(kArg0).GetReg()) {
+ m2l_->OpRegCopy(m2l_->TargetReg(kArg2), index_);
+ new_index = m2l_->TargetReg(kArg2);
+ } else {
+ m2l_->OpRegCopy(m2l_->TargetReg(kArg0), index_);
+ new_index = m2l_->TargetReg(kArg0);
+ }
+ }
+ // Load array length to kArg1.
+ m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_);
+ m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+ new_index, m2l_->TargetReg(kArg1), true);
+ }
+
+ private:
+ const RegStorage index_;
+ const RegStorage array_base_;
+ const int32_t len_offset_;
+ };
+
+ OpRegMem(kOpCmp, index, array_base, len_offset);
+ LIR* branch = OpCondBranch(kCondUge, nullptr);
+ AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch,
+ index, array_base, len_offset));
+}
+
+void X86Mir2Lir::GenArrayBoundsCheck(int32_t index,
+ RegStorage array_base,
+ int32_t len_offset) {
+ class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath {
+ public:
+ ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch,
+ int32_t index, RegStorage array_base, int32_t len_offset)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch),
+ index_(index), array_base_(array_base), len_offset_(len_offset) {
+ }
+
+ void Compile() OVERRIDE {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+
+ // Load array length to kArg1.
+ m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_);
+ m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_);
+ m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds),
+ m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true);
+ }
+
+ private:
+ const int32_t index_;
+ const RegStorage array_base_;
+ const int32_t len_offset_;
+ };
+
+ NewLIR3(IS_SIMM8(index) ? kX86Cmp32MI8 : kX86Cmp32MI, array_base.GetReg(), len_offset, index);
+ LIR* branch = OpCondBranch(kCondLs, nullptr);
+ AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch,
+ index, array_base, len_offset));
+}
+
// Test suspend flag, return target of taken suspend branch
LIR* X86Mir2Lir::OpTestSuspend(LIR* target) {
OpTlsCmp(Thread::ThreadFlagsOffset<4>(), 0);
@@ -944,7 +1000,7 @@ void X86Mir2Lir::GenImulMemImm(RegStorage dest, int sreg, int displacement, int
NewLIR2(kX86Xor32RR, dest.GetReg(), dest.GetReg());
break;
case 1:
- LoadBaseDisp(rs_rX86_SP, displacement, dest, kWord, sreg);
+ LoadBaseDisp(rs_rX86_SP, displacement, dest, k32, sreg);
break;
default:
m = NewLIR4(IS_SIMM8(val) ? kX86Imul32RMI8 : kX86Imul32RMI, dest.GetReg(), rX86_SP,
@@ -1050,7 +1106,7 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation
NewLIR2(kX86Mov32RR, r1, rl_src1.reg.GetHighReg());
} else {
LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src1.s_reg_low) + HIWORD_OFFSET, rs_r1,
- kWord, GetSRegHi(rl_src1.s_reg_low));
+ k32, GetSRegHi(rl_src1.s_reg_low));
}
if (is_square) {
@@ -1073,7 +1129,7 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation
NewLIR2(kX86Mov32RR, r0, rl_src2.reg.GetHighReg());
} else {
LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, rs_r0,
- kWord, GetSRegHi(rl_src2.s_reg_low));
+ k32, GetSRegHi(rl_src2.s_reg_low));
}
// EAX <- EAX * 1L (2H * 1L)
@@ -1105,7 +1161,7 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation
NewLIR2(kX86Mov32RR, r0, rl_src2.reg.GetLowReg());
} else {
LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + LOWORD_OFFSET, rs_r0,
- kWord, rl_src2.s_reg_low);
+ k32, rl_src2.s_reg_low);
}
// EDX:EAX <- 2L * 1L (double precision)
@@ -1325,7 +1381,7 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
rl_array = LoadValue(rl_array, kCoreReg);
int data_offset;
- if (size == kLong || size == kDouble) {
+ if (size == k64 || size == kDouble) {
data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
} else {
data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
@@ -1348,14 +1404,13 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
if (constant_index) {
- GenMemImmedCheck(kCondLs, rl_array.reg, len_offset,
- constant_index_value, kThrowConstantArrayBounds);
+ GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset);
} else {
- GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset);
}
}
rl_result = EvalLoc(rl_dest, reg_class, true);
- if ((size == kLong) || (size == kDouble)) {
+ if ((size == k64) || (size == kDouble)) {
LoadBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, rl_result.reg.GetLow(),
rl_result.reg.GetHigh(), size, INVALID_SREG);
StoreValueWide(rl_dest, rl_result);
@@ -1376,7 +1431,7 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
- if (size == kLong || size == kDouble) {
+ if (size == k64 || size == kDouble) {
data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
} else {
data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
@@ -1400,13 +1455,12 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
if (constant_index) {
- GenMemImmedCheck(kCondLs, rl_array.reg, len_offset,
- constant_index_value, kThrowConstantArrayBounds);
+ GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset);
} else {
- GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds);
+ GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset);
}
}
- if ((size == kLong) || (size == kDouble)) {
+ if ((size == k64) || (size == kDouble)) {
rl_src = LoadValueWide(rl_src, reg_class);
} else {
rl_src = LoadValue(rl_src, reg_class);
@@ -1793,22 +1847,22 @@ void X86Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx,
if (rl_method.location == kLocPhysReg) {
if (use_declaring_class) {
- LoadWordDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+ LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
check_class);
} else {
- LoadWordDisp(rl_method.reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
check_class);
- LoadWordDisp(check_class, offset_of_type, check_class);
+ LoadRefDisp(check_class, offset_of_type, check_class);
}
} else {
LoadCurrMethodDirect(check_class);
if (use_declaring_class) {
- LoadWordDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+ LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
check_class);
} else {
- LoadWordDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
check_class);
- LoadWordDisp(check_class, offset_of_type, check_class);
+ LoadRefDisp(check_class, offset_of_type, check_class);
}
}
@@ -1849,17 +1903,17 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k
LoadValueDirectFixed(rl_src, TargetReg(kArg0));
} else if (use_declaring_class) {
LoadValueDirectFixed(rl_src, TargetReg(kArg0));
- LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+ LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
class_reg);
} else {
// Load dex cache entry into class_reg (kArg2).
LoadValueDirectFixed(rl_src, TargetReg(kArg0));
- LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
class_reg);
int32_t offset_of_type =
mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
* type_idx);
- LoadWordDisp(class_reg, offset_of_type, class_reg);
+ LoadRefDisp(class_reg, offset_of_type, class_reg);
if (!can_assume_type_is_in_dex_cache) {
// Need to test presence of type in dex cache at runtime.
LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
@@ -1883,7 +1937,7 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k
/* Load object->klass_. */
DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
- LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
+ LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
/* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class. */
LIR* branchover = nullptr;
if (type_known_final) {
@@ -2056,6 +2110,8 @@ void X86Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
// Can we do this directly into memory?
rl_result = UpdateLoc(rl_dest);
if (rl_result.location == kLocPhysReg) {
+ // Ensure res is in a core reg
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
// Can we do this from memory directly?
rl_rhs = UpdateLoc(rl_rhs);
if (rl_rhs.location != kLocPhysReg) {
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 5a8ad7a2b4..3e3fa72150 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -369,12 +369,13 @@ void X86Mir2Lir::FlushRegWide(RegStorage reg) {
}
void X86Mir2Lir::FlushReg(RegStorage reg) {
+ // FIXME: need to handle 32 bits in 64-bit register as well as wide values held in single reg.
DCHECK(!reg.IsPair());
RegisterInfo* info = GetRegInfo(reg.GetReg());
if (info->live && info->dirty) {
info->dirty = false;
int v_reg = mir_graph_->SRegToVReg(info->s_reg);
- StoreBaseDisp(rs_rX86_SP, VRegOffset(v_reg), reg, kWord);
+ StoreBaseDisp(rs_rX86_SP, VRegOffset(v_reg), reg, k32);
}
}
@@ -1033,14 +1034,14 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked.
// Does the character fit in 16 bits?
- LIR* launchpad_branch = nullptr;
+ LIR* slowpath_branch = nullptr;
if (rl_char.is_const) {
// We need the value in EAX.
LoadConstantNoClobber(rs_rAX, char_value);
} else {
// Character is not a constant; compare at runtime.
LoadValueDirectFixed(rl_char, rs_rAX);
- launchpad_branch = OpCmpImmBranch(kCondGt, rs_rAX, 0xFFFF, nullptr);
+ slowpath_branch = OpCmpImmBranch(kCondGt, rs_rAX, 0xFFFF, nullptr);
}
// From here down, we know that we are looking for a char that fits in 16 bits.
@@ -1061,7 +1062,7 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
NewLIR1(kX86Push32R, rDI);
// Compute the number of words to search in to rCX.
- LoadWordDisp(rs_rDX, count_offset, rs_rCX);
+ Load32Disp(rs_rDX, count_offset, rs_rCX);
LIR *length_compare = nullptr;
int start_value = 0;
bool is_index_on_stack = false;
@@ -1101,7 +1102,7 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
} else {
// Load the start index from stack, remembering that we pushed EDI.
int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
- LoadWordDisp(rs_rX86_SP, displacement, rs_rBX);
+ Load32Disp(rs_rX86_SP, displacement, rs_rBX);
OpRegReg(kOpXor, rs_rDI, rs_rDI);
OpRegReg(kOpCmp, rs_rBX, rs_rDI);
OpCondRegReg(kOpCmov, kCondLt, rs_rBX, rs_rDI);
@@ -1120,8 +1121,8 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
// Load the address of the string into EBX.
// The string starts at VALUE(String) + 2 * OFFSET(String) + DATA_OFFSET.
- LoadWordDisp(rs_rDX, value_offset, rs_rDI);
- LoadWordDisp(rs_rDX, offset_offset, rs_rBX);
+ Load32Disp(rs_rDX, value_offset, rs_rDI);
+ Load32Disp(rs_rDX, offset_offset, rs_rBX);
OpLea(rs_rBX, rs_rDI, rs_rBX, 1, data_offset);
// Now compute into EDI where the search will start.
@@ -1167,9 +1168,9 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
NewLIR1(kX86Pop32R, rDI);
// Out of line code returns here.
- if (launchpad_branch != nullptr) {
+ if (slowpath_branch != nullptr) {
LIR *return_point = NewLIR0(kPseudoTargetLabel);
- AddIntrinsicLaunchpad(info, launchpad_branch, return_point);
+ AddIntrinsicSlowPath(info, slowpath_branch, return_point);
}
StoreValue(rl_dest, rl_return);
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index e9faa7ff53..4d45055927 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -426,7 +426,8 @@ LIR* X86Mir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1,
RegStorage t_reg = AllocTemp();
OpRegCopy(t_reg, r_src1);
OpRegReg(op, t_reg, r_src2);
- LIR* res = OpRegCopy(r_dest, t_reg);
+ LIR* res = OpRegCopyNoInsert(r_dest, t_reg);
+ AppendLIR(res);
FreeTemp(t_reg);
return res;
}
@@ -554,7 +555,7 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int
bool is64bit = false;
X86OpCode opcode = kX86Nop;
switch (size) {
- case kLong:
+ case k64:
case kDouble:
// TODO: use regstorage attributes here.
is64bit = true;
@@ -567,8 +568,9 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int
// TODO: double store is to unaligned address
DCHECK_EQ((displacement & 0x3), 0);
break;
- case kWord:
+ case k32:
case kSingle:
+ case kReference: // TODO: update for reference decompression on 64-bit targets.
opcode = is_array ? kX86Mov32RA : kX86Mov32RM;
if (X86_FPREG(r_dest.GetReg())) {
opcode = is_array ? kX86MovssRA : kX86MovssRM;
@@ -669,6 +671,10 @@ LIR* X86Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStora
LIR* X86Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement,
RegStorage r_dest, OpSize size, int s_reg) {
+ // TODO: base this on target.
+ if (size == kWord) {
+ size = k32;
+ }
return LoadBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement,
r_dest, RegStorage::InvalidReg(), size, s_reg);
}
@@ -676,7 +682,7 @@ LIR* X86Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement,
LIR* X86Mir2Lir::LoadBaseDispWide(RegStorage r_base, int displacement, RegStorage r_dest,
int s_reg) {
return LoadBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement,
- r_dest.GetLow(), r_dest.GetHigh(), kLong, s_reg);
+ r_dest.GetLow(), r_dest.GetHigh(), k64, s_reg);
}
LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale,
@@ -690,7 +696,7 @@ LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int
bool is64bit = false;
X86OpCode opcode = kX86Nop;
switch (size) {
- case kLong:
+ case k64:
case kDouble:
is64bit = true;
if (X86_FPREG(r_src.GetReg())) {
@@ -702,8 +708,9 @@ LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int
// TODO: double store is to unaligned address
DCHECK_EQ((displacement & 0x3), 0);
break;
- case kWord:
+ case k32:
case kSingle:
+ case kReference:
opcode = is_array ? kX86Mov32AR : kX86Mov32MR;
if (X86_FPREG(r_src.GetReg())) {
opcode = is_array ? kX86MovssAR : kX86MovssMR;
@@ -763,13 +770,17 @@ LIR* X86Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor
LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement,
RegStorage r_src, OpSize size) {
- return StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src,
- RegStorage::InvalidReg(), size, INVALID_SREG);
+ // TODO: base this on target.
+ if (size == kWord) {
+ size = k32;
+ }
+ return StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src,
+ RegStorage::InvalidReg(), size, INVALID_SREG);
}
LIR* X86Mir2Lir::StoreBaseDispWide(RegStorage r_base, int displacement, RegStorage r_src) {
return StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement,
- r_src.GetLow(), r_src.GetHigh(), kLong, INVALID_SREG);
+ r_src.GetLow(), r_src.GetHigh(), k64, INVALID_SREG);
}
/*
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 8175c35077..864dadc963 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -50,7 +50,11 @@ TEST_F(ElfWriterTest, dlsym) {
CHECK(host_dir != NULL);
elf_filename = StringPrintf("%s/framework/core.oat", host_dir);
} else {
+#ifdef __LP64__
+ elf_filename = "/data/art-test64/core.oat";
+#else
elf_filename = "/data/art-test/core.oat";
+#endif
}
LOG(INFO) << "elf_filename=" << elf_filename;
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 05d6693f70..7c5741bb23 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -164,7 +164,7 @@ TEST_F(ImageTest, WriteRead) {
EXPECT_TRUE(reinterpret_cast<byte*>(klass) >= image_end ||
reinterpret_cast<byte*>(klass) < image_begin) << descriptor;
}
- EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord()));
+ EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false)));
}
}
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f76587a26e..c35d4007b5 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -179,7 +179,7 @@ void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) {
image_bitmap_->Set(obj);
// Before we stomp over the lock word, save the hash code for later.
Monitor::Deflate(Thread::Current(), object);;
- LockWord lw(object->GetLockWord());
+ LockWord lw(object->GetLockWord(false));
switch (lw.GetState()) {
case LockWord::kFatLocked: {
LOG(FATAL) << "Fat locked object " << obj << " found during object copy";
@@ -199,7 +199,7 @@ void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) {
LOG(FATAL) << "Unreachable.";
break;
}
- object->SetLockWord(LockWord::FromForwardingAddress(offset));
+ object->SetLockWord(LockWord::FromForwardingAddress(offset), false);
DCHECK(IsImageOffsetAssigned(object));
}
@@ -212,13 +212,13 @@ void ImageWriter::AssignImageOffset(mirror::Object* object) {
bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const {
DCHECK(object != nullptr);
- return object->GetLockWord().GetState() == LockWord::kForwardingAddress;
+ return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress;
}
size_t ImageWriter::GetImageOffset(mirror::Object* object) const {
DCHECK(object != nullptr);
DCHECK(IsImageOffsetAssigned(object));
- LockWord lock_word = object->GetLockWord();
+ LockWord lock_word = object->GetLockWord(false);
size_t offset = lock_word.ForwardingAddress();
DCHECK_LT(offset, image_end_);
return offset;
@@ -555,15 +555,15 @@ void ImageWriter::CopyAndFixupObjects()
heap->VisitObjects(CopyAndFixupObjectsCallback, this);
// Fix up the object previously had hash codes.
for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) {
- hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second));
+ hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second), false);
}
saved_hashes_.clear();
self->EndAssertNoThreadSuspension(old_cause);
}
void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
- DCHECK(obj != NULL);
- DCHECK(arg != NULL);
+ DCHECK(obj != nullptr);
+ DCHECK(arg != nullptr);
ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
// see GetLocalAddress for similar computation
size_t offset = image_writer->GetImageOffset(obj);
@@ -575,7 +575,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
Object* copy = reinterpret_cast<Object*>(dst);
// Write in a hash code of objects which have inflated monitors or a hash code in their monitor
// word.
- copy->SetLockWord(LockWord());
+ copy->SetLockWord(LockWord(), false);
image_writer->FixupObject(obj, copy);
}
@@ -680,14 +680,6 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_));
} else {
// Normal (non-abstract non-native) methods have various tables to relocate.
- uint32_t mapping_table_off = orig->GetOatMappingTableOffset();
- const byte* mapping_table = GetOatAddress(mapping_table_off);
- copy->SetMappingTable<kVerifyNone>(mapping_table);
-
- uint32_t vmap_table_offset = orig->GetOatVmapTableOffset();
- const byte* vmap_table = GetOatAddress(vmap_table_offset);
- copy->SetVmapTable<kVerifyNone>(vmap_table);
-
uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
const byte* native_gc_map = GetOatAddress(native_gc_map_offset);
copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 9cfef12b26..b5d39232ca 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -155,19 +155,19 @@ TEST_F(OatTest, WriteRead) {
SirtRef<mirror::ClassLoader> loader(soa.Self(), nullptr);
mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor, loader);
- UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i));
- CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor;
+ const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(i);
+ CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class.GetStatus()) << descriptor;
CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled,
- oat_class->GetType()) << descriptor;
+ oat_class.GetType()) << descriptor;
size_t method_index = 0;
for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) {
CheckMethod(klass->GetDirectMethod(i),
- oat_class->GetOatMethod(method_index), dex_file);
+ oat_class.GetOatMethod(method_index), dex_file);
}
for (size_t i = 0; i < num_virtual_methods; i++, method_index++) {
CheckMethod(klass->GetVirtualMethod(i),
- oat_class->GetOatMethod(method_index), dex_file);
+ oat_class.GetOatMethod(method_index), dex_file);
}
}
}
@@ -176,7 +176,8 @@ TEST_F(OatTest, OatHeaderSizeCheck) {
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
EXPECT_EQ(80U, sizeof(OatHeader));
- EXPECT_EQ(28U, sizeof(OatMethodOffsets));
+ EXPECT_EQ(20U, sizeof(OatMethodOffsets));
+ EXPECT_EQ(12U, sizeof(OatMethodHeader));
}
TEST_F(OatTest, OatHeaderIsValid) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index dc66e9c108..bbc9c3e325 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -38,6 +38,14 @@
namespace art {
+#define DCHECK_OFFSET() \
+ DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
+ << "file_offset=" << file_offset << " relative_offset=" << relative_offset
+
+#define DCHECK_OFFSET_() \
+ DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \
+ << "file_offset=" << file_offset << " offset_=" << offset_
+
OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uintptr_t image_file_location_oat_begin,
@@ -66,7 +74,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
size_quick_resolution_trampoline_(0),
size_quick_to_interpreter_bridge_(0),
size_trampoline_alignment_(0),
- size_code_size_(0),
+ size_method_header_(0),
size_code_(0),
size_code_alignment_(0),
size_mapping_table_(0),
@@ -99,6 +107,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
offset = InitOatClasses(offset);
}
{
+ TimingLogger::ScopedSplit split("InitOatMaps", timings);
+ offset = InitOatMaps(offset);
+ }
+ {
TimingLogger::ScopedSplit split("InitOatCode", timings);
offset = InitOatCode(offset);
}
@@ -118,6 +130,605 @@ OatWriter::~OatWriter() {
STLDeleteElements(&oat_classes_);
}
+struct OatWriter::GcMapDataAccess {
+ static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ return &compiled_method->GetGcMap();
+ }
+
+ static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
+ return oat_class->method_offsets_[method_offsets_index].gc_map_offset_;
+ }
+
+ static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
+ ALWAYS_INLINE {
+ oat_class->method_offsets_[method_offsets_index].gc_map_offset_ = offset;
+ }
+
+ static const char* Name() ALWAYS_INLINE {
+ return "GC map";
+ }
+};
+
+struct OatWriter::MappingTableDataAccess {
+ static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ return &compiled_method->GetMappingTable();
+ }
+
+ static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
+ uint32_t offset = oat_class->method_headers_[method_offsets_index].mapping_table_offset_;
+ return offset == 0u ? 0u :
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
+ }
+
+ static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
+ ALWAYS_INLINE {
+ oat_class->method_headers_[method_offsets_index].mapping_table_offset_ =
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
+ }
+
+ static const char* Name() ALWAYS_INLINE {
+ return "mapping table";
+ }
+};
+
+struct OatWriter::VmapTableDataAccess {
+ static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ return &compiled_method->GetVmapTable();
+ }
+
+ static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
+ uint32_t offset = oat_class->method_headers_[method_offsets_index].vmap_table_offset_;
+ return offset == 0u ? 0u :
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
+ }
+
+ static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
+ ALWAYS_INLINE {
+ oat_class->method_headers_[method_offsets_index].vmap_table_offset_ =
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
+ }
+
+ static const char* Name() ALWAYS_INLINE {
+ return "vmap table";
+ }
+};
+
+class OatWriter::DexMethodVisitor {
+ public:
+ DexMethodVisitor(OatWriter* writer, size_t offset)
+ : writer_(writer),
+ offset_(offset),
+ dex_file_(nullptr),
+ class_def_index_(DexFile::kDexNoIndex) {
+ }
+
+ virtual bool StartClass(const DexFile* dex_file, size_t class_def_index) {
+ DCHECK(dex_file_ == nullptr);
+ DCHECK_EQ(class_def_index_, DexFile::kDexNoIndex);
+ dex_file_ = dex_file;
+ class_def_index_ = class_def_index;
+ return true;
+ }
+
+ virtual bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) = 0;
+
+ virtual bool EndClass() {
+ if (kIsDebugBuild) {
+ dex_file_ = nullptr;
+ class_def_index_ = DexFile::kDexNoIndex;
+ }
+ return true;
+ }
+
+ size_t GetOffset() const {
+ return offset_;
+ }
+
+ protected:
+ virtual ~DexMethodVisitor() { }
+
+ OatWriter* const writer_;
+
+ // The offset is usually advanced for each visited method by the derived class.
+ size_t offset_;
+
+ // The dex file and class def index are set in StartClass().
+ const DexFile* dex_file_;
+ size_t class_def_index_;
+};
+
+class OatWriter::OatDexMethodVisitor : public DexMethodVisitor {
+ public:
+ OatDexMethodVisitor(OatWriter* writer, size_t offset)
+ : DexMethodVisitor(writer, offset),
+ oat_class_index_(0u),
+ method_offsets_index_(0u) {
+ }
+
+ bool StartClass(const DexFile* dex_file, size_t class_def_index) {
+ DexMethodVisitor::StartClass(dex_file, class_def_index);
+ DCHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+ method_offsets_index_ = 0u;
+ return true;
+ }
+
+ bool EndClass() {
+ ++oat_class_index_;
+ return DexMethodVisitor::EndClass();
+ }
+
+ protected:
+ size_t oat_class_index_;
+ size_t method_offsets_index_;
+};
+
+class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor {
+ public:
+ InitOatClassesMethodVisitor(OatWriter* writer, size_t offset)
+ : DexMethodVisitor(writer, offset),
+ compiled_methods_(),
+ num_non_null_compiled_methods_(0u) {
+ compiled_methods_.reserve(256u);
+ }
+
+ bool StartClass(const DexFile* dex_file, size_t class_def_index) {
+ DexMethodVisitor::StartClass(dex_file, class_def_index);
+ compiled_methods_.clear();
+ num_non_null_compiled_methods_ = 0u;
+ return true;
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+ // Fill in the compiled_methods_ array for methods that have a
+ // CompiledMethod. We track the number of non-null entries in
+ // num_non_null_compiled_methods_ since we only want to allocate
+ // OatMethodOffsets for the compiled methods.
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx));
+ compiled_methods_.push_back(compiled_method);
+ if (compiled_method != nullptr) {
+ ++num_non_null_compiled_methods_;
+ }
+ return true;
+ }
+
+ bool EndClass() {
+ ClassReference class_ref(dex_file_, class_def_index_);
+ CompiledClass* compiled_class = writer_->compiler_driver_->GetCompiledClass(class_ref);
+ mirror::Class::Status status;
+ if (compiled_class != NULL) {
+ status = compiled_class->GetStatus();
+ } else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
+ status = mirror::Class::kStatusError;
+ } else {
+ status = mirror::Class::kStatusNotReady;
+ }
+
+ OatClass* oat_class = new OatClass(offset_, compiled_methods_,
+ num_non_null_compiled_methods_, status);
+ writer_->oat_classes_.push_back(oat_class);
+ offset_ += oat_class->SizeOf();
+ return DexMethodVisitor::EndClass();
+ }
+
+ private:
+ std::vector<CompiledMethod*> compiled_methods_;
+ size_t num_non_null_compiled_methods_;
+};
+
+class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
+ public:
+ InitCodeMethodVisitor(OatWriter* writer, size_t offset)
+ : OatDexMethodVisitor(writer, offset) {
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (compiled_method != nullptr) {
+ // Derived from CompiledMethod.
+ uint32_t quick_code_offset = 0;
+ uint32_t frame_size_in_bytes = kStackAlignment;
+ uint32_t core_spill_mask = 0;
+ uint32_t fp_spill_mask = 0;
+
+ const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
+ const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+ if (portable_code != nullptr) {
+ CHECK(quick_code == nullptr);
+ size_t oat_method_offsets_offset =
+ oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
+ compiled_method->AddOatdataOffsetToCompliledCodeOffset(
+ oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
+ } else {
+ CHECK(quick_code != nullptr);
+ offset_ = compiled_method->AlignCode(offset_);
+ DCHECK_ALIGNED_PARAM(offset_,
+ GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+ CHECK_NE(code_size, 0U);
+ uint32_t thumb_offset = compiled_method->CodeDelta();
+ quick_code_offset = offset_ + sizeof(OatMethodHeader) + thumb_offset;
+
+ std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
+ if (cfi_info != nullptr) {
+ // Copy in the FDE, if present
+ const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
+ if (fde != nullptr) {
+ // Copy the information into cfi_info and then fix the address in the new copy.
+ int cur_offset = cfi_info->size();
+ cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+ // Set the 'initial_location' field to address the start of the method.
+ uint32_t new_value = quick_code_offset - writer_->oat_header_->GetExecutableOffset();
+ uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t);
+ (*cfi_info)[offset_to_update+0] = new_value;
+ (*cfi_info)[offset_to_update+1] = new_value >> 8;
+ (*cfi_info)[offset_to_update+2] = new_value >> 16;
+ (*cfi_info)[offset_to_update+3] = new_value >> 24;
+ std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, false);
+ writer_->method_info_.push_back(DebugInfo(name, new_value, new_value + code_size));
+ }
+ }
+
+ DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
+ OatMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
+ method_header->code_size_ = code_size;
+
+ // Deduplicate code arrays.
+ auto code_iter = dedupe_map_.find(compiled_method);
+ if (code_iter != dedupe_map_.end()) {
+ quick_code_offset = code_iter->second;
+ FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
+ } else {
+ dedupe_map_.Put(compiled_method, quick_code_offset);
+ FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
+ writer_->oat_header_->UpdateChecksum(method_header, sizeof(*method_header));
+ offset_ += sizeof(*method_header); // Method header is prepended before code.
+ writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
+ offset_ += code_size;
+ }
+ }
+ frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
+ core_spill_mask = compiled_method->GetCoreSpillMask();
+ fp_spill_mask = compiled_method->GetFpSpillMask();
+
+ if (kIsDebugBuild) {
+ // We expect GC maps except when the class hasn't been verified or the method is native.
+ const CompilerDriver* compiler_driver = writer_->compiler_driver_;
+ ClassReference class_ref(dex_file_, class_def_index_);
+ CompiledClass* compiled_class = compiler_driver->GetCompiledClass(class_ref);
+ mirror::Class::Status status;
+ if (compiled_class != NULL) {
+ status = compiled_class->GetStatus();
+ } else if (compiler_driver->GetVerificationResults()->IsClassRejected(class_ref)) {
+ status = mirror::Class::kStatusError;
+ } else {
+ status = mirror::Class::kStatusNotReady;
+ }
+ const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
+ size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
+ bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
+ CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
+ << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
+ << (status < mirror::Class::kStatusVerified) << " " << status << " "
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+ }
+
+ DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+ OatMethodOffsets* offsets = &oat_class->method_offsets_[method_offsets_index_];
+ offsets->code_offset_ = quick_code_offset;
+ offsets->frame_size_in_bytes_ = frame_size_in_bytes;
+ offsets->core_spill_mask_ = core_spill_mask;
+ offsets->fp_spill_mask_ = fp_spill_mask;
+ ++method_offsets_index_;
+ }
+
+ return true;
+ }
+
+ private:
+ static void FixupMethodHeader(OatMethodHeader* method_header, uint32_t code_offset) {
+ // The code offset was 0 when the mapping/vmap table offset was set, so it's set
+ // to 0-offset and we need to adjust it by code_offset.
+ if (method_header->mapping_table_offset_ != 0u) {
+ method_header->mapping_table_offset_ += code_offset;
+ DCHECK_LT(method_header->mapping_table_offset_, code_offset);
+ }
+ if (method_header->vmap_table_offset_ != 0u) {
+ method_header->vmap_table_offset_ += code_offset;
+ DCHECK_LT(method_header->vmap_table_offset_, code_offset);
+ }
+ }
+
+ // Deduplication is already done on a pointer basis by the compiler driver,
+ // so we can simply compare the pointers to find out if things are duplicated.
+ SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
+};
+
+template <typename DataAccess>
+class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
+ public:
+ InitMapMethodVisitor(OatWriter* writer, size_t offset)
+ : OatDexMethodVisitor(writer, offset) {
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (compiled_method != nullptr) {
+ DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+ DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
+
+ const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+ uint32_t map_size = map->size() * sizeof((*map)[0]);
+ if (map_size != 0u) {
+ auto it = dedupe_map_.find(map);
+ if (it != dedupe_map_.end()) {
+ DataAccess::SetOffset(oat_class, method_offsets_index_, it->second);
+ } else {
+ DataAccess::SetOffset(oat_class, method_offsets_index_, offset_);
+ dedupe_map_.Put(map, offset_);
+ offset_ += map_size;
+ writer_->oat_header_->UpdateChecksum(&(*map)[0], map_size);
+ }
+ }
+ ++method_offsets_index_;
+ }
+
+ return true;
+ }
+
+ private:
+ // Deduplication is already done on a pointer basis by the compiler driver,
+ // so we can simply compare the pointers to find out if things are duplicated.
+ SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+};
+
+class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
+ public:
+ InitImageMethodVisitor(OatWriter* writer, size_t offset)
+ : OatDexMethodVisitor(writer, offset) {
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u);
+ if (compiled_method != nullptr) {
+ DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+ offsets = oat_class->method_offsets_[method_offsets_index_];
+ ++method_offsets_index_;
+ }
+
+ // Derive frame size and spill masks for native methods without code:
+ // These are generic JNI methods...
+ uint32_t method_idx = it.GetMemberIndex();
+ bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
+ if (is_native && compiled_method == nullptr) {
+ // Compute Sirt size as putting _every_ reference into it, even null ones.
+ uint32_t s_len;
+ const char* shorty = dex_file_->GetMethodShorty(dex_file_->GetMethodId(method_idx),
+ &s_len);
+ DCHECK(shorty != nullptr);
+ uint32_t refs = 1; // Native method always has "this" or class.
+ for (uint32_t i = 1; i < s_len; ++i) {
+ if (shorty[i] == 'L') {
+ refs++;
+ }
+ }
+ size_t pointer_size = GetInstructionSetPointerSize(
+ writer_->compiler_driver_->GetInstructionSet());
+ size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs);
+
+ // Get the generic spill masks and base frame size.
+ mirror::ArtMethod* callee_save_method =
+ Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
+
+ offsets.frame_size_in_bytes_ = callee_save_method->GetFrameSizeInBytes() + sirt_size;
+ offsets.core_spill_mask_ = callee_save_method->GetCoreSpillMask();
+ offsets.fp_spill_mask_ = callee_save_method->GetFpSpillMask();
+ DCHECK_EQ(offsets.gc_map_offset_, 0u);
+ }
+
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ InvokeType invoke_type = it.GetMethodInvokeType(dex_file_->GetClassDef(class_def_index_));
+ // Unchecked as we hold mutator_lock_ on entry.
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(*dex_file_));
+ SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
+ mirror::ArtMethod* method = linker->ResolveMethod(*dex_file_, method_idx, dex_cache,
+ class_loader, nullptr, invoke_type);
+ CHECK(method != NULL);
+ method->SetFrameSizeInBytes(offsets.frame_size_in_bytes_);
+ method->SetCoreSpillMask(offsets.core_spill_mask_);
+ method->SetFpSpillMask(offsets.fp_spill_mask_);
+ // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
+ method->SetQuickOatCodeOffset(offsets.code_offset_);
+ method->SetOatNativeGcMapOffset(offsets.gc_map_offset_);
+
+ return true;
+ }
+};
+
+class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
+ public:
+ WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
+ size_t relative_offset)
+ : OatDexMethodVisitor(writer, relative_offset),
+ out_(out),
+ file_offset_(file_offset) {
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+ OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (compiled_method != NULL) { // ie. not an abstract method
+ size_t file_offset = file_offset_;
+ OutputStream* out = out_;
+
+ const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+ if (quick_code != nullptr) {
+ CHECK(compiled_method->GetPortableCode() == nullptr);
+ uint32_t aligned_offset = compiled_method->AlignCode(offset_);
+ uint32_t aligned_code_delta = aligned_offset - offset_;
+ if (aligned_code_delta != 0) {
+ static const uint8_t kPadding[] = {
+ 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
+ };
+ DCHECK_LE(aligned_code_delta, sizeof(kPadding));
+ if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
+ ReportWriteFailure("code alignment padding", it);
+ return false;
+ }
+ writer_->size_code_alignment_ += aligned_code_delta;
+ offset_ += aligned_code_delta;
+ DCHECK_OFFSET_();
+ }
+ DCHECK_ALIGNED_PARAM(offset_,
+ GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+ CHECK_NE(code_size, 0U);
+
+ // Deduplicate code arrays.
+ const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_];
+ DCHECK(method_offsets.code_offset_ < offset_ || method_offsets.code_offset_ ==
+ offset_ + sizeof(OatMethodHeader) + compiled_method->CodeDelta())
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+ if (method_offsets.code_offset_ >= offset_) {
+ const OatMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_];
+ if (!out->WriteFully(&method_header, sizeof(method_header))) {
+ ReportWriteFailure("method header", it);
+ return false;
+ }
+ writer_->size_method_header_ += sizeof(method_header);
+ offset_ += sizeof(method_header);
+ DCHECK_OFFSET_();
+ if (!out->WriteFully(&(*quick_code)[0], code_size)) {
+ ReportWriteFailure("method code", it);
+ return false;
+ }
+ writer_->size_code_ += code_size;
+ offset_ += code_size;
+ }
+ DCHECK_OFFSET_();
+ }
+ ++method_offsets_index_;
+ }
+
+ return true;
+ }
+
+ private:
+ OutputStream* const out_;
+ size_t const file_offset_;
+
+ void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) {
+ PLOG(ERROR) << "Failed to write " << what << " for "
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation();
+ }
+};
+
+template <typename DataAccess>
+class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor {
+ public:
+ WriteMapMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
+ size_t relative_offset)
+ : OatDexMethodVisitor(writer, relative_offset),
+ out_(out),
+ file_offset_(file_offset) {
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+ OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+ if (compiled_method != NULL) { // ie. not an abstract method
+ size_t file_offset = file_offset_;
+ OutputStream* out = out_;
+
+ uint32_t map_offset = DataAccess::GetOffset(oat_class, method_offsets_index_);
+ ++method_offsets_index_;
+
+ // Write deduplicated map.
+ const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+ size_t map_size = map->size() * sizeof((*map)[0]);
+ DCHECK((map_size == 0u && map_offset == 0u) ||
+ (map_size != 0u && map_offset != 0u && map_offset <= offset_))
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+ if (map_size != 0u && map_offset == offset_) {
+ if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) {
+ ReportWriteFailure(it);
+ return false;
+ }
+ offset_ += map_size;
+ }
+ DCHECK_OFFSET_();
+ }
+
+ return true;
+ }
+
+ private:
+ OutputStream* const out_;
+ size_t const file_offset_;
+
+ void ReportWriteFailure(const ClassDataItemIterator& it) {
+ PLOG(ERROR) << "Failed to write " << DataAccess::Name() << " for "
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation();
+ }
+};
+
+// Visit all methods from all classes in all dex files with the specified visitor.
+bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) {
+ for (const DexFile* dex_file : *dex_files_) {
+ const size_t class_def_count = dex_file->NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) {
+ if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
+ return false;
+ }
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const byte* class_data = dex_file->GetClassData(class_def);
+ if (class_data != NULL) { // ie not an empty class, such as a marker interface
+ ClassDataItemIterator it(*dex_file, class_data);
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+ size_t class_def_method_index = 0u;
+ while (it.HasNextDirectMethod()) {
+ if (!visitor->VisitMethod(class_def_method_index, it)) {
+ return false;
+ }
+ ++class_def_method_index;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
+ return false;
+ }
+ ++class_def_method_index;
+ it.Next();
+ }
+ }
+ if (UNLIKELY(!visitor->EndClass())) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
size_t OatWriter::InitOatHeader() {
// create the OatHeader
oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(),
@@ -161,78 +772,42 @@ size_t OatWriter::InitDexFiles(size_t offset) {
}
size_t OatWriter::InitOatClasses(size_t offset) {
- // create the OatClasses
// calculate the offsets within OatDexFiles to OatClasses
- for (size_t i = 0; i != dex_files_->size(); ++i) {
- const DexFile* dex_file = (*dex_files_)[i];
- for (size_t class_def_index = 0;
- class_def_index < dex_file->NumClassDefs();
- class_def_index++) {
- oat_dex_files_[i]->methods_offsets_[class_def_index] = offset;
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
- const byte* class_data = dex_file->GetClassData(class_def);
- uint32_t num_non_null_compiled_methods = 0;
- UniquePtr<std::vector<CompiledMethod*> > compiled_methods(new std::vector<CompiledMethod*>());
- if (class_data != NULL) { // ie not an empty class, such as a marker interface
- ClassDataItemIterator it(*dex_file, class_data);
- size_t num_direct_methods = it.NumDirectMethods();
- size_t num_virtual_methods = it.NumVirtualMethods();
- size_t num_methods = num_direct_methods + num_virtual_methods;
-
- // Fill in the compiled_methods_ array for methods that have a
- // CompiledMethod. We track the number of non-null entries in
- // num_non_null_compiled_methods since we only want to allocate
- // OatMethodOffsets for the compiled methods.
- compiled_methods->reserve(num_methods);
- while (it.HasNextStaticField()) {
- it.Next();
- }
- while (it.HasNextInstanceField()) {
- it.Next();
- }
- size_t class_def_method_index = 0;
- while (it.HasNextDirectMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
- compiled_methods->push_back(compiled_method);
- if (compiled_method != NULL) {
- num_non_null_compiled_methods++;
- }
- class_def_method_index++;
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
- compiled_methods->push_back(compiled_method);
- if (compiled_method != NULL) {
- num_non_null_compiled_methods++;
- }
- class_def_method_index++;
- it.Next();
- }
- }
-
- ClassReference class_ref(dex_file, class_def_index);
- CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref);
- mirror::Class::Status status;
- if (compiled_class != NULL) {
- status = compiled_class->GetStatus();
- } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
- status = mirror::Class::kStatusError;
- } else {
- status = mirror::Class::kStatusNotReady;
- }
-
- OatClass* oat_class = new OatClass(offset, compiled_methods.release(),
- num_non_null_compiled_methods, status);
- oat_classes_.push_back(oat_class);
- offset += oat_class->SizeOf();
+ InitOatClassesMethodVisitor visitor(this, offset);
+ bool success = VisitDexMethods(&visitor);
+ CHECK(success);
+ offset = visitor.GetOffset();
+
+ // Update oat_dex_files_.
+ auto oat_class_it = oat_classes_.begin();
+ for (OatDexFile* oat_dex_file : oat_dex_files_) {
+ for (uint32_t& offset : oat_dex_file->methods_offsets_) {
+ DCHECK(oat_class_it != oat_classes_.end());
+ offset = (*oat_class_it)->offset_;
+ ++oat_class_it;
}
- oat_dex_files_[i]->UpdateChecksum(oat_header_);
+ oat_dex_file->UpdateChecksum(oat_header_);
}
+ CHECK(oat_class_it == oat_classes_.end());
+
+ return offset;
+}
+
+size_t OatWriter::InitOatMaps(size_t offset) {
+ #define VISIT(VisitorType) \
+ do { \
+ VisitorType visitor(this, offset); \
+ bool success = VisitDexMethods(&visitor); \
+ DCHECK(success); \
+ offset = visitor.GetOffset(); \
+ } while (false)
+
+ VISIT(InitMapMethodVisitor<GcMapDataAccess>);
+ VISIT(InitMapMethodVisitor<MappingTableDataAccess>);
+ VISIT(InitMapMethodVisitor<VmapTableDataAccess>);
+
+ #undef VISIT
+
return offset;
}
@@ -280,280 +855,24 @@ size_t OatWriter::InitOatCode(size_t offset) {
}
size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
- size_t oat_class_index = 0;
- for (size_t i = 0; i != dex_files_->size(); ++i) {
- const DexFile* dex_file = (*dex_files_)[i];
- CHECK(dex_file != NULL);
- offset = InitOatCodeDexFile(offset, &oat_class_index, *dex_file);
- }
- return offset;
-}
-
-size_t OatWriter::InitOatCodeDexFile(size_t offset,
- size_t* oat_class_index,
- const DexFile& dex_file) {
- for (size_t class_def_index = 0;
- class_def_index < dex_file.NumClassDefs();
- class_def_index++, (*oat_class_index)++) {
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def);
- oat_classes_[*oat_class_index]->UpdateChecksum(oat_header_);
- }
- return offset;
-}
-
-size_t OatWriter::InitOatCodeClassDef(size_t offset,
- size_t oat_class_index, size_t class_def_index,
- const DexFile& dex_file,
- const DexFile::ClassDef& class_def) {
- const byte* class_data = dex_file.GetClassData(class_def);
- if (class_data == NULL) {
- // empty class, such as a marker interface
- return offset;
- }
- ClassDataItemIterator it(dex_file, class_data);
- CHECK_LE(oat_classes_[oat_class_index]->method_offsets_.size(),
- it.NumDirectMethods() + it.NumVirtualMethods());
- // Skip fields
- while (it.HasNextStaticField()) {
- it.Next();
- }
- while (it.HasNextInstanceField()) {
- it.Next();
- }
- // Process methods
- size_t class_def_method_index = 0;
- size_t method_offsets_index = 0;
- while (it.HasNextDirectMethod()) {
- bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
- offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
- &method_offsets_index, is_native,
- it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
- class_def_method_index++;
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
- offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
- &method_offsets_index, is_native,
- it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
- class_def_method_index++;
- it.Next();
- }
- DCHECK(!it.HasNext());
- CHECK_LE(method_offsets_index, class_def_method_index);
- return offset;
-}
-
-size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
- size_t __attribute__((unused)) class_def_index,
- size_t class_def_method_index,
- size_t* method_offsets_index,
- bool __attribute__((unused)) is_native,
- InvokeType invoke_type,
- uint32_t method_idx, const DexFile& dex_file) {
- // Derived from CompiledMethod if available.
- uint32_t quick_code_offset = 0;
- uint32_t frame_size_in_bytes = kStackAlignment;
- uint32_t core_spill_mask = 0;
- uint32_t fp_spill_mask = 0;
- uint32_t mapping_table_offset = 0;
- uint32_t vmap_table_offset = 0;
- uint32_t gc_map_offset = 0;
-
- OatClass* oat_class = oat_classes_[oat_class_index];
- CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
-
- if (compiled_method != nullptr) {
- const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
- const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
- if (portable_code != nullptr) {
- CHECK(quick_code == nullptr);
- size_t oat_method_offsets_offset =
- oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
- compiled_method->AddOatdataOffsetToCompliledCodeOffset(
- oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
- } else {
- CHECK(quick_code != nullptr);
- offset = compiled_method->AlignCode(offset);
- DCHECK_ALIGNED_PARAM(offset,
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
-
- uint32_t code_size = quick_code->size() * sizeof(uint8_t);
- CHECK_NE(code_size, 0U);
- uint32_t thumb_offset = compiled_method->CodeDelta();
- quick_code_offset = offset + sizeof(code_size) + thumb_offset;
-
- std::vector<uint8_t>* cfi_info = compiler_driver_->GetCallFrameInformation();
- if (cfi_info != nullptr) {
- // Copy in the FDE, if present
- const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
- if (fde != nullptr) {
- // Copy the information into cfi_info and then fix the address in the new copy.
- int cur_offset = cfi_info->size();
- cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
- // Set the 'initial_location' field to address the start of the method.
- uint32_t new_value = quick_code_offset - oat_header_->GetExecutableOffset();
- uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t);
- (*cfi_info)[offset_to_update+0] = new_value;
- (*cfi_info)[offset_to_update+1] = new_value >> 8;
- (*cfi_info)[offset_to_update+2] = new_value >> 16;
- (*cfi_info)[offset_to_update+3] = new_value >> 24;
- method_info_.push_back(DebugInfo(PrettyMethod(method_idx, dex_file, false),
- new_value, new_value + code_size));
- }
- }
-
- // Deduplicate code arrays
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter =
- code_offsets_.find(quick_code);
- if (code_iter != code_offsets_.end()) {
- quick_code_offset = code_iter->second;
- } else {
- code_offsets_.Put(quick_code, quick_code_offset);
- offset += sizeof(code_size); // code size is prepended before code
- offset += code_size;
- oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
- }
- }
- frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
- core_spill_mask = compiled_method->GetCoreSpillMask();
- fp_spill_mask = compiled_method->GetFpSpillMask();
-
- const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
- size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]);
- mapping_table_offset = (mapping_table_size == 0) ? 0 : offset;
-
- // Deduplicate mapping tables
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter =
- mapping_table_offsets_.find(&mapping_table);
- if (mapping_iter != mapping_table_offsets_.end()) {
- mapping_table_offset = mapping_iter->second;
- } else {
- mapping_table_offsets_.Put(&mapping_table, mapping_table_offset);
- offset += mapping_table_size;
- oat_header_->UpdateChecksum(&mapping_table[0], mapping_table_size);
- }
-
- const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
- size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]);
- vmap_table_offset = (vmap_table_size == 0) ? 0 : offset;
-
- // Deduplicate vmap tables
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter =
- vmap_table_offsets_.find(&vmap_table);
- if (vmap_iter != vmap_table_offsets_.end()) {
- vmap_table_offset = vmap_iter->second;
- } else {
- vmap_table_offsets_.Put(&vmap_table, vmap_table_offset);
- offset += vmap_table_size;
- oat_header_->UpdateChecksum(&vmap_table[0], vmap_table_size);
- }
-
- const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
- size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
- gc_map_offset = (gc_map_size == 0) ? 0 : offset;
-
- if (kIsDebugBuild) {
- // We expect GC maps except when the class hasn't been verified or the method is native
- ClassReference class_ref(&dex_file, class_def_index);
- CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref);
- mirror::Class::Status status;
- if (compiled_class != NULL) {
- status = compiled_class->GetStatus();
- } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
- status = mirror::Class::kStatusError;
- } else {
- status = mirror::Class::kStatusNotReady;
- }
- CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
- << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
- << (status < mirror::Class::kStatusVerified) << " " << status << " "
- << PrettyMethod(method_idx, dex_file);
- }
-
- // Deduplicate GC maps
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter =
- gc_map_offsets_.find(&gc_map);
- if (gc_map_iter != gc_map_offsets_.end()) {
- gc_map_offset = gc_map_iter->second;
- } else {
- gc_map_offsets_.Put(&gc_map, gc_map_offset);
- offset += gc_map_size;
- oat_header_->UpdateChecksum(&gc_map[0], gc_map_size);
- }
-
- oat_class->method_offsets_[*method_offsets_index] =
- OatMethodOffsets(quick_code_offset,
- frame_size_in_bytes,
- core_spill_mask,
- fp_spill_mask,
- mapping_table_offset,
- vmap_table_offset,
- gc_map_offset);
- (*method_offsets_index)++;
- }
-
-
+ #define VISIT(VisitorType) \
+ do { \
+ VisitorType visitor(this, offset); \
+ bool success = VisitDexMethods(&visitor); \
+ DCHECK(success); \
+ offset = visitor.GetOffset(); \
+ } while (false)
+
+ VISIT(InitCodeMethodVisitor);
if (compiler_driver_->IsImage()) {
- // Derive frame size and spill masks for native methods without code:
- // These are generic JNI methods...
- if (is_native && compiled_method == nullptr) {
- // Compute Sirt size as putting _every_ reference into it, even null ones.
- uint32_t s_len;
- const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx), &s_len);
- DCHECK(shorty != nullptr);
- uint32_t refs = 1; // Native method always has "this" or class.
- for (uint32_t i = 1; i < s_len; ++i) {
- if (shorty[i] == 'L') {
- refs++;
- }
- }
- size_t pointer_size = GetInstructionSetPointerSize(compiler_driver_->GetInstructionSet());
- size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs);
-
- // Get the generic spill masks and base frame size.
- mirror::ArtMethod* callee_save_method =
- Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
-
- frame_size_in_bytes = callee_save_method->GetFrameSizeInBytes() + sirt_size;
- core_spill_mask = callee_save_method->GetCoreSpillMask();
- fp_spill_mask = callee_save_method->GetFpSpillMask();
- mapping_table_offset = 0;
- vmap_table_offset = 0;
- gc_map_offset = 0;
- }
-
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- // Unchecked as we hold mutator_lock_ on entry.
- ScopedObjectAccessUnchecked soa(Thread::Current());
- SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(dex_file));
- SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
- mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache,
- class_loader, nullptr, invoke_type);
- CHECK(method != NULL);
- method->SetFrameSizeInBytes(frame_size_in_bytes);
- method->SetCoreSpillMask(core_spill_mask);
- method->SetFpSpillMask(fp_spill_mask);
- method->SetOatMappingTableOffset(mapping_table_offset);
- // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
- method->SetQuickOatCodeOffset(quick_code_offset);
- method->SetOatVmapTableOffset(vmap_table_offset);
- method->SetOatNativeGcMapOffset(gc_map_offset);
+ VISIT(InitImageMethodVisitor);
}
+ #undef VISIT
+
return offset;
}
-#define DCHECK_OFFSET() \
- DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
- << "file_offset=" << file_offset << " relative_offset=" << relative_offset
-
-#define DCHECK_OFFSET_() \
- DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \
- << "file_offset=" << file_offset << " offset_=" << offset_
-
bool OatWriter::Write(OutputStream* out) {
const size_t file_offset = out->Seek(0, kSeekCurrent);
@@ -574,7 +893,14 @@ bool OatWriter::Write(OutputStream* out) {
return false;
}
- size_t relative_offset = WriteCode(out, file_offset);
+ size_t relative_offset = out->Seek(0, kSeekCurrent) - file_offset;
+ relative_offset = WriteMaps(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
+ LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
+ return false;
+ }
+
+ relative_offset = WriteCode(out, file_offset, relative_offset);
if (relative_offset == 0) {
LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
return false;
@@ -608,7 +934,7 @@ bool OatWriter::Write(OutputStream* out) {
DO_STAT(size_quick_resolution_trampoline_);
DO_STAT(size_quick_to_interpreter_bridge_);
DO_STAT(size_trampoline_alignment_);
- DO_STAT(size_code_size_);
+ DO_STAT(size_method_header_);
DO_STAT(size_code_);
DO_STAT(size_code_alignment_);
DO_STAT(size_mapping_table_);
@@ -669,9 +995,37 @@ bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) {
return true;
}
-size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) {
- size_t relative_offset = oat_header_->GetExecutableOffset();
+size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset) {
+ #define VISIT(VisitorType) \
+ do { \
+ VisitorType visitor(this, out, file_offset, relative_offset); \
+ if (UNLIKELY(!VisitDexMethods(&visitor))) { \
+ return 0; \
+ } \
+ relative_offset = visitor.GetOffset(); \
+ } while (false)
+
+ size_t gc_maps_offset = relative_offset;
+ VISIT(WriteMapMethodVisitor<GcMapDataAccess>);
+ size_gc_map_ = relative_offset - gc_maps_offset;
+
+ size_t mapping_tables_offset = relative_offset;
+ VISIT(WriteMapMethodVisitor<MappingTableDataAccess>);
+ size_mapping_table_ = relative_offset - mapping_tables_offset;
+
+ size_t vmap_tables_offset = relative_offset;
+ VISIT(WriteMapMethodVisitor<VmapTableDataAccess>);
+ size_vmap_table_ = relative_offset - vmap_tables_offset;
+
+ #undef VISIT
+
+ return relative_offset;
+}
+
+size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset) {
off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent);
+ relative_offset += size_executable_offset_alignment_;
+ DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset());
size_t expected_file_offset = file_offset + relative_offset;
if (static_cast<uint32_t>(new_offset) != expected_file_offset) {
PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
@@ -715,218 +1069,18 @@ size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) {
size_t OatWriter::WriteCodeDexFiles(OutputStream* out,
const size_t file_offset,
size_t relative_offset) {
- size_t oat_class_index = 0;
- for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
- const DexFile* dex_file = (*dex_files_)[i];
- CHECK(dex_file != NULL);
- relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, &oat_class_index,
- *dex_file);
- if (relative_offset == 0) {
- return 0;
- }
- }
- return relative_offset;
-}
-
-size_t OatWriter::WriteCodeDexFile(OutputStream* out, const size_t file_offset,
- size_t relative_offset, size_t* oat_class_index,
- const DexFile& dex_file) {
- for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs();
- class_def_index++, (*oat_class_index)++) {
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, *oat_class_index,
- dex_file, class_def);
- if (relative_offset == 0) {
- return 0;
- }
- }
- return relative_offset;
-}
-
-void OatWriter::ReportWriteFailure(const char* what, uint32_t method_idx,
- const DexFile& dex_file, const OutputStream& out) const {
- PLOG(ERROR) << "Failed to write " << what << " for " << PrettyMethod(method_idx, dex_file)
- << " to " << out.GetLocation();
-}
+ #define VISIT(VisitorType) \
+ do { \
+ VisitorType visitor(this, out, file_offset, relative_offset); \
+ if (UNLIKELY(!VisitDexMethods(&visitor))) { \
+ return 0; \
+ } \
+ relative_offset = visitor.GetOffset(); \
+ } while (false)
-size_t OatWriter::WriteCodeClassDef(OutputStream* out,
- const size_t file_offset,
- size_t relative_offset,
- size_t oat_class_index,
- const DexFile& dex_file,
- const DexFile::ClassDef& class_def) {
- const byte* class_data = dex_file.GetClassData(class_def);
- if (class_data == NULL) {
- // ie. an empty class such as a marker interface
- return relative_offset;
- }
- ClassDataItemIterator it(dex_file, class_data);
- // Skip fields
- while (it.HasNextStaticField()) {
- it.Next();
- }
- while (it.HasNextInstanceField()) {
- it.Next();
- }
- // Process methods
- size_t class_def_method_index = 0;
- size_t method_offsets_index = 0;
- while (it.HasNextDirectMethod()) {
- bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0;
- relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
- class_def_method_index, &method_offsets_index, is_static,
- it.GetMemberIndex(), dex_file);
- if (relative_offset == 0) {
- return 0;
- }
- class_def_method_index++;
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
- class_def_method_index, &method_offsets_index, false,
- it.GetMemberIndex(), dex_file);
- if (relative_offset == 0) {
- return 0;
- }
- class_def_method_index++;
- it.Next();
- }
- DCHECK(!it.HasNext());
- CHECK_LE(method_offsets_index, class_def_method_index);
- return relative_offset;
-}
+ VISIT(WriteCodeMethodVisitor);
-size_t OatWriter::WriteCodeMethod(OutputStream* out, const size_t file_offset,
- size_t relative_offset, size_t oat_class_index,
- size_t class_def_method_index, size_t* method_offsets_index,
- bool is_static, uint32_t method_idx, const DexFile& dex_file) {
- OatClass* oat_class = oat_classes_[oat_class_index];
- const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
-
- if (compiled_method != NULL) { // ie. not an abstract method
- const OatMethodOffsets method_offsets = oat_class->method_offsets_[*method_offsets_index];
- (*method_offsets_index)++;
- const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
- if (quick_code != nullptr) {
- CHECK(compiled_method->GetPortableCode() == nullptr);
- uint32_t aligned_offset = compiled_method->AlignCode(relative_offset);
- uint32_t aligned_code_delta = aligned_offset - relative_offset;
- if (aligned_code_delta != 0) {
- off_t new_offset = out->Seek(aligned_code_delta, kSeekCurrent);
- size_code_alignment_ += aligned_code_delta;
- uint32_t expected_offset = file_offset + aligned_offset;
- if (static_cast<uint32_t>(new_offset) != expected_offset) {
- PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset
- << " Expected: " << expected_offset << " File: " << out->GetLocation();
- return 0;
- }
- relative_offset += aligned_code_delta;
- DCHECK_OFFSET();
- }
- DCHECK_ALIGNED_PARAM(relative_offset,
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
-
- uint32_t code_size = quick_code->size() * sizeof(uint8_t);
- CHECK_NE(code_size, 0U);
-
- // Deduplicate code arrays
- size_t code_offset = relative_offset + sizeof(code_size) + compiled_method->CodeDelta();
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter =
- code_offsets_.find(quick_code);
- if (code_iter != code_offsets_.end() && code_offset != method_offsets.code_offset_) {
- DCHECK(code_iter->second == method_offsets.code_offset_)
- << PrettyMethod(method_idx, dex_file);
- } else {
- DCHECK(code_offset == method_offsets.code_offset_) << PrettyMethod(method_idx, dex_file);
- if (!out->WriteFully(&code_size, sizeof(code_size))) {
- ReportWriteFailure("method code size", method_idx, dex_file, *out);
- return 0;
- }
- size_code_size_ += sizeof(code_size);
- relative_offset += sizeof(code_size);
- DCHECK_OFFSET();
- if (!out->WriteFully(&(*quick_code)[0], code_size)) {
- ReportWriteFailure("method code", method_idx, dex_file, *out);
- return 0;
- }
- size_code_ += code_size;
- relative_offset += code_size;
- }
- DCHECK_OFFSET();
- }
- const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
- size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]);
-
- // Deduplicate mapping tables
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter =
- mapping_table_offsets_.find(&mapping_table);
- if (mapping_iter != mapping_table_offsets_.end() &&
- relative_offset != method_offsets.mapping_table_offset_) {
- DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0)
- || mapping_iter->second == method_offsets.mapping_table_offset_)
- << PrettyMethod(method_idx, dex_file);
- } else {
- DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0)
- || relative_offset == method_offsets.mapping_table_offset_)
- << PrettyMethod(method_idx, dex_file);
- if (!out->WriteFully(&mapping_table[0], mapping_table_size)) {
- ReportWriteFailure("mapping table", method_idx, dex_file, *out);
- return 0;
- }
- size_mapping_table_ += mapping_table_size;
- relative_offset += mapping_table_size;
- }
- DCHECK_OFFSET();
-
- const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
- size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]);
-
- // Deduplicate vmap tables
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter =
- vmap_table_offsets_.find(&vmap_table);
- if (vmap_iter != vmap_table_offsets_.end() &&
- relative_offset != method_offsets.vmap_table_offset_) {
- DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0)
- || vmap_iter->second == method_offsets.vmap_table_offset_)
- << PrettyMethod(method_idx, dex_file);
- } else {
- DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0)
- || relative_offset == method_offsets.vmap_table_offset_)
- << PrettyMethod(method_idx, dex_file);
- if (!out->WriteFully(&vmap_table[0], vmap_table_size)) {
- ReportWriteFailure("vmap table", method_idx, dex_file, *out);
- return 0;
- }
- size_vmap_table_ += vmap_table_size;
- relative_offset += vmap_table_size;
- }
- DCHECK_OFFSET();
-
- const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
- size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
-
- // Deduplicate GC maps
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter =
- gc_map_offsets_.find(&gc_map);
- if (gc_map_iter != gc_map_offsets_.end() &&
- relative_offset != method_offsets.gc_map_offset_) {
- DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0)
- || gc_map_iter->second == method_offsets.gc_map_offset_)
- << PrettyMethod(method_idx, dex_file);
- } else {
- DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0)
- || relative_offset == method_offsets.gc_map_offset_)
- << PrettyMethod(method_idx, dex_file);
- if (!out->WriteFully(&gc_map[0], gc_map_size)) {
- ReportWriteFailure("GC map", method_idx, dex_file, *out);
- return 0;
- }
- size_gc_map_ += gc_map_size;
- relative_offset += gc_map_size;
- }
- DCHECK_OFFSET();
- }
+ #undef VISIT
return relative_offset;
}
@@ -993,15 +1147,14 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer,
}
OatWriter::OatClass::OatClass(size_t offset,
- std::vector<CompiledMethod*>* compiled_methods,
+ const std::vector<CompiledMethod*>& compiled_methods,
uint32_t num_non_null_compiled_methods,
- mirror::Class::Status status) {
- CHECK(compiled_methods != NULL);
- uint32_t num_methods = compiled_methods->size();
+ mirror::Class::Status status)
+ : compiled_methods_(compiled_methods) {
+ uint32_t num_methods = compiled_methods.size();
CHECK_LE(num_non_null_compiled_methods, num_methods);
offset_ = offset;
- compiled_methods_ = compiled_methods;
oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
// Since both kOatClassNoneCompiled and kOatClassAllCompiled could
@@ -1020,6 +1173,7 @@ OatWriter::OatClass::OatClass(size_t offset,
status_ = status;
method_offsets_.resize(num_non_null_compiled_methods);
+ method_headers_.resize(num_non_null_compiled_methods);
uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
if (type_ == kOatClassSomeCompiled) {
@@ -1033,7 +1187,7 @@ OatWriter::OatClass::OatClass(size_t offset,
}
for (size_t i = 0; i < num_methods; i++) {
- CompiledMethod* compiled_method = (*compiled_methods_)[i];
+ CompiledMethod* compiled_method = compiled_methods_[i];
if (compiled_method == NULL) {
oat_method_offsets_offsets_from_oat_class_[i] = 0;
} else {
@@ -1048,7 +1202,6 @@ OatWriter::OatClass::OatClass(size_t offset,
OatWriter::OatClass::~OatClass() {
delete method_bitmap_;
- delete compiled_methods_;
}
size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index bab1a26d44..7cdd5329bd 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -50,16 +50,30 @@ class OutputStream;
// ...
// OatClass[C]
//
+// GcMap one variable sized blob with GC map.
+// GcMap GC maps are deduplicated.
+// ...
+// GcMap
+//
+// VmapTable one variable sized VmapTable blob (quick compiler only).
+// VmapTable VmapTables are deduplicated.
+// ...
+// VmapTable
+//
+// MappingTable one variable sized blob with MappingTable (quick compiler only).
+// MappingTable MappingTables are deduplicated.
+// ...
+// MappingTable
+//
// padding if necessary so that the following code will be page aligned
//
-// CompiledMethod one variable sized blob with the contents of each CompiledMethod
-// CompiledMethod
-// CompiledMethod
-// CompiledMethod
-// CompiledMethod
-// CompiledMethod
+// OatMethodHeader fixed size header for a CompiledMethod including the size of the MethodCode.
+// MethodCode one variable sized blob with the code of a CompiledMethod.
+// OatMethodHeader (OatMethodHeader, MethodCode) pairs are deduplicated.
+// MethodCode
// ...
-// CompiledMethod
+// OatMethodHeader
+// MethodCode
//
class OatWriter {
public:
@@ -96,43 +110,47 @@ class OatWriter {
}
private:
+ // The DataAccess classes are helper classes that provide access to members related to
+ // a given map, i.e. GC map, mapping table or vmap table. By abstracting these away
+ // we can share a lot of code for processing the maps with template classes below.
+ struct GcMapDataAccess;
+ struct MappingTableDataAccess;
+ struct VmapTableDataAccess;
+
+ // The function VisitDexMethods() below iterates through all the methods in all
+ // the compiled dex files in order of their definitions. The method visitor
+ // classes provide individual bits of processing for each of the passes we need to
+ // first collect the data we want to write to the oat file and then, in later passes,
+ // to actually write it.
+ class DexMethodVisitor;
+ class OatDexMethodVisitor;
+ class InitOatClassesMethodVisitor;
+ class InitCodeMethodVisitor;
+ template <typename DataAccess>
+ class InitMapMethodVisitor;
+ class InitImageMethodVisitor;
+ class WriteCodeMethodVisitor;
+ template <typename DataAccess>
+ class WriteMapMethodVisitor;
+
+ // Visit all the methods in all the compiled dex files in their definition order
+ // with a given DexMethodVisitor.
+ bool VisitDexMethods(DexMethodVisitor* visitor);
+
size_t InitOatHeader();
size_t InitOatDexFiles(size_t offset);
size_t InitDexFiles(size_t offset);
size_t InitOatClasses(size_t offset);
+ size_t InitOatMaps(size_t offset);
size_t InitOatCode(size_t offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t InitOatCodeDexFiles(size_t offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- size_t InitOatCodeDexFile(size_t offset,
- size_t* oat_class_index,
- const DexFile& dex_file)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- size_t InitOatCodeClassDef(size_t offset,
- size_t oat_class_index, size_t class_def_index,
- const DexFile& dex_file,
- const DexFile::ClassDef& class_def)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index,
- size_t class_def_method_index, size_t* method_offsets_index,
- bool is_native, InvokeType type, uint32_t method_idx, const DexFile&)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool WriteTables(OutputStream* out, const size_t file_offset);
- size_t WriteCode(OutputStream* out, const size_t file_offset);
+ size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset);
+ size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
- size_t WriteCodeDexFile(OutputStream* out, const size_t file_offset, size_t relative_offset,
- size_t* oat_class_index, const DexFile& dex_file);
- size_t WriteCodeClassDef(OutputStream* out, const size_t file_offset, size_t relative_offset,
- size_t oat_class_index, const DexFile& dex_file,
- const DexFile::ClassDef& class_def);
- size_t WriteCodeMethod(OutputStream* out, const size_t file_offset, size_t relative_offset,
- size_t oat_class_index, size_t class_def_method_index,
- size_t* method_offsets_index, bool is_static, uint32_t method_idx,
- const DexFile& dex_file);
-
- void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file,
- const OutputStream& out) const;
class OatDexFile {
public:
@@ -159,7 +177,7 @@ class OatWriter {
class OatClass {
public:
explicit OatClass(size_t offset,
- std::vector<CompiledMethod*>* compiled_methods,
+ const std::vector<CompiledMethod*>& compiled_methods,
uint32_t num_non_null_compiled_methods,
mirror::Class::Status status);
~OatClass();
@@ -170,8 +188,8 @@ class OatWriter {
bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
- DCHECK(compiled_methods_ != NULL);
- return (*compiled_methods_)[class_def_method_index];
+ DCHECK_LT(class_def_method_index, compiled_methods_.size());
+ return compiled_methods_[class_def_method_index];
}
// Offset of start of OatClass from beginning of OatHeader. It is
@@ -182,7 +200,7 @@ class OatWriter {
size_t offset_;
// CompiledMethods for each class_def_method_index, or NULL if no method is available.
- std::vector<CompiledMethod*>* compiled_methods_;
+ std::vector<CompiledMethod*> compiled_methods_;
// Offset from OatClass::offset_ to the OatMethodOffsets for the
// class_def_method_index. If 0, it means the corresponding
@@ -207,12 +225,13 @@ class OatWriter {
// not is kOatClassBitmap, the bitmap will be NULL.
BitVector* method_bitmap_;
- // OatMethodOffsets for each CompiledMethod present in the
- // OatClass. Note that some may be missing if
+ // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
+ // present in the OatClass. Note that some may be missing if
// OatClass::compiled_methods_ contains NULL values (and
// oat_method_offsets_offsets_from_oat_class_ should contain 0
// values in this case).
std::vector<OatMethodOffsets> method_offsets_;
+ std::vector<OatMethodHeader> method_headers_;
private:
DISALLOW_COPY_AND_ASSIGN(OatClass);
@@ -265,7 +284,7 @@ class OatWriter {
uint32_t size_quick_resolution_trampoline_;
uint32_t size_quick_to_interpreter_bridge_;
uint32_t size_trampoline_alignment_;
- uint32_t size_code_size_;
+ uint32_t size_method_header_;
uint32_t size_code_;
uint32_t size_code_alignment_;
uint32_t size_mapping_table_;
@@ -281,12 +300,21 @@ class OatWriter {
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
- // Code mappings for deduplication. Deduplication is already done on a pointer basis by the
- // compiler driver, so we can simply compare the pointers to find out if things are duplicated.
- SafeMap<const std::vector<uint8_t>*, uint32_t> code_offsets_;
- SafeMap<const std::vector<uint8_t>*, uint32_t> vmap_table_offsets_;
- SafeMap<const std::vector<uint8_t>*, uint32_t> mapping_table_offsets_;
- SafeMap<const std::vector<uint8_t>*, uint32_t> gc_map_offsets_;
+ struct CodeOffsetsKeyComparator {
+ bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
+ if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
+ return lhs->GetQuickCode() < rhs->GetQuickCode();
+ }
+ // If the code is the same, all other fields are likely to be the same as well.
+ if (UNLIKELY(&lhs->GetMappingTable() != &rhs->GetMappingTable())) {
+ return &lhs->GetMappingTable() < &rhs->GetMappingTable();
+ }
+ if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
+ return &lhs->GetVmapTable() < &rhs->GetVmapTable();
+ }
+ return false;
+ }
+ };
DISALLOW_COPY_AND_ASSIGN(OatWriter);
};
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 7e63c69f5c..ff316e5b04 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -47,7 +47,7 @@ void CodeGenerator::CompileBlock(HBasicBlock* block) {
Bind(GetLabelOf(block));
HGraphVisitor* location_builder = GetLocationBuilder();
HGraphVisitor* instruction_visitor = GetInstructionVisitor();
- for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
current->Accept(location_builder);
InitLocations(current);
@@ -55,9 +55,105 @@ void CodeGenerator::CompileBlock(HBasicBlock* block) {
}
}
+size_t CodeGenerator::AllocateFreeRegisterInternal(
+ bool* blocked_registers, size_t number_of_registers) const {
+ for (size_t regno = 0; regno < number_of_registers; regno++) {
+ if (!blocked_registers[regno]) {
+ blocked_registers[regno] = true;
+ return regno;
+ }
+ }
+ LOG(FATAL) << "Unreachable";
+ return -1;
+}
+
+
+void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const {
+ LocationSummary* locations = instruction->GetLocations();
+ if (locations == nullptr) return;
+
+ for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) {
+ blocked_registers_[i] = false;
+ }
+
+ // Mark all fixed input, temp and output registers as used.
+ for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
+ Location loc = locations->InAt(i);
+ if (loc.IsRegister()) {
+ // Check that a register is not specified twice in the summary.
+ DCHECK(!blocked_registers_[loc.GetEncoding()]);
+ blocked_registers_[loc.GetEncoding()] = true;
+ }
+ }
+
+ for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) {
+ Location loc = locations->GetTemp(i);
+ if (loc.IsRegister()) {
+ // Check that a register is not specified twice in the summary.
+ DCHECK(!blocked_registers_[loc.GetEncoding()]);
+ blocked_registers_[loc.GetEncoding()] = true;
+ }
+ }
+
+ SetupBlockedRegisters(blocked_registers_);
+
+ // Allocate all unallocated input locations.
+ for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
+ Location loc = locations->InAt(i);
+ HInstruction* input = instruction->InputAt(i);
+ if (loc.IsUnallocated()) {
+ if (loc.GetPolicy() == Location::kRequiresRegister) {
+ loc = Location::RegisterLocation(
+ AllocateFreeRegister(input->GetType(), blocked_registers_));
+ } else {
+ DCHECK_EQ(loc.GetPolicy(), Location::kAny);
+ HLoadLocal* load = input->AsLoadLocal();
+ if (load != nullptr) {
+ loc = GetStackLocation(load);
+ } else {
+ loc = Location::RegisterLocation(
+ AllocateFreeRegister(input->GetType(), blocked_registers_));
+ }
+ }
+ locations->SetInAt(i, loc);
+ }
+ }
+
+ // Allocate all unallocated temp locations.
+ for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) {
+ Location loc = locations->GetTemp(i);
+ if (loc.IsUnallocated()) {
+ DCHECK_EQ(loc.GetPolicy(), Location::kRequiresRegister);
+ // TODO: Adjust handling of temps. We currently consider temps to use
+ // core registers. They may also use floating point registers at some point.
+ loc = Location::RegisterLocation(static_cast<ManagedRegister>(
+ AllocateFreeRegister(Primitive::kPrimInt, blocked_registers_)));
+ locations->SetTempAt(i, loc);
+ }
+ }
+
+ Location result_location = locations->Out();
+ if (result_location.IsUnallocated()) {
+ switch (result_location.GetPolicy()) {
+ case Location::kAny:
+ case Location::kRequiresRegister:
+ result_location = Location::RegisterLocation(
+ AllocateFreeRegister(instruction->GetType(), blocked_registers_));
+ break;
+ case Location::kSameAsFirstInput:
+ result_location = locations->InAt(0);
+ break;
+ }
+ locations->SetOut(result_location);
+ }
+}
+
void CodeGenerator::InitLocations(HInstruction* instruction) {
- if (instruction->GetLocations() == nullptr) return;
- for (int i = 0; i < instruction->InputCount(); i++) {
+ if (instruction->GetLocations() == nullptr) {
+ return;
+ }
+ AllocateRegistersLocally(instruction);
+ for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
Location location = instruction->GetLocations()->InAt(i);
if (location.IsValid()) {
// Move the input to the desired location.
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 5c7cac1e5c..74cbccc4b8 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -62,6 +62,12 @@ class Location : public ValueObject {
// bits are in a stack slot. The kQuickParameter kind is for
// handling this special case.
kQuickParameter = 4,
+
+ // Unallocated location represents a location that is not fixed and can be
+ // allocated by a register allocator. Each unallocated location has
+ // a policy that specifies what kind of location is suitable. Payload
+ // contains register allocation policy.
+ kUnallocated = 5,
};
Location() : value_(kInvalid) {
@@ -166,10 +172,50 @@ class Location : public ValueObject {
case kStackSlot: return "S";
case kDoubleStackSlot: return "DS";
case kQuickParameter: return "Q";
+ case kUnallocated: return "U";
}
return "?";
}
+ // Unallocated locations.
+ enum Policy {
+ kAny,
+ kRequiresRegister,
+ kSameAsFirstInput,
+ };
+
+ bool IsUnallocated() const {
+ return GetKind() == kUnallocated;
+ }
+
+ static Location UnallocatedLocation(Policy policy) {
+ return Location(kUnallocated, PolicyField::Encode(policy));
+ }
+
+ // Any free register is suitable to replace this unallocated location.
+ static Location Any() {
+ return UnallocatedLocation(kAny);
+ }
+
+ static Location RequiresRegister() {
+ return UnallocatedLocation(kRequiresRegister);
+ }
+
+ // The location of the first input to the instruction will be
+ // used to replace this unallocated location.
+ static Location SameAsFirstInput() {
+ return UnallocatedLocation(kSameAsFirstInput);
+ }
+
+ Policy GetPolicy() const {
+ DCHECK(IsUnallocated());
+ return PolicyField::Decode(GetPayload());
+ }
+
+ uword GetEncoding() const {
+ return GetPayload();
+ }
+
private:
// Number of bits required to encode Kind value.
static constexpr uint32_t kBitsForKind = 4;
@@ -187,6 +233,9 @@ class Location : public ValueObject {
typedef BitField<Kind, 0, kBitsForKind> KindField;
typedef BitField<uword, kBitsForKind, kBitsForPayload> PayloadField;
+ // Layout for kUnallocated locations payload.
+ typedef BitField<Policy, 0, 3> PolicyField;
+
// Layout for stack slots.
static const intptr_t kStackIndexBias =
static_cast<intptr_t>(1) << (kBitsForPayload - 1);
@@ -208,40 +257,52 @@ class Location : public ValueObject {
class LocationSummary : public ArenaObject {
public:
explicit LocationSummary(HInstruction* instruction)
- : inputs(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
- temps(instruction->GetBlock()->GetGraph()->GetArena(), 0) {
- inputs.SetSize(instruction->InputCount());
- for (int i = 0; i < instruction->InputCount(); i++) {
- inputs.Put(i, Location());
+ : inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
+ temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0) {
+ inputs_.SetSize(instruction->InputCount());
+ for (size_t i = 0; i < instruction->InputCount(); i++) {
+ inputs_.Put(i, Location());
}
}
void SetInAt(uint32_t at, Location location) {
- inputs.Put(at, location);
+ inputs_.Put(at, location);
}
Location InAt(uint32_t at) const {
- return inputs.Get(at);
+ return inputs_.Get(at);
+ }
+
+ size_t GetInputCount() const {
+ return inputs_.Size();
}
void SetOut(Location location) {
- output = Location(location);
+ output_ = Location(location);
}
void AddTemp(Location location) {
- temps.Add(location);
+ temps_.Add(location);
}
Location GetTemp(uint32_t at) const {
- return temps.Get(at);
+ return temps_.Get(at);
+ }
+
+ void SetTempAt(uint32_t at, Location location) {
+ temps_.Put(at, location);
}
- Location Out() const { return output; }
+ size_t GetTempCount() const {
+ return temps_.Size();
+ }
+
+ Location Out() const { return output_; }
private:
- GrowableArray<Location> inputs;
- GrowableArray<Location> temps;
- Location output;
+ GrowableArray<Location> inputs_;
+ GrowableArray<Location> temps_;
+ Location output_;
DISALLOW_COPY_AND_ASSIGN(LocationSummary);
};
@@ -286,15 +347,33 @@ class CodeGenerator : public ArenaObject {
std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
protected:
- explicit CodeGenerator(HGraph* graph)
+ CodeGenerator(HGraph* graph, size_t number_of_registers)
: frame_size_(0),
graph_(graph),
block_labels_(graph->GetArena(), 0),
- pc_infos_(graph->GetArena(), 32) {
+ pc_infos_(graph->GetArena(), 32),
+ blocked_registers_(static_cast<bool*>(
+ graph->GetArena()->Alloc(number_of_registers * sizeof(bool), kArenaAllocData))) {
block_labels_.SetSize(graph->GetBlocks()->Size());
}
~CodeGenerator() { }
+ // Register allocation logic.
+ void AllocateRegistersLocally(HInstruction* instruction) const;
+
+ // Backend specific implementation for allocating a register.
+ virtual ManagedRegister AllocateFreeRegister(Primitive::Type type,
+ bool* blocked_registers) const = 0;
+
+ // Raw implementation of allocating a register: loops over blocked_registers to find
+ // the first available register.
+ size_t AllocateFreeRegisterInternal(bool* blocked_registers, size_t number_of_registers) const;
+
+ virtual void SetupBlockedRegisters(bool* blocked_registers) const = 0;
+ virtual size_t GetNumberOfRegisters() const = 0;
+
+ virtual Location GetStackLocation(HLoadLocal* load) const = 0;
+
// Frame size required for this method.
uint32_t frame_size_;
uint32_t core_spill_mask_;
@@ -309,6 +388,9 @@ class CodeGenerator : public ArenaObject {
GrowableArray<Label> block_labels_;
GrowableArray<PcInfo> pc_infos_;
+ // Temporary data structure used when doing register allocation.
+ bool* const blocked_registers_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 27691ac080..a446701b39 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -35,6 +35,81 @@ namespace arm {
static constexpr int kNumberOfPushedRegistersAtEntry = 1;
static constexpr int kCurrentMethodStackOffset = 0;
+CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
+ : CodeGenerator(graph, kNumberOfRegIds),
+ location_builder_(graph, this),
+ instruction_visitor_(graph, this) {}
+
+static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
+ return blocked_registers + kNumberOfAllocIds;
+}
+
+ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type,
+ bool* blocked_registers) const {
+ switch (type) {
+ case Primitive::kPrimLong: {
+ size_t reg = AllocateFreeRegisterInternal(
+ GetBlockedRegisterPairs(blocked_registers), kNumberOfRegisterPairs);
+ ArmManagedRegister pair =
+ ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg));
+ blocked_registers[pair.AsRegisterPairLow()] = true;
+ blocked_registers[pair.AsRegisterPairHigh()] = true;
+ return pair;
+ }
+
+ case Primitive::kPrimByte:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ size_t reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters);
+ return ArmManagedRegister::FromCoreRegister(static_cast<Register>(reg));
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << type;
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << type;
+ }
+
+ return ManagedRegister::NoRegister();
+}
+
+void CodeGeneratorARM::SetupBlockedRegisters(bool* blocked_registers) const {
+ bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
+
+ // Don't allocate the dalvik style register pair passing.
+ blocked_register_pairs[R1_R2] = true;
+
+ // Stack register, LR and PC are always reserved.
+ blocked_registers[SP] = true;
+ blocked_registers[LR] = true;
+ blocked_registers[PC] = true;
+
+ // Reserve R4 for suspend check.
+ blocked_registers[R4] = true;
+ blocked_register_pairs[R4_R5] = true;
+
+ // Reserve thread register.
+ blocked_registers[TR] = true;
+
+ // TODO: We currently don't use Quick's callee saved registers.
+ blocked_registers[R5] = true;
+ blocked_registers[R6] = true;
+ blocked_registers[R7] = true;
+ blocked_registers[R8] = true;
+ blocked_registers[R10] = true;
+ blocked_registers[R11] = true;
+ blocked_register_pairs[R6_R7] = true;
+}
+
+size_t CodeGeneratorARM::GetNumberOfRegisters() const {
+ return kNumberOfRegIds;
+}
+
static Location ArmCoreLocation(Register reg) {
return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg));
}
@@ -85,6 +160,32 @@ int32_t CodeGeneratorARM::GetStackSlot(HLocal* local) const {
}
}
+Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
+ switch (load->GetType()) {
+ case Primitive::kPrimLong:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+ break;
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented type " << load->GetType();
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << load->GetType();
+ }
+
+ LOG(FATAL) << "Unreachable";
+ return Location();
+}
+
Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -302,7 +403,7 @@ void InstructionCodeGeneratorARM::VisitExit(HExit* exit) {
void LocationsBuilderARM::VisitIf(HIf* if_instr) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
- locations->SetInAt(0, ArmCoreLocation(R0));
+ locations->SetInAt(0, Location::RequiresRegister());
if_instr->SetLocations(locations);
}
@@ -317,9 +418,9 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
void LocationsBuilderARM::VisitEqual(HEqual* equal) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal);
- locations->SetInAt(0, ArmCoreLocation(R0));
- locations->SetInAt(1, ArmCoreLocation(R1));
- locations->SetOut(ArmCoreLocation(R0));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
equal->SetLocations(locations);
}
@@ -409,7 +510,8 @@ void LocationsBuilderARM::VisitReturn(HReturn* ret) {
break;
case Primitive::kPrimLong:
- locations->SetInAt(0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
+ locations->SetInAt(
+ 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
break;
default:
@@ -444,10 +546,10 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
- locations->AddTemp(ArmCoreLocation(R0));
+ locations->AddTemp(Location::RequiresRegister());
InvokeDexCallingConventionVisitor calling_convention_visitor;
- for (int i = 0; i < invoke->InputCount(); i++) {
+ for (size_t i = 0; i < invoke->InputCount(); i++) {
HInstruction* input = invoke->InputAt(i);
locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
}
@@ -512,19 +614,11 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
void LocationsBuilderARM::VisitAdd(HAdd* add) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
switch (add->GetResultType()) {
- case Primitive::kPrimInt: {
- locations->SetInAt(0, ArmCoreLocation(R0));
- locations->SetInAt(1, ArmCoreLocation(R1));
- locations->SetOut(ArmCoreLocation(R0));
- break;
- }
-
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
- locations->SetInAt(
- 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
- locations->SetInAt(
- 1, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R2_R3)));
- locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
break;
}
@@ -574,19 +668,11 @@ void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
void LocationsBuilderARM::VisitSub(HSub* sub) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
switch (sub->GetResultType()) {
- case Primitive::kPrimInt: {
- locations->SetInAt(0, ArmCoreLocation(R0));
- locations->SetInAt(1, ArmCoreLocation(R1));
- locations->SetOut(ArmCoreLocation(R0));
- break;
- }
-
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
- locations->SetInAt(
- 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
- locations->SetInAt(
- 1, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R2_R3)));
- locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
break;
}
@@ -649,6 +735,9 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
+ locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
locations->SetOut(ArmCoreLocation(R0));
instruction->SetLocations(locations);
}
@@ -683,8 +772,8 @@ void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instructi
void LocationsBuilderARM::VisitNot(HNot* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- locations->SetInAt(0, ArmCoreLocation(R0));
- locations->SetOut(ArmCoreLocation(R0));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
instruction->SetLocations(locations);
}
@@ -694,5 +783,13 @@ void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(1));
}
+void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
+ LOG(FATAL) << "Unimplemented";
+}
+
+void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
+ LOG(FATAL) << "Unimplemented";
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index ed35f94e2b..2405d4b5a6 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -101,10 +101,7 @@ class InstructionCodeGeneratorARM : public HGraphVisitor {
class CodeGeneratorARM : public CodeGenerator {
public:
- explicit CodeGeneratorARM(HGraph* graph)
- : CodeGenerator(graph),
- location_builder_(graph, this),
- instruction_visitor_(graph, this) { }
+ explicit CodeGeneratorARM(HGraph* graph);
virtual ~CodeGeneratorARM() { }
virtual void GenerateFrameEntry() OVERRIDE;
@@ -128,7 +125,13 @@ class CodeGeneratorARM : public CodeGenerator {
return &assembler_;
}
+ virtual void SetupBlockedRegisters(bool* blocked_registers) const OVERRIDE;
+ virtual ManagedRegister AllocateFreeRegister(
+ Primitive::Type type, bool* blocked_registers) const OVERRIDE;
+ virtual size_t GetNumberOfRegisters() const OVERRIDE;
+
int32_t GetStackSlot(HLocal* local) const;
+ virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
private:
// Helper method to move a 32bits value between two locations.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 114263161d..fbb054ae88 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -35,6 +35,72 @@ namespace x86 {
static constexpr int kNumberOfPushedRegistersAtEntry = 1;
static constexpr int kCurrentMethodStackOffset = 0;
+CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
+ : CodeGenerator(graph, kNumberOfRegIds),
+ location_builder_(graph, this),
+ instruction_visitor_(graph, this) {}
+
+static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
+ return blocked_registers + kNumberOfAllocIds;
+}
+
+ManagedRegister CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type,
+ bool* blocked_registers) const {
+ switch (type) {
+ case Primitive::kPrimLong: {
+ size_t reg = AllocateFreeRegisterInternal(
+ GetBlockedRegisterPairs(blocked_registers), kNumberOfRegisterPairs);
+ X86ManagedRegister pair =
+ X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg));
+ blocked_registers[pair.AsRegisterPairLow()] = true;
+ blocked_registers[pair.AsRegisterPairHigh()] = true;
+ return pair;
+ }
+
+ case Primitive::kPrimByte:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ size_t reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCpuRegisters);
+ return X86ManagedRegister::FromCpuRegister(static_cast<Register>(reg));
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << type;
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << type;
+ }
+
+ return ManagedRegister::NoRegister();
+}
+
+void CodeGeneratorX86::SetupBlockedRegisters(bool* blocked_registers) const {
+ bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers);
+
+ // Don't allocate the dalvik style register pair passing.
+ blocked_register_pairs[ECX_EDX] = true;
+
+ // Stack register is always reserved.
+ blocked_registers[ESP] = true;
+
+ // TODO: We currently don't use Quick's callee saved registers.
+ blocked_registers[EBP] = true;
+ blocked_registers[ESI] = true;
+ blocked_registers[EDI] = true;
+ blocked_register_pairs[EAX_EDI] = true;
+ blocked_register_pairs[EDX_EDI] = true;
+ blocked_register_pairs[ECX_EDI] = true;
+ blocked_register_pairs[EBX_EDI] = true;
+}
+
+size_t CodeGeneratorX86::GetNumberOfRegisters() const {
+ return kNumberOfRegIds;
+}
+
static Location X86CpuLocation(Register reg) {
return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg));
}
@@ -90,6 +156,33 @@ int32_t CodeGeneratorX86::GetStackSlot(HLocal* local) const {
}
}
+
+Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const {
+ switch (load->GetType()) {
+ case Primitive::kPrimLong:
+ return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
+ break;
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ return Location::StackSlot(GetStackSlot(load->GetLocal()));
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented type " << load->GetType();
+
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type " << load->GetType();
+ }
+
+ LOG(FATAL) << "Unreachable";
+ return Location();
+}
+
static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX };
static constexpr size_t kRuntimeParameterCoreRegistersLength =
arraysize(kRuntimeParameterCoreRegisters);
@@ -311,13 +404,18 @@ void InstructionCodeGeneratorX86::VisitExit(HExit* exit) {
void LocationsBuilderX86::VisitIf(HIf* if_instr) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
- locations->SetInAt(0, X86CpuLocation(EAX));
+ locations->SetInAt(0, Location::Any());
if_instr->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
// TODO: Generate the input as a condition, instead of materializing in a register.
- __ cmpl(if_instr->GetLocations()->InAt(0).AsX86().AsCpuRegister(), Immediate(0));
+ Location location = if_instr->GetLocations()->InAt(0);
+ if (location.IsRegister()) {
+ __ cmpl(location.AsX86().AsCpuRegister(), Immediate(0));
+ } else {
+ __ cmpl(Address(ESP, location.GetStackIndex()), Immediate(0));
+ }
__ j(kEqual, codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) {
__ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
@@ -367,16 +465,22 @@ void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) {
void LocationsBuilderX86::VisitEqual(HEqual* equal) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal);
- locations->SetInAt(0, X86CpuLocation(EAX));
- locations->SetInAt(1, X86CpuLocation(ECX));
- locations->SetOut(X86CpuLocation(EAX));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ locations->SetOut(Location::SameAsFirstInput());
equal->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitEqual(HEqual* equal) {
- __ cmpl(equal->GetLocations()->InAt(0).AsX86().AsCpuRegister(),
- equal->GetLocations()->InAt(1).AsX86().AsCpuRegister());
- __ setb(kEqual, equal->GetLocations()->Out().AsX86().AsCpuRegister());
+ LocationSummary* locations = equal->GetLocations();
+ if (locations->InAt(1).IsRegister()) {
+ __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
+ locations->InAt(1).AsX86().AsCpuRegister());
+ } else {
+ __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
+ Address(ESP, locations->InAt(1).GetStackIndex()));
+ }
+ __ setb(kEqual, locations->Out().AsX86().AsCpuRegister());
}
void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) {
@@ -453,10 +557,10 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
- locations->AddTemp(X86CpuLocation(EAX));
+ locations->AddTemp(Location::RequiresRegister());
InvokeDexCallingConventionVisitor calling_convention_visitor;
- for (int i = 0; i < invoke->InputCount(); i++) {
+ for (size_t i = 0; i < invoke->InputCount(); i++) {
HInstruction* input = invoke->InputAt(i);
locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
}
@@ -514,18 +618,11 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
void LocationsBuilderX86::VisitAdd(HAdd* add) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
switch (add->GetResultType()) {
- case Primitive::kPrimInt: {
- locations->SetInAt(0, X86CpuLocation(EAX));
- locations->SetInAt(1, X86CpuLocation(ECX));
- locations->SetOut(X86CpuLocation(EAX));
- break;
- }
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
- locations->SetInAt(
- 0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX)));
- locations->SetInAt(
- 1, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(ECX_EBX)));
- locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX)));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -548,18 +645,30 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
case Primitive::kPrimInt: {
DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(),
locations->Out().AsX86().AsCpuRegister());
- __ addl(locations->InAt(0).AsX86().AsCpuRegister(),
- locations->InAt(1).AsX86().AsCpuRegister());
+ if (locations->InAt(1).IsRegister()) {
+ __ addl(locations->InAt(0).AsX86().AsCpuRegister(),
+ locations->InAt(1).AsX86().AsCpuRegister());
+ } else {
+ __ addl(locations->InAt(0).AsX86().AsCpuRegister(),
+ Address(ESP, locations->InAt(1).GetStackIndex()));
+ }
break;
}
case Primitive::kPrimLong: {
DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(),
locations->Out().AsX86().AsRegisterPair());
- __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(),
- locations->InAt(1).AsX86().AsRegisterPairLow());
- __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
- locations->InAt(1).AsX86().AsRegisterPairHigh());
+ if (locations->InAt(1).IsRegister()) {
+ __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(),
+ locations->InAt(1).AsX86().AsRegisterPairLow());
+ __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
+ locations->InAt(1).AsX86().AsRegisterPairHigh());
+ } else {
+ __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(),
+ Address(ESP, locations->InAt(1).GetStackIndex()));
+ __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
+ Address(ESP, locations->InAt(1).GetHighStackIndex(kX86WordSize)));
+ }
break;
}
@@ -578,19 +687,11 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
void LocationsBuilderX86::VisitSub(HSub* sub) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
switch (sub->GetResultType()) {
- case Primitive::kPrimInt: {
- locations->SetInAt(0, X86CpuLocation(EAX));
- locations->SetInAt(1, X86CpuLocation(ECX));
- locations->SetOut(X86CpuLocation(EAX));
- break;
- }
-
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
- locations->SetInAt(
- 0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX)));
- locations->SetInAt(
- 1, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(ECX_EBX)));
- locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX)));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -613,18 +714,30 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
case Primitive::kPrimInt: {
DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(),
locations->Out().AsX86().AsCpuRegister());
- __ subl(locations->InAt(0).AsX86().AsCpuRegister(),
- locations->InAt(1).AsX86().AsCpuRegister());
+ if (locations->InAt(1).IsRegister()) {
+ __ subl(locations->InAt(0).AsX86().AsCpuRegister(),
+ locations->InAt(1).AsX86().AsCpuRegister());
+ } else {
+ __ subl(locations->InAt(0).AsX86().AsCpuRegister(),
+ Address(ESP, locations->InAt(1).GetStackIndex()));
+ }
break;
}
case Primitive::kPrimLong: {
DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(),
locations->Out().AsX86().AsRegisterPair());
- __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(),
- locations->InAt(1).AsX86().AsRegisterPairLow());
- __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
- locations->InAt(1).AsX86().AsRegisterPairHigh());
+ if (locations->InAt(1).IsRegister()) {
+ __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(),
+ locations->InAt(1).AsX86().AsRegisterPairLow());
+ __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
+ locations->InAt(1).AsX86().AsRegisterPairHigh());
+ } else {
+ __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(),
+ Address(ESP, locations->InAt(1).GetStackIndex()));
+ __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(),
+ Address(ESP, locations->InAt(1).GetHighStackIndex(kX86WordSize)));
+ }
break;
}
@@ -643,14 +756,16 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetOut(X86CpuLocation(EAX));
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(0)));
+ locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(1)));
instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
InvokeRuntimeCallingConvention calling_convention;
LoadCurrentMethod(calling_convention.GetRegisterAt(1));
- __ movl(calling_convention.GetRegisterAt(0),
- Immediate(instruction->GetTypeIndex()));
+ __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
__ fs()->call(
Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
@@ -676,15 +791,24 @@ void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instructi
void LocationsBuilderX86::VisitNot(HNot* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- locations->SetInAt(0, X86CpuLocation(EAX));
- locations->SetOut(X86CpuLocation(EAX));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) {
LocationSummary* locations = instruction->GetLocations();
- DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), locations->Out().AsX86().AsCpuRegister());
- __ xorl(locations->Out().AsX86().AsCpuRegister(), Immediate(1));
+ Location out = locations->Out();
+ DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), out.AsX86().AsCpuRegister());
+ __ xorl(out.AsX86().AsCpuRegister(), Immediate(1));
+}
+
+void LocationsBuilderX86::VisitPhi(HPhi* instruction) {
+ LOG(FATAL) << "Unimplemented";
+}
+
+void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) {
+ LOG(FATAL) << "Unimplemented";
}
} // namespace x86
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index f22890e708..1ee11bf0e8 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -102,10 +102,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
class CodeGeneratorX86 : public CodeGenerator {
public:
- explicit CodeGeneratorX86(HGraph* graph)
- : CodeGenerator(graph),
- location_builder_(graph, this),
- instruction_visitor_(graph, this) { }
+ explicit CodeGeneratorX86(HGraph* graph);
virtual ~CodeGeneratorX86() { }
virtual void GenerateFrameEntry() OVERRIDE;
@@ -129,7 +126,13 @@ class CodeGeneratorX86 : public CodeGenerator {
return &assembler_;
}
+ virtual size_t GetNumberOfRegisters() const OVERRIDE;
+ virtual void SetupBlockedRegisters(bool* blocked_registers) const OVERRIDE;
+ virtual ManagedRegister AllocateFreeRegister(
+ Primitive::Type type, bool* blocked_registers) const OVERRIDE;
+
int32_t GetStackSlot(HLocal* local) const;
+ virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
private:
// Helper method to move a 32bits value between two locations.
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 498deba2b4..3d6aeb7300 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -15,6 +15,7 @@
*/
#include "nodes.h"
+#include "ssa_builder.h"
#include "utils/growable_array.h"
namespace art {
@@ -34,7 +35,13 @@ void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const {
if (!visited.IsBitSet(i)) {
HBasicBlock* block = blocks_.Get(i);
for (size_t j = 0; j < block->GetSuccessors()->Size(); j++) {
- block->GetSuccessors()->Get(j)->RemovePredecessor(block);
+ block->GetSuccessors()->Get(j)->RemovePredecessor(block, false);
+ }
+ for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) {
+ block->RemovePhi(it.Current()->AsPhi());
+ }
+ for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
+ block->RemoveInstruction(it.Current());
}
}
}
@@ -120,11 +127,112 @@ void HGraph::VisitBlockForDominatorTree(HBasicBlock* block,
}
}
-void HBasicBlock::AddInstruction(HInstruction* instruction) {
+void HGraph::TransformToSSA() {
+ DCHECK(!dominator_order_.IsEmpty());
+ SimplifyCFG();
+ SsaBuilder ssa_builder(this);
+ ssa_builder.BuildSsa();
+}
+
+void HGraph::SimplifyCFG() {
+ for (size_t i = 0; i < dominator_order_.Size(); i++) {
+ HBasicBlock* current = dominator_order_.Get(i);
+ if (current->IsLoopHeader()) {
+ // Make sure the loop has only one pre header. This simplifies SSA building by having
+ // to just look at the pre header to know which locals are initialized at entry of the
+ // loop.
+ HLoopInformation* info = current->GetLoopInformation();
+ size_t number_of_incomings = current->GetPredecessors()->Size() - info->NumberOfBackEdges();
+ if (number_of_incomings != 1) {
+ HBasicBlock* pre_header = new (arena_) HBasicBlock(this);
+ AddBlock(pre_header);
+ pre_header->AddInstruction(new (arena_) HGoto());
+ pre_header->SetDominator(current->GetDominator());
+ current->SetDominator(pre_header);
+ dominator_order_.InsertAt(i, pre_header);
+ i++;
+
+ ArenaBitVector back_edges(arena_, GetBlocks()->Size(), false);
+ for (size_t pred = 0; pred < info->GetBackEdges()->Size(); pred++) {
+ back_edges.SetBit(info->GetBackEdges()->Get(pred)->GetBlockId());
+ }
+ for (size_t pred = 0; pred < current->GetPredecessors()->Size(); pred++) {
+ HBasicBlock* predecessor = current->GetPredecessors()->Get(pred);
+ if (!back_edges.IsBitSet(predecessor->GetBlockId())) {
+ current->RemovePredecessor(predecessor);
+ pred--;
+ predecessor->AddSuccessor(pre_header);
+ }
+ }
+ pre_header->AddSuccessor(current);
+ }
+ info->SetPreHeader(current->GetDominator());
+ }
+ }
+}
+
+void HLoopInformation::SetPreHeader(HBasicBlock* block) {
+ DCHECK_EQ(header_->GetDominator(), block);
+ pre_header_ = block;
+}
+
+static void Add(HInstructionList* instruction_list,
+ HBasicBlock* block,
+ HInstruction* instruction) {
DCHECK(instruction->GetBlock() == nullptr);
DCHECK_EQ(instruction->GetId(), -1);
- instruction->SetBlock(this);
- instruction->SetId(GetGraph()->GetNextInstructionId());
+ instruction->SetBlock(block);
+ instruction->SetId(block->GetGraph()->GetNextInstructionId());
+ instruction_list->AddInstruction(instruction);
+}
+
+void HBasicBlock::AddInstruction(HInstruction* instruction) {
+ Add(&instructions_, this, instruction);
+}
+
+void HBasicBlock::AddPhi(HPhi* phi) {
+ Add(&phis_, this, phi);
+}
+
+static void Remove(HInstructionList* instruction_list,
+ HBasicBlock* block,
+ HInstruction* instruction) {
+ DCHECK_EQ(block, instruction->GetBlock());
+ DCHECK(instruction->GetUses() == nullptr);
+ DCHECK(instruction->GetEnvUses() == nullptr);
+ instruction->SetBlock(nullptr);
+ instruction_list->RemoveInstruction(instruction);
+
+ for (size_t i = 0; i < instruction->InputCount(); i++) {
+ instruction->InputAt(i)->RemoveUser(instruction, i);
+ }
+}
+
+void HBasicBlock::RemoveInstruction(HInstruction* instruction) {
+ Remove(&instructions_, this, instruction);
+}
+
+void HBasicBlock::RemovePhi(HPhi* phi) {
+ Remove(&phis_, this, phi);
+}
+
+void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
+ HUseListNode<HInstruction>* previous = nullptr;
+ HUseListNode<HInstruction>* current = uses_;
+ while (current != nullptr) {
+ if (current->GetUser() == user && current->GetIndex() == input_index) {
+ if (previous == NULL) {
+ uses_ = current->GetTail();
+ } else {
+ previous->SetTail(current->GetTail());
+ }
+ }
+ previous = current;
+ current = current->GetTail();
+ }
+}
+
+void HInstructionList::AddInstruction(HInstruction* instruction) {
if (first_instruction_ == nullptr) {
DCHECK(last_instruction_ == nullptr);
first_instruction_ = last_instruction_ = instruction;
@@ -133,9 +241,51 @@ void HBasicBlock::AddInstruction(HInstruction* instruction) {
instruction->previous_ = last_instruction_;
last_instruction_ = instruction;
}
- for (int i = 0; i < instruction->InputCount(); i++) {
- instruction->InputAt(i)->AddUse(instruction);
+ for (size_t i = 0; i < instruction->InputCount(); i++) {
+ instruction->InputAt(i)->AddUseAt(instruction, i);
+ }
+}
+
+void HInstructionList::RemoveInstruction(HInstruction* instruction) {
+ if (instruction->previous_ != nullptr) {
+ instruction->previous_->next_ = instruction->next_;
+ }
+ if (instruction->next_ != nullptr) {
+ instruction->next_->previous_ = instruction->previous_;
}
+ if (instruction == first_instruction_) {
+ first_instruction_ = instruction->next_;
+ }
+ if (instruction == last_instruction_) {
+ last_instruction_ = instruction->previous_;
+ }
+}
+
+void HInstruction::ReplaceWith(HInstruction* other) {
+ for (HUseIterator<HInstruction> it(GetUses()); !it.Done(); it.Advance()) {
+ HUseListNode<HInstruction>* current = it.Current();
+ HInstruction* user = current->GetUser();
+ size_t input_index = current->GetIndex();
+ user->SetRawInputAt(input_index, other);
+ other->AddUseAt(user, input_index);
+ }
+
+ for (HUseIterator<HEnvironment> it(GetEnvUses()); !it.Done(); it.Advance()) {
+ HUseListNode<HEnvironment>* current = it.Current();
+ HEnvironment* user = current->GetUser();
+ size_t input_index = current->GetIndex();
+ user->SetRawEnvAt(input_index, other);
+ other->AddEnvUseAt(user, input_index);
+ }
+
+ uses_ = nullptr;
+ env_uses_ = nullptr;
+}
+
+void HPhi::AddInput(HInstruction* input) {
+ DCHECK(input->GetBlock() != nullptr);
+ inputs_.Add(input);
+ input->AddUseAt(this, inputs_.Size() - 1);
}
#define DEFINE_ACCEPT(name) \
@@ -155,7 +305,10 @@ void HGraphVisitor::VisitInsertionOrder() {
}
void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) {
- for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) {
+ it.Current()->Accept(this);
+ }
+ for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
it.Current()->Accept(this);
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3da9ed9461..581c1d56f2 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -24,9 +24,11 @@
namespace art {
class HBasicBlock;
+class HEnvironment;
class HInstruction;
class HIntConstant;
class HGraphVisitor;
+class HPhi;
class LocationSummary;
static const int kDefaultNumberOfBlocks = 8;
@@ -34,6 +36,23 @@ static const int kDefaultNumberOfSuccessors = 2;
static const int kDefaultNumberOfPredecessors = 2;
static const int kDefaultNumberOfBackEdges = 1;
+class HInstructionList {
+ public:
+ HInstructionList() : first_instruction_(nullptr), last_instruction_(nullptr) {}
+
+ void AddInstruction(HInstruction* instruction);
+ void RemoveInstruction(HInstruction* instruction);
+
+ private:
+ HInstruction* first_instruction_;
+ HInstruction* last_instruction_;
+
+ friend class HBasicBlock;
+ friend class HInstructionIterator;
+
+ DISALLOW_COPY_AND_ASSIGN(HInstructionList);
+};
+
// Control-flow graph of a method. Contains a list of basic blocks.
class HGraph : public ArenaObject {
public:
@@ -56,7 +75,10 @@ class HGraph : public ArenaObject {
void SetExitBlock(HBasicBlock* block) { exit_block_ = block; }
void AddBlock(HBasicBlock* block);
+
void BuildDominatorTree();
+ void TransformToSSA();
+ void SimplifyCFG();
int GetNextInstructionId() {
return current_instruction_id_++;
@@ -86,6 +108,9 @@ class HGraph : public ArenaObject {
return number_of_in_vregs_;
}
+ GrowableArray<HBasicBlock*>* GetDominatorOrder() {
+ return &dominator_order_;
+ }
private:
HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const;
@@ -138,7 +163,18 @@ class HLoopInformation : public ArenaObject {
return back_edges_.Size();
}
+ void SetPreHeader(HBasicBlock* block);
+
+ HBasicBlock* GetPreHeader() const {
+ return pre_header_;
+ }
+
+ const GrowableArray<HBasicBlock*>* GetBackEdges() const {
+ return &back_edges_;
+ }
+
private:
+ HBasicBlock* pre_header_;
HBasicBlock* header_;
GrowableArray<HBasicBlock*> back_edges_;
@@ -154,8 +190,6 @@ class HBasicBlock : public ArenaObject {
: graph_(graph),
predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors),
successors_(graph->GetArena(), kDefaultNumberOfSuccessors),
- first_instruction_(nullptr),
- last_instruction_(nullptr),
loop_information_(nullptr),
dominator_(nullptr),
block_id_(-1) { }
@@ -189,26 +223,42 @@ class HBasicBlock : public ArenaObject {
: loop_information_->NumberOfBackEdges();
}
- HInstruction* GetFirstInstruction() const { return first_instruction_; }
- HInstruction* GetLastInstruction() const { return last_instruction_; }
+ HInstruction* GetFirstInstruction() const { return instructions_.first_instruction_; }
+ HInstruction* GetLastInstruction() const { return instructions_.last_instruction_; }
+ HInstructionList const* GetInstructions() const { return &instructions_; }
+ HInstructionList const* GetPhis() const { return &phis_; }
void AddSuccessor(HBasicBlock* block) {
successors_.Add(block);
block->predecessors_.Add(this);
}
- void RemovePredecessor(HBasicBlock* block) {
+ void RemovePredecessor(HBasicBlock* block, bool remove_in_successor = true) {
predecessors_.Delete(block);
+ if (remove_in_successor) {
+ block->successors_.Delete(this);
+ }
}
void AddInstruction(HInstruction* instruction);
+ void RemoveInstruction(HInstruction* instruction);
+ void AddPhi(HPhi* phi);
+ void RemovePhi(HPhi* phi);
+
+ bool IsLoopHeader() const {
+ return loop_information_ != nullptr;
+ }
+
+ HLoopInformation* GetLoopInformation() const {
+ return loop_information_;
+ }
private:
HGraph* const graph_;
GrowableArray<HBasicBlock*> predecessors_;
GrowableArray<HBasicBlock*> successors_;
- HInstruction* first_instruction_;
- HInstruction* last_instruction_;
+ HInstructionList instructions_;
+ HInstructionList phis_;
HLoopInformation* loop_information_;
HBasicBlock* dominator_;
int block_id_;
@@ -230,6 +280,7 @@ class HBasicBlock : public ArenaObject {
M(NewInstance) \
M(Not) \
M(ParameterValue) \
+ M(Phi) \
M(Return) \
M(ReturnVoid) \
M(StoreLocal) \
@@ -244,17 +295,22 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION)
virtual const char* DebugName() const { return #type; } \
virtual H##type* As##type() { return this; } \
+template <typename T>
class HUseListNode : public ArenaObject {
public:
- HUseListNode(HInstruction* instruction, HUseListNode* tail)
- : instruction_(instruction), tail_(tail) { }
+ HUseListNode(T* user, size_t index, HUseListNode* tail)
+ : user_(user), index_(index), tail_(tail) { }
HUseListNode* GetTail() const { return tail_; }
- HInstruction* GetInstruction() const { return instruction_; }
+ T* GetUser() const { return user_; }
+ size_t GetIndex() const { return index_; }
+
+ void SetTail(HUseListNode<T>* node) { tail_ = node; }
private:
- HInstruction* const instruction_;
- HUseListNode* const tail_;
+ T* const user_;
+ const size_t index_;
+ HUseListNode<T>* tail_;
DISALLOW_COPY_AND_ASSIGN(HUseListNode);
};
@@ -267,6 +323,8 @@ class HInstruction : public ArenaObject {
block_(nullptr),
id_(-1),
uses_(nullptr),
+ env_uses_(nullptr),
+ environment_(nullptr),
locations_(nullptr) { }
virtual ~HInstruction() { }
@@ -277,28 +335,43 @@ class HInstruction : public ArenaObject {
HBasicBlock* GetBlock() const { return block_; }
void SetBlock(HBasicBlock* block) { block_ = block; }
- virtual intptr_t InputCount() const = 0;
- virtual HInstruction* InputAt(intptr_t i) const = 0;
+ virtual size_t InputCount() const = 0;
+ virtual HInstruction* InputAt(size_t i) const = 0;
virtual void Accept(HGraphVisitor* visitor) = 0;
virtual const char* DebugName() const = 0;
virtual Primitive::Type GetType() const { return Primitive::kPrimVoid; }
+ virtual void SetRawInputAt(size_t index, HInstruction* input) = 0;
+
+ virtual bool NeedsEnvironment() const { return false; }
- void AddUse(HInstruction* user) {
- uses_ = new (block_->GetGraph()->GetArena()) HUseListNode(user, uses_);
+ void AddUseAt(HInstruction* user, size_t index) {
+ uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HInstruction>(user, index, uses_);
}
- HUseListNode* GetUses() const { return uses_; }
+ void AddEnvUseAt(HEnvironment* user, size_t index) {
+ env_uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HEnvironment>(
+ user, index, env_uses_);
+ }
+
+ void RemoveUser(HInstruction* user, size_t index);
+
+ HUseListNode<HInstruction>* GetUses() const { return uses_; }
+ HUseListNode<HEnvironment>* GetEnvUses() const { return env_uses_; }
bool HasUses() const { return uses_ != nullptr; }
int GetId() const { return id_; }
void SetId(int id) { id_ = id; }
+ void SetEnvironment(HEnvironment* environment) { environment_ = environment; }
+
LocationSummary* GetLocations() const { return locations_; }
void SetLocations(LocationSummary* locations) { locations_ = locations; }
+ void ReplaceWith(HInstruction* instruction);
+
#define INSTRUCTION_TYPE_CHECK(type) \
virtual H##type* As##type() { return nullptr; }
@@ -315,19 +388,27 @@ class HInstruction : public ArenaObject {
// has not beed added to the graph.
int id_;
- HUseListNode* uses_;
+ // List of instructions that have this instruction as input.
+ HUseListNode<HInstruction>* uses_;
+
+ // List of environments that contain this instruction.
+ HUseListNode<HEnvironment>* env_uses_;
+
+ HEnvironment* environment_;
// Set by the code generator.
LocationSummary* locations_;
friend class HBasicBlock;
+ friend class HInstructionList;
DISALLOW_COPY_AND_ASSIGN(HInstruction);
};
+template<typename T>
class HUseIterator : public ValueObject {
public:
- explicit HUseIterator(HInstruction* instruction) : current_(instruction->GetUses()) { }
+ explicit HUseIterator(HUseListNode<T>* uses) : current_(uses) {}
bool Done() const { return current_ == nullptr; }
@@ -336,17 +417,51 @@ class HUseIterator : public ValueObject {
current_ = current_->GetTail();
}
- HInstruction* Current() const {
+ HUseListNode<T>* Current() const {
DCHECK(!Done());
- return current_->GetInstruction();
+ return current_;
}
private:
- HUseListNode* current_;
+ HUseListNode<T>* current_;
friend class HValue;
};
+// A HEnvironment object contains the values of virtual registers at a given location.
+class HEnvironment : public ArenaObject {
+ public:
+ HEnvironment(ArenaAllocator* arena, size_t number_of_vregs) : vregs_(arena, number_of_vregs) {
+ vregs_.SetSize(number_of_vregs);
+ for (size_t i = 0; i < number_of_vregs; i++) {
+ vregs_.Put(i, nullptr);
+ }
+ }
+
+ void Populate(const GrowableArray<HInstruction*>& env) {
+ for (size_t i = 0; i < env.Size(); i++) {
+ HInstruction* instruction = env.Get(i);
+ vregs_.Put(i, instruction);
+ if (instruction != nullptr) {
+ instruction->AddEnvUseAt(this, i);
+ }
+ }
+ }
+
+ void SetRawEnvAt(size_t index, HInstruction* instruction) {
+ vregs_.Put(index, instruction);
+ }
+
+ GrowableArray<HInstruction*>* GetVRegs() {
+ return &vregs_;
+ }
+
+ private:
+ GrowableArray<HInstruction*> vregs_;
+
+ DISALLOW_COPY_AND_ASSIGN(HEnvironment);
+};
+
class HInputIterator : public ValueObject {
public:
explicit HInputIterator(HInstruction* instruction) : instruction_(instruction), index_(0) { }
@@ -357,15 +472,15 @@ class HInputIterator : public ValueObject {
private:
HInstruction* instruction_;
- int index_;
+ size_t index_;
DISALLOW_COPY_AND_ASSIGN(HInputIterator);
};
class HInstructionIterator : public ValueObject {
public:
- explicit HInstructionIterator(HBasicBlock* block)
- : instruction_(block->GetFirstInstruction()) {
+ explicit HInstructionIterator(const HInstructionList& instructions)
+ : instruction_(instructions.first_instruction_) {
next_ = Done() ? nullptr : instruction_->GetNext();
}
@@ -434,16 +549,18 @@ class HTemplateInstruction: public HInstruction {
HTemplateInstruction<N>() : inputs_() { }
virtual ~HTemplateInstruction() { }
- virtual intptr_t InputCount() const { return N; }
- virtual HInstruction* InputAt(intptr_t i) const { return inputs_[i]; }
+ virtual size_t InputCount() const { return N; }
+ virtual HInstruction* InputAt(size_t i) const { return inputs_[i]; }
protected:
- void SetRawInputAt(intptr_t i, HInstruction* instruction) {
+ virtual void SetRawInputAt(size_t i, HInstruction* instruction) {
inputs_[i] = instruction;
}
private:
EmbeddedArray<HInstruction*, N> inputs_;
+
+ friend class SsaBuilder;
};
// Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow
@@ -658,11 +775,19 @@ class HInvoke : public HInstruction {
inputs_.SetSize(number_of_arguments);
}
- virtual intptr_t InputCount() const { return inputs_.Size(); }
- virtual HInstruction* InputAt(intptr_t i) const { return inputs_.Get(i); }
+ virtual size_t InputCount() const { return inputs_.Size(); }
+ virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
+
+ // Runtime needs to walk the stack, so Dex -> Dex calls need to
+ // know their environment.
+ virtual bool NeedsEnvironment() const { return true; }
void SetArgumentAt(size_t index, HInstruction* argument) {
- inputs_.Put(index, argument);
+ SetRawInputAt(index, argument);
+ }
+
+ virtual void SetRawInputAt(size_t index, HInstruction* input) {
+ inputs_.Put(index, input);
}
virtual Primitive::Type GetType() const { return return_type_; }
@@ -707,6 +832,9 @@ class HNewInstance : public HTemplateInstruction<0> {
virtual Primitive::Type GetType() const { return Primitive::kPrimNot; }
+ // Calls runtime so needs an environment.
+ virtual bool NeedsEnvironment() const { return true; }
+
DECLARE_INSTRUCTION(NewInstance)
private:
@@ -779,6 +907,39 @@ class HNot : public HTemplateInstruction<1> {
DISALLOW_COPY_AND_ASSIGN(HNot);
};
+class HPhi : public HInstruction {
+ public:
+ HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
+ : inputs_(arena, number_of_inputs),
+ reg_number_(reg_number),
+ type_(type) {
+ inputs_.SetSize(number_of_inputs);
+ }
+
+ virtual size_t InputCount() const { return inputs_.Size(); }
+ virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
+
+ virtual void SetRawInputAt(size_t index, HInstruction* input) {
+ inputs_.Put(index, input);
+ }
+
+ void AddInput(HInstruction* input);
+
+ virtual Primitive::Type GetType() const { return type_; }
+
+ uint32_t GetRegNumber() const { return reg_number_; }
+
+ DECLARE_INSTRUCTION(Phi)
+
+ protected:
+ GrowableArray<HInstruction*> inputs_;
+ const uint32_t reg_number_;
+ const Primitive::Type type_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HPhi);
+};
+
class HGraphVisitor : public ValueObject {
public:
explicit HGraphVisitor(HGraph* graph) : graph_(graph) { }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index d19c40c291..9438890941 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -100,6 +100,10 @@ CompiledMethod* OptimizingCompiler::TryCompile(CompilerDriver& driver,
std::vector<uint8_t> gc_map;
codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+ // Run these phases to get some test coverage.
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+
return new CompiledMethod(driver,
instruction_set,
allocator.GetMemory(),
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 606c91519e..c82d0cc6a4 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -25,11 +25,19 @@ class HPrettyPrinter : public HGraphVisitor {
public:
explicit HPrettyPrinter(HGraph* graph) : HGraphVisitor(graph) { }
- virtual void VisitInstruction(HInstruction* instruction) {
+ void PrintPreInstruction(HInstruction* instruction) {
PrintString(" ");
PrintInt(instruction->GetId());
PrintString(": ");
+ }
+
+ virtual void VisitInstruction(HInstruction* instruction) {
+ PrintPreInstruction(instruction);
PrintString(instruction->DebugName());
+ PrintPostInstruction(instruction);
+ }
+
+ void PrintPostInstruction(HInstruction* instruction) {
if (instruction->InputCount() != 0) {
PrintString("(");
bool first = true;
@@ -46,13 +54,13 @@ class HPrettyPrinter : public HGraphVisitor {
if (instruction->HasUses()) {
PrintString(" [");
bool first = true;
- for (HUseIterator it(instruction); !it.Done(); it.Advance()) {
+ for (HUseIterator<HInstruction> it(instruction->GetUses()); !it.Done(); it.Advance()) {
if (first) {
first = false;
} else {
PrintString(", ");
}
- PrintInt(it.Current()->GetId());
+ PrintInt(it.Current()->GetUser()->GetId());
}
PrintString("]");
}
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
new file mode 100644
index 0000000000..bfb4f38f50
--- /dev/null
+++ b/compiler/optimizing/ssa_builder.cc
@@ -0,0 +1,134 @@
+/*
+ * 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 "ssa_builder.h"
+#include "nodes.h"
+
+namespace art {
+
+void SsaBuilder::BuildSsa() {
+ // 1) Visit in dominator 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 (size_t i = 0; i < GetGraph()->GetDominatorOrder()->Size(); i++) {
+ VisitBasicBlock(GetGraph()->GetDominatorOrder()->Get(i));
+ }
+
+ // 2) Set inputs of loop phis.
+ for (size_t i = 0; i < loop_headers_.Size(); i++) {
+ HBasicBlock* block = loop_headers_.Get(i);
+ for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ for (size_t pred = 0; pred < block->GetPredecessors()->Size(); pred++) {
+ phi->AddInput(ValueOfLocal(block->GetPredecessors()->Get(pred), phi->GetRegNumber()));
+ }
+ }
+ }
+
+ // 3) Clear locals.
+ // TODO: Move this to a dead code eliminator phase.
+ for (HInstructionIterator it(*GetGraph()->GetEntryBlock()->GetInstructions());
+ !it.Done();
+ it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->AsLocal() != nullptr) {
+ current->GetBlock()->RemoveInstruction(current);
+ }
+ }
+}
+
+HInstruction* SsaBuilder::ValueOfLocal(HBasicBlock* block, size_t local) {
+ return GetLocalsFor(block)->Get(local);
+}
+
+void SsaBuilder::VisitBasicBlock(HBasicBlock* block) {
+ current_locals_ = GetLocalsFor(block);
+
+ if (block->IsLoopHeader()) {
+ // If the block is a loop header, we know we only have visited the pre header
+ // because we are visiting in dominator order. We create phis for all initialized
+ // locals from the pre header. Their inputs will be populated at the end of
+ // the analysis.
+ for (size_t local = 0; local < current_locals_->Size(); local++) {
+ HInstruction* incoming = ValueOfLocal(block->GetLoopInformation()->GetPreHeader(), local);
+ if (incoming != nullptr) {
+ // TODO: Compute union type.
+ HPhi* phi = new (GetGraph()->GetArena()) HPhi(
+ GetGraph()->GetArena(), local, 0, Primitive::kPrimVoid);
+ block->AddPhi(phi);
+ current_locals_->Put(local, phi);
+ }
+ }
+ // Save the loop header so that the last phase of the analysis knows which
+ // blocks need to be updated.
+ loop_headers_.Add(block);
+ } else if (block->GetPredecessors()->Size() > 0) {
+ // All predecessors have already been visited because we are visiting in dominator order.
+ // We merge the values of all locals, creating phis if those values differ.
+ for (size_t local = 0; local < current_locals_->Size(); local++) {
+ bool is_different = false;
+ HInstruction* value = ValueOfLocal(block->GetPredecessors()->Get(0), local);
+ for (size_t i = 1; i < block->GetPredecessors()->Size(); i++) {
+ if (ValueOfLocal(block->GetPredecessors()->Get(i), local) != value) {
+ is_different = true;
+ break;
+ }
+ }
+ if (is_different) {
+ // TODO: Compute union type.
+ HPhi* phi = new (GetGraph()->GetArena()) HPhi(
+ GetGraph()->GetArena(), local, block->GetPredecessors()->Size(), Primitive::kPrimVoid);
+ for (size_t i = 0; i < block->GetPredecessors()->Size(); i++) {
+ phi->SetRawInputAt(i, ValueOfLocal(block->GetPredecessors()->Get(i), local));
+ }
+ block->AddPhi(phi);
+ value = phi;
+ }
+ current_locals_->Put(local, value);
+ }
+ }
+
+ // Visit all instructions. The instructions of interest are:
+ // - HLoadLocal: replace them with the current value of the local.
+ // - HStoreLocal: update current value of the local and remove the instruction.
+ // - Instructions that require an environment: populate their environment
+ // with the current values of the locals.
+ for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
+ it.Current()->Accept(this);
+ }
+}
+
+void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {
+ load->ReplaceWith(current_locals_->Get(load->GetLocal()->GetRegNumber()));
+ load->GetBlock()->RemoveInstruction(load);
+}
+
+void SsaBuilder::VisitStoreLocal(HStoreLocal* store) {
+ current_locals_->Put(store->GetLocal()->GetRegNumber(), store->InputAt(1));
+ store->GetBlock()->RemoveInstruction(store);
+}
+
+void SsaBuilder::VisitInstruction(HInstruction* instruction) {
+ if (!instruction->NeedsEnvironment()) {
+ return;
+ }
+ HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment(
+ GetGraph()->GetArena(), current_locals_->Size());
+ environment->Populate(*current_locals_);
+ instruction->SetEnvironment(environment);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
new file mode 100644
index 0000000000..b6c6c0b658
--- /dev/null
+++ b/compiler/optimizing/ssa_builder.h
@@ -0,0 +1,71 @@
+/*
+ * 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_SSA_BUILDER_H_
+#define ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_
+
+#include "nodes.h"
+
+namespace art {
+
+static constexpr int kDefaultNumberOfLoops = 2;
+
+class SsaBuilder : public HGraphVisitor {
+ public:
+ explicit SsaBuilder(HGraph* graph)
+ : HGraphVisitor(graph),
+ current_locals_(nullptr),
+ loop_headers_(graph->GetArena(), kDefaultNumberOfLoops),
+ locals_for_(graph->GetArena(), graph->GetBlocks()->Size()) {
+ locals_for_.SetSize(graph->GetBlocks()->Size());
+ }
+
+ void BuildSsa();
+
+ GrowableArray<HInstruction*>* GetLocalsFor(HBasicBlock* block) {
+ HEnvironment* env = locals_for_.Get(block->GetBlockId());
+ if (env == nullptr) {
+ env = new (GetGraph()->GetArena()) HEnvironment(
+ GetGraph()->GetArena(), GetGraph()->GetNumberOfVRegs());
+ locals_for_.Put(block->GetBlockId(), env);
+ }
+ return env->GetVRegs();
+ }
+
+ HInstruction* ValueOfLocal(HBasicBlock* block, size_t local);
+
+ void VisitBasicBlock(HBasicBlock* block);
+ void VisitLoadLocal(HLoadLocal* load);
+ void VisitStoreLocal(HStoreLocal* store);
+ void VisitInstruction(HInstruction* instruction);
+
+ private:
+ // Locals for the current block being visited.
+ GrowableArray<HInstruction*>* current_locals_;
+
+ // Keep track of loop headers found. The last phase of the analysis iterates
+ // over these blocks to set the inputs of their phis.
+ GrowableArray<HBasicBlock*> loop_headers_;
+
+ // HEnvironment for each block.
+ GrowableArray<HEnvironment*> locals_for_;
+
+ DISALLOW_COPY_AND_ASSIGN(SsaBuilder);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
new file mode 100644
index 0000000000..7c3633b5e9
--- /dev/null
+++ b/compiler/optimizing/ssa_test.cc
@@ -0,0 +1,444 @@
+/*
+ * 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 "base/stringprintf.h"
+#include "builder.h"
+#include "dex_file.h"
+#include "dex_instruction.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "pretty_printer.h"
+#include "ssa_builder.h"
+#include "utils/arena_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+class StringPrettyPrinter : public HPrettyPrinter {
+ public:
+ explicit StringPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {}
+
+ virtual void PrintInt(int value) {
+ str_ += StringPrintf("%d", value);
+ }
+
+ virtual void PrintString(const char* value) {
+ str_ += value;
+ }
+
+ virtual void PrintNewLine() {
+ str_ += '\n';
+ }
+
+ void Clear() { str_.clear(); }
+
+ std::string str() const { return str_; }
+
+ virtual void VisitIntConstant(HIntConstant* constant) {
+ PrintPreInstruction(constant);
+ str_ += constant->DebugName();
+ str_ += " ";
+ PrintInt(constant->GetValue());
+ PrintPostInstruction(constant);
+ }
+
+ private:
+ std::string str_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringPrettyPrinter);
+};
+
+static void ReNumberInstructions(HGraph* graph) {
+ int id = 0;
+ for (size_t i = 0; i < graph->GetBlocks()->Size(); i++) {
+ HBasicBlock* block = graph->GetBlocks()->Get(i);
+ for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) {
+ it.Current()->SetId(id++);
+ }
+ for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
+ it.Current()->SetId(id++);
+ }
+ }
+}
+
+static void TestCode(const uint16_t* data, const char* expected) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraphBuilder builder(&allocator);
+ const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
+ HGraph* graph = builder.BuildGraph(*item);
+ ASSERT_NE(graph, nullptr);
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+ ReNumberInstructions(graph);
+
+ StringPrettyPrinter printer(graph);
+ printer.VisitInsertionOrder();
+
+ ASSERT_STREQ(expected, printer.str().c_str());
+}
+
+TEST(SsaTest, CFG1) {
+ // Test that we get rid of loads and stores.
+ const char* expected =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0 [2, 2]\n"
+ " 1: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 3, 2\n"
+ " 2: Equal(0, 0) [3]\n"
+ " 3: If(2)\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 4: Goto\n"
+ "BasicBlock 3, pred: 1, 2, succ: 4\n"
+ " 5: ReturnVoid\n"
+ "BasicBlock 4, pred: 3\n"
+ " 6: Exit\n";
+
+ 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, expected);
+}
+
+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 =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0 [6, 3, 3]\n"
+ " 1: IntConstant 4 [6]\n"
+ " 2: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 3, 2\n"
+ " 3: Equal(0, 0) [4]\n"
+ " 4: If(3)\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 5: Goto\n"
+ "BasicBlock 3, pred: 1, 2, succ: 4\n"
+ " 6: Phi(0, 1) [7]\n"
+ " 7: Return(6)\n"
+ "BasicBlock 4, pred: 3\n"
+ " 8: Exit\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 3,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::RETURN | 0 << 8);
+
+ TestCode(data, expected);
+}
+
+TEST(SsaTest, CFG3) {
+ // Test that we create a phi for the join block of an if control flow instruction
+ // when there both branches update a local.
+ const char* expected =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0 [4, 4]\n"
+ " 1: IntConstant 4 [8]\n"
+ " 2: IntConstant 5 [8]\n"
+ " 3: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 3, 2\n"
+ " 4: Equal(0, 0) [5]\n"
+ " 5: If(4)\n"
+ "BasicBlock 2, pred: 1, succ: 4\n"
+ " 6: Goto\n"
+ "BasicBlock 3, pred: 1, succ: 4\n"
+ " 7: Goto\n"
+ "BasicBlock 4, pred: 2, 3, succ: 5\n"
+ " 8: Phi(1, 2) [9]\n"
+ " 9: Return(8)\n"
+ "BasicBlock 5, pred: 4\n"
+ " 10: Exit\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 4,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::GOTO | 0x200,
+ Instruction::CONST_4 | 5 << 12 | 0,
+ Instruction::RETURN | 0 << 8);
+
+ TestCode(data, expected);
+}
+
+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"
+ " 0: IntConstant 0 [6, 4, 2, 2]\n"
+ " 1: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 3, 2\n"
+ " 2: Equal(0, 0) [3]\n"
+ " 3: If(2)\n"
+ "BasicBlock 2, pred: 1, 3, succ: 3\n"
+ " 4: Phi(0, 6) [6]\n"
+ " 5: Goto\n"
+ "BasicBlock 3, pred: 1, 2, succ: 2\n"
+ " 6: Phi(0, 4) [4]\n"
+ " 7: Goto\n"
+ "BasicBlock 4\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 3,
+ Instruction::GOTO | 0x100,
+ Instruction::GOTO | 0xFF00);
+
+ TestCode(data, expected);
+}
+
+TEST(SsaTest, Loop2) {
+ // Simple loop with one preheader and one back edge.
+ const char* expected =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0 [4]\n"
+ " 1: IntConstant 4 [4]\n"
+ " 2: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 3: Goto\n"
+ "BasicBlock 2, pred: 1, 3, succ: 4, 3\n"
+ " 4: Phi(0, 1) [5, 5]\n"
+ " 5: Equal(4, 4) [6]\n"
+ " 6: If(5)\n"
+ "BasicBlock 3, pred: 2, succ: 2\n"
+ " 7: Goto\n"
+ "BasicBlock 4, pred: 2, succ: 5\n"
+ " 8: ReturnVoid\n"
+ "BasicBlock 5, pred: 4\n"
+ " 9: Exit\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 4,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::GOTO | 0xFD00,
+ Instruction::RETURN_VOID);
+
+ TestCode(data, expected);
+}
+
+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"
+ " 0: IntConstant 0 [5]\n"
+ " 1: IntConstant 4 [5]\n"
+ " 2: IntConstant 5 [9]\n"
+ " 3: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Goto\n"
+ "BasicBlock 2, pred: 1, 3, succ: 4, 3\n"
+ " 5: Phi(0, 1) [6, 6]\n"
+ " 6: Equal(5, 5) [7]\n"
+ " 7: If(6)\n"
+ "BasicBlock 3, pred: 2, succ: 2\n"
+ " 8: Goto\n"
+ "BasicBlock 4, pred: 2, succ: 5\n"
+ " 9: Return(2)\n"
+ "BasicBlock 5, pred: 4\n"
+ " 10: Exit\n";
+
+ const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 4,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::GOTO | 0xFD00,
+ Instruction::CONST_4 | 5 << 12 | 1 << 8,
+ Instruction::RETURN | 1 << 8);
+
+ TestCode(data, expected);
+}
+
+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 =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0 [4]\n"
+ " 1: IntConstant 4 [4]\n"
+ " 2: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 4\n"
+ " 3: Goto\n"
+ "BasicBlock 2, pred: 3, 4, succ: 5, 3\n"
+ " 4: Phi(1, 0) [9, 5, 5]\n"
+ " 5: Equal(4, 4) [6]\n"
+ " 6: If(5)\n"
+ "BasicBlock 3, pred: 2, succ: 2\n"
+ " 7: Goto\n"
+ "BasicBlock 4, pred: 1, succ: 2\n"
+ " 8: Goto\n"
+ "BasicBlock 5, pred: 2, succ: 6\n"
+ " 9: Return(4)\n"
+ "BasicBlock 6, pred: 5\n"
+ " 10: Exit\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::GOTO | 0x500,
+ Instruction::IF_EQ, 5,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::GOTO | 0xFD00,
+ Instruction::GOTO | 0xFC00,
+ Instruction::RETURN | 0 << 8);
+
+ TestCode(data, expected);
+}
+
+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 =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0 [4, 4]\n"
+ " 1: IntConstant 4 [14]\n"
+ " 2: IntConstant 5 [14]\n"
+ " 3: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 3, 2\n"
+ " 4: Equal(0, 0) [5]\n"
+ " 5: If(4)\n"
+ "BasicBlock 2, pred: 1, succ: 8\n"
+ " 6: Goto\n"
+ "BasicBlock 3, pred: 1, succ: 8\n"
+ " 7: Goto\n"
+ "BasicBlock 4, pred: 5, 8, succ: 6, 5\n"
+ " 8: Phi(8, 14) [8, 12, 9, 9]\n"
+ " 9: Equal(8, 8) [10]\n"
+ " 10: If(9)\n"
+ "BasicBlock 5, pred: 4, succ: 4\n"
+ " 11: Goto\n"
+ "BasicBlock 6, pred: 4, succ: 7\n"
+ " 12: Return(8)\n"
+ "BasicBlock 7, pred: 6\n"
+ " 13: Exit\n"
+ "BasicBlock 8, pred: 2, 3, succ: 4\n"
+ " 14: Phi(1, 2) [8]\n"
+ " 15: Goto\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 4,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::GOTO | 0x200,
+ Instruction::CONST_4 | 5 << 12 | 0,
+ Instruction::IF_EQ, 3,
+ Instruction::GOTO | 0xFE00,
+ Instruction::RETURN | 0 << 8);
+
+ TestCode(data, expected);
+}
+
+TEST(SsaTest, Loop6) {
+ // Test a loop with one preheader and two back edges (e.g. continue).
+ const char* expected =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0 [5]\n"
+ " 1: IntConstant 4 [5, 8, 8]\n"
+ " 2: IntConstant 5 [5]\n"
+ " 3: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Goto\n"
+ "BasicBlock 2, pred: 1, 4, 5, succ: 6, 3\n"
+ " 5: Phi(0, 2, 1) [12, 6, 6]\n"
+ " 6: Equal(5, 5) [7]\n"
+ " 7: If(6)\n"
+ "BasicBlock 3, pred: 2, succ: 5, 4\n"
+ " 8: Equal(1, 1) [9]\n"
+ " 9: If(8)\n"
+ "BasicBlock 4, pred: 3, succ: 2\n"
+ " 10: Goto\n"
+ "BasicBlock 5, pred: 3, succ: 2\n"
+ " 11: Goto\n"
+ "BasicBlock 6, pred: 2, succ: 7\n"
+ " 12: Return(5)\n"
+ "BasicBlock 7, pred: 6\n"
+ " 13: Exit\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 8,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::IF_EQ, 4,
+ Instruction::CONST_4 | 5 << 12 | 0,
+ Instruction::GOTO | 0xFA00,
+ Instruction::GOTO | 0xF900,
+ Instruction::RETURN | 0 << 8);
+
+ TestCode(data, expected);
+}
+
+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"
+ " 0: IntConstant 0 [5]\n"
+ " 1: IntConstant 4 [5, 8, 8]\n"
+ " 2: IntConstant 5 [12]\n"
+ " 3: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Goto\n"
+ "BasicBlock 2, pred: 1, 5, succ: 6, 3\n"
+ " 5: Phi(0, 1) [12, 6, 6]\n"
+ " 6: Equal(5, 5) [7]\n"
+ " 7: If(6)\n"
+ "BasicBlock 3, pred: 2, succ: 5, 4\n"
+ " 8: Equal(1, 1) [9]\n"
+ " 9: If(8)\n"
+ "BasicBlock 4, pred: 3, succ: 6\n"
+ " 10: Goto\n"
+ "BasicBlock 5, pred: 3, succ: 2\n"
+ " 11: Goto\n"
+ "BasicBlock 6, pred: 2, 4, succ: 7\n"
+ " 12: Phi(5, 2) [13]\n"
+ " 13: Return(12)\n"
+ "BasicBlock 7, pred: 6\n"
+ " 14: Exit\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 8,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::IF_EQ, 4,
+ Instruction::CONST_4 | 5 << 12 | 0,
+ Instruction::GOTO | 0x0200,
+ Instruction::GOTO | 0xF900,
+ Instruction::RETURN | 0 << 8);
+
+ TestCode(data, expected);
+}
+
+TEST(SsaTest, DeadLocal) {
+ // Test that we correctly handle a local not being used.
+ const char* expected =
+ "BasicBlock 0, succ: 1\n"
+ " 0: IntConstant 0\n"
+ " 1: Goto\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 2: ReturnVoid\n"
+ "BasicBlock 2, pred: 1\n"
+ " 3: Exit\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::RETURN_VOID);
+
+ TestCode(data, expected);
+}
+
+} // namespace art
diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h
index e6c53dab24..993492da6d 100644
--- a/compiler/utils/growable_array.h
+++ b/compiler/utils/growable_array.h
@@ -31,7 +31,6 @@ enum OatListKind {
kGrowableArrayDfsOrder,
kGrowableArrayDfsPostOrder,
kGrowableArrayDomPostOrderTraversal,
- kGrowableArrayThrowLaunchPads,
kGrowableArraySuspendLaunchPads,
kGrowableArraySwitchTables,
kGrowableArrayFillArrayData,
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 7d02c7c8a8..9507e1207a 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1493,7 +1493,7 @@ void X86_64Assembler::EmitOptionalRex(bool force, bool w, bool r, bool x, bool b
}
void X86_64Assembler::EmitOptionalRex32(CpuRegister reg) {
- EmitOptionalRex(false, false, reg.NeedsRex(), false, false);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
}
void X86_64Assembler::EmitOptionalRex32(CpuRegister dst, CpuRegister src) {
@@ -1540,8 +1540,9 @@ void X86_64Assembler::EmitOptionalRex32(XmmRegister dst, const Operand& operand)
}
void X86_64Assembler::EmitRex64(CpuRegister reg) {
- EmitOptionalRex(false, true, reg.NeedsRex(), false, false);
+ EmitOptionalRex(false, true, false, false, reg.NeedsRex());
}
+
void X86_64Assembler::EmitRex64(CpuRegister dst, CpuRegister src) {
EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
}