Files
SDK_RK3288/art/compiler/dex/reference_map_calculator.cc

291 lines
12 KiB
C++

/*
* 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/bit_vector-inl.h"
#include "base/casts.h"
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
#include "dex/verified_method.h"
#include "reference_map_calculator.h"
#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
#include <algorithm>
#include <sstream>
namespace art {
bool ReferenceMapCalculator::Gate(const PassDataHolder* data) const {
const PassMEDataHolder* me_data = down_cast<const PassMEDataHolder*>(data);
MIRGraph* mir_graph = me_data->c_unit->mir_graph.get();
return mir_graph->NeedGcMapRecalculation();
}
void ReferenceMapCalculator::LogReferenceMap(CompilationUnit* c_unit, ReferenceMapDataHolder* ref_data) const {
if (c_unit->dex_file == nullptr) {
LOG(INFO) << "ReferenceMapCalculator: Reference map is:";
} else {
LOG(INFO) << "ReferenceMapCalculator: Reference map for "
<< PrettyMethod(c_unit->method_idx, *c_unit->dex_file) << " is:";
}
for (auto it : ref_data->mir_to_ref_vrs) {
std::stringstream ss;
for (int32_t vr : it.second->Indexes()) {
ss << " v" << vr;
}
LOG(INFO) << "\tReferenceMapCalculator: " << c_unit->mir_graph->GetDalvikDisassembly(it.first)
<< "@0x" << std::hex << it.first->offset << ":" << ss.str();
}
}
void ReferenceMapCalculator::CheckAndFixOffsetProblem(MIRGraph* mir_graph, ReferenceMapDataHolder* ref_data) const {
std::map<DexOffset, std::set<MIR*> > offset_to_mirs;
for (auto it : ref_data->mir_to_ref_vrs) {
offset_to_mirs[it.first->offset].insert(it.first);
}
for (auto it : offset_to_mirs) {
if (it.second.size() > 1) {
// Get the information from the first MIR. We will pick this one as baseline.
std::set<MIR*>::iterator mir_it = it.second.begin();
ArenaBitVector* first_map = ref_data->mir_to_ref_vrs.Get(*mir_it);
// Walk through each instruction. If its map doesn't match the first one, give it a new offset.
for (mir_it = std::next(mir_it); mir_it != it.second.end(); mir_it++) {
MIR* mir = (*mir_it);
ArenaBitVector* map = ref_data->mir_to_ref_vrs.Get(mir);
if (first_map->Equal(map) == false) {
// Since the maps don't match, assign new offset for instruction.
DexOffset new_offset = mir_graph->GetNewOffset();
// Since MIRs only hold narrow offsets, we need to check that we don't exceed.
CHECK_LE(new_offset, std::numeric_limits<uint16_t>::max());
mir->offset = new_offset;
}
}
}
}
}
void ReferenceMapCalculator::AddGCMapsToMirGraph(MIRGraph* mir_graph, ReferenceMapDataHolder* ref_data) const {
for (auto it : ref_data->mir_to_ref_vrs) {
mir_graph->RecordNewMirGCMap(it.first, it.second);
}
}
void ReferenceMapCalculator::CrossCheckGCMap(MIRGraph* mir_graph, ReferenceMapDataHolder* ref_data) const {
const std::vector<uint8_t>& gc_map_raw =
mir_graph->GetCurrentDexCompilationUnit()->GetVerifiedMethod()->GetDexGcMap();
verifier::DexPcToReferenceMap dex_gc_map(&(gc_map_raw)[0]);
size_t verifier_map_entry_size = dex_gc_map.RegWidth();
size_t compiler_calculated_entry_size = mir_graph->GetGCMapEntrySize();
// The compiler should have made a map that is at least the size of the original.
DCHECK_GE(compiler_calculated_entry_size, verifier_map_entry_size);
for (auto it : ref_data->mir_to_ref_vrs) {
MIR* mir = it.first;
const uint8_t* verifier_raw_map = dex_gc_map.FindBitMap(mir->offset, false);
const uint8_t* compiler_raw_map = mir_graph->GetGCMap(mir->offset, false);
for (size_t entry = 0; entry < compiler_calculated_entry_size; entry++) {
uint32_t compiler_map_entry = compiler_raw_map == nullptr ? 0 : compiler_raw_map[entry];
uint32_t verifier_map_entry = entry >= verifier_map_entry_size ?
0 : (verifier_raw_map == nullptr ? 0 : verifier_raw_map[entry]);
if (compiler_map_entry != verifier_map_entry) {
LOG(INFO) << "ReferenceMapCalculator: Found mismatched map entries @0x" << std::hex
<< mir->offset << ". At index " << entry << " found compiler map: 0x"
<< std::hex << compiler_map_entry << " and verifier map: 0x" << std::hex
<< verifier_map_entry;
}
}
}
}
bool ReferenceMapCalculator::HasSafepoint(MIRGraph* mir_graph, MIR* mir, ReferenceMapDataHolder* ref_data) const {
// The point of this helper is to allow skipping MIRs that are broken into a check half.
// This is because the real dataflow is expressed via the check half.
auto it = ref_data->mirs_with_check.find(mir);
if (it != ref_data->mirs_with_check.end()) {
// This has a check half. The "safepoints" should be captured by the check so return false.
return false;
} else {
return mir_graph->HasSafepoint(mir);
}
}
void ReferenceMapCalculator::FindCheckInstructions(BasicBlock* bb, ReferenceMapDataHolder* ref_data) const {
// Check instructions must be the last instructions of a block (since they are needed
// to express the control flow to catch blocks.
if (bb->last_mir_insn != nullptr) {
MIR* last_insn = bb->last_mir_insn;
if (static_cast<int>(last_insn->dalvikInsn.opcode) == kMirOpCheck) {
// Store the mapping of the thrower to its matching check.
MIR* throw_insn = last_insn->meta.throw_insn;
ref_data->mirs_with_check.insert(throw_insn);
}
}
}
/**
* @brief Used to calculate the reference maps for instructions in basic block.
* @details The algorithm that is used is as follows:
* -#) Go through the successors and calculate union of all live-in vrs.
* -#) Determine the current BB live out ssa registers by using the successor
* live-in information.
* -#) Record only the set of reference ssa registers. Keep this as working set.
* -#) Walk through the instructions backwards.
* -#) For each instruction, remove the define from working set. But add all
* reference ssa registers to the working set. This covers all live ssa reference
* registers up to this point.
* @param data The pass data.
* @return Always returns false because it does not change the control flow graph.
*/
bool ReferenceMapCalculator::CalculateBBRefInfo(MIRGraph* mir_graph, BasicBlock* bb,
ReferenceMapDataHolder* ref_data) const {
if (bb->last_mir_insn == nullptr) {
// No instructions and thus nothing to check here.
return false;
}
// We want to go backwards through the block but the MIR does not provide a previous pointer.
// So we walk forwards to insert into our vector (which we can then iterate through backwards).
// At the same time, we check if any instruction has safepoint semantics so we don't waste time
// computing the object registers live at end of block.
bool has_safepoints = false;
std::vector<MIR*> instructions;
for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
instructions.push_back(mir);
if (HasSafepoint(mir_graph, mir, ref_data)) {
has_safepoints = true;
}
}
if (has_safepoints == false) {
// No instructions with safepoint and thus nothing to compute here.
return false;
}
// Compute the set of live out ssa registers holding objects.
// Do this by going through all successor live ins, determining which ssa was live out from current
// block, and then tracking only if object.
std::set<int32_t> ref_ssa_regs;
ChildBlockIterator child_iter(bb, mir_graph);
for (BasicBlock* child = child_iter.Next(); child != nullptr; child = child_iter.Next()) {
CHECK(child->data_flow_info != nullptr);
for (int32_t vr : child->data_flow_info->live_in_v->Indexes()) {
int32_t live_out_ssa = bb->data_flow_info->vreg_to_ssa_map_exit[vr];
// If it is a reference, add it to our set.
if (mir_graph->IsObjectRegister(live_out_ssa) == true) {
ref_ssa_regs.insert(live_out_ssa);
}
}
}
// Go through instruction backwards.
for (auto it = instructions.rbegin(); it != instructions.rend(); it++) {
MIR* mir = *it;
// We must have ssa information for each mir.
CHECK(mir->ssa_rep != nullptr);
// The define is always last part of MIR. Thus we need to remove the
// defines from set of what is live up to this point.
for (int16_t def = 0; def < mir->ssa_rep->num_defs; def++) {
int32_t def_ssa = mir->ssa_rep->defs[def];
ref_ssa_regs.erase(def_ssa);
}
// Since the define may have clobbered an existing register, we ensure
// to add all reference uses of this MIR as being live up to this point.
for (int16_t use = 0; use < mir->ssa_rep->num_uses; use++) {
int32_t use_ssa = mir->ssa_rep->uses[use];
if (mir_graph->IsObjectRegister(use_ssa) == true) {
ref_ssa_regs.insert(use_ssa);
}
}
// Only record the GC maps for instructions that will have safepoint.
if (HasSafepoint(mir_graph, mir, ref_data)) {
// Create a new bitvector to hold the bits for references.
ArenaBitVector* vrs = new (mir_graph->GetArena()) ArenaBitVector(mir_graph->GetArena(),
mir_graph->GetNumOfCodeAndTempVRs(),
false);
vrs->ClearAllBits();
for (int32_t live_ssa_reg : ref_ssa_regs) {
int32_t vr = mir_graph->SRegToVReg(live_ssa_reg);
vrs->SetBit(vr);
}
// Finish off by inserting it into the tracker.
ref_data->mir_to_ref_vrs.Put(mir, vrs);
}
}
// There are never any updates to the CFG itself.
return false;
}
void ReferenceMapCalculator::Start(PassDataHolder* data) const {
PassMEDataHolder* me_data = down_cast<PassMEDataHolder*>(data);
CompilationUnit* c_unit = me_data->c_unit;
MIRGraph* mir_graph = c_unit->mir_graph.get();
ReferenceMapDataHolder ref_data;
// Now iterate through all of the blocks to:
// 1) Find all kMirOpCheck instructions. This is needed so that we can properly
// capture where the "safepoint" is since codegen will end up moving the real
// instruction in the place of the check.
// 2) Calculate the reference information for each block.
AllNodesIterator iter(mir_graph);
for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
FindCheckInstructions(bb, &ref_data);
}
iter.Reset();
for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
CalculateBBRefInfo(mir_graph, bb, &ref_data);
}
// Now fix any offset problems (overlapping offsets).
CheckAndFixOffsetProblem(mir_graph, &ref_data);
// Add the maps to the MIRGraph.
AddGCMapsToMirGraph(mir_graph, &ref_data);
// If verbosity is enabled, log the maps to logcat for manual verification.
// Also print out differences by doing a cross check against the real GC map.
if (c_unit->print_pass) {
LogReferenceMap(c_unit, &ref_data);
// Cross check GC map if requested.
if (GetIntegerPassOption("CrossCheckMaps", c_unit) != 0) {
CrossCheckGCMap(mir_graph, &ref_data);
}
}
// Inform MIRGraph that it no longer needs to generate new maps (since they have
// just been generated for current CFG).
mir_graph->ChangeGCMapRecalculationState(false);
}
} // namespace art