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 #ifndef ART_COMPILER_DEX_PASS_DRIVER_ME_H_
18 #define ART_COMPILER_DEX_PASS_DRIVER_ME_H_
19 
20 #include <cstdlib>
21 #include <cstring>
22 
23 #include "bb_optimizations.h"
24 #include "dataflow_iterator.h"
25 #include "dataflow_iterator-inl.h"
26 #include "dex_flags.h"
27 #include "pass_driver.h"
28 #include "pass_manager.h"
29 #include "pass_me.h"
30 #include "safe_map.h"
31 
32 namespace art {
33 
34 class PassManager;
35 class PassManagerOptions;
36 
37 class PassDriverME: public PassDriver {
38  public:
PassDriverME(const PassManager * const pass_manager,CompilationUnit * cu)39   explicit PassDriverME(const PassManager* const pass_manager, CompilationUnit* cu)
40       : PassDriver(pass_manager), pass_me_data_holder_(), dump_cfg_folder_("/sdcard/") {
41         pass_me_data_holder_.bb = nullptr;
42         pass_me_data_holder_.c_unit = cu;
43   }
44 
~PassDriverME()45   ~PassDriverME() {
46   }
47 
DispatchPass(const Pass * pass)48   void DispatchPass(const Pass* pass) {
49     VLOG(compiler) << "Dispatching " << pass->GetName();
50     const PassME* me_pass = down_cast<const PassME*>(pass);
51 
52     DataFlowAnalysisMode mode = me_pass->GetTraversal();
53 
54     switch (mode) {
55       case kPreOrderDFSTraversal:
56         DoWalkBasicBlocks<PreOrderDfsIterator>(&pass_me_data_holder_, me_pass);
57         break;
58       case kRepeatingPreOrderDFSTraversal:
59         DoWalkBasicBlocks<RepeatingPreOrderDfsIterator>(&pass_me_data_holder_, me_pass);
60         break;
61       case kRepeatingPostOrderDFSTraversal:
62         DoWalkBasicBlocks<RepeatingPostOrderDfsIterator>(&pass_me_data_holder_, me_pass);
63         break;
64       case kReversePostOrderDFSTraversal:
65         DoWalkBasicBlocks<ReversePostOrderDfsIterator>(&pass_me_data_holder_, me_pass);
66         break;
67       case kRepeatingReversePostOrderDFSTraversal:
68         DoWalkBasicBlocks<RepeatingReversePostOrderDfsIterator>(&pass_me_data_holder_, me_pass);
69         break;
70       case kPostOrderDOMTraversal:
71         DoWalkBasicBlocks<PostOrderDOMIterator>(&pass_me_data_holder_, me_pass);
72         break;
73       case kTopologicalSortTraversal:
74         DoWalkBasicBlocks<TopologicalSortIterator>(&pass_me_data_holder_, me_pass);
75         break;
76       case kLoopRepeatingTopologicalSortTraversal:
77         DoWalkBasicBlocks<LoopRepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass);
78         break;
79       case kAllNodes:
80         DoWalkBasicBlocks<AllNodesIterator>(&pass_me_data_holder_, me_pass);
81         break;
82       case kNoNodes:
83         break;
84       default:
85         LOG(FATAL) << "Iterator mode not handled in dispatcher: " << mode;
86         break;
87     }
88   }
89 
RunPass(const Pass * pass,bool time_split)90   bool RunPass(const Pass* pass, bool time_split) OVERRIDE {
91     // Paranoid: c_unit and pass cannot be null, and the pass should have a name.
92     DCHECK(pass != nullptr);
93     DCHECK(pass->GetName() != nullptr && pass->GetName()[0] != 0);
94     CompilationUnit* c_unit = pass_me_data_holder_.c_unit;
95     DCHECK(c_unit != nullptr);
96 
97     // Do we perform a time split
98     if (time_split) {
99       c_unit->NewTimingSplit(pass->GetName());
100     }
101 
102     // First, work on determining pass verbosity.
103     bool old_print_pass = c_unit->print_pass;
104     c_unit->print_pass = pass_manager_->GetOptions().GetPrintAllPasses();
105     auto* const options = &pass_manager_->GetOptions();
106     const std::string& print_pass_list = options->GetPrintPassList();
107     if (!print_pass_list.empty() && strstr(print_pass_list.c_str(), pass->GetName()) != nullptr) {
108       c_unit->print_pass = true;
109     }
110 
111     // Next, check if there are any overridden settings for the pass that change default
112     // configuration.
113     c_unit->overridden_pass_options.clear();
114     FillOverriddenPassSettings(options, pass->GetName(), c_unit->overridden_pass_options);
115     if (c_unit->print_pass) {
116       for (auto setting_it : c_unit->overridden_pass_options) {
117         LOG(INFO) << "Overridden option \"" << setting_it.first << ":"
118           << setting_it.second << "\" for pass \"" << pass->GetName() << "\"";
119       }
120     }
121 
122     // Check the pass gate first.
123     bool should_apply_pass = pass->Gate(&pass_me_data_holder_);
124     if (should_apply_pass) {
125       // Applying the pass: first start, doWork, and end calls.
126       this->ApplyPass(&pass_me_data_holder_, pass);
127 
128       bool should_dump = (c_unit->enable_debug & (1 << kDebugDumpCFG)) != 0;
129 
130       const std::string& dump_pass_list = pass_manager_->GetOptions().GetDumpPassList();
131       if (!dump_pass_list.empty()) {
132         const bool found = strstr(dump_pass_list.c_str(), pass->GetName());
133         should_dump = should_dump || found;
134       }
135 
136       if (should_dump) {
137         // Do we want to log it?
138         if ((c_unit->enable_debug&  (1 << kDebugDumpCFG)) != 0) {
139           // Do we have a pass folder?
140           const PassME* me_pass = (down_cast<const PassME*>(pass));
141           const char* passFolder = me_pass->GetDumpCFGFolder();
142           DCHECK(passFolder != nullptr);
143 
144           if (passFolder[0] != 0) {
145             // Create directory prefix.
146             std::string prefix = GetDumpCFGFolder();
147             prefix += passFolder;
148             prefix += "/";
149 
150             c_unit->mir_graph->DumpCFG(prefix.c_str(), false);
151           }
152         }
153       }
154     }
155 
156     // Before wrapping up with this pass, restore old pass verbosity flag.
157     c_unit->print_pass = old_print_pass;
158 
159     // If the pass gate passed, we can declare success.
160     return should_apply_pass;
161   }
162 
PrintPassOptions(PassManager * manager)163   static void PrintPassOptions(PassManager* manager) {
164     for (const auto* pass : *manager->GetDefaultPassList()) {
165       const PassME* me_pass = down_cast<const PassME*>(pass);
166       if (me_pass->HasOptions()) {
167         LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:";
168         SafeMap<const std::string, const OptionContent> overridden_settings;
169         FillOverriddenPassSettings(&manager->GetOptions(), me_pass->GetName(),
170                                    overridden_settings);
171         me_pass->PrintPassOptions(overridden_settings);
172       }
173     }
174   }
175 
GetDumpCFGFolder()176   const char* GetDumpCFGFolder() const {
177     return dump_cfg_folder_;
178   }
179 
180  protected:
181   /** @brief The data holder that contains data needed for the PassDriverME. */
182   PassMEDataHolder pass_me_data_holder_;
183 
184   /** @brief Dump CFG base folder: where is the base folder for dumping CFGs. */
185   const char* dump_cfg_folder_;
186 
DoWalkBasicBlocks(PassMEDataHolder * data,const PassME * pass,DataflowIterator * iterator)187   static void DoWalkBasicBlocks(PassMEDataHolder* data, const PassME* pass,
188                                 DataflowIterator* iterator) {
189     // Paranoid: Check the iterator before walking the BasicBlocks.
190     DCHECK(iterator != nullptr);
191     bool change = false;
192     for (BasicBlock* bb = iterator->Next(change); bb != nullptr; bb = iterator->Next(change)) {
193       data->bb = bb;
194       change = pass->Worker(data);
195     }
196   }
197 
198   template <typename Iterator>
DoWalkBasicBlocks(PassMEDataHolder * data,const PassME * pass)199   inline static void DoWalkBasicBlocks(PassMEDataHolder* data, const PassME* pass) {
200       DCHECK(data != nullptr);
201       CompilationUnit* c_unit = data->c_unit;
202       DCHECK(c_unit != nullptr);
203       Iterator iterator(c_unit->mir_graph.get());
204       DoWalkBasicBlocks(data, pass, &iterator);
205     }
206 
207   /**
208    * @brief Fills the settings_to_fill by finding all of the applicable options in the
209    * overridden_pass_options_list_.
210    * @param pass_name The pass name for which to fill settings.
211    * @param settings_to_fill Fills the options to contain the mapping of name of option to the new
212    * configuration.
213    */
FillOverriddenPassSettings(const PassManagerOptions * options,const char * pass_name,SafeMap<const std::string,const OptionContent> & settings_to_fill)214   static void FillOverriddenPassSettings(
215       const PassManagerOptions* options, const char* pass_name,
216       SafeMap<const std::string, const OptionContent>& settings_to_fill) {
217     const std::string& settings = options->GetOverriddenPassOptions();
218     const size_t settings_len = settings.size();
219 
220     // Before anything, check if we care about anything right now.
221     if (settings_len == 0) {
222       return;
223     }
224 
225     const size_t pass_name_len = strlen(pass_name);
226     const size_t min_setting_size = 4;  // 2 delimiters, 1 setting name, 1 setting
227     size_t search_pos = 0;
228 
229     // If there is no room for pass options, exit early.
230     if (settings_len < pass_name_len + min_setting_size) {
231       return;
232     }
233 
234     do {
235       search_pos = settings.find(pass_name, search_pos);
236 
237       // Check if we found this pass name in rest of string.
238       if (search_pos == std::string::npos) {
239         // No more settings for this pass.
240         break;
241       }
242 
243       // The string contains the pass name. Now check that there is
244       // room for the settings: at least one char for setting name,
245       // two chars for two delimiter, and at least one char for setting.
246       if (search_pos + pass_name_len + min_setting_size >= settings_len) {
247         // No more settings for this pass.
248         break;
249       }
250 
251       // Update the current search position to not include the pass name.
252       search_pos += pass_name_len;
253 
254       // The format must be "PassName:SettingName:#" where # is the setting.
255       // Thus look for the first ":" which must exist.
256       if (settings[search_pos] != ':') {
257         // Missing delimiter right after pass name.
258         continue;
259       } else {
260         search_pos += 1;
261       }
262 
263       // Now look for the actual setting by finding the next ":" delimiter.
264       const size_t setting_name_pos = search_pos;
265       size_t setting_pos = settings.find(':', setting_name_pos);
266 
267       if (setting_pos == std::string::npos) {
268         // Missing a delimiter that would capture where setting starts.
269         continue;
270       } else if (setting_pos == setting_name_pos) {
271         // Missing setting thus did not move from setting name
272         continue;
273       } else {
274         // Skip the delimiter.
275         setting_pos += 1;
276       }
277 
278       // Look for the terminating delimiter which must be a comma.
279       size_t next_configuration_separator = settings.find(',', setting_pos);
280       if (next_configuration_separator == std::string::npos) {
281         next_configuration_separator = settings_len;
282       }
283 
284       // Prevent end of string errors.
285       if (next_configuration_separator == setting_pos) {
286           continue;
287       }
288 
289       // Get the actual setting itself.
290       std::string setting_string =
291           settings.substr(setting_pos, next_configuration_separator - setting_pos);
292 
293       std::string setting_name =
294           settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
295 
296       // We attempt to convert the option value to integer. Strtoll is being used to
297       // convert because it is exception safe.
298       char* end_ptr = nullptr;
299       const char* setting_ptr = setting_string.c_str();
300       DCHECK(setting_ptr != nullptr);  // Paranoid: setting_ptr must be a valid pointer.
301       int64_t int_value = strtoll(setting_ptr, &end_ptr, 0);
302       DCHECK(end_ptr != nullptr);  // Paranoid: end_ptr must be set by the strtoll call.
303 
304       // If strtoll call succeeded, the option is now considered as integer.
305       if (*setting_ptr != '\0' && end_ptr != setting_ptr && *end_ptr == '\0') {
306         settings_to_fill.Put(setting_name, OptionContent(int_value));
307       } else {
308         // Otherwise, it is considered as a string.
309         settings_to_fill.Put(setting_name, OptionContent(setting_string.c_str()));
310       }
311       search_pos = next_configuration_separator;
312     } while (true);
313   }
314 };
315 }  // namespace art
316 #endif  // ART_COMPILER_DEX_PASS_DRIVER_ME_H_
317 
318