Brian Carlstrom | 7940e44 | 2013-07-12 13:46:57 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2013 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 "sea.h" |
| 18 | |
| 19 | #include "file_output_stream.h" |
| 20 | |
| 21 | #define MAX_REACHING_DEF_ITERERATIONS (10) |
| 22 | |
| 23 | namespace sea_ir { |
| 24 | |
| 25 | SeaGraph SeaGraph::graph_; |
| 26 | int SeaNode::current_max_node_id_ = 0; |
| 27 | |
| 28 | |
| 29 | SeaGraph* SeaGraph::GetCurrentGraph() { |
| 30 | return &sea_ir::SeaGraph::graph_; |
| 31 | } |
| 32 | |
| 33 | void SeaGraph::DumpSea(std::string filename) const { |
| 34 | std::string result; |
| 35 | result += "digraph seaOfNodes {\n"; |
| 36 | for (std::vector<Region*>::const_iterator cit = regions_.begin(); cit != regions_.end(); cit++) { |
| 37 | (*cit)->ToDot(result); |
| 38 | } |
| 39 | result += "}\n"; |
| 40 | art::File* file = art::OS::OpenFile(filename.c_str(), true, true); |
| 41 | art::FileOutputStream fos(file); |
| 42 | fos.WriteFully(result.c_str(), result.size()); |
| 43 | LOG(INFO) << "Written SEA string to file."; |
| 44 | } |
| 45 | |
| 46 | void SeaGraph::AddEdge(Region* src, Region* dst) const { |
| 47 | src->AddSuccessor(dst); |
| 48 | dst->AddPredecessor(src); |
| 49 | } |
| 50 | |
| 51 | void SeaGraph::ComputeDownExposedDefs() { |
| 52 | for (std::vector<Region*>::iterator region_it = regions_.begin(); |
| 53 | region_it != regions_.end(); region_it++) { |
| 54 | (*region_it)->ComputeDownExposedDefs(); |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | void SeaGraph::ComputeReachingDefs() { |
| 59 | // Iterate until the reaching definitions set doesn't change anymore. |
| 60 | // (See Cooper & Torczon, "Engineering a Compiler", second edition, page 487) |
| 61 | bool changed = true; |
| 62 | int iteration = 0; |
| 63 | while (changed && (iteration < MAX_REACHING_DEF_ITERERATIONS)) { |
| 64 | iteration++; |
| 65 | changed = false; |
| 66 | // TODO: optimize the ordering if this becomes performance bottleneck. |
| 67 | for (std::vector<Region*>::iterator regions_it = regions_.begin(); |
| 68 | regions_it != regions_.end(); |
| 69 | regions_it++) { |
| 70 | changed |= (*regions_it)->UpdateReachingDefs(); |
| 71 | } |
| 72 | } |
| 73 | DCHECK(!changed) << "Reaching definitions computation did not reach a fixed point."; |
| 74 | } |
| 75 | |
| 76 | |
| 77 | void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item, |
| 78 | uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file) { |
| 79 | const uint16_t* code = code_item->insns_; |
| 80 | const size_t size_in_code_units = code_item->insns_size_in_code_units_; |
| 81 | |
| 82 | Region* r = NULL; |
| 83 | // This maps target instruction pointers to their corresponding region objects. |
| 84 | std::map<const uint16_t*, Region*> target_regions; |
| 85 | size_t i = 0; |
| 86 | |
| 87 | // Pass: Find the start instruction of basic blocks |
| 88 | // by locating targets and flow-though instructions of branches. |
| 89 | while (i < size_in_code_units) { |
| 90 | const art::Instruction* inst = art::Instruction::At(&code[i]); |
| 91 | if (inst->IsBranch()||inst->IsUnconditional()) { |
| 92 | int32_t offset = inst->GetTargetOffset(); |
| 93 | if (target_regions.end() == target_regions.find(&code[i+offset])) { |
| 94 | Region* region = GetNewRegion(); |
| 95 | target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i+offset], region)); |
| 96 | } |
| 97 | if (inst->CanFlowThrough() && |
| 98 | (target_regions.end() == target_regions.find(&code[i+inst->SizeInCodeUnits()]))) { |
| 99 | Region* region = GetNewRegion(); |
| 100 | target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i+inst->SizeInCodeUnits()], region)); |
| 101 | } |
| 102 | } |
| 103 | i += inst->SizeInCodeUnits(); |
| 104 | } |
| 105 | |
| 106 | // Pass: Assign instructions to region nodes and |
| 107 | // assign branches their control flow successors. |
| 108 | i = 0; |
| 109 | r = GetNewRegion(); |
| 110 | sea_ir::InstructionNode* last_node = NULL; |
| 111 | sea_ir::InstructionNode* node = NULL; |
| 112 | while (i < size_in_code_units) { |
| 113 | const art::Instruction* inst = art::Instruction::At(&code[i]); //TODO: find workaround for this |
| 114 | last_node = node; |
| 115 | node = new sea_ir::InstructionNode(inst); |
| 116 | |
| 117 | if (inst->IsBranch() || inst->IsUnconditional()) { |
| 118 | int32_t offset = inst->GetTargetOffset(); |
| 119 | std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i+offset]); |
| 120 | DCHECK(it != target_regions.end()); |
| 121 | AddEdge(r, it->second); // Add edge to branch target. |
| 122 | } |
| 123 | |
| 124 | std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i]); |
| 125 | if (target_regions.end() != it) { |
| 126 | // Get the already created region because this is a branch target. |
| 127 | Region* nextRegion = it->second; |
| 128 | if (last_node->GetInstruction()->IsBranch() && last_node->GetInstruction()->CanFlowThrough()) { |
| 129 | AddEdge(r, it->second); // Add flow-through edge. |
| 130 | } |
| 131 | r = nextRegion; |
| 132 | } |
| 133 | bool definesRegister = (0 != |
| 134 | InstructionTools::instruction_attributes_[inst->Opcode()] && (1 << kDA)); |
| 135 | LOG(INFO) << inst->GetDexPc(code) << "*** " << inst->DumpString(&dex_file) |
| 136 | << " region:" <<r->StringId() << "Definition?" << definesRegister << std::endl; |
| 137 | r->AddChild(node); |
| 138 | i += inst->SizeInCodeUnits(); |
| 139 | } |
| 140 | |
| 141 | // Pass: compute downward-exposed definitions. |
| 142 | ComputeDownExposedDefs(); |
| 143 | |
| 144 | // Multiple Passes: Compute reaching definitions (iterative fixed-point algorithm) |
| 145 | ComputeReachingDefs(); |
| 146 | } |
| 147 | |
| 148 | Region* SeaGraph::GetNewRegion() { |
| 149 | Region* new_region = new Region(); |
| 150 | AddRegion(new_region); |
| 151 | return new_region; |
| 152 | } |
| 153 | |
| 154 | void SeaGraph::AddRegion(Region* r) { |
| 155 | DCHECK(r) << "Tried to add NULL region to SEA graph."; |
| 156 | regions_.push_back(r); |
| 157 | } |
| 158 | |
| 159 | void Region::AddChild(sea_ir::InstructionNode* instruction) { |
| 160 | DCHECK(instruction) << "Tried to add NULL instruction to region node."; |
| 161 | instructions_.push_back(instruction); |
| 162 | } |
| 163 | |
| 164 | SeaNode* Region::GetLastChild() const { |
| 165 | if (instructions_.size() > 0) { |
| 166 | return instructions_.back(); |
| 167 | } |
| 168 | return NULL; |
| 169 | } |
| 170 | |
| 171 | void InstructionNode::ToDot(std::string& result) const { |
| 172 | result += "// Instruction: \n" + StringId() + |
| 173 | " [label=\"" + instruction_->DumpString(NULL) + "\""; |
| 174 | if (de_def_) { |
| 175 | result += "style=bold"; |
| 176 | } |
| 177 | result += "];\n"; |
| 178 | } |
| 179 | |
| 180 | int InstructionNode::GetResultRegister() const { |
| 181 | if (!InstructionTools::IsDefinition(instruction_)) { |
| 182 | return NO_REGISTER; |
| 183 | } |
| 184 | return instruction_->VRegA(); |
| 185 | } |
| 186 | |
| 187 | void InstructionNode::MarkAsDEDef() { |
| 188 | de_def_ = true; |
| 189 | } |
| 190 | |
| 191 | void Region::ToDot(std::string& result) const { |
| 192 | result += "\n// Region: \n" + StringId() + " [label=\"region " + StringId() + "\"];"; |
| 193 | // Save instruction nodes that belong to this region. |
| 194 | for (std::vector<InstructionNode*>::const_iterator cit = instructions_.begin(); |
| 195 | cit != instructions_.end(); cit++) { |
| 196 | (*cit)->ToDot(result); |
| 197 | result += StringId() + " -> " + (*cit)->StringId() + ";\n"; |
| 198 | } |
| 199 | |
| 200 | for (std::vector<Region*>::const_iterator cit = successors_.begin(); cit != successors_.end(); |
| 201 | cit++) { |
| 202 | DCHECK(NULL != *cit) << "Null successor found for SeaNode" << GetLastChild()->StringId() << "."; |
| 203 | result += GetLastChild()->StringId() + " -> " + (*cit)->StringId() + ";\n\n"; |
| 204 | } |
| 205 | |
| 206 | // Save reaching definitions. |
| 207 | for (std::map<int, std::set<sea_ir::InstructionNode*>* >::const_iterator cit = |
| 208 | reaching_defs_.begin(); |
| 209 | cit != reaching_defs_.end(); cit++) { |
| 210 | for (std::set<sea_ir::InstructionNode*>::const_iterator |
| 211 | reaching_set_it = (*cit).second->begin(); |
| 212 | reaching_set_it != (*cit).second->end(); |
| 213 | reaching_set_it++) { |
| 214 | result += (*reaching_set_it)->StringId() + |
| 215 | " -> " + StringId() + |
| 216 | " [style=dotted]; // Reaching def.\n"; |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | result += "// End Region.\n"; |
| 221 | } |
| 222 | |
| 223 | |
| 224 | void Region::ComputeDownExposedDefs() { |
| 225 | for (std::vector<InstructionNode*>::const_iterator inst_it = instructions_.begin(); |
| 226 | inst_it != instructions_.end(); inst_it++) { |
| 227 | int reg_no = (*inst_it)->GetResultRegister(); |
| 228 | std::map<int, InstructionNode*>::iterator res = de_defs_.find(reg_no); |
| 229 | if ((reg_no != NO_REGISTER) && (res == de_defs_.end())) { |
| 230 | de_defs_.insert(std::pair<int, InstructionNode*>(reg_no, *inst_it)); |
| 231 | } else { |
| 232 | res->second = *inst_it; |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | for (std::map<int, sea_ir::InstructionNode*>::const_iterator cit = de_defs_.begin(); |
| 237 | cit != de_defs_.end(); cit++) { |
| 238 | (*cit).second->MarkAsDEDef(); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | |
| 243 | const std::map<int, sea_ir::InstructionNode*>* Region::GetDownExposedDefs() const { |
| 244 | return &de_defs_; |
| 245 | } |
| 246 | |
| 247 | std::map<int, std::set<sea_ir::InstructionNode*>* >* Region::GetReachingDefs() { |
| 248 | return &reaching_defs_; |
| 249 | } |
| 250 | |
| 251 | bool Region::UpdateReachingDefs() { |
| 252 | std::map<int, std::set<sea_ir::InstructionNode*>* > new_reaching; |
| 253 | for (std::vector<Region*>::const_iterator pred_it = predecessors_.begin(); |
| 254 | pred_it != predecessors_.end(); pred_it++) { |
| 255 | // The reaching_defs variable will contain reaching defs __for current predecessor only__ |
| 256 | std::map<int, std::set<sea_ir::InstructionNode*>* > reaching_defs; |
| 257 | std::map<int, std::set<sea_ir::InstructionNode*>* >* pred_reaching = (*pred_it)->GetReachingDefs(); |
| 258 | const std::map<int, InstructionNode*>* de_defs = (*pred_it)->GetDownExposedDefs(); |
| 259 | |
| 260 | // The definitions from the reaching set of the predecessor |
| 261 | // may be shadowed by downward exposed definitions from the predecessor, |
| 262 | // otherwise the defs from the reaching set are still good. |
| 263 | for (std::map<int, InstructionNode*>::const_iterator de_def = de_defs->begin(); |
| 264 | de_def != de_defs->end(); de_def++) { |
| 265 | std::set<InstructionNode*>* solo_def; |
| 266 | solo_def = new std::set<InstructionNode*>(); |
| 267 | solo_def->insert(de_def->second); |
| 268 | reaching_defs.insert( |
| 269 | std::pair<int const, std::set<InstructionNode*>*>(de_def->first, solo_def)); |
| 270 | } |
| 271 | LOG(INFO) << "Adding to " <<StringId() << "reaching set of " << (*pred_it)->StringId(); |
| 272 | reaching_defs.insert(pred_reaching->begin(), pred_reaching->end()); |
| 273 | |
| 274 | // Now we combine the reaching map coming from the current predecessor (reaching_defs) |
| 275 | // with the accumulated set from all predecessors so far (from new_reaching). |
| 276 | std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it = reaching_defs.begin(); |
| 277 | for (; reaching_it != reaching_defs.end(); reaching_it++) { |
| 278 | std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator crt_entry = |
| 279 | new_reaching.find(reaching_it->first); |
| 280 | if (new_reaching.end() != crt_entry) { |
| 281 | crt_entry->second->insert(reaching_it->second->begin(), reaching_it->second->end()); |
| 282 | } else { |
| 283 | new_reaching.insert( |
| 284 | std::pair<int, std::set<sea_ir::InstructionNode*>*>( |
| 285 | reaching_it->first, |
| 286 | reaching_it->second) ); |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | bool changed = false; |
| 291 | // Because the sets are monotonically increasing, |
| 292 | // we can compare sizes instead of using set comparison. |
| 293 | // TODO: Find formal proof. |
| 294 | int old_size = 0; |
| 295 | if (-1 == reaching_defs_size_) { |
| 296 | std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it = reaching_defs_.begin(); |
| 297 | for (; reaching_it != reaching_defs_.end(); reaching_it++) { |
| 298 | old_size += (*reaching_it).second->size(); |
| 299 | } |
| 300 | } else { |
| 301 | old_size = reaching_defs_size_; |
| 302 | } |
| 303 | int new_size = 0; |
| 304 | std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it = new_reaching.begin(); |
| 305 | for (; reaching_it != new_reaching.end(); reaching_it++) { |
| 306 | new_size += (*reaching_it).second->size(); |
| 307 | } |
| 308 | if (old_size != new_size) { |
| 309 | changed = true; |
| 310 | } |
| 311 | if (changed) { |
| 312 | reaching_defs_ = new_reaching; |
| 313 | reaching_defs_size_ = new_size; |
| 314 | } |
| 315 | return changed; |
| 316 | } |
| 317 | |
| 318 | void SeaNode::AddSuccessor(Region* successor) { |
| 319 | DCHECK(successor) << "Tried to add NULL successor to SEA node."; |
| 320 | successors_.push_back(successor); |
| 321 | return; |
| 322 | } |
| 323 | |
| 324 | void SeaNode::AddPredecessor(Region* predecessor) { |
| 325 | DCHECK(predecessor) << "Tried to add NULL predecessor to SEA node."; |
| 326 | predecessors_.push_back(predecessor); |
| 327 | } |
| 328 | |
| 329 | } // end namespace sea_ir |