graph_checker.cc revision 7c5367badfe61b96c5836d495d286cee64861579
1ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain/*
2ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * Copyright (C) 2014 The Android Open Source Project
3ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain *
4ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * Licensed under the Apache License, Version 2.0 (the "License");
5ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * you may not use this file except in compliance with the License.
6ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * You may obtain a copy of the License at
7ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain *
8ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain *      http://www.apache.org/licenses/LICENSE-2.0
9ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain *
10ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * Unless required by applicable law or agreed to in writing, software
11ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * distributed under the License is distributed on an "AS IS" BASIS,
12ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * See the License for the specific language governing permissions and
14ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain * limitations under the License.
15ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain */
16ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
17ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain#include "graph_checker.h"
18ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
19ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain#include <map>
20ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain#include <sstream>
217c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray#include <string>
22ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
237e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain#include "base/bit_vector-inl.h"
247e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain
25ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillainnamespace art {
26ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
27ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillainvoid GraphChecker::VisitBasicBlock(HBasicBlock* block) {
28ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  current_block_ = block;
29ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
30ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // Check consistency with respect to predecessors of `block`.
31ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
32ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  std::map<HBasicBlock*, size_t> predecessors_count;
33ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
34ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    HBasicBlock* p = predecessors.Get(i);
35ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    ++predecessors_count[p];
36ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
37ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  for (auto& pc : predecessors_count) {
38ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    HBasicBlock* p = pc.first;
39ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    size_t p_count_in_block_predecessors = pc.second;
40ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    const GrowableArray<HBasicBlock*>& p_successors = p->GetSuccessors();
41ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    size_t block_count_in_p_successors = 0;
42ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    for (size_t j = 0, f = p_successors.Size(); j < f; ++j) {
43ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      if (p_successors.Get(j) == block) {
44ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain        ++block_count_in_p_successors;
45ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      }
46ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
47ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    if (p_count_in_block_predecessors != block_count_in_p_successors) {
48ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      std::stringstream error;
49ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << "Block " << block->GetBlockId()
50ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " lists " << p_count_in_block_predecessors
51ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " occurrences of block " << p->GetBlockId()
52ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " in its predecessors, whereas block " << p->GetBlockId()
53ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " lists " << block_count_in_p_successors
54ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " occurrences of block " << block->GetBlockId()
55ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " in its successors.";
5691356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
57ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
58ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
59ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
60ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // Check consistency with respect to successors of `block`.
61ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
62ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  std::map<HBasicBlock*, size_t> successors_count;
63ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  for (size_t i = 0, e = successors.Size(); i < e; ++i) {
64ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    HBasicBlock* s = successors.Get(i);
65ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    ++successors_count[s];
66ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
67ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  for (auto& sc : successors_count) {
68ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    HBasicBlock* s = sc.first;
69ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    size_t s_count_in_block_successors = sc.second;
70ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    const GrowableArray<HBasicBlock*>& s_predecessors = s->GetPredecessors();
71ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    size_t block_count_in_s_predecessors = 0;
72ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    for (size_t j = 0, f = s_predecessors.Size(); j < f; ++j) {
73ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      if (s_predecessors.Get(j) == block) {
74ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain        ++block_count_in_s_predecessors;
75ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      }
76ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
77ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    if (s_count_in_block_successors != block_count_in_s_predecessors) {
78ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      std::stringstream error;
79ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << "Block " << block->GetBlockId()
80ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " lists " << s_count_in_block_successors
81ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " occurrences of block " << s->GetBlockId()
82ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " in its successors, whereas block " << s->GetBlockId()
83ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " lists " << block_count_in_s_predecessors
84ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " occurrences of block " << block->GetBlockId()
85ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " in its predecessors.";
8691356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
87ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
88ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
89ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
90ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // Ensure `block` ends with a branch instruction.
91ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  HInstruction* last_inst = block->GetLastInstruction();
92ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  if (last_inst == nullptr || !last_inst->IsControlFlow()) {
93ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    std::stringstream error;
94ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    error  << "Block " << block->GetBlockId()
95ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain           << " does not end with a branch instruction.";
9691356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe    errors_.push_back(error.str());
97ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
98ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
99ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // Visit this block's list of phis.
100ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
101ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    // Ensure this block's list of phis contains only phis.
102ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    if (!it.Current()->IsPhi()) {
103ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      std::stringstream error;
104ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << "Block " << current_block_->GetBlockId()
105ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " has a non-phi in its phi list.";
10691356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
107ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
108ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    it.Current()->Accept(this);
109ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
110ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
111ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // Visit this block's list of instructions.
112ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  for (HInstructionIterator it(block->GetInstructions()); !it.Done();
113ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain       it.Advance()) {
114ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    // Ensure this block's list of instructions does not contains phis.
115ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    if (it.Current()->IsPhi()) {
116ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      std::stringstream error;
117ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << "Block " << current_block_->GetBlockId()
118ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << " has a phi in its non-phi list.";
11991356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
120ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
121ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    it.Current()->Accept(this);
122ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
123ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain}
124ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
125ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillainvoid GraphChecker::VisitInstruction(HInstruction* instruction) {
1267c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray  if (seen_ids_.IsBitSet(instruction->GetId())) {
1277c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray    std::stringstream error;
1287c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray    error << "Duplicate id in graph " << instruction->GetId() << ".";
1297c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray    errors_.push_back(error.str());
1307c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray  } else {
1317c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray    seen_ids_.SetBit(instruction->GetId());
1327c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray  }
1337c5367badfe61b96c5836d495d286cee64861579Nicolas Geoffray
134ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // Ensure `instruction` is associated with `current_block_`.
135ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  if (instruction->GetBlock() != current_block_) {
136ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    std::stringstream error;
137ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    if (instruction->IsPhi()) {
138ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << "Phi ";
139ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    } else {
140ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << "Instruction ";
141ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
142ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    error << instruction->GetId() << " in block "
143ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain          << current_block_->GetBlockId();
144ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    if (instruction->GetBlock() != nullptr) {
145ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << " associated with block "
146ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain            << instruction->GetBlock()->GetBlockId() << ".";
147ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    } else {
148ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      error << " not associated with any block.";
149ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
15091356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe    errors_.push_back(error.str());
151ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
1526b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain
1536b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain  // Ensure the inputs of `instruction` are defined in a block of the graph.
1546b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain  for (HInputIterator input_it(instruction); !input_it.Done();
1556b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain       input_it.Advance()) {
1566b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    HInstruction* input = input_it.Current();
1576b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    const HInstructionList& list = input->IsPhi()
1586b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain        ? input->GetBlock()->GetPhis()
1596b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain        : input->GetBlock()->GetInstructions();
1606b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    if (!list.Contains(input)) {
1616b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain      std::stringstream error;
1626b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain      error << "Input " << input->GetId()
1636b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain            << " of instruction " << instruction->GetId()
1646b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain            << " is not defined in a basic block of the control-flow graph.";
16591356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
1666b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    }
1676b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain  }
1686b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain
1696b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain  // Ensure the uses of `instruction` are defined in a block of the graph.
1706b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain  for (HUseIterator<HInstruction> use_it(instruction->GetUses());
1716b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain       !use_it.Done(); use_it.Advance()) {
1726b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    HInstruction* use = use_it.Current()->GetUser();
1736b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    const HInstructionList& list = use->IsPhi()
1746b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain        ? use->GetBlock()->GetPhis()
1756b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain        : use->GetBlock()->GetInstructions();
1766b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    if (!list.Contains(use)) {
1776b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain      std::stringstream error;
1786b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain      error << "User " << use->GetId()
1796b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain            << " of instruction " << instruction->GetId()
1806b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain            << " is not defined in a basic block of the control-flow graph.";
18191356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
1826b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain    }
1836b46923ff0197c95f1e7ea0bc730961df6725cc9Roland Levillain  }
184ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain}
185ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
186ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillainvoid SSAChecker::VisitBasicBlock(HBasicBlock* block) {
187ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  super_type::VisitBasicBlock(block);
188ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
189ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // Ensure there is no critical edge (i.e., an edge connecting a
190ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // block with multiple successors to a block with multiple
191ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  // predecessors).
192ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  if (block->GetSuccessors().Size() > 1) {
193ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
194ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      HBasicBlock* successor = block->GetSuccessors().Get(j);
195ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      if (successor->GetPredecessors().Size() > 1) {
196ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain        std::stringstream error;
197ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain        error << "Critical edge between blocks " << block->GetBlockId()
198ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain              << " and "  << successor->GetBlockId() << ".";
19991356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe        errors_.push_back(error.str());
200ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      }
201ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
202ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
2036b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
2046b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  if (block->IsLoopHeader()) {
2056b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    CheckLoop(block);
2066b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  }
2076b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain}
2086b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
2096b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillainvoid SSAChecker::CheckLoop(HBasicBlock* loop_header) {
2106b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  int id = loop_header->GetBlockId();
2116b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
2126b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // Ensure the pre-header block is first in the list of
2136b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // predecessors of a loop header.
2146b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  if (!loop_header->IsLoopPreHeaderFirstPredecessor()) {
2156b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    std::stringstream error;
2166b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    error << "Loop pre-header is not the first predecessor of the loop header "
2176b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain          << id << ".";
21891356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe    errors_.push_back(error.str());
2196b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  }
2206b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
2216b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // Ensure the loop header has only two predecessors and that only the
2226b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // second one is a back edge.
2236b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  if (loop_header->GetPredecessors().Size() < 2) {
2246b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    std::stringstream error;
2256b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    error << "Loop header " << id << " has less than two predecessors.";
22691356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe    errors_.push_back(error.str());
2276b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  } else if (loop_header->GetPredecessors().Size() > 2) {
2286b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    std::stringstream error;
2296b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    error << "Loop header " << id << " has more than two predecessors.";
23091356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe    errors_.push_back(error.str());
2316b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  } else {
2326b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    HLoopInformation* loop_information = loop_header->GetLoopInformation();
2336b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
2346b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    if (loop_information->IsBackEdge(first_predecessor)) {
2356b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      std::stringstream error;
2366b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      error << "First predecessor of loop header " << id << " is a back edge.";
23791356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
2386b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    }
2396b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    HBasicBlock* second_predecessor = loop_header->GetPredecessors().Get(1);
2406b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    if (!loop_information->IsBackEdge(second_predecessor)) {
2416b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      std::stringstream error;
2426b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      error << "Second predecessor of loop header " << id
2436b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain            << " is not a back edge.";
24491356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
2456b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    }
2466b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  }
2476b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
2486b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // Ensure there is only one back edge per loop.
2496b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  size_t num_back_edges =
2506b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    loop_header->GetLoopInformation()->GetBackEdges().Size();
2516b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  if (num_back_edges != 1) {
2526b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      std::stringstream error;
2536b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      error << "Loop defined by header " << id << " has "
2546b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain            << num_back_edges << " back edge(s).";
25591356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
2566b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  }
2577e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain
2587e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain  // Ensure all blocks in the loop are dominated by the loop header.
2597e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain  const ArenaBitVector& loop_blocks =
2607e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain    loop_header->GetLoopInformation()->GetBlocks();
2617e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain  for (uint32_t i : loop_blocks.Indexes()) {
2627e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain    HBasicBlock* loop_block = GetGraph()->GetBlocks().Get(i);
2637e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain    if (!loop_header->Dominates(loop_block)) {
2647e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain      std::stringstream error;
2657e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain      error << "Loop block " << loop_block->GetBlockId()
2667e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain            << " not dominated by loop header " << id;
26791356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
2687e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain    }
2697e53b415e5e587cd7961978f6da7347248f40b29Roland Levillain  }
270ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain}
271ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
272ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillainvoid SSAChecker::VisitInstruction(HInstruction* instruction) {
273ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  super_type::VisitInstruction(instruction);
274ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
275a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain  // Ensure an instruction dominates all its uses.
276a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain  for (HUseIterator<HInstruction> use_it(instruction->GetUses());
277a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain       !use_it.Done(); use_it.Advance()) {
278a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain    HInstruction* use = use_it.Current()->GetUser();
2796c82d40eb142771086f5531998de2273ba5cc08cRoland Levillain    if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
280ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain      std::stringstream error;
281a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain      error << "Instruction " << instruction->GetId()
282a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain            << " in block " << current_block_->GetBlockId()
283a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain            << " does not dominate use " << use->GetId()
284a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain            << " in block " << use->GetBlock()->GetBlockId() << ".";
28591356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
286ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain    }
287ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain  }
288a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain
289a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain  // Ensure an instruction having an environment is dominated by the
290a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain  // instructions contained in the environment.
291a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain  HEnvironment* environment = instruction->GetEnvironment();
292a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain  if (environment != nullptr) {
293a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
294a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain      HInstruction* env_instruction = environment->GetInstructionAt(i);
295a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain      if (env_instruction != nullptr
2966c82d40eb142771086f5531998de2273ba5cc08cRoland Levillain          && !env_instruction->StrictlyDominates(instruction)) {
297a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain        std::stringstream error;
298a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain        error << "Instruction " << env_instruction->GetId()
299a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain              << " in environment of instruction " << instruction->GetId()
300a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain              << " from block " << current_block_->GetBlockId()
301a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain              << " does not dominate instruction " << instruction->GetId()
302a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain              << ".";
30391356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe        errors_.push_back(error.str());
304a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain      }
305a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain    }
306a8069ce1c3caa4f9b1651988986f3732152c186dRoland Levillain  }
307ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain}
308ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain
3096b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillainvoid SSAChecker::VisitPhi(HPhi* phi) {
3106b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  VisitInstruction(phi);
3116b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
3126b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // Ensure the first input of a phi is not itself.
3136b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  if (phi->InputAt(0) == phi) {
3146b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      std::stringstream error;
3156b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      error << "Loop phi " << phi->GetId()
3166b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain            << " in block " << phi->GetBlock()->GetBlockId()
3176b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain            << " is its own first input.";
31891356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe      errors_.push_back(error.str());
3196b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  }
3206b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
3216b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // Ensure the number of phi inputs is the same as the number of
3226b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  // its predecessors.
3236b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  const GrowableArray<HBasicBlock*>& predecessors =
3246b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    phi->GetBlock()->GetPredecessors();
3256b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  if (phi->InputCount() != predecessors.Size()) {
3266b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    std::stringstream error;
3276b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    error << "Phi " << phi->GetId()
3286b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain          << " in block " << phi->GetBlock()->GetBlockId()
3296b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain          << " has " << phi->InputCount() << " inputs, but block "
3306b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain          << phi->GetBlock()->GetBlockId() << " has "
3316b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain          << predecessors.Size() << " predecessors.";
33291356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe    errors_.push_back(error.str());
3336b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  } else {
3346b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    // Ensure phi input at index I either comes from the Ith
3356b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    // predecessor or from a block that dominates this predecessor.
3366b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
3376b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      HInstruction* input = phi->InputAt(i);
3386b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      HBasicBlock* predecessor = predecessors.Get(i);
3396b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      if (!(input->GetBlock() == predecessor
3406b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain            || input->GetBlock()->Dominates(predecessor))) {
3416b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain        std::stringstream error;
3426b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain        error << "Input " << input->GetId() << " at index " << i
3436b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain              << " of phi " << phi->GetId()
3446b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain              << " from block " << phi->GetBlock()->GetBlockId()
3456b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain              << " is not defined in predecessor number " << i
3466b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain              << " nor in a block dominating it.";
34791356c028022180dfbe54ed7f5f465041c8b23ffAndreas Gampe        errors_.push_back(error.str());
3486b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain      }
3496b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain    }
3506b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain  }
3516b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain}
3526b879ddc0959df1cec871f0d41f11cce35a11716Roland Levillain
3533159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffraystatic Primitive::Type PrimitiveKind(Primitive::Type type) {
3543159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  switch (type) {
3553159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    case Primitive::kPrimBoolean:
3563159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    case Primitive::kPrimByte:
3573159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    case Primitive::kPrimShort:
3583159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    case Primitive::kPrimChar:
3593159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    case Primitive::kPrimInt:
3603159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      return Primitive::kPrimInt;
3613159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    default:
3623159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      return type;
3633159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  }
3643159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray}
3653159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray
3663159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffrayvoid SSAChecker::VisitCondition(HCondition* op) {
3673159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  VisitInstruction(op);
3683159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  // TODO: check inputs types, and special case the `null` check.
3693159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  if (op->GetType() != Primitive::kPrimBoolean) {
3703159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    std::stringstream error;
3713159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    error << "Condition " << op->DebugName() << " " << op->GetId()
3723159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray          << " has a non-boolean result type: "
3733159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray          << op->GetType() << ".";
3743159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    errors_.push_back(error.str());
3753159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  }
3763159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray}
3773159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray
3783159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffrayvoid SSAChecker::VisitBinaryOperation(HBinaryOperation* op) {
3793159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  VisitInstruction(op);
3803159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  if (op->IsUShr() || op->IsShr() || op->IsShl()) {
3813159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) {
3823159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      std::stringstream error;
3833159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      error << "Shift operation " << op->DebugName() << " " << op->GetId()
3843159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << " has a non-int kind second input: "
3853159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << op->InputAt(1)->DebugName() << " of type " << op->InputAt(1)->GetType()
3863159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << ".";
3873159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      errors_.push_back(error.str());
3883159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    }
3893159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  } else {
3903159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    if (PrimitiveKind(op->InputAt(1)->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
3913159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      std::stringstream error;
3923159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      error << "Binary operation " << op->DebugName() << " " << op->GetId()
3933159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << " has inputs of different type: "
3943159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << op->InputAt(0)->GetType() << ", and " << op->InputAt(1)->GetType()
3953159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << ".";
3963159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      errors_.push_back(error.str());
3973159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    }
3983159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  }
3993159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray
4003159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  if (op->IsCompare()) {
4013159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    if (op->GetType() != Primitive::kPrimInt) {
4023159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      std::stringstream error;
4033159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      error << "Compare operation " << op->GetId()
4043159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << " has a non-int result type: "
4053159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << op->GetType() << ".";
4063159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      errors_.push_back(error.str());
4073159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    }
4083159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  } else {
4093159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    // Use the first input, so that we can also make this check for shift operations.
4103159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    if (PrimitiveKind(op->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
4113159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      std::stringstream error;
4123159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      error << "Binary operation " << op->DebugName() << " " << op->GetId()
4133159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << " has a result type different than its input type: "
4143159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << op->GetType() << ", and " << op->InputAt(1)->GetType()
4153159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray            << ".";
4163159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray      errors_.push_back(error.str());
4173159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray    }
4183159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray  }
4193159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray}
4203159674c0863f53cfbc1913d493550221ac47f02Nicolas Geoffray
421ccc07a9579c554443cd03a306ca9b4f943fd2a93Roland Levillain}  // namespace art
422