Home | History | Annotate | Download | only in dex
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "dex_instruction-inl.h"
     18 
     19 #include <inttypes.h>
     20 
     21 #include <iomanip>
     22 #include <sstream>
     23 
     24 #include "android-base/stringprintf.h"
     25 
     26 #include "dex_file-inl.h"
     27 #include "utf.h"
     28 
     29 namespace art {
     30 
     31 using android::base::StringPrintf;
     32 
     33 const char* const Instruction::kInstructionNames[] = {
     34 #define INSTRUCTION_NAME(o, c, pname, f, i, a, e, v) pname,
     35 #include "dex_instruction_list.h"
     36   DEX_INSTRUCTION_LIST(INSTRUCTION_NAME)
     37 #undef DEX_INSTRUCTION_LIST
     38 #undef INSTRUCTION_NAME
     39 };
     40 
     41 static_assert(sizeof(Instruction::InstructionDescriptor) == 8u, "Unexpected descriptor size");
     42 
     43 static constexpr int8_t InstructionSizeInCodeUnitsByOpcode(Instruction::Code opcode,
     44                                                            Instruction::Format format) {
     45   if (opcode == Instruction::Code::NOP) {
     46     return -1;
     47   } else if ((format >= Instruction::Format::k10x) && (format <= Instruction::Format::k10t)) {
     48     return 1;
     49   } else if ((format >= Instruction::Format::k20t) && (format <= Instruction::Format::k22c)) {
     50     return 2;
     51   } else if ((format >= Instruction::Format::k32x) && (format <= Instruction::Format::k3rc)) {
     52     return 3;
     53   } else if ((format >= Instruction::Format::k45cc) && (format <= Instruction::Format::k4rcc)) {
     54     return 4;
     55   } else if (format == Instruction::Format::k51l) {
     56     return 5;
     57   } else {
     58     return -1;
     59   }
     60 }
     61 
     62 Instruction::InstructionDescriptor const Instruction::kInstructionDescriptors[] = {
     63 #define INSTRUCTION_DESCR(opcode, c, p, format, index, flags, eflags, vflags) \
     64     { vflags, \
     65       format, \
     66       index, \
     67       flags, \
     68       InstructionSizeInCodeUnitsByOpcode((c), (format)), \
     69     },
     70 #include "dex_instruction_list.h"
     71   DEX_INSTRUCTION_LIST(INSTRUCTION_DESCR)
     72 #undef DEX_INSTRUCTION_LIST
     73 #undef INSTRUCTION_DESCR
     74 };
     75 
     76 int32_t Instruction::GetTargetOffset() const {
     77   switch (FormatOf(Opcode())) {
     78     // Cases for conditional branches follow.
     79     case k22t: return VRegC_22t();
     80     case k21t: return VRegB_21t();
     81     // Cases for unconditional branches follow.
     82     case k10t: return VRegA_10t();
     83     case k20t: return VRegA_20t();
     84     case k30t: return VRegA_30t();
     85     default: LOG(FATAL) << "Tried to access the branch offset of an instruction " << Name() <<
     86         " which does not have a target operand.";
     87   }
     88   return 0;
     89 }
     90 
     91 bool Instruction::CanFlowThrough() const {
     92   const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
     93   uint16_t insn = *insns;
     94   Code opcode = static_cast<Code>(insn & 0xFF);
     95   return  FlagsOf(opcode) & Instruction::kContinue;
     96 }
     97 
     98 size_t Instruction::SizeInCodeUnitsComplexOpcode() const {
     99   const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
    100   // Handle special NOP encoded variable length sequences.
    101   switch (*insns) {
    102     case kPackedSwitchSignature:
    103       return (4 + insns[1] * 2);
    104     case kSparseSwitchSignature:
    105       return (2 + insns[1] * 4);
    106     case kArrayDataSignature: {
    107       uint16_t element_size = insns[1];
    108       uint32_t length = insns[2] | (((uint32_t)insns[3]) << 16);
    109       // The plus 1 is to round up for odd size and width.
    110       return (4 + (element_size * length + 1) / 2);
    111     }
    112     default:
    113       if ((*insns & 0xFF) == 0) {
    114         return 1;  // NOP.
    115       } else {
    116         LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
    117         UNREACHABLE();
    118       }
    119   }
    120 }
    121 
    122 size_t Instruction::CodeUnitsRequiredForSizeOfComplexOpcode() const {
    123   const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
    124   // Handle special NOP encoded variable length sequences.
    125   switch (*insns) {
    126     case kPackedSwitchSignature:
    127       FALLTHROUGH_INTENDED;
    128     case kSparseSwitchSignature:
    129       return 2;
    130     case kArrayDataSignature:
    131       return 4;
    132     default:
    133       if ((*insns & 0xFF) == 0) {
    134         return 1;  // NOP.
    135       } else {
    136         LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
    137         UNREACHABLE();
    138       }
    139   }
    140 }
    141 
    142 std::string Instruction::DumpHex(size_t code_units) const {
    143   size_t inst_length = SizeInCodeUnits();
    144   if (inst_length > code_units) {
    145     inst_length = code_units;
    146   }
    147   std::ostringstream os;
    148   const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
    149   for (size_t i = 0; i < inst_length; i++) {
    150     os << StringPrintf("0x%04x", insn[i]) << " ";
    151   }
    152   for (size_t i = inst_length; i < code_units; i++) {
    153     os << "       ";
    154   }
    155   return os.str();
    156 }
    157 
    158 std::string Instruction::DumpHexLE(size_t instr_code_units) const {
    159   size_t inst_length = SizeInCodeUnits();
    160   if (inst_length > instr_code_units) {
    161     inst_length = instr_code_units;
    162   }
    163   std::ostringstream os;
    164   const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
    165   for (size_t i = 0; i < inst_length; i++) {
    166     os << StringPrintf("%02x%02x", static_cast<uint8_t>(insn[i] & 0x00FF),
    167                        static_cast<uint8_t>((insn[i] & 0xFF00) >> 8)) << " ";
    168   }
    169   for (size_t i = inst_length; i < instr_code_units; i++) {
    170     os << "     ";
    171   }
    172   return os.str();
    173 }
    174 
    175 std::string Instruction::DumpString(const DexFile* file) const {
    176   std::ostringstream os;
    177   const char* opcode = kInstructionNames[Opcode()];
    178   switch (FormatOf(Opcode())) {
    179     case k10x:  os << opcode; break;
    180     case k12x:  os << StringPrintf("%s v%d, v%d", opcode, VRegA_12x(), VRegB_12x()); break;
    181     case k11n:  os << StringPrintf("%s v%d, #%+d", opcode, VRegA_11n(), VRegB_11n()); break;
    182     case k11x:  os << StringPrintf("%s v%d", opcode, VRegA_11x()); break;
    183     case k10t:  os << StringPrintf("%s %+d", opcode, VRegA_10t()); break;
    184     case k20t:  os << StringPrintf("%s %+d", opcode, VRegA_20t()); break;
    185     case k22x:  os << StringPrintf("%s v%d, v%d", opcode, VRegA_22x(), VRegB_22x()); break;
    186     case k21t:  os << StringPrintf("%s v%d, %+d", opcode, VRegA_21t(), VRegB_21t()); break;
    187     case k21s:  os << StringPrintf("%s v%d, #%+d", opcode, VRegA_21s(), VRegB_21s()); break;
    188     case k21h: {
    189         // op vAA, #+BBBB0000[00000000]
    190         if (Opcode() == CONST_HIGH16) {
    191           uint32_t value = VRegB_21h() << 16;
    192           os << StringPrintf("%s v%d, #int %+d // 0x%x", opcode, VRegA_21h(), value, value);
    193         } else {
    194           uint64_t value = static_cast<uint64_t>(VRegB_21h()) << 48;
    195           os << StringPrintf("%s v%d, #long %+" PRId64 " // 0x%" PRIx64, opcode, VRegA_21h(),
    196                              value, value);
    197         }
    198       }
    199       break;
    200     case k21c: {
    201       switch (Opcode()) {
    202         case CONST_STRING:
    203           if (file != nullptr) {
    204             uint32_t string_idx = VRegB_21c();
    205             if (string_idx < file->NumStringIds()) {
    206               os << StringPrintf(
    207                   "const-string v%d, %s // string@%d",
    208                   VRegA_21c(),
    209                   PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(),
    210                   string_idx);
    211             } else {
    212               os << StringPrintf("const-string v%d, <<invalid-string-idx-%d>> // string@%d",
    213                                  VRegA_21c(),
    214                                  string_idx,
    215                                  string_idx);
    216             }
    217             break;
    218           }
    219           FALLTHROUGH_INTENDED;
    220         case CHECK_CAST:
    221         case CONST_CLASS:
    222         case NEW_INSTANCE:
    223           if (file != nullptr) {
    224             dex::TypeIndex type_idx(VRegB_21c());
    225             os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", "
    226                << file->PrettyType(type_idx) << " // type@" << type_idx;
    227             break;
    228           }
    229           FALLTHROUGH_INTENDED;
    230         case SGET:
    231         case SGET_WIDE:
    232         case SGET_OBJECT:
    233         case SGET_BOOLEAN:
    234         case SGET_BYTE:
    235         case SGET_CHAR:
    236         case SGET_SHORT:
    237           if (file != nullptr) {
    238             uint32_t field_idx = VRegB_21c();
    239             os << opcode << "  v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true)
    240                << " // field@" << field_idx;
    241             break;
    242           }
    243           FALLTHROUGH_INTENDED;
    244         case SPUT:
    245         case SPUT_WIDE:
    246         case SPUT_OBJECT:
    247         case SPUT_BOOLEAN:
    248         case SPUT_BYTE:
    249         case SPUT_CHAR:
    250         case SPUT_SHORT:
    251           if (file != nullptr) {
    252             uint32_t field_idx = VRegB_21c();
    253             os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true)
    254                << " // field@" << field_idx;
    255             break;
    256           }
    257           FALLTHROUGH_INTENDED;
    258         default:
    259           os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_21c(), VRegB_21c());
    260           break;
    261       }
    262       break;
    263     }
    264     case k23x:  os << StringPrintf("%s v%d, v%d, v%d", opcode, VRegA_23x(), VRegB_23x(), VRegC_23x()); break;
    265     case k22b:  os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22b(), VRegB_22b(), VRegC_22b()); break;
    266     case k22t:  os << StringPrintf("%s v%d, v%d, %+d", opcode, VRegA_22t(), VRegB_22t(), VRegC_22t()); break;
    267     case k22s:  os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22s(), VRegB_22s(), VRegC_22s()); break;
    268     case k22c: {
    269       switch (Opcode()) {
    270         case IGET:
    271         case IGET_WIDE:
    272         case IGET_OBJECT:
    273         case IGET_BOOLEAN:
    274         case IGET_BYTE:
    275         case IGET_CHAR:
    276         case IGET_SHORT:
    277           if (file != nullptr) {
    278             uint32_t field_idx = VRegC_22c();
    279             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
    280                << file->PrettyField(field_idx, true) << " // field@" << field_idx;
    281             break;
    282           }
    283           FALLTHROUGH_INTENDED;
    284         case IGET_QUICK:
    285         case IGET_OBJECT_QUICK:
    286           if (file != nullptr) {
    287             uint32_t field_idx = VRegC_22c();
    288             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
    289                << "// offset@" << field_idx;
    290             break;
    291           }
    292           FALLTHROUGH_INTENDED;
    293         case IPUT:
    294         case IPUT_WIDE:
    295         case IPUT_OBJECT:
    296         case IPUT_BOOLEAN:
    297         case IPUT_BYTE:
    298         case IPUT_CHAR:
    299         case IPUT_SHORT:
    300           if (file != nullptr) {
    301             uint32_t field_idx = VRegC_22c();
    302             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
    303                << file->PrettyField(field_idx, true) << " // field@" << field_idx;
    304             break;
    305           }
    306           FALLTHROUGH_INTENDED;
    307         case IPUT_QUICK:
    308         case IPUT_OBJECT_QUICK:
    309           if (file != nullptr) {
    310             uint32_t field_idx = VRegC_22c();
    311             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
    312                << "// offset@" << field_idx;
    313             break;
    314           }
    315           FALLTHROUGH_INTENDED;
    316         case INSTANCE_OF:
    317           if (file != nullptr) {
    318             dex::TypeIndex type_idx(VRegC_22c());
    319             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
    320                << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
    321                << " // type@" << type_idx.index_;
    322             break;
    323           }
    324           FALLTHROUGH_INTENDED;
    325         case NEW_ARRAY:
    326           if (file != nullptr) {
    327             dex::TypeIndex type_idx(VRegC_22c());
    328             os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
    329                << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
    330                << " // type@" << type_idx.index_;
    331             break;
    332           }
    333           FALLTHROUGH_INTENDED;
    334         default:
    335           os << StringPrintf("%s v%d, v%d, thing@%d", opcode, VRegA_22c(), VRegB_22c(), VRegC_22c());
    336           break;
    337       }
    338       break;
    339     }
    340     case k32x:  os << StringPrintf("%s v%d, v%d", opcode, VRegA_32x(), VRegB_32x()); break;
    341     case k30t:  os << StringPrintf("%s %+d", opcode, VRegA_30t()); break;
    342     case k31t:  os << StringPrintf("%s v%d, %+d", opcode, VRegA_31t(), VRegB_31t()); break;
    343     case k31i:  os << StringPrintf("%s v%d, #%+d", opcode, VRegA_31i(), VRegB_31i()); break;
    344     case k31c:
    345       if (Opcode() == CONST_STRING_JUMBO) {
    346         uint32_t string_idx = VRegB_31c();
    347         if (file != nullptr) {
    348           if (string_idx < file->NumStringIds()) {
    349             os << StringPrintf(
    350                 "%s v%d, %s // string@%d",
    351                 opcode,
    352                 VRegA_31c(),
    353                 PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(),
    354                 string_idx);
    355           } else {
    356             os << StringPrintf("%s v%d, <<invalid-string-idx-%d>> // string@%d",
    357                                opcode,
    358                                VRegA_31c(),
    359                                string_idx,
    360                                string_idx);
    361           }
    362         } else {
    363           os << StringPrintf("%s v%d, string@%d", opcode, VRegA_31c(), string_idx);
    364         }
    365       } else {
    366         os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_31c(), VRegB_31c()); break;
    367       }
    368       break;
    369     case k35c: {
    370       uint32_t arg[kMaxVarArgRegs];
    371       GetVarArgs(arg);
    372       auto DumpArgs = [&](size_t count) {
    373         for (size_t i = 0; i < count; ++i) {
    374           if (i != 0) {
    375             os << ", ";
    376           }
    377           os << "v" << arg[i];
    378         }
    379       };
    380       switch (Opcode()) {
    381         case FILLED_NEW_ARRAY:
    382         {
    383           os << opcode << " {";
    384           DumpArgs(VRegA_35c());
    385           os << "}, type@" << VRegB_35c();
    386         }
    387         break;
    388 
    389         case INVOKE_VIRTUAL:
    390         case INVOKE_SUPER:
    391         case INVOKE_DIRECT:
    392         case INVOKE_STATIC:
    393         case INVOKE_INTERFACE:
    394           if (file != nullptr) {
    395             os << opcode << " {";
    396             uint32_t method_idx = VRegB_35c();
    397             DumpArgs(VRegA_35c());
    398             os << "}, " << file->PrettyMethod(method_idx) << " // method@" << method_idx;
    399             break;
    400           }
    401           FALLTHROUGH_INTENDED;
    402         case INVOKE_VIRTUAL_QUICK:
    403           if (file != nullptr) {
    404             os << opcode << " {";
    405             uint32_t method_idx = VRegB_35c();
    406             DumpArgs(VRegA_35c());
    407             os << "},  // vtable@" << method_idx;
    408             break;
    409           }
    410           FALLTHROUGH_INTENDED;
    411         case INVOKE_CUSTOM:
    412           if (file != nullptr) {
    413             os << opcode << " {";
    414             uint32_t call_site_idx = VRegB_35c();
    415             DumpArgs(VRegA_35c());
    416             os << "},  // call_site@" << call_site_idx;
    417             break;
    418           }
    419           FALLTHROUGH_INTENDED;
    420         default:
    421           os << opcode << " {";
    422           DumpArgs(VRegA_35c());
    423           os << "}, thing@" << VRegB_35c();
    424           break;
    425       }
    426       break;
    427     }
    428     case k3rc: {
    429       uint16_t first_reg = VRegC_3rc();
    430       uint16_t last_reg =  VRegC_3rc() + VRegA_3rc() - 1;
    431       switch (Opcode()) {
    432         case INVOKE_VIRTUAL_RANGE:
    433         case INVOKE_SUPER_RANGE:
    434         case INVOKE_DIRECT_RANGE:
    435         case INVOKE_STATIC_RANGE:
    436         case INVOKE_INTERFACE_RANGE:
    437           if (file != nullptr) {
    438             uint32_t method_idx = VRegB_3rc();
    439             os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
    440                << file->PrettyMethod(method_idx) << " // method@" << method_idx;
    441             break;
    442           }
    443           FALLTHROUGH_INTENDED;
    444         case INVOKE_VIRTUAL_RANGE_QUICK:
    445           if (file != nullptr) {
    446             uint32_t method_idx = VRegB_3rc();
    447             os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
    448                << "// vtable@" << method_idx;
    449             break;
    450           }
    451           FALLTHROUGH_INTENDED;
    452         case INVOKE_CUSTOM_RANGE:
    453           if (file != nullptr) {
    454             uint32_t call_site_idx = VRegB_3rc();
    455             os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
    456                << "// call_site@" << call_site_idx;
    457             break;
    458           }
    459           FALLTHROUGH_INTENDED;
    460         default:
    461           os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
    462              << "thing@" << VRegB_3rc();
    463           break;
    464       }
    465       break;
    466     }
    467     case k45cc: {
    468       uint32_t arg[kMaxVarArgRegs];
    469       GetVarArgs(arg);
    470       uint32_t method_idx = VRegB_45cc();
    471       uint32_t proto_idx = VRegH_45cc();
    472       os << opcode << " {";
    473       for (int i = 0; i < VRegA_45cc(); ++i) {
    474         if (i != 0) {
    475           os << ", ";
    476         }
    477         os << "v" << arg[i];
    478       }
    479       os << "}";
    480       if (file != nullptr) {
    481         os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
    482            << " // ";
    483       } else {
    484         os << ", ";
    485       }
    486       os << "method@" << method_idx << ", proto@" << proto_idx;
    487       break;
    488     }
    489     case k4rcc:
    490       switch (Opcode()) {
    491         case INVOKE_POLYMORPHIC_RANGE: {
    492           if (file != nullptr) {
    493             uint32_t method_idx = VRegB_4rcc();
    494             uint32_t proto_idx = VRegH_4rcc();
    495             os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
    496                << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
    497                << " // method@" << method_idx << ", proto@" << proto_idx;
    498             break;
    499           }
    500         }
    501         FALLTHROUGH_INTENDED;
    502         default: {
    503           uint32_t method_idx = VRegB_4rcc();
    504           uint32_t proto_idx = VRegH_4rcc();
    505           os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
    506              << "}, method@" << method_idx << ", proto@" << proto_idx;
    507         }
    508       }
    509       break;
    510     case k51l: os << StringPrintf("%s v%d, #%+" PRId64, opcode, VRegA_51l(), VRegB_51l()); break;
    511   }
    512   return os.str();
    513 }
    514 
    515 // Add some checks that ensure the flags make sense. We need a subclass to be in the context of
    516 // Instruction. Otherwise the flags from the instruction list don't work.
    517 struct InstructionStaticAsserts : private Instruction {
    518   #define IMPLIES(a, b) (!(a) || (b))
    519 
    520   #define VAR_ARGS_CHECK(o, c, pname, f, i, a, e, v) \
    521     static_assert(IMPLIES((f) == k35c || (f) == k45cc, \
    522                           ((v) & (kVerifyVarArg | kVerifyVarArgNonZero)) != 0), \
    523                   "Missing var-arg verification");
    524   #include "dex_instruction_list.h"
    525     DEX_INSTRUCTION_LIST(VAR_ARGS_CHECK)
    526   #undef DEX_INSTRUCTION_LIST
    527   #undef VAR_ARGS_CHECK
    528 
    529   #define VAR_ARGS_RANGE_CHECK(o, c, pname, f, i, a, e, v) \
    530     static_assert(IMPLIES((f) == k3rc || (f) == k4rcc, \
    531                           ((v) & (kVerifyVarArgRange | kVerifyVarArgRangeNonZero)) != 0), \
    532                   "Missing var-arg verification");
    533   #include "dex_instruction_list.h"
    534     DEX_INSTRUCTION_LIST(VAR_ARGS_RANGE_CHECK)
    535   #undef DEX_INSTRUCTION_LIST
    536   #undef VAR_ARGS_RANGE_CHECK
    537 
    538   #define EXPERIMENTAL_CHECK(o, c, pname, f, i, a, e, v) \
    539     static_assert(kHaveExperimentalInstructions || (((a) & kExperimental) == 0), \
    540                   "Unexpected experimental instruction.");
    541     #include "dex_instruction_list.h"
    542   DEX_INSTRUCTION_LIST(EXPERIMENTAL_CHECK)
    543   #undef DEX_INSTRUCTION_LIST
    544   #undef EXPERIMENTAL_CHECK
    545 };
    546 
    547 std::ostream& operator<<(std::ostream& os, const Instruction::Code& code) {
    548   return os << Instruction::Name(code);
    549 }
    550 
    551 uint32_t RangeInstructionOperands::GetOperand(size_t operand_index) const {
    552   DCHECK_LT(operand_index, GetNumberOfOperands());
    553   return first_operand_ + operand_index;
    554 }
    555 
    556 uint32_t VarArgsInstructionOperands::GetOperand(size_t operand_index) const {
    557   DCHECK_LT(operand_index, GetNumberOfOperands());
    558   return operands_[operand_index];
    559 }
    560 
    561 uint32_t NoReceiverInstructionOperands::GetOperand(size_t operand_index) const {
    562   DCHECK_LT(GetNumberOfOperands(), inner_->GetNumberOfOperands());
    563   // The receiver is the first operand and since we're skipping it, we need to
    564   // add 1 to the operand_index.
    565   return inner_->GetOperand(operand_index + 1);
    566 }
    567 
    568 }  // namespace art
    569