| /* |
| * 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_DEX_INSTRUCTION_H_ |
| #define ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ |
| |
| #include <android-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 Signatures { |
| 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); |
| }; |
| |
| enum Code { // private marker to avoid generate-operator-out.py from processing. |
| #define INSTRUCTION_ENUM(opcode, cname, p, f, i, a, e, v) cname = (opcode), |
| #include "dex_instruction_list.h" |
| DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM) |
| #undef DEX_INSTRUCTION_LIST |
| #undef INSTRUCTION_ENUM |
| RSUB_INT_LIT16 = RSUB_INT, |
| }; |
| |
| enum Format : uint8_t { |
| 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 |
| |
| // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH (A: count) |
| // format: AG op BBBB FEDC HHHH |
| k45cc, |
| |
| // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count) |
| // format: AA op BBBB CCCC HHHH |
| k4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count) |
| |
| k51l, // op vAA, #+BBBBBBBBBBBBBBBB |
| }; |
| |
| enum IndexType : uint8_t { |
| kIndexUnknown = 0, |
| kIndexNone, // has no index |
| kIndexTypeRef, // type reference index |
| kIndexStringRef, // string reference index |
| kIndexMethodRef, // method reference index |
| kIndexFieldRef, // field reference index |
| kIndexFieldOffset, // field offset (for static linked fields) |
| kIndexVtableOffset, // vtable offset (for static linked methods) |
| kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic) |
| kIndexCallSiteRef, // call site reference index |
| kIndexMethodHandleRef, // constant method handle reference index |
| kIndexProtoRef, // prototype reference index |
| }; |
| |
| enum Flags : uint8_t { |
| 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 |
| kExperimental = 0x80, // is an experimental opcode |
| }; |
| |
| // Old flags. Keeping them around in case we might need them again some day. |
| enum ExtendedFlags : uint32_t { |
| kAdd = 0x0000080, // addition |
| kSubtract = 0x0000100, // subtract |
| kMultiply = 0x0000200, // multiply |
| kDivide = 0x0000400, // division |
| kRemainder = 0x0000800, // remainder |
| kAnd = 0x0001000, // and |
| kOr = 0x0002000, // or |
| kXor = 0x0004000, // xor |
| kShl = 0x0008000, // shl |
| kShr = 0x0010000, // shr |
| kUshr = 0x0020000, // ushr |
| kCast = 0x0040000, // cast |
| kStore = 0x0080000, // store opcode |
| kLoad = 0x0100000, // load opcode |
| kClobber = 0x0200000, // clobbers memory in a big way (not just a write) |
| kRegCFieldOrConstant = 0x0400000, // is the third virtual register a field or literal constant (vC) |
| kRegBFieldOrConstant = 0x0800000, // is the second virtual register a field or literal constant (vB) |
| }; |
| |
| enum VerifyFlag : uint32_t { |
| kVerifyNone = 0x0000000, |
| kVerifyRegA = 0x0000001, |
| kVerifyRegAWide = 0x0000002, |
| kVerifyRegB = 0x0000004, |
| kVerifyRegBField = 0x0000008, |
| kVerifyRegBMethod = 0x0000010, |
| kVerifyRegBNewInstance = 0x0000020, |
| kVerifyRegBString = 0x0000040, |
| kVerifyRegBType = 0x0000080, |
| kVerifyRegBWide = 0x0000100, |
| kVerifyRegC = 0x0000200, |
| kVerifyRegCField = 0x0000400, |
| kVerifyRegCNewArray = 0x0000800, |
| kVerifyRegCType = 0x0001000, |
| kVerifyRegCWide = 0x0002000, |
| kVerifyArrayData = 0x0004000, |
| kVerifyBranchTarget = 0x0008000, |
| kVerifySwitchTargets = 0x0010000, |
| kVerifyVarArg = 0x0020000, |
| kVerifyVarArgNonZero = 0x0040000, |
| kVerifyVarArgRange = 0x0080000, |
| kVerifyVarArgRangeNonZero = 0x0100000, |
| kVerifyRuntimeOnly = 0x0200000, |
| kVerifyError = 0x0400000, |
| kVerifyRegHPrototype = 0x0800000, |
| kVerifyRegBCallSite = 0x1000000, |
| kVerifyRegBMethodHandle = 0x2000000, |
| kVerifyRegBPrototype = 0x4000000, |
| }; |
| |
| // Collect the enums in a struct for better locality. |
| struct InstructionDescriptor { |
| uint32_t verify_flags; // Set of VerifyFlag. |
| Format format; |
| IndexType index_type; |
| uint8_t flags; // Set of Flags. |
| int8_t size_in_code_units; |
| }; |
| |
| static constexpr uint32_t kMaxVarArgRegs = 5; |
| |
| static constexpr bool kHaveExperimentalInstructions = false; |
| |
| // Returns the size (in 2 byte code units) of this instruction. |
| size_t SizeInCodeUnits() const { |
| int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units; |
| if (UNLIKELY(result < 0)) { |
| return SizeInCodeUnitsComplexOpcode(); |
| } else { |
| return static_cast<size_t>(result); |
| } |
| } |
| |
| // Code units required to calculate the size of the instruction. |
| size_t CodeUnitsRequiredForSizeComputation() const { |
| const int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units; |
| return UNLIKELY(result < 0) ? CodeUnitsRequiredForSizeOfComplexOpcode() : 1; |
| } |
| |
| // Reads an instruction out of the stream at the specified address. |
| static const Instruction* At(const uint16_t* code) { |
| DCHECK(code != nullptr); |
| return reinterpret_cast<const Instruction*>(code); |
| } |
| |
| // Reads an instruction out of the stream from the current address plus an offset. |
| const Instruction* RelativeAt(int32_t offset) const WARN_UNUSED { |
| return At(reinterpret_cast<const uint16_t*>(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 4xx instruction in the stream. |
| const Instruction* Next_4xx() const { |
| DCHECK(FormatOf(Opcode()) >= k45cc && FormatOf(Opcode()) <= k4rcc); |
| return RelativeAt(4); |
| } |
| |
| // 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; |
| ALWAYS_INLINE int32_t VRegA() const; |
| |
| int8_t VRegA_10t() const { |
| return VRegA_10t(Fetch16(0)); |
| } |
| uint8_t VRegA_10x() const { |
| return VRegA_10x(Fetch16(0)); |
| } |
| uint4_t VRegA_11n() const { |
| return VRegA_11n(Fetch16(0)); |
| } |
| uint8_t VRegA_11x() const { |
| return VRegA_11x(Fetch16(0)); |
| } |
| uint4_t VRegA_12x() const { |
| return VRegA_12x(Fetch16(0)); |
| } |
| int16_t VRegA_20t() const; |
| uint8_t VRegA_21c() const { |
| return VRegA_21c(Fetch16(0)); |
| } |
| uint8_t VRegA_21h() const { |
| return VRegA_21h(Fetch16(0)); |
| } |
| uint8_t VRegA_21s() const { |
| return VRegA_21s(Fetch16(0)); |
| } |
| uint8_t VRegA_21t() const { |
| return VRegA_21t(Fetch16(0)); |
| } |
| uint8_t VRegA_22b() const { |
| return VRegA_22b(Fetch16(0)); |
| } |
| uint4_t VRegA_22c() const { |
| return VRegA_22c(Fetch16(0)); |
| } |
| uint4_t VRegA_22s() const { |
| return VRegA_22s(Fetch16(0)); |
| } |
| uint4_t VRegA_22t() const { |
| return VRegA_22t(Fetch16(0)); |
| } |
| uint8_t VRegA_22x() const { |
| return VRegA_22x(Fetch16(0)); |
| } |
| uint8_t VRegA_23x() const { |
| return VRegA_23x(Fetch16(0)); |
| } |
| int32_t VRegA_30t() const; |
| uint8_t VRegA_31c() const { |
| return VRegA_31c(Fetch16(0)); |
| } |
| uint8_t VRegA_31i() const { |
| return VRegA_31i(Fetch16(0)); |
| } |
| uint8_t VRegA_31t() const { |
| return VRegA_31t(Fetch16(0)); |
| } |
| uint16_t VRegA_32x() const; |
| uint4_t VRegA_35c() const { |
| return VRegA_35c(Fetch16(0)); |
| } |
| uint8_t VRegA_3rc() const { |
| return VRegA_3rc(Fetch16(0)); |
| } |
| uint8_t VRegA_51l() const { |
| return VRegA_51l(Fetch16(0)); |
| } |
| uint4_t VRegA_45cc() const { |
| return VRegA_45cc(Fetch16(0)); |
| } |
| uint8_t VRegA_4rcc() const { |
| return VRegA_4rcc(Fetch16(0)); |
| } |
| |
| // The following methods return the vA operand for various instruction formats. The "inst_data" |
| // parameter holds the first 16 bits of instruction which the returned value is decoded from. |
| int8_t VRegA_10t(uint16_t inst_data) const; |
| uint8_t VRegA_10x(uint16_t inst_data) const; |
| uint4_t VRegA_11n(uint16_t inst_data) const; |
| uint8_t VRegA_11x(uint16_t inst_data) const; |
| uint4_t VRegA_12x(uint16_t inst_data) const; |
| uint8_t VRegA_21c(uint16_t inst_data) const; |
| uint8_t VRegA_21h(uint16_t inst_data) const; |
| uint8_t VRegA_21s(uint16_t inst_data) const; |
| uint8_t VRegA_21t(uint16_t inst_data) const; |
| uint8_t VRegA_22b(uint16_t inst_data) const; |
| uint4_t VRegA_22c(uint16_t inst_data) const; |
| uint4_t VRegA_22s(uint16_t inst_data) const; |
| uint4_t VRegA_22t(uint16_t inst_data) const; |
| uint8_t VRegA_22x(uint16_t inst_data) const; |
| uint8_t VRegA_23x(uint16_t inst_data) const; |
| uint8_t VRegA_31c(uint16_t inst_data) const; |
| uint8_t VRegA_31i(uint16_t inst_data) const; |
| uint8_t VRegA_31t(uint16_t inst_data) const; |
| uint4_t VRegA_35c(uint16_t inst_data) const; |
| uint8_t VRegA_3rc(uint16_t inst_data) const; |
| uint8_t VRegA_51l(uint16_t inst_data) const; |
| uint4_t VRegA_45cc(uint16_t inst_data) const; |
| uint8_t VRegA_4rcc(uint16_t inst_data) const; |
| |
| // VRegB |
| bool HasVRegB() const; |
| int32_t VRegB() const; |
| |
| bool HasWideVRegB() const; |
| uint64_t WideVRegB() const; |
| |
| int4_t VRegB_11n() const { |
| return VRegB_11n(Fetch16(0)); |
| } |
| uint4_t VRegB_12x() const { |
| return VRegB_12x(Fetch16(0)); |
| } |
| 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 { |
| return VRegB_22c(Fetch16(0)); |
| } |
| uint4_t VRegB_22s() const { |
| return VRegB_22s(Fetch16(0)); |
| } |
| uint4_t VRegB_22t() const { |
| return VRegB_22t(Fetch16(0)); |
| } |
| 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 |
| uint16_t VRegB_45cc() const; |
| uint16_t VRegB_4rcc() const; |
| |
| // The following methods return the vB operand for all instruction formats where it is encoded in |
| // the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned |
| // value is decoded from it. |
| int4_t VRegB_11n(uint16_t inst_data) const; |
| uint4_t VRegB_12x(uint16_t inst_data) const; |
| uint4_t VRegB_22c(uint16_t inst_data) const; |
| uint4_t VRegB_22s(uint16_t inst_data) const; |
| uint4_t VRegB_22t(uint16_t inst_data) const; |
| |
| // 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; |
| uint4_t VRegC_45cc() const; |
| uint16_t VRegC_4rcc() const; |
| |
| |
| // VRegH |
| bool HasVRegH() const; |
| int32_t VRegH() const; |
| uint16_t VRegH_45cc() const; |
| uint16_t VRegH_4rcc() const; |
| |
| // Fills the given array with the 'arg' array of the instruction. |
| bool HasVarArgs() const; |
| void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const; |
| void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const { |
| return GetVarArgs(args, Fetch16(0)); |
| } |
| |
| // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first |
| // 16 bits of instruction. |
| Code Opcode(uint16_t inst_data) const { |
| DCHECK_EQ(inst_data, Fetch16(0)); |
| return static_cast<Code>(inst_data & 0xFF); |
| } |
| |
| // Returns the opcode field of the instruction from the first 16 bits of instruction. |
| Code Opcode() const { |
| return Opcode(Fetch16(0)); |
| } |
| |
| void SetOpcode(Code opcode) { |
| DCHECK_LT(static_cast<uint16_t>(opcode), 256u); |
| uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
| insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode); |
| } |
| |
| void SetVRegA_10x(uint8_t val) { |
| DCHECK(FormatOf(Opcode()) == k10x); |
| uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
| insns[0] = (val << 8) | (insns[0] & 0x00ff); |
| } |
| |
| void SetVRegB_3rc(uint16_t val) { |
| DCHECK(FormatOf(Opcode()) == k3rc); |
| uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
| insns[1] = val; |
| } |
| |
| void SetVRegB_35c(uint16_t val) { |
| DCHECK(FormatOf(Opcode()) == k35c); |
| uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
| insns[1] = val; |
| } |
| |
| void SetVRegC_22c(uint16_t val) { |
| DCHECK(FormatOf(Opcode()) == k22c); |
| uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
| insns[1] = val; |
| } |
| |
| void SetVRegA_21c(uint8_t val) { |
| DCHECK(FormatOf(Opcode()) == k21c); |
| uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
| insns[0] = (val << 8) | (insns[0] & 0x00ff); |
| } |
| |
| void SetVRegB_21c(uint16_t val) { |
| DCHECK(FormatOf(Opcode()) == k21c); |
| uint16_t* insns = reinterpret_cast<uint16_t*>(this); |
| insns[1] = val; |
| } |
| |
| // Returns the format of the given opcode. |
| static Format FormatOf(Code opcode) { |
| return kInstructionDescriptors[opcode].format; |
| } |
| |
| // Returns the index type of the given opcode. |
| static IndexType IndexTypeOf(Code opcode) { |
| return kInstructionDescriptors[opcode].index_type; |
| } |
| |
| // Returns the flags for the given opcode. |
| static uint8_t FlagsOf(Code opcode) { |
| return kInstructionDescriptors[opcode].flags; |
| } |
| |
| // Return the verify flags for the given opcode. |
| static uint32_t VerifyFlagsOf(Code opcode) { |
| return kInstructionDescriptors[opcode].verify_flags; |
| } |
| |
| // Returns true if this instruction is a branch. |
| bool IsBranch() const { |
| return (kInstructionDescriptors[Opcode()].flags & kBranch) != 0; |
| } |
| |
| // Returns true if this instruction is a unconditional branch. |
| bool IsUnconditional() const { |
| return (kInstructionDescriptors[Opcode()].flags & 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 the instruction is a quickened instruction. |
| bool IsQuickened() const { |
| return (kInstructionDescriptors[Opcode()].index_type == kIndexFieldOffset) || |
| (kInstructionDescriptors[Opcode()].index_type == kIndexVtableOffset); |
| } |
| |
| // Returns true if this instruction is a switch. |
| bool IsSwitch() const { |
| return (kInstructionDescriptors[Opcode()].flags & kSwitch) != 0; |
| } |
| |
| // Returns true if this instruction can throw. |
| bool IsThrow() const { |
| return (kInstructionDescriptors[Opcode()].flags & kThrow) != 0; |
| } |
| |
| // Determine if the instruction is any of 'return' instructions. |
| bool IsReturn() const { |
| return (kInstructionDescriptors[Opcode()].flags & 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 (kInstructionDescriptors[Opcode()].flags & kInvoke) != 0; |
| } |
| |
| // Determine if this instruction is experimental. |
| bool IsExperimental() const { |
| return (kInstructionDescriptors[Opcode()].flags & kExperimental) != 0; |
| } |
| |
| int GetVerifyTypeArgumentA() const { |
| return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegA | kVerifyRegAWide)); |
| } |
| |
| int GetVerifyTypeArgumentB() const { |
| return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegB | kVerifyRegBField | |
| kVerifyRegBMethod | kVerifyRegBNewInstance | kVerifyRegBString | kVerifyRegBType | |
| kVerifyRegBWide)); |
| } |
| |
| int GetVerifyTypeArgumentC() const { |
| return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegC | kVerifyRegCField | |
| kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide)); |
| } |
| |
| int GetVerifyTypeArgumentH() const { |
| return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRegHPrototype); |
| } |
| |
| int GetVerifyExtraFlags() const { |
| return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyArrayData | |
| kVerifyBranchTarget | kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgNonZero | |
| kVerifyVarArgRange | kVerifyVarArgRangeNonZero | kVerifyError)); |
| } |
| |
| bool GetVerifyIsRuntimeOnly() const { |
| return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRuntimeOnly) != 0; |
| } |
| |
| // 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<const uint16_t*>(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; |
| |
| // Little-endian dump code_units worth of this instruction, padding to code_units for |
| // shorter instructions |
| std::string DumpHexLE(size_t instr_code_units) const; |
| |
| uint16_t Fetch16(size_t offset) const { |
| const uint16_t* insns = reinterpret_cast<const uint16_t*>(this); |
| return insns[offset]; |
| } |
| |
| private: |
| size_t SizeInCodeUnitsComplexOpcode() const; |
| |
| // Return how many code unit words are required to compute the size of the opcode. |
| size_t CodeUnitsRequiredForSizeOfComplexOpcode() const; |
| |
| uint32_t Fetch32(size_t offset) const { |
| return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16)); |
| } |
| |
| uint4_t InstA() const { |
| return InstA(Fetch16(0)); |
| } |
| |
| uint4_t InstB() const { |
| return InstB(Fetch16(0)); |
| } |
| |
| uint8_t InstAA() const { |
| return InstAA(Fetch16(0)); |
| } |
| |
| uint4_t InstA(uint16_t inst_data) const { |
| DCHECK_EQ(inst_data, Fetch16(0)); |
| return static_cast<uint4_t>((inst_data >> 8) & 0x0f); |
| } |
| |
| uint4_t InstB(uint16_t inst_data) const { |
| DCHECK_EQ(inst_data, Fetch16(0)); |
| return static_cast<uint4_t>(inst_data >> 12); |
| } |
| |
| uint8_t InstAA(uint16_t inst_data) const { |
| DCHECK_EQ(inst_data, Fetch16(0)); |
| return static_cast<uint8_t>(inst_data >> 8); |
| } |
| |
| static const char* const kInstructionNames[]; |
| |
| static const InstructionDescriptor kInstructionDescriptors[]; |
| |
| 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); |
| |
| // Base class for accessing instruction operands. Unifies operand |
| // access for instructions that have range and varargs forms |
| // (e.g. invoke-polymoprhic/range and invoke-polymorphic). |
| class InstructionOperands { |
| public: |
| explicit InstructionOperands(size_t num_operands) : num_operands_(num_operands) {} |
| virtual ~InstructionOperands() {} |
| virtual uint32_t GetOperand(size_t index) const = 0; |
| size_t GetNumberOfOperands() const { return num_operands_; } |
| |
| private: |
| const size_t num_operands_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(InstructionOperands); |
| }; |
| |
| // Class for accessing operands for instructions with a range format |
| // (e.g. 3rc and 4rcc). |
| class RangeInstructionOperands FINAL : public InstructionOperands { |
| public: |
| RangeInstructionOperands(uint32_t first_operand, size_t num_operands) |
| : InstructionOperands(num_operands), first_operand_(first_operand) {} |
| ~RangeInstructionOperands() {} |
| uint32_t GetOperand(size_t operand_index) const OVERRIDE; |
| |
| private: |
| const uint32_t first_operand_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(RangeInstructionOperands); |
| }; |
| |
| // Class for accessing operands for instructions with a variable |
| // number of arguments format (e.g. 35c and 45cc). |
| class VarArgsInstructionOperands FINAL : public InstructionOperands { |
| public: |
| VarArgsInstructionOperands(const uint32_t (&operands)[Instruction::kMaxVarArgRegs], |
| size_t num_operands) |
| : InstructionOperands(num_operands), operands_(operands) {} |
| ~VarArgsInstructionOperands() {} |
| uint32_t GetOperand(size_t operand_index) const OVERRIDE; |
| |
| private: |
| const uint32_t (&operands_)[Instruction::kMaxVarArgRegs]; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(VarArgsInstructionOperands); |
| }; |
| |
| // Class for accessing operands without the receiver by wrapping an |
| // existing InstructionOperands instance. |
| class NoReceiverInstructionOperands FINAL : public InstructionOperands { |
| public: |
| explicit NoReceiverInstructionOperands(InstructionOperands* inner) |
| : InstructionOperands(inner->GetNumberOfOperands() - 1), inner_(inner) {} |
| ~NoReceiverInstructionOperands() {} |
| uint32_t GetOperand(size_t operand_index) const OVERRIDE; |
| |
| private: |
| const InstructionOperands* const inner_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(NoReceiverInstructionOperands); |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ |