• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "optimize_bytecode.h"
17 
18 #include "assembler/assembly-emitter.h"
19 #include "assembler/extensions/extensions.h"
20 #include "bytecode_instruction.h"
21 #include "bytecodeopt_options.h"
22 #include "codegen.h"
23 #include "common.h"
24 #include "compiler/optimizer/ir/constants.h"
25 #include "compiler/optimizer/ir_builder/ir_builder.h"
26 #include "compiler/optimizer/ir_builder/pbc_iterator.h"
27 #include "compiler/optimizer/optimizations/cleanup.h"
28 #include "compiler/optimizer/optimizations/lowering.h"
29 #include "compiler/optimizer/optimizations/move_constants.h"
30 #include "compiler/optimizer/optimizations/regalloc/reg_alloc.h"
31 #include "compiler/optimizer/optimizations/vn.h"
32 #include "libpandabase/mem/arena_allocator.h"
33 #include "libpandabase/mem/pool_manager.h"
34 #include "libpandafile/class_data_accessor.h"
35 #include "libpandafile/class_data_accessor-inl.h"
36 #include "libpandafile/method_data_accessor.h"
37 #include "reg_acc_alloc.h"
38 #include "reg_encoder.h"
39 #include "runtime_adapter.h"
40 
41 #include <regex>
42 
43 namespace panda::bytecodeopt {
44 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
45 panda::bytecodeopt::Options options("");
46 
47 template <typename T>
RunOpts(compiler::Graph * graph)48 constexpr void RunOpts(compiler::Graph *graph)
49 {
50     graph->RunPass<compiler::Cleanup>();
51     graph->RunPass<T>();
52 }
53 
54 template <typename First, typename Second, typename... Rest>
RunOpts(compiler::Graph * graph)55 constexpr void RunOpts(compiler::Graph *graph)
56 {
57     RunOpts<First>(graph);
58     RunOpts<Second, Rest...>(graph);
59 }
60 
RunOptimizations(compiler::Graph * graph,BytecodeOptIrInterface * iface)61 bool RunOptimizations(compiler::Graph *graph, BytecodeOptIrInterface *iface)
62 {
63     constexpr int OPT_LEVEL_0 = 0;
64 
65     if (panda::bytecodeopt::options.GetOptLevel() == OPT_LEVEL_0) {
66         return false;
67     }
68 
69     graph->RunPass<compiler::Cleanup>();
70     ASSERT(graph->IsDynamicMethod());
71     RunOpts<compiler::ValNum, compiler::Lowering, compiler::MoveConstants>(graph);
72 
73     // this pass should run just before register allocator
74     graph->RunPass<compiler::Cleanup>();
75     graph->RunPass<RegAccAlloc>();
76 
77     graph->RunPass<compiler::Cleanup>();
78     if (!RegAlloc(graph)) {
79         LOG(ERROR, BYTECODE_OPTIMIZER) << "Failed compiler::RegAlloc";
80         return false;
81     }
82 
83     graph->RunPass<compiler::Cleanup>();
84     if (!graph->RunPass<RegEncoder>()) {
85         LOG(ERROR, BYTECODE_OPTIMIZER) << "Failed RegEncoder";
86         return false;
87     }
88 
89     return true;
90 }
91 
BuildMapFromPcToIns(pandasm::Function & function,BytecodeOptIrInterface & ir_interface,const compiler::Graph * graph,compiler::RuntimeInterface::MethodPtr method_ptr)92 void BuildMapFromPcToIns(pandasm::Function &function, BytecodeOptIrInterface &ir_interface,
93                          const compiler::Graph *graph, compiler::RuntimeInterface::MethodPtr method_ptr)
94 {
95     function.local_variable_debug.clear();
96     auto *pc_ins_map = ir_interface.GetPcInsMap();
97     pc_ins_map->reserve(function.ins.size());
98     auto instructions_buf = graph->GetRuntime()->GetMethodCode(method_ptr);
99     compiler::BytecodeInstructions instructions(instructions_buf, graph->GetRuntime()->GetMethodCodeSize(method_ptr));
100     size_t idx = 0;
101     for (auto insn : instructions) {
102         pandasm::Ins &ins = function.ins[idx++];
103         pc_ins_map->emplace(instructions.GetPc(insn), &ins);
104         if (idx >= function.ins.size()) {
105             break;
106         }
107     }
108 }
109 
ExtractTypeInfo(const pandasm::Function & function,compiler::RuntimeInterface * adapter,std::unordered_map<int32_t,TypeInfoIndex> * order_type_map,const pandasm::Program * prog)110 static void ExtractTypeInfo(const pandasm::Function &function, compiler::RuntimeInterface *adapter,
111                             std::unordered_map<int32_t, TypeInfoIndex> *order_type_map, const pandasm::Program *prog)
112 {
113     const auto &annos = function.metadata->GetAnnotations();
114     const auto type_anno = std::find_if(annos.begin(), annos.end(),
115                                         [](const auto &an) { return an.GetName() == TSTYPE_ANNO_RECORD_NAME; });
116     if (type_anno == annos.end()) {
117         return;
118     }
119     const auto &elems = type_anno->GetElements();
120     const auto type_elem = std::find_if(elems.begin(), elems.end(),
121                                         [](const auto &e) { return e.GetName() == TSTYPE_ANNO_ELEMENT_NAME; });
122     if (type_elem == elems.end()) {
123         return;
124     }
125     const auto *key_val = type_elem->GetValue();
126     ASSERT(key_val != nullptr);
127     ASSERT(key_val->GetType() == pandasm::Value::Type::LITERALARRAY);
128     const auto key = key_val->GetAsScalar()->GetValue<std::string>();
129     adapter->SetTypeLiteralArrayKey(key);
130     auto array_iter = prog->literalarray_table.find(key);
131     ASSERT(array_iter != prog->literalarray_table.end());
132     const auto &array = array_iter->second.literals_;
133     // 4: size must be multiple of 4 because values consits of tuple of tag, order, tag, type
134     ASSERT(array.size() % 4 == 0);
135     size_t i = 1;
136     while (i < array.size()) {
137         auto order = bit_cast<int32_t>(std::get<uint32_t>(array[i].value_));
138         i += 2;  // 2: skip tag between order and type
139         TypeInfoIndex type;
140         if (array[i].tag_ == panda_file::LiteralTag::LITERALARRAY) {
141             type = std::get<std::string>(array[i].value_);
142         } else {
143             ASSERT(array[i].tag_ == panda_file::LiteralTag::BUILTINTYPEINDEX);
144             type = std::get<BuiltinIndexType>(array[i].value_);
145         }
146 
147         if (order < 0) {
148             adapter->AddPcTypePair(order, type);  // arguments
149         } else {
150             order_type_map->emplace(order, type);  // instructions
151         }
152         i += 2;  // 2: skip tag between type and order
153     }
154 }
155 
BuildMapFromPcToType(const pandasm::Function & function,const compiler::Graph * graph,compiler::RuntimeInterface::MethodPtr method_ptr,const pandasm::Program * prog)156 static void BuildMapFromPcToType(const pandasm::Function &function, const compiler::Graph *graph,
157                                  compiler::RuntimeInterface::MethodPtr method_ptr, const pandasm::Program *prog)
158 {
159     std::unordered_map<int32_t, TypeInfoIndex> tmp_order_type_map;
160     ExtractTypeInfo(function, graph->GetRuntime(), &tmp_order_type_map, prog);
161     if (tmp_order_type_map.empty()) {
162         return;
163     }
164     const auto *instruction_buf = graph->GetRuntime()->GetMethodCode(method_ptr);
165     compiler::BytecodeInstructions instructions(instruction_buf, graph->GetRuntime()->GetMethodCodeSize(method_ptr));
166     int32_t order = 0;
167     size_t num_collected = 0;
168     for (const auto &insn : instructions) {
169         const auto it = tmp_order_type_map.find(order++);
170         if (it == tmp_order_type_map.end()) {
171             continue;
172         }
173         auto pc = static_cast<int32_t>(instructions.GetPc(insn));
174         graph->GetRuntime()->AddPcTypePair(pc, it->second);
175         num_collected++;
176 
177         // stop when all typeinfo has been collected
178         if (num_collected == tmp_order_type_map.size()) {
179             break;
180         }
181     }
182 }
183 
ColumnNumberPropagate(pandasm::Function * function)184 static void ColumnNumberPropagate(pandasm::Function *function)
185 {
186     auto &ins_vec = function->ins;
187     uint32_t cn = compiler::INVALID_COLUMN_NUM;
188     // handle the instructions that are at the beginning of code but do not have column number
189     size_t k = 0;
190     while (k < ins_vec.size() && cn == compiler::INVALID_COLUMN_NUM) {
191         cn = ins_vec[k++].ins_debug.column_number;
192     }
193     if (cn == compiler::INVALID_COLUMN_NUM) {
194         LOG(DEBUG, BYTECODE_OPTIMIZER) << "Failed ColumnNumberPropagate: All insts have invalid column number";
195         return;
196     }
197     for (size_t j = 0; j < k - 1; j++) {
198         ins_vec[j].ins_debug.SetColumnNumber(cn);
199     }
200 
201     // handle other instructions that do not have column number
202     for (; k < ins_vec.size(); k++) {
203         if (ins_vec[k].ins_debug.column_number != compiler::INVALID_COLUMN_NUM) {
204             cn = ins_vec[k].ins_debug.column_number;
205         } else {
206             ins_vec[k].ins_debug.SetColumnNumber(cn);
207         }
208     }
209 }
210 
LineNumberPropagate(pandasm::Function * function)211 static void LineNumberPropagate(pandasm::Function *function)
212 {
213     if (function == nullptr || function->ins.empty()) {
214         return;
215     }
216     size_t ln = 0;
217     auto &ins_vec = function->ins;
218 
219     // handle the instructions that are at the beginning of code but do not have line number
220     size_t i = 0;
221     while (i < ins_vec.size() && ln == 0) {
222         ln = ins_vec[i++].ins_debug.line_number;
223     }
224     if (ln == 0) {
225         LOG(DEBUG, BYTECODE_OPTIMIZER) << "Failed LineNumberPropagate: All insts have invalid line number";
226         return;
227     }
228     for (size_t j = 0; j < i - 1; j++) {
229         ins_vec[j].ins_debug.SetLineNumber(ln);
230     }
231 
232     // handle other instructions that do not have line number
233     for (; i < ins_vec.size(); i++) {
234         if (ins_vec[i].ins_debug.line_number != 0) {
235             ln = ins_vec[i].ins_debug.line_number;
236         } else {
237             ins_vec[i].ins_debug.SetLineNumber(ln);
238         }
239     }
240 }
241 
DebugInfoPropagate(pandasm::Function & function,const compiler::Graph * graph,BytecodeOptIrInterface & ir_interface)242 static void DebugInfoPropagate(pandasm::Function &function, const compiler::Graph *graph,
243                                BytecodeOptIrInterface &ir_interface)
244 {
245     LineNumberPropagate(&function);
246     if (graph->IsDynamicMethod()) {
247         ColumnNumberPropagate(&function);
248     }
249     ir_interface.ClearPcInsMap();
250 }
251 
SkipFunction(const pandasm::Function & function,const std::string & func_name)252 static bool SkipFunction(const pandasm::Function &function, const std::string &func_name)
253 {
254     if (panda::bytecodeopt::options.WasSetMethodRegex()) {
255         static std::regex rgx(panda::bytecodeopt::options.GetMethodRegex());
256         if (!std::regex_match(func_name, rgx)) {
257             LOG(INFO, BYTECODE_OPTIMIZER) << "Skip Function " << func_name << ": Function's name doesn't match regex";
258             return true;
259         }
260     }
261 
262     if (panda::bytecodeopt::options.IsSkipMethodsWithEh() && !function.catch_blocks.empty()) {
263         LOG(INFO, BYTECODE_OPTIMIZER) << "Was not optimized " << func_name << ": Function has catch blocks";
264         return true;
265     }
266 
267     if ((function.regs_num + function.GetParamsNum()) > compiler::VIRTUAL_FRAME_SIZE) {
268         LOG(ERROR, BYTECODE_OPTIMIZER) << "Unable to optimize " << func_name
269                                        << ": Function frame size is larger than allowed one";
270         return true;
271     }
272     return false;
273 }
274 
SetCompilerOptions(bool is_dynamic)275 static void SetCompilerOptions(bool is_dynamic)
276 {
277     compiler::options.SetCompilerUseSafepoint(false);
278     compiler::options.SetCompilerSupportInitObjectInst(true);
279     if (!compiler::options.WasSetCompilerMaxBytecodeSize()) {
280         compiler::options.SetCompilerMaxBytecodeSize(MAX_BYTECODE_SIZE);
281     }
282     if (is_dynamic) {
283         panda::bytecodeopt::options.SetSkipMethodsWithEh(true);
284     }
285 }
286 
OptimizeFunction(pandasm::Program * prog,const pandasm::AsmEmitter::PandaFileToPandaAsmMaps * maps,const panda_file::MethodDataAccessor & mda,bool is_dynamic)287 bool OptimizeFunction(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
288                       const panda_file::MethodDataAccessor &mda, bool is_dynamic)
289 {
290     ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER};
291     ArenaAllocator local_allocator {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
292 
293     SetCompilerOptions(is_dynamic);
294 
295     auto ir_interface = BytecodeOptIrInterface(maps, prog);
296 
297     auto func_name = ir_interface.GetMethodIdByOffset(mda.GetMethodId().GetOffset());
298     LOG(INFO, BYTECODE_OPTIMIZER) << "Optimizing function: " << func_name;
299 
300     auto it = prog->function_table.find(func_name);
301     if (it == prog->function_table.end()) {
302         LOG(ERROR, BYTECODE_OPTIMIZER) << "Cannot find function: " << func_name;
303         return false;
304     }
305     auto method_ptr = reinterpret_cast<compiler::RuntimeInterface::MethodPtr>(mda.GetMethodId().GetOffset());
306 
307     panda::BytecodeOptimizerRuntimeAdapter adapter(mda.GetPandaFile());
308     auto graph = allocator.New<compiler::Graph>(&allocator, &local_allocator, Arch::NONE, method_ptr, &adapter, false,
309                                                 nullptr, is_dynamic, true);
310 
311     panda::pandasm::Function &function = it->second;
312 
313     if (SkipFunction(function, func_name)) {
314         return false;
315     }
316 
317     BuildMapFromPcToType(function, graph, method_ptr, prog);
318 
319     // build map from pc to pandasm::ins (to re-build line-number info in BytecodeGen)
320     BuildMapFromPcToIns(function, ir_interface, graph, method_ptr);
321 
322     if ((graph == nullptr) || !graph->RunPass<panda::compiler::IrBuilder>()) {
323         LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": IR builder failed!";
324         return false;
325     }
326 
327     if (graph->HasIrreducibleLoop()) {
328         LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": Graph has irreducible loop!";
329         return false;
330     }
331 
332     if (!RunOptimizations(graph, &ir_interface)) {
333         LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": Running optimizations failed!";
334         return false;
335     }
336 
337     if (!graph->RunPass<BytecodeGen>(&function, &ir_interface, prog)) {
338         LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": Code generation failed!";
339         return false;
340     }
341 
342     DebugInfoPropagate(function, graph, ir_interface);
343 
344     function.value_of_first_param =
345         static_cast<int64_t>(graph->GetStackSlotsCount()) - 1;  // Work-around promotion rules
346     function.regs_num = static_cast<size_t>(function.value_of_first_param + 1);
347 
348     if (auto frame_size = function.regs_num + function.GetParamsNum(); frame_size >= NUM_COMPACTLY_ENCODED_REGS) {
349         LOG(INFO, BYTECODE_OPTIMIZER) << "Function " << func_name << " has frame size " << frame_size;
350     }
351 
352     LOG(DEBUG, BYTECODE_OPTIMIZER) << "Optimized " << func_name;
353 
354     return true;
355 }
356 
OptimizePandaFile(pandasm::Program * prog,const pandasm::AsmEmitter::PandaFileToPandaAsmMaps * maps,const std::string & pfile_name,bool is_dynamic)357 bool OptimizePandaFile(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
358                        const std::string &pfile_name, bool is_dynamic)
359 {
360     auto pfile = panda_file::OpenPandaFile(pfile_name);
361     if (!pfile) {
362         LOG(FATAL, BYTECODE_OPTIMIZER) << "Can not open binary file: " << pfile_name;
363     }
364 
365     bool result = true;
366 
367     for (uint32_t id : pfile->GetClasses()) {
368         panda_file::File::EntityId record_id {id};
369 
370         if (pfile->IsExternal(record_id)) {
371             continue;
372         }
373 
374         panda_file::ClassDataAccessor cda {*pfile, record_id};
375         cda.EnumerateMethods([prog, maps, is_dynamic, &result](panda_file::MethodDataAccessor &mda) {
376             if (!mda.IsExternal()) {
377                 result = OptimizeFunction(prog, maps, mda, is_dynamic) && result;
378             }
379         });
380     }
381 
382     return result;
383 }
384 
OptimizeBytecode(pandasm::Program * prog,const pandasm::AsmEmitter::PandaFileToPandaAsmMaps * maps,const std::string & pandafile_name,bool is_dynamic,bool has_memory_pool)385 bool OptimizeBytecode(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
386                       const std::string &pandafile_name, bool is_dynamic, bool has_memory_pool)
387 {
388     ASSERT(prog != nullptr);
389     ASSERT(maps != nullptr);
390 
391     if (!has_memory_pool) {
392         PoolManager::Initialize(PoolType::MALLOC);
393     }
394 
395     auto res = OptimizePandaFile(prog, maps, pandafile_name, is_dynamic);
396 
397     if (!has_memory_pool) {
398         PoolManager::Finalize();
399     }
400 
401     return res;
402 }
403 }  // namespace panda::bytecodeopt
404