blob: 95c36e559d5be5152c5e67681f5e878dfc470f19 [file] [log] [blame]
Brian Carlstrom7940e442013-07-12 13:46:57 -07001/*
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
23namespace sea_ir {
24
25SeaGraph SeaGraph::graph_;
26int SeaNode::current_max_node_id_ = 0;
27
28
29SeaGraph* SeaGraph::GetCurrentGraph() {
30 return &sea_ir::SeaGraph::graph_;
31}
32
33void 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
46void SeaGraph::AddEdge(Region* src, Region* dst) const {
47 src->AddSuccessor(dst);
48 dst->AddPredecessor(src);
49}
50
51void 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
58void 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
77void 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
148Region* SeaGraph::GetNewRegion() {
149 Region* new_region = new Region();
150 AddRegion(new_region);
151 return new_region;
152}
153
154void SeaGraph::AddRegion(Region* r) {
155 DCHECK(r) << "Tried to add NULL region to SEA graph.";
156 regions_.push_back(r);
157}
158
159void Region::AddChild(sea_ir::InstructionNode* instruction) {
160 DCHECK(instruction) << "Tried to add NULL instruction to region node.";
161 instructions_.push_back(instruction);
162}
163
164SeaNode* Region::GetLastChild() const {
165 if (instructions_.size() > 0) {
166 return instructions_.back();
167 }
168 return NULL;
169}
170
171void 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
180int InstructionNode::GetResultRegister() const {
181 if (!InstructionTools::IsDefinition(instruction_)) {
182 return NO_REGISTER;
183 }
184 return instruction_->VRegA();
185}
186
187void InstructionNode::MarkAsDEDef() {
188 de_def_ = true;
189}
190
191void 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
224void 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
243const std::map<int, sea_ir::InstructionNode*>* Region::GetDownExposedDefs() const {
244 return &de_defs_;
245}
246
247std::map<int, std::set<sea_ir::InstructionNode*>* >* Region::GetReachingDefs() {
248 return &reaching_defs_;
249}
250
251bool 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
318void SeaNode::AddSuccessor(Region* successor) {
319 DCHECK(successor) << "Tried to add NULL successor to SEA node.";
320 successors_.push_back(successor);
321 return;
322}
323
324void 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