summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/verifier/instruction_flags.cc15
-rw-r--r--runtime/verifier/instruction_flags.h17
-rw-r--r--runtime/verifier/method_verifier.cc37
-rw-r--r--runtime/verifier/register_line.cc27
-rw-r--r--runtime/verifier/register_line.h7
-rw-r--r--test/ReferenceMap/stack_walk_refmap_jni.cc6
6 files changed, 97 insertions, 12 deletions
diff --git a/runtime/verifier/instruction_flags.cc b/runtime/verifier/instruction_flags.cc
index 358791d..f76c226 100644
--- a/runtime/verifier/instruction_flags.cc
+++ b/runtime/verifier/instruction_flags.cc
@@ -22,16 +22,17 @@ namespace art {
namespace verifier {
std::string InstructionFlags::ToString() const {
- char encoding[6];
+ char encoding[7];
if (!IsOpcode()) {
- strncpy(encoding, "XXXXX", sizeof(encoding));
+ strncpy(encoding, "XXXXXX", sizeof(encoding));
} else {
- strncpy(encoding, "-----", sizeof(encoding));
- if (IsInTry()) encoding[kInTry] = 'T';
- if (IsBranchTarget()) encoding[kBranchTarget] = 'B';
+ strncpy(encoding, "------", sizeof(encoding));
+ if (IsVisited()) encoding[kVisited] = 'V';
+ if (IsChanged()) encoding[kChanged] = 'C';
+ if (IsInTry()) encoding[kInTry] = 'T';
+ if (IsBranchTarget()) encoding[kBranchTarget] = 'B';
if (IsCompileTimeInfoPoint()) encoding[kCompileTimeInfoPoint] = 'G';
- if (IsVisited()) encoding[kVisited] = 'V';
- if (IsChanged()) encoding[kChanged] = 'C';
+ if (IsReturn()) encoding[kReturn] = 'R';
}
return encoding;
}
diff --git a/runtime/verifier/instruction_flags.h b/runtime/verifier/instruction_flags.h
index 9b2e595..e50ba13 100644
--- a/runtime/verifier/instruction_flags.h
+++ b/runtime/verifier/instruction_flags.h
@@ -93,6 +93,21 @@ class InstructionFlags {
return IsVisited() || IsChanged();
}
+ void SetReturn() {
+ flags_ |= 1 << kReturn;
+ }
+ void ClearReturn() {
+ flags_ &= ~(1 << kReturn);
+ }
+ bool IsReturn() const {
+ return (flags_ & (1 << kReturn)) != 0;
+ }
+
+ void SetCompileTimeInfoPointAndReturn() {
+ SetCompileTimeInfoPoint();
+ SetReturn();
+ }
+
std::string ToString() const;
private:
@@ -108,6 +123,8 @@ class InstructionFlags {
kBranchTarget = 3,
// Location of interest to the compiler for GC maps and verifier based method sharpening.
kCompileTimeInfoPoint = 4,
+ // A return instruction.
+ kReturn = 5,
};
// Size of instruction in code units.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 2bf78d8..e182af7 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -566,8 +566,10 @@ bool MethodVerifier::VerifyInstructions() {
/* Flag instructions that are garbage collection points */
// All invoke points are marked as "Throw" points already.
// We are relying on this to also count all the invokes as interesting.
- if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow() || inst->IsReturn()) {
+ if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow()) {
insn_flags_[dex_pc].SetCompileTimeInfoPoint();
+ } else if (inst->IsReturn()) {
+ insn_flags_[dex_pc].SetCompileTimeInfoPointAndReturn();
}
dex_pc += inst->SizeInCodeUnits();
inst = inst->Next();
@@ -2656,6 +2658,20 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
// Make workline consistent with fallthrough computed from peephole optimization.
work_line_->CopyFromLine(fallthrough_line.get());
}
+ if (insn_flags_[next_insn_idx].IsReturn()) {
+ // For returns we only care about the operand to the return, all other registers are dead.
+ const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx);
+ Instruction::Code opcode = ret_inst->Opcode();
+ if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
+ work_line_->MarkAllRegistersAsConflicts();
+ } else {
+ if (opcode == Instruction::RETURN_WIDE) {
+ work_line_->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+ } else {
+ work_line_->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+ }
+ }
+ }
RegisterLine* next_line = reg_table_.GetLine(next_insn_idx);
if (next_line != NULL) {
// Merge registers into what we have for the next instruction,
@@ -3643,7 +3659,24 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, const RegisterLine* mer
* there's nothing to "merge". Copy the registers over and mark it as changed. (This is the
* only way a register can transition out of "unknown", so this is not just an optimization.)
*/
- target_line->CopyFromLine(merge_line);
+ if (!insn_flags_[next_insn].IsReturn()) {
+ target_line->CopyFromLine(merge_line);
+ } else {
+ // For returns we only care about the operand to the return, all other registers are dead.
+ // Initialize them as conflicts so they don't add to GC and deoptimization information.
+ const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn);
+ Instruction::Code opcode = ret_inst->Opcode();
+ if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
+ target_line->MarkAllRegistersAsConflicts();
+ } else {
+ target_line->CopyFromLine(merge_line);
+ if (opcode == Instruction::RETURN_WIDE) {
+ target_line->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+ } else {
+ target_line->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+ }
+ }
+ }
} else {
UniquePtr<RegisterLine> copy(gDebugVerify ? new RegisterLine(target_line->NumRegs(), this) : NULL);
if (gDebugVerify) {
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index d2abaac..7965c06 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -167,7 +167,7 @@ void RegisterLine::MarkRefsAsInitialized(const RegType& uninit_type) {
DCHECK(uninit_type.IsUninitializedTypes());
const RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
size_t changed = 0;
- for (size_t i = 0; i < num_regs_; i++) {
+ for (uint32_t i = 0; i < num_regs_; i++) {
if (GetRegisterType(i).Equals(uninit_type)) {
line_[i] = init_type.GetId();
changed++;
@@ -176,6 +176,31 @@ void RegisterLine::MarkRefsAsInitialized(const RegType& uninit_type) {
DCHECK_GT(changed, 0u);
}
+void RegisterLine::MarkAllRegistersAsConflicts() {
+ uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+ for (uint32_t i = 0; i < num_regs_; i++) {
+ line_[i] = conflict_type_id;
+ }
+}
+
+void RegisterLine::MarkAllRegistersAsConflictsExcept(uint32_t vsrc) {
+ uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+ for (uint32_t i = 0; i < num_regs_; i++) {
+ if (i != vsrc) {
+ line_[i] = conflict_type_id;
+ }
+ }
+}
+
+void RegisterLine::MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc) {
+ uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+ for (uint32_t i = 0; i < num_regs_; i++) {
+ if ((i != vsrc) && (i != (vsrc + 1))) {
+ line_[i] = conflict_type_id;
+ }
+ }
+}
+
std::string RegisterLine::Dump() const {
std::string result;
for (size_t i = 0; i < num_regs_; i++) {
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index cde7b9b..f380877 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -141,6 +141,13 @@ class RegisterLine {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
+ * Update all registers to be Conflict except vsrc.
+ */
+ void MarkAllRegistersAsConflicts();
+ void MarkAllRegistersAsConflictsExcept(uint32_t vsrc);
+ void MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc);
+
+ /*
* Check constraints on constructor return. Specifically, make sure that the "this" argument got
* initialized.
* The "this" argument to <init> uses code offset kUninitThisArgAddr, which puts it at the start
diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/ReferenceMap/stack_walk_refmap_jni.cc
index 7b6483d..885a658 100644
--- a/test/ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/ReferenceMap/stack_walk_refmap_jni.cc
@@ -103,7 +103,9 @@ struct ReferenceMap2Visitor : public StackVisitor {
// 0024: move-object v3, v2
// 0025: goto 0013
// Detaled dex instructions for ReferenceMap.java are at the end of this function.
- CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1); // v8: this, v3: y, v2: y, v1: x
+ //CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1); // v8: this, v3: y, v2: y, v1: x
+ // We eliminate the non-live registers at a return, so only v3 is live:
+ CHECK_REGS_CONTAIN_REFS(3); // v3: y
ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToFirstNativeSafepointPc(0x18U)));
CHECK(ref_bitmap);
@@ -188,7 +190,7 @@ struct ReferenceMap2Visitor : public StackVisitor {
//0:[Unknown],1:[Reference: java.lang.Object[]],2:[Zero],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap],
// |0010: +invoke-virtual-quick {v8, v7}, [000c] // vtable #000c
-//0:[Conflict],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap],
+//0:[Conflict],1:[Conflict],2:[Conflict],3:[Reference: java.lang.Object],4:[Conflict],5:[Conflict],6:[Conflict],7:[Conflict],8:[Conflict],
// |0013: return-object v3
// |0014: move-exception v0