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

464 lines
17 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 "bb_optimizations.h"
#include "compiler_internals.h"
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
#include "global_value_numbering.h"
#include "local_value_numbering.h"
#include "mir_graph_test.h"
#include "pass_driver.h"
#include "pass_me.h"
#include "reference_map_calculator.h"
namespace art {
class ReferenceMapCalculatorTest : public MirGraphTest {
public:
struct MIRDef {
BasicBlockId bbid;
NarrowDexOffset offset;
int opcode;
uint32_t vA;
uint32_t vB;
uint32_t vC;
};
struct RefInfo {
static constexpr size_t kMaxRefs = 4;
size_t num_refs;
uint32_t refs[kMaxRefs];
};
void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
cu_.mir_graph->block_id_map_.clear();
cu_.mir_graph->block_list_.Reset();
// There are at least 4 blocks: null, entry, exit and at least one bytecode block.
ASSERT_LE(4u, count);
ASSERT_EQ(kNullBlock, defs[0].type);
ASSERT_EQ(kEntryBlock, defs[1].type);
ASSERT_EQ(kExitBlock, defs[2].type);
for (size_t i = 0u; i != count; ++i) {
const BBDef* def = &defs[i];
BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : NullBasicBlockId;
bb->taken = (def->num_successors >= 2) ? def->successors[1] : NullBasicBlockId;
if (def->num_successors > 2) {
bb->successor_block_list_type = kCatch;
bb->successor_blocks->Resize(def->num_successors);
for (size_t j = 2u; j != def->num_successors; ++j) {
SuccessorBlockInfo* successor_block_info =
static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
kArenaAllocSuccessor));
successor_block_info->block = def->successors[j];
successor_block_info->key = j; // Not used by reference map calculator.
bb->successor_blocks->Insert(successor_block_info);
}
}
bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
&cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
for (size_t j = 0u; j != def->num_predecessors; ++j) {
ASSERT_NE(0u, def->predecessors[j]);
bb->predecessors->Insert(def->predecessors[j]);
}
}
cu_.mir_graph->num_blocks_ = count;
ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
}
template <size_t count>
void PrepareBasicBlocks(const BBDef (&defs)[count]) {
DoPrepareBasicBlocks(defs, count);
}
void DoPrepareMIRs(const MIRDef* defs, size_t count) {
mir_count_ = count;
mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR));
for (size_t i = 0u; i != count; ++i) {
const MIRDef* def = &defs[i];
MIR* mir = &mirs_[i];
ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.Size());
BasicBlock* bb = cu_.mir_graph->block_list_.Get(def->bbid);
bb->AppendMIR(mir);
mir->dalvikInsn.opcode = static_cast<Instruction::Code>(def->opcode);
mir->dalvikInsn.vA = def->vA;
mir->dalvikInsn.vB = def->vB;
mir->dalvikInsn.vC = def->vC;
mir->offset = def->offset;
if (def->opcode == kMirOpCheck) {
DCHECK_NE(i + 1, count);
mir->meta.throw_insn = &mirs_[i + 1];
}
if (mir->dalvikInsn.IsInvoke()) {
mir->dalvikInsn.arg[0] = mir->dalvikInsn.vC;
}
}
DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
code_item->insns_size_in_code_units_ = 2u * count;
code_item->registers_size_ = 7;
cu_.mir_graph->current_code_item_ = code_item;
}
template <size_t count>
void PrepareMIRs(const MIRDef (&defs)[count]) {
DoPrepareMIRs(defs, count);
}
void DoPrepareShortys(const char** shortys, size_t count) {
cu_.mir_graph->shorty_for_test_ = reinterpret_cast<const char**>(cu_.arena.Alloc(sizeof(char*) * count,
kArenaAllocMisc));
for (size_t i = 0u; i != count; ++i) {
cu_.mir_graph->shorty_for_test_[i] = shortys[i];
if (i == 0u) {
// The first shorty is the caller's shorty, so update the CompilationUnit
// to also hold this information.
cu_.shorty = shortys[0];
}
}
}
template <size_t count>
void PrepareShortys(const char* (&shortys)[count]) {
DoPrepareShortys(shortys, count);
}
void DoPrepareRefMaps(const RefInfo* ref_infos, size_t count) {
ref_maps = reinterpret_cast<ArenaBitVector**>(cu_.arena.Alloc(sizeof(ArenaBitVector*) * count, kArenaAllocRefMaps));
ASSERT_EQ(count, mir_count_);
for (size_t i = 0u; i != count; ++i) {
const RefInfo* ref_info = &ref_infos[i];
if (ref_info->num_refs > 0) {
ref_maps[i] = new (&(cu_.arena)) ArenaBitVector(&(cu_.arena), RefInfo::kMaxRefs, false);
ref_maps[i]->ClearAllBits();
for (size_t j = 0u; j != ref_info->num_refs; j++) {
ref_maps[i]->SetBit(ref_info->refs[j]);
}
}
}
}
template <size_t count>
void PrepareRefMaps(const RefInfo (&ref_infos)[count]) {
DoPrepareRefMaps(ref_infos, count);
}
template <typename Iterator>
void WalkBasicBlocks(PassMEDataHolder* data, const Pass* pass) {
DCHECK(data != nullptr);
CompilationUnit* c_unit = data->c_unit;
DCHECK(c_unit != nullptr);
Iterator iterator(c_unit->mir_graph.get());
bool change = false;
for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
data->bb = bb;
change = pass->Worker(data);
}
}
void DoCalculateReferenceMaps() {
// Disable NCE because all we need is type inference.
cu_.disable_opt |= (1 << kNullCheckElimination);
// Mark MIRGraph as needing GC map recalculation.
cu_.mir_graph->ChangeGCMapRecalculationState(true);
// Run the post-opts that initialize all of the data structures including ssa.
cu_.mir_graph->CalculateBasicBlockInformation();
// Prepare the data holder to be able to run the ME passes.
PassMEDataHolder data_holder;
data_holder.c_unit = &cu_;
// First run the type inference because it is required for GC map calculation.
const Pass* type_pass = GetPassInstance<TypeInference>();
type_pass->Start(&data_holder);
WalkBasicBlocks<RepeatingPreOrderDfsIterator>(&data_holder, type_pass);
type_pass->End(&data_holder);
// Now run the reference map calculator.
const Pass* ref_map_pass = GetPassInstance<ReferenceMapCalculator>();
ref_map_pass->Start(&data_holder);
ref_map_pass->End(&data_holder);
// Finally, compare the reference maps to those generated by the pass.
for (size_t i = 0u; i < mir_count_; ++i) {
const ArenaBitVector* test_map = ref_maps[i];
const ArenaBitVector* compiler_map = cu_.mir_graph->GetGCMapAsBitVector(mirs_[i].offset);
// Empty map could be either not allocated or have no bits set. Make sure both cases are accounted for.
bool test_map_empty = test_map == nullptr ||
(test_map != nullptr && test_map->GetHighestBitSet() < 0);
bool compiler_map_empty = compiler_map == nullptr ||
(compiler_map != nullptr && compiler_map->GetHighestBitSet() < 0);
ASSERT_EQ(test_map_empty, compiler_map_empty);
if (test_map != nullptr && compiler_map != nullptr) {
ASSERT_TRUE(test_map->Equal(compiler_map));
}
}
}
ReferenceMapCalculatorTest() :
MirGraphTest(), pool_(),
cu_(&pool_),
mir_count_(0u),
mirs_(nullptr),
ref_maps(nullptr) {
cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
cu_.access_flags = kAccPrivate;
}
ArenaPool pool_;
CompilationUnit cu_;
size_t mir_count_;
MIR* mirs_;
ArenaBitVector** ref_maps;
#define DEF_MIR(bb, offset, opcode, vA, vB, vC) \
{ bb, offset, opcode, vA, vB, vC }
#define DEF_REFS0() \
{ 0u, { } }
#define DEF_REFS1(v1) \
{ 1u, { v1 } }
#define DEF_REFS2(v1, v2) \
{ 2u, { v1, v2 } }
#define DEF_REFS3(v1, v2, v3) \
{ 3u, { v1, v2, v3 } }
#define DEF_REFS4(v1, v2, v3, v4) \
{ 4u, { v1, v2, v3, v4 } }
};
class ReferenceMapUnitTest004 : public ReferenceMapCalculatorTest {
public:
ReferenceMapUnitTest004();
private:
static const BBDef kRefTest004bbs[];
static const MIRDef kRefTest004mirs[];
static const char* kRefTest004shortys[];
static const RefInfo kRefTest004refs[];
};
/**
* @details This is the CFG for the following java code:
*
* private Object runTest() {
* Object x[] = new Object[2];
* Object y = null;
* try {
* y = new Object();
* x[2] = y; // out-of-bound exception
* } catch(Exception ex) {
* if (y == null) {
* x[1] = new Object();
* }
* } finally {
* x[1] = y;
* };
* return y;
* }
*/
const ReferenceMapUnitTest004::BBDef ReferenceMapUnitTest004::kRefTest004bbs[] = {
/* BB00 */ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
/* BB01 */ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
/* BB02 */ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(16)),
/* BB03 */ DEF_BB(kDalvikByteCode, DEF_SUCC4(8, 0, 4, 5), DEF_PRED1(1)),
/* BB04 */ DEF_BB(kDalvikByteCode, DEF_SUCC1(19), DEF_PRED2(3, 8)),
/* BB05 */ DEF_BB(kDalvikByteCode, DEF_SUCC1(18), DEF_PRED2(3, 8)),
/* BB06 */ DEF_BB(kDalvikByteCode, DEF_SUCC2(0, 18), DEF_PRED4(9, 12, 13, 14)),
/* BB07 */ DEF_BB(kDalvikByteCode, DEF_SUCC2(0, 19), DEF_PRED1(9)),
/* BB08 */ DEF_BB(kDalvikByteCode, DEF_SUCC4(9, 0, 4, 5), DEF_PRED1(3)),
/* BB09 */ DEF_BB(kDalvikByteCode, DEF_SUCC4(10, 0, 7, 6), DEF_PRED1(8)),
/* BB10 */ DEF_BB(kDalvikByteCode, DEF_SUCC1(16), DEF_PRED1(9)),
/* BB11 */ DEF_BB(kDalvikByteCode, DEF_SUCC2(0, 16), DEF_PRED2(15, 19)),
/* BB12 */ DEF_BB(kDalvikByteCode, DEF_SUCC3(13, 0, 6), DEF_PRED1(19)),
/* BB13 */ DEF_BB(kDalvikByteCode, DEF_SUCC3(14, 0, 6), DEF_PRED1(12)),
/* BB14 */ DEF_BB(kDalvikByteCode, DEF_SUCC3(15, 0, 6), DEF_PRED1(13)),
/* BB15 */ DEF_BB(kDalvikByteCode, DEF_SUCC1(11), DEF_PRED1(14)),
/* BB16 */ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(10, 11)),
/* BB17 */ DEF_BB(kExceptionHandling, DEF_SUCC0(), DEF_PRED1(18)),
/* BB18 */ DEF_BB(kDalvikByteCode, DEF_SUCC1(17), DEF_PRED2(5, 6)),
/* BB19 */ DEF_BB(kDalvikByteCode, DEF_SUCC2(12, 11), DEF_PRED2(4, 7)),
};
/**
* @details
* 0x0000: const/4 v0, #+2
* 0x0001: const/4 v4, #+1
* 0x0002: new-array v2, v0, java.lang.Object[] // type@9
* 0x0004: const/4 v1, #+0
* 0x0005: new-instance v0, java.lang.Object // type@4
* 0x0007: invoke-direct {v0}, void java.lang.Object.<init>() // method@4
* 0x000a: const/4 v1, #+2
* 0x000b: aput-object v0, v2, v1
* 0x000d: aput-object v0, v2, v4
* 0x000f: return-object v0
* 0x0010: move-exception v0
* 0x0011: move-object v0, v1
* 0x0012: if-nez v0, +10
* 0x0014: const/4 v1, #+1
* 0x0015: new-instance v3, java.lang.Object // type@4
* 0x0017: invoke-direct {v3}, void java.lang.Object.<init>() // method@4
* 0x001a: aput-object v3, v2, v1
* 0x001c: aput-object v0, v2, v4
* 0x001e: goto -15
* 0x001f: move-exception v0
* 0x0020: aput-object v1, v2, v4
* 0x0022: throw v0
* 0x0023: move-exception v1
* 0x0024: move-object v5, v1
* 0x0025: move-object v1, v0
* 0x0026: move-object v0, v5
* 0x0027: goto -7
* 0x0028: move-exception v1
* 0x0029: goto -23
*
* Note that for invokes we put type as 1 instead of 4 as in original java test case,
* so we can create a shorty cache.
*/
const ReferenceMapUnitTest004::MIRDef ReferenceMapUnitTest004::kRefTest004mirs[] = {
DEF_MIR(3, 0x0, Instruction::CONST_4, 0, 2, 0),
DEF_MIR(3, 0x1, Instruction::CONST_4, 4, 1, 0),
DEF_MIR(3, 0x2, Instruction::NEW_ARRAY, 2, 0, 9),
DEF_MIR(3, 0x4, Instruction::CONST_4, 1, 0, 0),
DEF_MIR(3, 0x5, kMirOpCheck, 0, 0, 0),
DEF_MIR(8, 0x5, Instruction::NEW_INSTANCE, 0, 4, 0),
DEF_MIR(8, 0x7, kMirOpCheck, 0, 0, 0),
DEF_MIR(9, 0x7, Instruction::INVOKE_DIRECT, 1, 1, 0),
DEF_MIR(9, 0xa, Instruction::CONST_4, 1, 2, 0),
DEF_MIR(9, 0xb, kMirOpCheck, 0, 0, 0),
DEF_MIR(10, 0xb, Instruction::APUT_OBJECT, 0, 2, 1),
DEF_MIR(10, 0xd, Instruction::APUT_OBJECT, 0, 2, 4),
DEF_MIR(16, 0xf, Instruction::RETURN_OBJECT, 0, 0, 0),
DEF_MIR(4, 0x10, Instruction::MOVE_EXCEPTION, 0, 0, 0),
DEF_MIR(4, 0x11, Instruction::MOVE_OBJECT, 0, 1, 0),
DEF_MIR(19, 0x12, Instruction::IF_NEZ, 0, 10, 0),
DEF_MIR(12, 0x14, Instruction::CONST_4, 1, 1, 0),
DEF_MIR(12, 0x15, kMirOpCheck, 0, 0, 0),
DEF_MIR(13, 0x15, Instruction::NEW_INSTANCE, 3, 4, 0),
DEF_MIR(13, 0x17, kMirOpCheck, 0, 0, 0),
DEF_MIR(14, 0x17, Instruction::INVOKE_DIRECT, 1, 1, 3),
DEF_MIR(14, 0x1a, kMirOpCheck, 0, 0, 0),
DEF_MIR(15, 0x1a, Instruction::APUT_OBJECT, 3, 2, 1),
DEF_MIR(11, 0x1c, Instruction::APUT_OBJECT, 0, 2, 4),
DEF_MIR(11, 0x1e, Instruction::GOTO, 0xfffffff1, 0, 0),
DEF_MIR(5, 0x1f, Instruction::MOVE_EXCEPTION, 0, 0, 0),
DEF_MIR(18, 0x20, Instruction::APUT_OBJECT, 1, 2, 4),
DEF_MIR(18, 0x22, Instruction::THROW, 0, 0, 0),
DEF_MIR(6, 0x23, Instruction::MOVE_EXCEPTION, 1, 0, 0),
DEF_MIR(6, 0x24, Instruction::MOVE_OBJECT, 5, 1, 0),
DEF_MIR(6, 0x25, Instruction::MOVE_OBJECT, 1, 0, 0),
DEF_MIR(6, 0x26, Instruction::MOVE_OBJECT, 0, 5, 0),
DEF_MIR(6, 0x27, Instruction::GOTO, 0xfffffff9, 0, 0),
DEF_MIR(7, 0x28, Instruction::MOVE_EXCEPTION, 1, 0, 0),
DEF_MIR(7, 0x29, Instruction::GOTO, 0xffffffe9, 0, 0),
};
/**
* @details Entry "0" represents ourselves. Rest of entries represent
* methods being called.
*/
const char* ReferenceMapUnitTest004::kRefTest004shortys[] = {
"LL", // ()Ljava/lang/Object; - "this" is first argument.
"V", // ()V
};
/**
* @details
* new-array v2_1, v0_1, index #0x9@0x2:
* Check1: new-instance v0_2, index #0x4@0x5: v1 v2
* Check1: invoke-direct v0_2@0x7: v0 v1 v2
* Check1: aput-object v0_2, v2_1, v1_2@0xb: v0 v2
* aput-object v0_2, v2_1, v4_1@0xd: v0 v2
* return-object v0_3@0xf: v0
* move-exception v0_8@0x10: v1 v2
* if-nez v0_4, 0x1c (+a)@0x12: v0 v2
* Check1: new-instance v3_1, index #0x4@0x15: v0 v2
* Check1: invoke-direct v3_1@0x17: v0 v2 v3
* Check1: aput-object v3_1, v2_1, v1_4@0x1a: v0 v2 v3
* aput-object v0_4, v2_1, v4_1@0x1c: v0 v2
* goto, 0xf (-f)@0x1e: v0
* move-exception v0_10@0x1f: v1 v2
* aput-object v1_7, v2_1, v4_1@0x20: v0 v1 v2
* throw v0_7@0x22: v0
* move-exception v1_5@0x23: v0 v2
* goto, 0x20 (-7)@0x27: v0 v1 v2
* move-exception v1_3@0x28: v0 v2
* goto, 0x12 (-17)@0x29: v0 v2
*/
const ReferenceMapUnitTest004::RefInfo ReferenceMapUnitTest004::kRefTest004refs[] = {
DEF_REFS0(),
DEF_REFS0(),
DEF_REFS0(),
DEF_REFS0(),
DEF_REFS2(1, 2),
DEF_REFS2(1, 2), // Set same ref map as the "Check" since they share offset.
DEF_REFS3(0, 1, 2),
DEF_REFS3(0, 1, 2), // Set same ref map as the "Check" since they share offset.
DEF_REFS0(),
DEF_REFS2(0, 2),
DEF_REFS2(0, 2), // Set same ref map as the "Check" since they share offset.
DEF_REFS2(0, 2),
DEF_REFS1(0),
DEF_REFS2(1, 2),
DEF_REFS0(),
DEF_REFS2(0, 2),
DEF_REFS0(),
DEF_REFS2(0, 2),
DEF_REFS2(0, 2), // Set same ref map as the "Check" since they share offset.
DEF_REFS3(0, 2, 3),
DEF_REFS3(0, 2, 3), // Set same ref map as the "Check" since they share offset.
DEF_REFS3(0, 2, 3),
DEF_REFS3(0, 2, 3), // Set same ref map as the "Check" since they share offset.
DEF_REFS2(0, 2),
DEF_REFS1(0),
DEF_REFS2(1, 2),
DEF_REFS3(0, 1, 2),
DEF_REFS1(0),
DEF_REFS2(0, 2),
DEF_REFS0(),
DEF_REFS0(),
DEF_REFS0(),
DEF_REFS3(0, 1, 2),
DEF_REFS2(0, 2),
DEF_REFS2(0, 2),
};
ReferenceMapUnitTest004::ReferenceMapUnitTest004() : ReferenceMapCalculatorTest() {
PrepareBasicBlocks(kRefTest004bbs);
PrepareMIRs(kRefTest004mirs);
PrepareShortys(kRefTest004shortys);
PrepareRefMaps(kRefTest004refs);
DoCalculateReferenceMaps();
}
TEST_F(ReferenceMapUnitTest004, RefMap004) {
}
} // namespace art