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