Switch-interpreter: Add new 'next' instruction field.
This field is set by handlers to the next instruction to execute.
It is initialized to fall-through and can be overwritten as needed.
As the first step, only DCHECK its value against the 'inst' field.
The next step will be to remove all the inst->Next_xxx() calls.
Test: test.py -b -r --host --64 --interpreter
Test: run-libjdwp-tests.sh --mode=host --variant=X64 --debug --no-jit
Change-Id: I92740d21871cf3a5b34d9d1615ad2a71ba021339
diff --git a/libdexfile/dex/dex_instruction-inl.h b/libdexfile/dex/dex_instruction-inl.h
index 88e9381..a1e7267 100644
--- a/libdexfile/dex/dex_instruction-inl.h
+++ b/libdexfile/dex/dex_instruction-inl.h
@@ -21,6 +21,38 @@
namespace art {
+inline constexpr size_t Instruction::SizeInCodeUnits(Format format) {
+ switch (format) {
+ case k10x:
+ case k12x:
+ case k11n:
+ case k11x:
+ case k10t: return 1;
+ case k20t:
+ case k22x:
+ case k21t:
+ case k21s:
+ case k21h:
+ case k21c:
+ case k23x:
+ case k22b:
+ case k22t:
+ case k22s:
+ case k22c: return 2;
+ case k32x:
+ case k30t:
+ case k31t:
+ case k31i:
+ case k31c:
+ case k35c:
+ case k3rc: return 3;
+ case k45cc:
+ case k4rcc: return 4;
+ case k51l: return 5;
+ case kInvalidFormat: return 0;
+ }
+}
+
//------------------------------------------------------------------------------
// VRegA
//------------------------------------------------------------------------------
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index a783301..c15fa43 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -227,6 +227,9 @@
}
}
+ // Returns the size (in 2 byte code units) of the given instruction format.
+ ALWAYS_INLINE static constexpr size_t SizeInCodeUnits(Format format);
+
// Code units required to calculate the size of the instruction.
size_t CodeUnitsRequiredForSizeComputation() const {
const int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units;
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index 00d8b9d..a487cd6 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -134,6 +134,7 @@
}
int32_t displacement =
static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc);
+ SetNextInstruction(inst->RelativeAt(displacement));
inst = inst->RelativeAt(displacement);
return false; // Stop executing this opcode and continue in the exception handler.
}
@@ -147,13 +148,15 @@
// address (for 'this' argument). Make a copy of the handler just for the slow path.
// * The modifiable fields should also be in registers, so we don't want to store their
// address even in the handler copy. Make a copy of them just for the call as well.
- const Instruction* inst_copy = inst;
- bool exit_loop_copy = exit_interpreter_loop;
+ const Instruction* inst2 = inst;
+ const Instruction* next2 = next;
+ bool exit2 = exit_interpreter_loop;
InstructionHandler<do_access_check, transaction_active, kFormat> handler_copy(
- ctx, instrumentation, self, shadow_frame, dex_pc, inst_copy, inst_data, exit_loop_copy);
+ ctx, instrumentation, self, shadow_frame, dex_pc, inst2, inst_data, next2, exit2);
bool result = handler_copy.HandlePendingExceptionWithInstrumentationImpl(instr);
- inst = inst_copy;
- exit_interpreter_loop = exit_loop_copy;
+ inst = inst2;
+ next = next2;
+ exit_interpreter_loop = exit2;
return result;
}
@@ -179,6 +182,7 @@
}
self->ClearException();
}
+ SetNextInstruction(inst);
} else if (UNLIKELY(is_exception_pending)) {
/* Should have succeeded. */
DCHECK(!shadow_frame.GetForceRetryInstruction());
@@ -186,6 +190,7 @@
return false;
}
} else {
+ SetNextInstruction(next_inst);
inst = next_inst;
}
return true;
@@ -202,6 +207,7 @@
return false;
}
} else {
+ SetNextInstruction(next_inst);
inst = next_inst;
}
return true;
@@ -422,6 +428,7 @@
return false;
}
BRANCH_INSTRUMENTATION(offset);
+ SetNextInstruction(inst->RelativeAt(offset));
inst = inst->RelativeAt(offset);
HandleBackwardBranch(offset);
return true;
@@ -467,6 +474,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
if (cond) {
BRANCH_INSTRUMENTATION(offset);
+ SetNextInstruction(inst->RelativeAt(offset));
inst = inst->RelativeAt(offset);
HandleBackwardBranch(offset);
} else {
@@ -1043,6 +1051,7 @@
ALWAYS_INLINE bool PACKED_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
BRANCH_INSTRUMENTATION(offset);
+ SetNextInstruction(inst->RelativeAt(offset));
inst = inst->RelativeAt(offset);
HandleBackwardBranch(offset);
return true;
@@ -1051,6 +1060,7 @@
ALWAYS_INLINE bool SPARSE_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
BRANCH_INSTRUMENTATION(offset);
+ SetNextInstruction(inst->RelativeAt(offset));
inst = inst->RelativeAt(offset);
HandleBackwardBranch(offset);
return true;
@@ -2173,6 +2183,7 @@
uint16_t dex_pc,
const Instruction*& inst,
uint16_t inst_data,
+ const Instruction*& next,
bool& exit_interpreter_loop)
: ctx(ctx),
instrumentation(instrumentation),
@@ -2181,6 +2192,7 @@
dex_pc(dex_pc),
inst(inst),
inst_data(inst_data),
+ next(next),
exit_interpreter_loop(exit_interpreter_loop) {
}
@@ -2212,6 +2224,12 @@
shadow_frame.SetVRegReference(i, val);
}
+ // Set the next instruction to be executed. It is the 'fall-through' instruction by default.
+ ALWAYS_INLINE void SetNextInstruction(const Instruction* next_inst) {
+ DCHECK_LT(next_inst->GetDexPc(Insns()), Accessor().InsnsSizeInCodeUnits());
+ next = next_inst;
+ }
+
SwitchImplContext* const ctx;
const instrumentation::Instrumentation* const instrumentation;
Thread* const self;
@@ -2219,6 +2237,8 @@
uint32_t const dex_pc;
const Instruction*& inst;
uint16_t const inst_data;
+ const Instruction*& next;
+
bool& exit_interpreter_loop;
};
@@ -2259,9 +2279,10 @@
TraceExecution(shadow_frame, inst, dex_pc);
inst_data = inst->Fetch16(0);
{
+ const Instruction* next = nullptr;
bool exit_loop = false;
InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat> handler(
- ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop);
+ ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit_loop);
if (!handler.Preamble()) {
if (UNLIKELY(exit_loop)) {
return;
@@ -2273,16 +2294,18 @@
}
}
switch (inst->Opcode(inst_data)) {
-#define OPCODE_CASE(OPCODE, OPCODE_NAME, pname, FORMAT, i, a, e, v) \
+#define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v) \
case OPCODE: { \
+ size_t inst_size = Instruction::SizeInCodeUnits(Instruction::FORMAT); \
+ const Instruction* next = inst->RelativeAt(inst_size); \
bool exit_loop = false; \
InstructionHandler<do_access_check, transaction_active, Instruction::FORMAT> handler( \
- ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop); \
+ ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit_loop); \
handler.OPCODE_NAME(); \
- /* TODO: Advance 'inst' here, instead of explicitly in each handler */ \
if (UNLIKELY(exit_loop)) { \
return; \
} \
+ DCHECK_EQ(next, inst) << NAME; \
break; \
}
DEX_INSTRUCTION_LIST(OPCODE_CASE)