Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2014 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 "graph_checker.h"
     18 
     19 #include <map>
     20 #include <string>
     21 #include <sstream>
     22 
     23 #include "base/bit_vector-inl.h"
     24 #include "base/stringprintf.h"
     25 
     26 namespace art {
     27 
     28 void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
     29   current_block_ = block;
     30 
     31   // Check consistency with respect to predecessors of `block`.
     32   const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
     33   std::map<HBasicBlock*, size_t> predecessors_count;
     34   for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
     35     HBasicBlock* p = predecessors.Get(i);
     36     ++predecessors_count[p];
     37   }
     38   for (auto& pc : predecessors_count) {
     39     HBasicBlock* p = pc.first;
     40     size_t p_count_in_block_predecessors = pc.second;
     41     const GrowableArray<HBasicBlock*>& p_successors = p->GetSuccessors();
     42     size_t block_count_in_p_successors = 0;
     43     for (size_t j = 0, f = p_successors.Size(); j < f; ++j) {
     44       if (p_successors.Get(j) == block) {
     45         ++block_count_in_p_successors;
     46       }
     47     }
     48     if (p_count_in_block_predecessors != block_count_in_p_successors) {
     49       AddError(StringPrintf(
     50           "Block %d lists %zu occurrences of block %d in its predecessors, whereas "
     51           "block %d lists %zu occurrences of block %d in its successors.",
     52           block->GetBlockId(), p_count_in_block_predecessors, p->GetBlockId(),
     53           p->GetBlockId(), block_count_in_p_successors, block->GetBlockId()));
     54     }
     55   }
     56 
     57   // Check consistency with respect to successors of `block`.
     58   const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
     59   std::map<HBasicBlock*, size_t> successors_count;
     60   for (size_t i = 0, e = successors.Size(); i < e; ++i) {
     61     HBasicBlock* s = successors.Get(i);
     62     ++successors_count[s];
     63   }
     64   for (auto& sc : successors_count) {
     65     HBasicBlock* s = sc.first;
     66     size_t s_count_in_block_successors = sc.second;
     67     const GrowableArray<HBasicBlock*>& s_predecessors = s->GetPredecessors();
     68     size_t block_count_in_s_predecessors = 0;
     69     for (size_t j = 0, f = s_predecessors.Size(); j < f; ++j) {
     70       if (s_predecessors.Get(j) == block) {
     71         ++block_count_in_s_predecessors;
     72       }
     73     }
     74     if (s_count_in_block_successors != block_count_in_s_predecessors) {
     75       AddError(StringPrintf(
     76           "Block %d lists %zu occurrences of block %d in its successors, whereas "
     77           "block %d lists %zu occurrences of block %d in its predecessors.",
     78           block->GetBlockId(), s_count_in_block_successors, s->GetBlockId(),
     79           s->GetBlockId(), block_count_in_s_predecessors, block->GetBlockId()));
     80     }
     81   }
     82 
     83   // Ensure `block` ends with a branch instruction.
     84   if (!block->EndsWithControlFlowInstruction()) {
     85     AddError(StringPrintf("Block %d does not end with a branch instruction.",
     86                           block->GetBlockId()));
     87   }
     88 
     89   // Visit this block's list of phis.
     90   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
     91     HInstruction* current = it.Current();
     92     // Ensure this block's list of phis contains only phis.
     93     if (!current->IsPhi()) {
     94       AddError(StringPrintf("Block %d has a non-phi in its phi list.",
     95                             current_block_->GetBlockId()));
     96     }
     97     if (current->GetNext() == nullptr && current != block->GetLastPhi()) {
     98       AddError(StringPrintf("The recorded last phi of block %d does not match "
     99                             "the actual last phi %d.",
    100                             current_block_->GetBlockId(),
    101                             current->GetId()));
    102     }
    103     current->Accept(this);
    104   }
    105 
    106   // Visit this block's list of instructions.
    107   for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
    108     HInstruction* current = it.Current();
    109     // Ensure this block's list of instructions does not contains phis.
    110     if (current->IsPhi()) {
    111       AddError(StringPrintf("Block %d has a phi in its non-phi list.",
    112                             current_block_->GetBlockId()));
    113     }
    114     if (current->GetNext() == nullptr && current != block->GetLastInstruction()) {
    115       AddError(StringPrintf("The recorded last instruction of block %d does not match "
    116                             "the actual last instruction %d.",
    117                             current_block_->GetBlockId(),
    118                             current->GetId()));
    119     }
    120     current->Accept(this);
    121   }
    122 }
    123 
    124 void GraphChecker::VisitBoundsCheck(HBoundsCheck* check) {
    125   if (!GetGraph()->HasBoundsChecks()) {
    126     AddError(StringPrintf("Instruction %s:%d is a HBoundsCheck, "
    127                           "but HasBoundsChecks() returns false",
    128                           check->DebugName(),
    129                           check->GetId()));
    130   }
    131 
    132   // Perform the instruction base checks too.
    133   VisitInstruction(check);
    134 }
    135 
    136 void GraphChecker::VisitInstruction(HInstruction* instruction) {
    137   if (seen_ids_.IsBitSet(instruction->GetId())) {
    138     AddError(StringPrintf("Instruction id %d is duplicate in graph.",
    139                           instruction->GetId()));
    140   } else {
    141     seen_ids_.SetBit(instruction->GetId());
    142   }
    143 
    144   // Ensure `instruction` is associated with `current_block_`.
    145   if (instruction->GetBlock() == nullptr) {
    146     AddError(StringPrintf("%s %d in block %d not associated with any block.",
    147                           instruction->IsPhi() ? "Phi" : "Instruction",
    148                           instruction->GetId(),
    149                           current_block_->GetBlockId()));
    150   } else if (instruction->GetBlock() != current_block_) {
    151     AddError(StringPrintf("%s %d in block %d associated with block %d.",
    152                           instruction->IsPhi() ? "Phi" : "Instruction",
    153                           instruction->GetId(),
    154                           current_block_->GetBlockId(),
    155                           instruction->GetBlock()->GetBlockId()));
    156   }
    157 
    158   // Ensure the inputs of `instruction` are defined in a block of the graph.
    159   for (HInputIterator input_it(instruction); !input_it.Done();
    160        input_it.Advance()) {
    161     HInstruction* input = input_it.Current();
    162     const HInstructionList& list = input->IsPhi()
    163         ? input->GetBlock()->GetPhis()
    164         : input->GetBlock()->GetInstructions();
    165     if (!list.Contains(input)) {
    166       AddError(StringPrintf("Input %d of instruction %d is not defined "
    167                             "in a basic block of the control-flow graph.",
    168                             input->GetId(),
    169                             instruction->GetId()));
    170     }
    171   }
    172 
    173   // Ensure the uses of `instruction` are defined in a block of the graph,
    174   // and the entry in the use list is consistent.
    175   for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
    176        !use_it.Done(); use_it.Advance()) {
    177     HInstruction* use = use_it.Current()->GetUser();
    178     const HInstructionList& list = use->IsPhi()
    179         ? use->GetBlock()->GetPhis()
    180         : use->GetBlock()->GetInstructions();
    181     if (!list.Contains(use)) {
    182       AddError(StringPrintf("User %s:%d of instruction %d is not defined "
    183                             "in a basic block of the control-flow graph.",
    184                             use->DebugName(),
    185                             use->GetId(),
    186                             instruction->GetId()));
    187     }
    188     size_t use_index = use_it.Current()->GetIndex();
    189     if ((use_index >= use->InputCount()) || (use->InputAt(use_index) != instruction)) {
    190       AddError(StringPrintf("User %s:%d of instruction %d has a wrong "
    191                             "UseListNode index.",
    192                             use->DebugName(),
    193                             use->GetId(),
    194                             instruction->GetId()));
    195     }
    196   }
    197 
    198   // Ensure the environment uses entries are consistent.
    199   for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
    200        !use_it.Done(); use_it.Advance()) {
    201     HEnvironment* use = use_it.Current()->GetUser();
    202     size_t use_index = use_it.Current()->GetIndex();
    203     if ((use_index >= use->Size()) || (use->GetInstructionAt(use_index) != instruction)) {
    204       AddError(StringPrintf("Environment user of %s:%d has a wrong "
    205                             "UseListNode index.",
    206                             instruction->DebugName(),
    207                             instruction->GetId()));
    208     }
    209   }
    210 
    211   // Ensure 'instruction' has pointers to its inputs' use entries.
    212   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
    213     HUserRecord<HInstruction*> input_record = instruction->InputRecordAt(i);
    214     HInstruction* input = input_record.GetInstruction();
    215     HUseListNode<HInstruction*>* use_node = input_record.GetUseNode();
    216     size_t use_index = use_node->GetIndex();
    217     if ((use_node == nullptr)
    218         || !input->GetUses().Contains(use_node)
    219         || (use_index >= e)
    220         || (use_index != i)) {
    221       AddError(StringPrintf("Instruction %s:%d has an invalid pointer to use entry "
    222                             "at input %u (%s:%d).",
    223                             instruction->DebugName(),
    224                             instruction->GetId(),
    225                             static_cast<unsigned>(i),
    226                             input->DebugName(),
    227                             input->GetId()));
    228     }
    229   }
    230 }
    231 
    232 void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
    233   VisitInstruction(invoke);
    234 
    235   if (invoke->IsStaticWithExplicitClinitCheck()) {
    236     size_t last_input_index = invoke->InputCount() - 1;
    237     HInstruction* last_input = invoke->InputAt(last_input_index);
    238     if (last_input == nullptr) {
    239       AddError(StringPrintf("Static invoke %s:%d marked as having an explicit clinit check "
    240                             "has a null pointer as last input.",
    241                             invoke->DebugName(),
    242                             invoke->GetId()));
    243     }
    244     if (!last_input->IsClinitCheck() && !last_input->IsLoadClass()) {
    245       AddError(StringPrintf("Static invoke %s:%d marked as having an explicit clinit check "
    246                             "has a last instruction (%s:%d) which is neither a clinit check "
    247                             "nor a load class instruction.",
    248                             invoke->DebugName(),
    249                             invoke->GetId(),
    250                             last_input->DebugName(),
    251                             last_input->GetId()));
    252     }
    253   }
    254 }
    255 
    256 void GraphChecker::VisitCheckCast(HCheckCast* check) {
    257   VisitInstruction(check);
    258   HInstruction* input = check->InputAt(1);
    259   if (!input->IsLoadClass()) {
    260     AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
    261                           check->DebugName(),
    262                           check->GetId(),
    263                           input->DebugName(),
    264                           input->GetId()));
    265   }
    266 }
    267 
    268 void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) {
    269   VisitInstruction(instruction);
    270   HInstruction* input = instruction->InputAt(1);
    271   if (!input->IsLoadClass()) {
    272     AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
    273                           instruction->DebugName(),
    274                           instruction->GetId(),
    275                           input->DebugName(),
    276                           input->GetId()));
    277   }
    278 }
    279 
    280 void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
    281   super_type::VisitBasicBlock(block);
    282 
    283   // Ensure there is no critical edge (i.e., an edge connecting a
    284   // block with multiple successors to a block with multiple
    285   // predecessors).
    286   if (block->GetSuccessors().Size() > 1) {
    287     for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
    288       HBasicBlock* successor = block->GetSuccessors().Get(j);
    289       if (successor->GetPredecessors().Size() > 1) {
    290         AddError(StringPrintf("Critical edge between blocks %d and %d.",
    291                               block->GetBlockId(),
    292                               successor->GetBlockId()));
    293       }
    294     }
    295   }
    296 
    297   // Check Phi uniqueness (no two Phis with the same type refer to the same register).
    298   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
    299     HPhi* phi = it.Current()->AsPhi();
    300     if (phi->GetNextEquivalentPhiWithSameType() != nullptr) {
    301       std::stringstream type_str;
    302       type_str << phi->GetType();
    303       AddError(StringPrintf("Equivalent phi (%d) found for VReg %d with type: %s",
    304           phi->GetId(), phi->GetRegNumber(), type_str.str().c_str()));
    305     }
    306   }
    307 
    308   if (block->IsLoopHeader()) {
    309     CheckLoop(block);
    310   }
    311 }
    312 
    313 void SSAChecker::CheckLoop(HBasicBlock* loop_header) {
    314   int id = loop_header->GetBlockId();
    315   HLoopInformation* loop_information = loop_header->GetLoopInformation();
    316 
    317   // Ensure the pre-header block is first in the list of
    318   // predecessors of a loop header.
    319   if (!loop_header->IsLoopPreHeaderFirstPredecessor()) {
    320     AddError(StringPrintf(
    321         "Loop pre-header is not the first predecessor of the loop header %d.",
    322         id));
    323   }
    324 
    325   // Ensure the loop header has only one incoming branch and the remaining
    326   // predecessors are back edges.
    327   size_t num_preds = loop_header->GetPredecessors().Size();
    328   if (num_preds < 2) {
    329     AddError(StringPrintf(
    330         "Loop header %d has less than two predecessors: %zu.",
    331         id,
    332         num_preds));
    333   } else {
    334     HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
    335     if (loop_information->IsBackEdge(*first_predecessor)) {
    336       AddError(StringPrintf(
    337           "First predecessor of loop header %d is a back edge.",
    338           id));
    339     }
    340     for (size_t i = 1, e = loop_header->GetPredecessors().Size(); i < e; ++i) {
    341       HBasicBlock* predecessor = loop_header->GetPredecessors().Get(i);
    342       if (!loop_information->IsBackEdge(*predecessor)) {
    343         AddError(StringPrintf(
    344             "Loop header %d has multiple incoming (non back edge) blocks.",
    345             id));
    346       }
    347     }
    348   }
    349 
    350   const ArenaBitVector& loop_blocks = loop_information->GetBlocks();
    351 
    352   // Ensure back edges belong to the loop.
    353   size_t num_back_edges = loop_information->GetBackEdges().Size();
    354   if (num_back_edges == 0) {
    355     AddError(StringPrintf(
    356         "Loop defined by header %d has no back edge.",
    357         id));
    358   } else {
    359     for (size_t i = 0; i < num_back_edges; ++i) {
    360       int back_edge_id = loop_information->GetBackEdges().Get(i)->GetBlockId();
    361       if (!loop_blocks.IsBitSet(back_edge_id)) {
    362         AddError(StringPrintf(
    363             "Loop defined by header %d has an invalid back edge %d.",
    364             id,
    365             back_edge_id));
    366       }
    367     }
    368   }
    369 
    370   // Ensure all blocks in the loop are live and dominated by the loop header.
    371   for (uint32_t i : loop_blocks.Indexes()) {
    372     HBasicBlock* loop_block = GetGraph()->GetBlocks().Get(i);
    373     if (loop_block == nullptr) {
    374       AddError(StringPrintf("Loop defined by header %d contains a previously removed block %d.",
    375                             id,
    376                             i));
    377     } else if (!loop_header->Dominates(loop_block)) {
    378       AddError(StringPrintf("Loop block %d not dominated by loop header %d.",
    379                             i,
    380                             id));
    381     }
    382   }
    383 
    384   // If this is a nested loop, ensure the outer loops contain a superset of the blocks.
    385   for (HLoopInformationOutwardIterator it(*loop_header); !it.Done(); it.Advance()) {
    386     HLoopInformation* outer_info = it.Current();
    387     if (!loop_blocks.IsSubsetOf(&outer_info->GetBlocks())) {
    388       AddError(StringPrintf("Blocks of loop defined by header %d are not a subset of blocks of "
    389                             "an outer loop defined by header %d.",
    390                             id,
    391                             outer_info->GetHeader()->GetBlockId()));
    392     }
    393   }
    394 }
    395 
    396 void SSAChecker::VisitInstruction(HInstruction* instruction) {
    397   super_type::VisitInstruction(instruction);
    398 
    399   // Ensure an instruction dominates all its uses.
    400   for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
    401        !use_it.Done(); use_it.Advance()) {
    402     HInstruction* use = use_it.Current()->GetUser();
    403     if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
    404       AddError(StringPrintf("Instruction %d in block %d does not dominate "
    405                             "use %d in block %d.",
    406                             instruction->GetId(), current_block_->GetBlockId(),
    407                             use->GetId(), use->GetBlock()->GetBlockId()));
    408     }
    409   }
    410 
    411   // Ensure an instruction having an environment is dominated by the
    412   // instructions contained in the environment.
    413   for (HEnvironment* environment = instruction->GetEnvironment();
    414        environment != nullptr;
    415        environment = environment->GetParent()) {
    416     for (size_t i = 0, e = environment->Size(); i < e; ++i) {
    417       HInstruction* env_instruction = environment->GetInstructionAt(i);
    418       if (env_instruction != nullptr
    419           && !env_instruction->StrictlyDominates(instruction)) {
    420         AddError(StringPrintf("Instruction %d in environment of instruction %d "
    421                               "from block %d does not dominate instruction %d.",
    422                               env_instruction->GetId(),
    423                               instruction->GetId(),
    424                               current_block_->GetBlockId(),
    425                               instruction->GetId()));
    426       }
    427     }
    428   }
    429 }
    430 
    431 static Primitive::Type PrimitiveKind(Primitive::Type type) {
    432   switch (type) {
    433     case Primitive::kPrimBoolean:
    434     case Primitive::kPrimByte:
    435     case Primitive::kPrimShort:
    436     case Primitive::kPrimChar:
    437     case Primitive::kPrimInt:
    438       return Primitive::kPrimInt;
    439     default:
    440       return type;
    441   }
    442 }
    443 
    444 void SSAChecker::VisitPhi(HPhi* phi) {
    445   VisitInstruction(phi);
    446 
    447   // Ensure the first input of a phi is not itself.
    448   if (phi->InputAt(0) == phi) {
    449     AddError(StringPrintf("Loop phi %d in block %d is its own first input.",
    450                           phi->GetId(),
    451                           phi->GetBlock()->GetBlockId()));
    452   }
    453 
    454   // Ensure the number of inputs of a phi is the same as the number of
    455   // its predecessors.
    456   const GrowableArray<HBasicBlock*>& predecessors =
    457     phi->GetBlock()->GetPredecessors();
    458   if (phi->InputCount() != predecessors.Size()) {
    459     AddError(StringPrintf(
    460         "Phi %d in block %d has %zu inputs, "
    461         "but block %d has %zu predecessors.",
    462         phi->GetId(), phi->GetBlock()->GetBlockId(), phi->InputCount(),
    463         phi->GetBlock()->GetBlockId(), predecessors.Size()));
    464   } else {
    465     // Ensure phi input at index I either comes from the Ith
    466     // predecessor or from a block that dominates this predecessor.
    467     for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
    468       HInstruction* input = phi->InputAt(i);
    469       HBasicBlock* predecessor = predecessors.Get(i);
    470       if (!(input->GetBlock() == predecessor
    471             || input->GetBlock()->Dominates(predecessor))) {
    472         AddError(StringPrintf(
    473             "Input %d at index %zu of phi %d from block %d is not defined in "
    474             "predecessor number %zu nor in a block dominating it.",
    475             input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(),
    476             i));
    477       }
    478     }
    479   }
    480   // Ensure that the inputs have the same primitive kind as the phi.
    481   for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
    482     HInstruction* input = phi->InputAt(i);
    483     if (PrimitiveKind(input->GetType()) != PrimitiveKind(phi->GetType())) {
    484         AddError(StringPrintf(
    485             "Input %d at index %zu of phi %d from block %d does not have the "
    486             "same type as the phi: %s versus %s",
    487             input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(),
    488             Primitive::PrettyDescriptor(input->GetType()),
    489             Primitive::PrettyDescriptor(phi->GetType())));
    490     }
    491   }
    492   if (phi->GetType() != HPhi::ToPhiType(phi->GetType())) {
    493     AddError(StringPrintf("Phi %d in block %d does not have an expected phi type: %s",
    494                           phi->GetId(),
    495                           phi->GetBlock()->GetBlockId(),
    496                           Primitive::PrettyDescriptor(phi->GetType())));
    497   }
    498 }
    499 
    500 void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) {
    501   HInstruction* input = instruction->InputAt(input_index);
    502   if (input->IsIntConstant()) {
    503     int32_t value = input->AsIntConstant()->GetValue();
    504     if (value != 0 && value != 1) {
    505       AddError(StringPrintf(
    506           "%s instruction %d has a non-Boolean constant input %d whose value is: %d.",
    507           instruction->DebugName(),
    508           instruction->GetId(),
    509           static_cast<int>(input_index),
    510           value));
    511     }
    512   } else if (input->GetType() == Primitive::kPrimInt
    513              && (input->IsPhi() || input->IsAnd() || input->IsOr() || input->IsXor())) {
    514     // TODO: We need a data-flow analysis to determine if the Phi or
    515     //       binary operation is actually Boolean. Allow for now.
    516   } else if (input->GetType() != Primitive::kPrimBoolean) {
    517     AddError(StringPrintf(
    518         "%s instruction %d has a non-Boolean input %d whose type is: %s.",
    519         instruction->DebugName(),
    520         instruction->GetId(),
    521         static_cast<int>(input_index),
    522         Primitive::PrettyDescriptor(input->GetType())));
    523   }
    524 }
    525 
    526 void SSAChecker::VisitIf(HIf* instruction) {
    527   VisitInstruction(instruction);
    528   HandleBooleanInput(instruction, 0);
    529 }
    530 
    531 void SSAChecker::VisitBooleanNot(HBooleanNot* instruction) {
    532   VisitInstruction(instruction);
    533   HandleBooleanInput(instruction, 0);
    534 }
    535 
    536 void SSAChecker::VisitCondition(HCondition* op) {
    537   VisitInstruction(op);
    538   if (op->GetType() != Primitive::kPrimBoolean) {
    539     AddError(StringPrintf(
    540         "Condition %s %d has a non-Boolean result type: %s.",
    541         op->DebugName(), op->GetId(),
    542         Primitive::PrettyDescriptor(op->GetType())));
    543   }
    544   HInstruction* lhs = op->InputAt(0);
    545   HInstruction* rhs = op->InputAt(1);
    546   if (PrimitiveKind(lhs->GetType()) != PrimitiveKind(rhs->GetType())) {
    547     AddError(StringPrintf(
    548         "Condition %s %d has inputs of different types: %s, and %s.",
    549         op->DebugName(), op->GetId(),
    550         Primitive::PrettyDescriptor(lhs->GetType()),
    551         Primitive::PrettyDescriptor(rhs->GetType())));
    552   }
    553   if (!op->IsEqual() && !op->IsNotEqual()) {
    554     if ((lhs->GetType() == Primitive::kPrimNot)) {
    555       AddError(StringPrintf(
    556           "Condition %s %d uses an object as left-hand side input.",
    557           op->DebugName(), op->GetId()));
    558     } else if (rhs->GetType() == Primitive::kPrimNot) {
    559       AddError(StringPrintf(
    560           "Condition %s %d uses an object as right-hand side input.",
    561           op->DebugName(), op->GetId()));
    562     }
    563   }
    564 }
    565 
    566 void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) {
    567   VisitInstruction(op);
    568   if (op->IsUShr() || op->IsShr() || op->IsShl()) {
    569     if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) {
    570       AddError(StringPrintf(
    571           "Shift operation %s %d has a non-int kind second input: "
    572           "%s of type %s.",
    573           op->DebugName(), op->GetId(),
    574           op->InputAt(1)->DebugName(),
    575           Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
    576     }
    577   } else {
    578     if (PrimitiveKind(op->InputAt(0)->GetType()) != PrimitiveKind(op->InputAt(1)->GetType())) {
    579       AddError(StringPrintf(
    580           "Binary operation %s %d has inputs of different types: "
    581           "%s, and %s.",
    582           op->DebugName(), op->GetId(),
    583           Primitive::PrettyDescriptor(op->InputAt(0)->GetType()),
    584           Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
    585     }
    586   }
    587 
    588   if (op->IsCompare()) {
    589     if (op->GetType() != Primitive::kPrimInt) {
    590       AddError(StringPrintf(
    591           "Compare operation %d has a non-int result type: %s.",
    592           op->GetId(),
    593           Primitive::PrettyDescriptor(op->GetType())));
    594     }
    595   } else {
    596     // Use the first input, so that we can also make this check for shift operations.
    597     if (PrimitiveKind(op->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
    598       AddError(StringPrintf(
    599           "Binary operation %s %d has a result type different "
    600           "from its input type: %s vs %s.",
    601           op->DebugName(), op->GetId(),
    602           Primitive::PrettyDescriptor(op->GetType()),
    603           Primitive::PrettyDescriptor(op->InputAt(0)->GetType())));
    604     }
    605   }
    606 }
    607 
    608 void SSAChecker::VisitConstant(HConstant* instruction) {
    609   HBasicBlock* block = instruction->GetBlock();
    610   if (!block->IsEntryBlock()) {
    611     AddError(StringPrintf(
    612         "%s %d should be in the entry block but is in block %d.",
    613         instruction->DebugName(),
    614         instruction->GetId(),
    615         block->GetBlockId()));
    616   }
    617 }
    618 
    619 }  // namespace art
    620