blob: 34544d7089e70565c55eb09ee6b41f16d8cb1538 [file] [log] [blame]
/*
* Copyright (C) 2017 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.
*/
#pragma once
#if defined(__clang__)
#if __has_feature(cxx_rtti)
#define RTTI_ENABLED 1
#endif
#endif
#include "common.h"
#include "memview.h"
#include "dex_bytecode.h"
#include "dex_format.h"
#include "dex_ir.h"
#include "intrusive_list.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <cstdint>
#include <list>
#include <map>
#include <memory>
#include <utility>
#include <vector>
namespace lir {
template <class T>
using own = std::unique_ptr<T>;
constexpr dex::u4 kInvalidOffset = dex::u4(-1);
struct Bytecode;
struct PackedSwitchPayload;
struct SparseSwitchPayload;
struct ArrayData;
struct Label;
struct TryBlockBegin;
struct TryBlockEnd;
struct Const32;
struct Const64;
struct VReg;
struct VRegPair;
struct VRegList;
struct VRegRange;
struct CodeLocation;
struct String;
struct Type;
struct Field;
struct Method;
struct MethodHandle;
struct Proto;
struct DbgInfoHeader;
struct LineNumber;
struct DbgInfoAnnotation;
// Code IR visitor interface
class Visitor {
public:
Visitor() = default;
virtual ~Visitor() = default;
Visitor(const Visitor&) = delete;
Visitor& operator=(const Visitor&) = delete;
// instructions
virtual bool Visit(Bytecode* bytecode) { return false; }
virtual bool Visit(PackedSwitchPayload* packed_switch) { return false; }
virtual bool Visit(SparseSwitchPayload* sparse_switch) { return false; }
virtual bool Visit(ArrayData* array_data) { return false; }
virtual bool Visit(Label* label) { return false; }
virtual bool Visit(DbgInfoHeader* dbg_header) { return false; }
virtual bool Visit(DbgInfoAnnotation* dbg_annotation) { return false; }
virtual bool Visit(TryBlockBegin* try_begin) { return false; }
virtual bool Visit(TryBlockEnd* try_end) { return false; }
// operands
virtual bool Visit(CodeLocation* location) { return false; }
virtual bool Visit(Const32* const32) { return false; }
virtual bool Visit(Const64* const64) { return false; }
virtual bool Visit(VReg* vreg) { return false; }
virtual bool Visit(VRegPair* vreg_pair) { return false; }
virtual bool Visit(VRegList* vreg_list) { return false; }
virtual bool Visit(VRegRange* vreg_range) { return false; }
virtual bool Visit(String* string) { return false; }
virtual bool Visit(Type* type) { return false; }
virtual bool Visit(Field* field) { return false; }
virtual bool Visit(Method* method) { return false; }
virtual bool Visit(Proto* proto) { return false; }
virtual bool Visit(LineNumber* line) { return false; }
virtual bool Visit(MethodHandle* mh) { return false; }
};
// The root of the polymorphic code IR nodes hierarchy
//
// NOTE: in general it's possible to "reuse" code IR nodes
// (ie. refcount > 1) although extra care is required since
// modifications to shared nodes will be visible in multiple places
// (notable exception: instruction nodes can't be reused)
//
struct Node {
Node() = default;
virtual ~Node() = default;
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
virtual bool Accept(Visitor* visitor) { return false; }
};
struct Operand : public Node {};
struct Const32 : public Operand {
union {
dex::s4 s4_value;
dex::u4 u4_value;
float float_value;
} u;
explicit Const32(dex::u4 value) { u.u4_value = value; }
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct Const64 : public Operand {
union {
dex::s8 s8_value;
dex::u8 u8_value;
double double_value;
} u;
explicit Const64(dex::u8 value) { u.u8_value = value; }
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct VReg : public Operand {
dex::u4 reg;
explicit VReg(dex::u4 reg) : reg(reg) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct VRegPair : public Operand {
dex::u4 base_reg;
explicit VRegPair(dex::u4 base_reg) : base_reg(base_reg) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct VRegList : public Operand {
std::vector<dex::u4> registers;
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct VRegRange : public Operand {
dex::u4 base_reg;
int count;
VRegRange(dex::u4 base_reg, int count) : base_reg(base_reg), count(count) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct IndexedOperand : public Operand {
dex::u4 index;
explicit IndexedOperand(dex::u4 index) : index(index) {}
};
struct String : public IndexedOperand {
ir::String* ir_string;
String(ir::String* ir_string, dex::u4 index) : IndexedOperand(index), ir_string(ir_string) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct Type : public IndexedOperand {
ir::Type* ir_type;
Type(ir::Type* ir_type, dex::u4 index) : IndexedOperand(index), ir_type(ir_type) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct Field : public IndexedOperand {
ir::FieldDecl* ir_field;
Field(ir::FieldDecl* ir_field, dex::u4 index) : IndexedOperand(index), ir_field(ir_field) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct Method : public IndexedOperand {
ir::MethodDecl* ir_method;
Method(ir::MethodDecl* ir_method, dex::u4 index) : IndexedOperand(index), ir_method(ir_method) {
SLICER_CHECK_NE(ir_method, nullptr);
}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct MethodHandle : public IndexedOperand {
ir::MethodHandle* ir_method_handle;
MethodHandle(ir::MethodHandle* ir_method_handle, dex::u4 index) : IndexedOperand(index), ir_method_handle(ir_method_handle) {
SLICER_CHECK_NE(ir_method_handle, nullptr);
}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct Proto : public IndexedOperand {
ir::Proto* ir_proto;
Proto(ir::Proto* ir_proto, dex::u4 index) : IndexedOperand(index), ir_proto(ir_proto) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct CodeLocation : public Operand {
Label* label;
explicit CodeLocation(Label* label) : label(label) {}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
// Code IR is a linked list of Instructions
struct Instruction : public Node {
// absolute offset from the start of the method
dex::u4 offset = 0;
Instruction* prev = nullptr;
Instruction* next = nullptr;
};
using InstructionsList = slicer::IntrusiveList<Instruction>;
namespace detail {
template<class T>
inline T* CastOperand(Operand* op) {
#ifdef RTTI_ENABLED
T* operand = dynamic_cast<T*>(op);
SLICER_CHECK_NE(operand, nullptr);
return operand;
#else
SLICER_CHECK_NE(op, nullptr);
struct CastVisitor : public Visitor {
T* converted = nullptr;
bool Visit(T* val) override {
converted = val;
return true;
}
};
CastVisitor cv;
op->Accept(&cv);
SLICER_CHECK_NE(cv.converted, nullptr);
return cv.converted;
#endif
}
// Special-case for IndexedOperand.
template<>
inline IndexedOperand* CastOperand<IndexedOperand>(Operand* op) {
#ifdef RTTI_ENABLED
IndexedOperand* operand = dynamic_cast<IndexedOperand*>(op);
SLICER_CHECK_NE(operand, nullptr);
return operand;
#else
SLICER_CHECK_NE(op, nullptr);
struct CastVisitor : public Visitor {
IndexedOperand* converted = nullptr;
bool Visit(String* val) override {
converted = val;
return true;
}
bool Visit(Type* val) override {
converted = val;
return true;
}
bool Visit(Field* val) override {
converted = val;
return true;
}
bool Visit(Method* val) override {
converted = val;
return true;
}
bool Visit(MethodHandle* val) override {
converted = val;
return true;
}
bool Visit(Proto* val) override {
converted = val;
return true;
}
};
CastVisitor cv;
op->Accept(&cv);
SLICER_CHECK_NE(cv.converted, nullptr);
return cv.converted;
#endif
}
} // namespace detail
struct Bytecode : public Instruction {
dex::Opcode opcode = dex::OP_NOP;
std::vector<Operand*> operands;
template<class T>
T* CastOperand(int index) const {
return detail::CastOperand<T>(operands[index]);
}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct PackedSwitchPayload : public Instruction {
dex::s4 first_key = 0;
std::vector<Label*> targets;
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct SparseSwitchPayload : public Instruction {
struct SwitchCase {
dex::s4 key = 0;
Label* target = nullptr;
};
std::vector<SwitchCase> switch_cases;
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct ArrayData : public Instruction {
slicer::MemView data;
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct Label : public Instruction {
int id = 0;
int refCount = 0;
bool aligned = false;
explicit Label(dex::u4 offset) { this->offset = offset; }
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct TryBlockBegin : public Instruction {
int id = 0;
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct CatchHandler {
ir::Type* ir_type = nullptr;
Label* label = nullptr;
};
struct TryBlockEnd : public Instruction {
TryBlockBegin* try_begin = nullptr;
std::vector<CatchHandler> handlers;
Label* catch_all = nullptr;
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct DbgInfoHeader : public Instruction {
std::vector<ir::String*> param_names;
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct LineNumber : public Operand {
int line = 0;
explicit LineNumber(int line) : line(line) {
SLICER_WEAK_CHECK(line >= 0);
}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
struct DbgInfoAnnotation : public Instruction {
dex::u1 dbg_opcode = 0;
std::vector<Operand*> operands;
explicit DbgInfoAnnotation(dex::u1 dbg_opcode) : dbg_opcode(dbg_opcode) {}
template<class T>
T* CastOperand(int index) const {
return detail::CastOperand<T>(operands[index]);
}
virtual bool Accept(Visitor* visitor) override { return visitor->Visit(this); }
};
// Code IR container and manipulation interface
struct CodeIr {
// linked list of the method's instructions
InstructionsList instructions;
ir::EncodedMethod* ir_method = nullptr;
std::shared_ptr<ir::DexFile> dex_ir;
public:
CodeIr(ir::EncodedMethod* ir_method, std::shared_ptr<ir::DexFile> dex_ir)
: ir_method(ir_method), dex_ir(dex_ir) {
Disassemble();
}
// No copy/move semantics
CodeIr(const CodeIr&) = delete;
CodeIr& operator=(const CodeIr&) = delete;
void Assemble();
void Accept(Visitor* visitor) {
for (auto instr : instructions) {
instr->Accept(visitor);
}
}
template <class T, class... Args>
T* Alloc(Args&&... args) {
auto p = new T(std::forward<Args>(args)...);
nodes_.push_back(own<T>(p));
return p;
}
private:
void Disassemble();
void DisassembleBytecode(const ir::Code* ir_code);
void DisassembleTryBlocks(const ir::Code* ir_code);
void DisassembleDebugInfo(const ir::DebugInfo* ir_debug_info);
void FixupSwitches();
void FixupPackedSwitch(PackedSwitchPayload* instr, dex::u4 base_offset, const dex::u2* ptr);
void FixupSparseSwitch(SparseSwitchPayload* instr, dex::u4 base_offset, const dex::u2* ptr);
SparseSwitchPayload* DecodeSparseSwitch(const dex::u2* /*ptr*/, dex::u4 offset);
PackedSwitchPayload* DecodePackedSwitch(const dex::u2* /*ptr*/, dex::u4 offset);
ArrayData* DecodeArrayData(const dex::u2* ptr, dex::u4 offset);
Bytecode* DecodeBytecode(const dex::u2* ptr, dex::u4 offset);
IndexedOperand* GetIndexedOperand(dex::InstructionIndexType index_type, dex::u4 index);
IndexedOperand* GetSecondIndexedOperand(dex::InstructionIndexType index_type, dex::u4 index);
Type* GetType(dex::u4 index);
String* GetString(dex::u4 index);
Label* GetLabel(dex::u4 offset);
Operand* GetRegA(const dex::Instruction& dex_instr);
Operand* GetRegB(const dex::Instruction& dex_instr);
Operand* GetRegC(const dex::Instruction& dex_instr);
private:
// the "master index" of all the LIR owned nodes
std::vector<own<Node>> nodes_;
// data structures for fixing up switch payloads
struct PackedSwitchFixup {
PackedSwitchPayload* instr = nullptr;
dex::u4 base_offset = kInvalidOffset;
};
struct SparseSwitchFixup {
SparseSwitchPayload* instr = nullptr;
dex::u4 base_offset = kInvalidOffset;
};
// used during bytecode raising
std::map<dex::u4, Label*> labels_;
std::map<dex::u4, PackedSwitchFixup> packed_switches_;
std::map<dex::u4, SparseSwitchFixup> sparse_switches_;
// extra instructions/annotations created during raising
// (intended to be merged in with the main instruction
// list at end of the IR raising phase)
std::vector<TryBlockBegin*> try_begins_;
std::vector<TryBlockEnd*> try_ends_;
std::vector<Instruction*> dbg_annotations_;
};
} // namespace lir