/* * Copyright (C) 2011 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. */ #ifndef ART_RUNTIME_DEX_INSTRUCTION_H_ #define ART_RUNTIME_DEX_INSTRUCTION_H_ #include "base/logging.h" #include "base/macros.h" #include "globals.h" typedef uint8_t uint4_t; typedef int8_t int4_t; namespace art { class DexFile; enum { kNumPackedOpcodes = 0x100 }; class Instruction { public: // NOP-encoded switch-statement signatures. enum { kPackedSwitchSignature = 0x0100, kSparseSwitchSignature = 0x0200, kArrayDataSignature = 0x0300, }; struct PACKED(4) PackedSwitchPayload { const uint16_t ident; const uint16_t case_count; const int32_t first_key; const int32_t targets[]; private: DISALLOW_COPY_AND_ASSIGN(PackedSwitchPayload); }; struct PACKED(4) SparseSwitchPayload { const uint16_t ident; const uint16_t case_count; const int32_t keys_and_targets[]; public: const int32_t* GetKeys() const { return keys_and_targets; } const int32_t* GetTargets() const { return keys_and_targets + case_count; } private: DISALLOW_COPY_AND_ASSIGN(SparseSwitchPayload); }; struct PACKED(4) ArrayDataPayload { const uint16_t ident; const uint16_t element_width; const uint32_t element_count; const uint8_t data[]; private: DISALLOW_COPY_AND_ASSIGN(ArrayDataPayload); }; // TODO: the code layout below is deliberate to avoid this enum being picked up by // generate-operator-out.py. enum Code { // NOLINT(whitespace/braces) #define INSTRUCTION_ENUM(opcode, cname, p, f, r, i, a, v) cname = opcode, #include "dex_instruction_list.h" DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM) #undef DEX_INSTRUCTION_LIST #undef INSTRUCTION_ENUM }; enum Format { k10x, // op k12x, // op vA, vB k11n, // op vA, #+B k11x, // op vAA k10t, // op +AA k20t, // op +AAAA k22x, // op vAA, vBBBB k21t, // op vAA, +BBBB k21s, // op vAA, #+BBBB k21h, // op vAA, #+BBBB00000[00000000] k21c, // op vAA, thing@BBBB k23x, // op vAA, vBB, vCC k22b, // op vAA, vBB, #+CC k22t, // op vA, vB, +CCCC k22s, // op vA, vB, #+CCCC k22c, // op vA, vB, thing@CCCC k32x, // op vAAAA, vBBBB k30t, // op +AAAAAAAA k31t, // op vAA, +BBBBBBBB k31i, // op vAA, #+BBBBBBBB k31c, // op vAA, thing@BBBBBBBB k35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG) k3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB k51l, // op vAA, #+BBBBBBBBBBBBBBBB }; enum Flags { kBranch = 0x01, // conditional or unconditional branch kContinue = 0x02, // flow can continue to next statement kSwitch = 0x04, // switch statement kThrow = 0x08, // could cause an exception to be thrown kReturn = 0x10, // returns, no additional statements kInvoke = 0x20, // a flavor of invoke kUnconditional = 0x40, // unconditional branch }; enum VerifyFlag { kVerifyNone = 0x00000, kVerifyRegA = 0x00001, kVerifyRegAWide = 0x00002, kVerifyRegB = 0x00004, kVerifyRegBField = 0x00008, kVerifyRegBMethod = 0x00010, kVerifyRegBNewInstance = 0x00020, kVerifyRegBString = 0x00040, kVerifyRegBType = 0x00080, kVerifyRegBWide = 0x00100, kVerifyRegC = 0x00200, kVerifyRegCField = 0x00400, kVerifyRegCNewArray = 0x00800, kVerifyRegCType = 0x01000, kVerifyRegCWide = 0x02000, kVerifyArrayData = 0x04000, kVerifyBranchTarget = 0x08000, kVerifySwitchTargets = 0x10000, kVerifyVarArg = 0x20000, kVerifyVarArgRange = 0x40000, kVerifyError = 0x80000, }; // Decodes this instruction, populating its arguments. void Decode(uint32_t &vA, uint32_t &vB, uint64_t &vB_wide, uint32_t &vC, uint32_t arg[]) const; // Returns the size (in 2 byte code units) of this instruction. size_t SizeInCodeUnits() const { int result = kInstructionSizeInCodeUnits[Opcode()]; if (UNLIKELY(result < 0)) { return SizeInCodeUnitsComplexOpcode(); } else { return static_cast(result); } } // Reads an instruction out of the stream at the specified address. static const Instruction* At(const uint16_t* code) { DCHECK(code != NULL); return reinterpret_cast(code); } // Reads an instruction out of the stream from the current address plus an offset. const Instruction* RelativeAt(int32_t offset) const { return At(reinterpret_cast(this) + offset); } // Returns a pointer to the next instruction in the stream. const Instruction* Next() const { return RelativeAt(SizeInCodeUnits()); } // Returns a pointer to the instruction after this 1xx instruction in the stream. const Instruction* Next_1xx() const { DCHECK(FormatOf(Opcode()) >= k10x && FormatOf(Opcode()) <= k10t); return RelativeAt(1); } // Returns a pointer to the instruction after this 2xx instruction in the stream. const Instruction* Next_2xx() const { DCHECK(FormatOf(Opcode()) >= k20t && FormatOf(Opcode()) <= k22c); return RelativeAt(2); } // Returns a pointer to the instruction after this 3xx instruction in the stream. const Instruction* Next_3xx() const { DCHECK(FormatOf(Opcode()) >= k32x && FormatOf(Opcode()) <= k3rc); return RelativeAt(3); } // Returns a pointer to the instruction after this 51l instruction in the stream. const Instruction* Next_51l() const { DCHECK(FormatOf(Opcode()) == k51l); return RelativeAt(5); } // Returns the name of this instruction's opcode. const char* Name() const { return Instruction::Name(Opcode()); } // Returns the name of the given opcode. static const char* Name(Code opcode) { return kInstructionNames[opcode]; } // VRegA bool HasVRegA() const; int32_t VRegA() const; int8_t VRegA_10t() const; uint8_t VRegA_10x() const; uint4_t VRegA_11n() const; uint8_t VRegA_11x() const; uint4_t VRegA_12x() const; int16_t VRegA_20t() const; uint8_t VRegA_21c() const; uint8_t VRegA_21h() const; uint8_t VRegA_21s() const; uint8_t VRegA_21t() const; uint8_t VRegA_22b() const; uint4_t VRegA_22c() const; uint4_t VRegA_22s() const; uint4_t VRegA_22t() const; uint8_t VRegA_22x() const; uint8_t VRegA_23x() const; int32_t VRegA_30t() const; uint8_t VRegA_31c() const; uint8_t VRegA_31i() const; uint8_t VRegA_31t() const; uint16_t VRegA_32x() const; uint4_t VRegA_35c() const; uint8_t VRegA_3rc() const; uint8_t VRegA_51l() const; // VRegB bool HasVRegB() const; int32_t VRegB() const; int4_t VRegB_11n() const; uint4_t VRegB_12x() const; uint16_t VRegB_21c() const; uint16_t VRegB_21h() const; int16_t VRegB_21s() const; int16_t VRegB_21t() const; uint8_t VRegB_22b() const; uint4_t VRegB_22c() const; uint4_t VRegB_22s() const; uint4_t VRegB_22t() const; uint16_t VRegB_22x() const; uint8_t VRegB_23x() const; uint32_t VRegB_31c() const; int32_t VRegB_31i() const; int32_t VRegB_31t() const; uint16_t VRegB_32x() const; uint16_t VRegB_35c() const; uint16_t VRegB_3rc() const; uint64_t VRegB_51l() const; // vB_wide // VRegC bool HasVRegC() const; int32_t VRegC() const; int8_t VRegC_22b() const; uint16_t VRegC_22c() const; int16_t VRegC_22s() const; int16_t VRegC_22t() const; uint8_t VRegC_23x() const; uint4_t VRegC_35c() const; uint16_t VRegC_3rc() const; // Fills the given array with the 'arg' array of the instruction. void GetArgs(uint32_t args[5]) const; // Returns the opcode field of the instruction. Code Opcode() const { const uint16_t* insns = reinterpret_cast(this); int opcode = *insns & 0xFF; return static_cast(opcode); } void SetOpcode(Code opcode) { DCHECK_LT(static_cast(opcode), 256u); uint16_t* insns = reinterpret_cast(this); insns[0] = (insns[0] & 0xff00) | static_cast(opcode); } void SetVRegA_10x(uint8_t val) { DCHECK(FormatOf(Opcode()) == k10x); uint16_t* insns = reinterpret_cast(this); insns[0] = (val << 8) | (insns[0] & 0x00ff); } void SetVRegB_3rc(uint16_t val) { DCHECK(FormatOf(Opcode()) == k3rc); uint16_t* insns = reinterpret_cast(this); insns[1] = val; } void SetVRegB_35c(uint16_t val) { DCHECK(FormatOf(Opcode()) == k35c); uint16_t* insns = reinterpret_cast(this); insns[1] = val; } void SetVRegC_22c(uint16_t val) { DCHECK(FormatOf(Opcode()) == k22c); uint16_t* insns = reinterpret_cast(this); insns[1] = val; } // Returns the format of the given opcode. static Format FormatOf(Code opcode) { return kInstructionFormats[opcode]; } // Returns the flags for the given opcode. static int FlagsOf(Code opcode) { return kInstructionFlags[opcode]; } // Returns true if this instruction is a branch. bool IsBranch() const { return (kInstructionFlags[Opcode()] & kBranch) != 0; } // Returns true if this instruction is a unconditional branch. bool IsUnconditional() const { return (kInstructionFlags[Opcode()] & kUnconditional) != 0; } // Returns the branch offset if this instruction is a branch. int32_t GetTargetOffset() const; // Returns true if the instruction allows control flow to go to the following instruction. bool CanFlowThrough() const; // Returns true if this instruction is a switch. bool IsSwitch() const { return (kInstructionFlags[Opcode()] & kSwitch) != 0; } // Returns true if this instruction can throw. bool IsThrow() const { return (kInstructionFlags[Opcode()] & kThrow) != 0; } // Determine if the instruction is any of 'return' instructions. bool IsReturn() const { return (kInstructionFlags[Opcode()] & kReturn) != 0; } // Determine if this instruction ends execution of its basic block. bool IsBasicBlockEnd() const { return IsBranch() || IsReturn() || Opcode() == THROW; } // Determine if this instruction is an invoke. bool IsInvoke() const { return (kInstructionFlags[Opcode()] & kInvoke) != 0; } int GetVerifyTypeArgumentA() const { return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegA | kVerifyRegAWide)); } int GetVerifyTypeArgumentB() const { return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegB | kVerifyRegBField | kVerifyRegBMethod | kVerifyRegBNewInstance | kVerifyRegBString | kVerifyRegBType | kVerifyRegBWide)); } int GetVerifyTypeArgumentC() const { return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegC | kVerifyRegCField | kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide)); } int GetVerifyExtraFlags() const { return (kInstructionVerifyFlags[Opcode()] & (kVerifyArrayData | kVerifyBranchTarget | kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgRange | kVerifyError)); } // Get the dex PC of this instruction as a offset in code units from the beginning of insns. uint32_t GetDexPc(const uint16_t* insns) const { return (reinterpret_cast(this) - insns); } // Dump decoded version of instruction std::string DumpString(const DexFile*) const; // Dump code_units worth of this instruction, padding to code_units for shorter instructions std::string DumpHex(size_t code_units) const; private: size_t SizeInCodeUnitsComplexOpcode() const; uint16_t Fetch16(size_t offset) const { const uint16_t* insns = reinterpret_cast(this); return insns[offset]; } uint32_t Fetch32(size_t offset) const { return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16)); } uint4_t InstA() const { return static_cast((Fetch16(0) >> 8) & 0x0f); } uint4_t InstB() const { return static_cast(Fetch16(0) >> 12); } uint8_t InstAA() const { return static_cast(Fetch16(0) >> 8); } static const char* const kInstructionNames[]; static Format const kInstructionFormats[]; static int const kInstructionFlags[]; static int const kInstructionVerifyFlags[]; static int const kInstructionSizeInCodeUnits[]; DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction); }; std::ostream& operator<<(std::ostream& os, const Instruction::Code& code); std::ostream& operator<<(std::ostream& os, const Instruction::Format& format); std::ostream& operator<<(std::ostream& os, const Instruction::Flags& flags); std::ostream& operator<<(std::ostream& os, const Instruction::VerifyFlag& vflags); /* * Holds the contents of a decoded instruction. */ struct DecodedInstruction { uint32_t vA; uint32_t vB; uint64_t vB_wide; /* for k51l */ uint32_t vC; uint32_t arg[5]; /* vC/D/E/F/G in invoke or filled-new-array */ Instruction::Code opcode; explicit DecodedInstruction(const Instruction* inst) { inst->Decode(vA, vB, vB_wide, vC, arg); opcode = inst->Opcode(); } }; } // namespace art #endif // ART_RUNTIME_DEX_INSTRUCTION_H_