• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 "abc_code_processor.h"
17 
18 namespace panda::abc2program {
19 
AbcCodeProcessor(panda_file::File::EntityId entity_id,Abc2ProgramEntityContainer & entity_container,panda_file::File::EntityId method_id,pandasm::Function & function)20 AbcCodeProcessor::AbcCodeProcessor(panda_file::File::EntityId entity_id, Abc2ProgramEntityContainer &entity_container,
21                                    panda_file::File::EntityId method_id, pandasm::Function &function)
22     : AbcFileEntityProcessor(entity_id, entity_container), method_id_(method_id), function_(function),
23       debug_info_extractor_(entity_container.GetDebugInfoExtractor())
24 {
25     code_data_accessor_ = std::make_unique<panda_file::CodeDataAccessor>(*file_, entity_id_);
26     code_converter_ = std::make_unique<AbcCodeConverter>(entity_container_);
27 }
28 
FillProgramData()29 void AbcCodeProcessor::FillProgramData()
30 {
31     FillFunctionRegsNum();
32     FillIns();
33     FillCatchBlocks();
34     FillLocalVariableTable();
35 }
36 
FillFunctionRegsNum()37 void AbcCodeProcessor::FillFunctionRegsNum()
38 {
39     function_.regs_num = code_data_accessor_->GetNumVregs();
40 }
41 
FillIns()42 void AbcCodeProcessor::FillIns()
43 {
44     FillInsWithoutLabels();
45     if (NeedToAddDummyEndIns()) {
46         AddDummyEndIns();
47     }
48     AddJumpLabels();
49     FillInsDebug();
50 }
51 
FillInsWithoutLabels()52 void AbcCodeProcessor::FillInsWithoutLabels()
53 {
54     ins_size_ = code_data_accessor_->GetCodeSize();
55     const uint8_t *ins_arr = code_data_accessor_->GetInstructions();
56     auto bc_ins = BytecodeInstruction(ins_arr);
57     const auto bc_ins_last = bc_ins.JumpTo(ins_size_);
58     uint32_t inst_pc = 0;
59     uint32_t inst_idx = 0;
60     uint32_t inst_cnt = 0;
61     while (bc_ins.GetAddress() != bc_ins_last.GetAddress()) {
62         inst_cnt++;
63         bc_ins = bc_ins.GetNext();
64     }
65     inst_cnt++;  // AbcCodeProcessor::AddDummyEndIns() may add an instruction.
66     function_.ins.reserve(inst_cnt);
67     bc_ins = BytecodeInstruction(ins_arr);
68     while (bc_ins.GetAddress() != bc_ins_last.GetAddress()) {
69         pandasm::Ins pa_ins = code_converter_->BytecodeInstructionToPandasmInstruction(bc_ins, method_id_);
70         /*
71          * Private field jump_inst_idx_vec_ store all jump inst idx in a pandasm::Function.
72          * For example, a pandasm::Function has 100 pandasm::Ins with only 4 jump pandasm::Ins.
73          * When we add label for jump target and add jump id for jump inst,
74          * we only need to enumerate the 4 jump pandasm::Ins which stored in jump_inst_idx_vec_,
75          * while no need to enumerate the whole 100 pandasm::Ins.
76          * It will improve our compile performance.
77         */
78         if (pa_ins.IsJump()) {
79             jump_inst_idx_vec_.emplace_back(inst_idx);
80         }
81         function_.AddInstruction(pa_ins);
82         inst_pc_idx_map_.emplace(inst_pc, inst_idx);
83         inst_idx_pc_map_.emplace(inst_idx, inst_pc);
84         inst_idx++;
85         inst_pc += bc_ins.GetSize();
86         bc_ins = bc_ins.GetNext();
87     }
88 }
89 
NeedToAddDummyEndIns() const90 bool AbcCodeProcessor::NeedToAddDummyEndIns() const
91 {
92     bool need_to_add_dummy_end_ins = false;
93     code_data_accessor_->EnumerateTryBlocks([&](panda_file::CodeDataAccessor::TryBlock &try_block) {
94         try_block.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catch_block) {
95             uint32_t catch_end_pc = catch_block.GetHandlerPc() + catch_block.GetCodeSize();
96             if (catch_end_pc == ins_size_) {
97                 need_to_add_dummy_end_ins = true;
98             }
99             return true;
100         });
101         return true;
102     });
103     return need_to_add_dummy_end_ins;
104 }
105 
AddDummyEndIns()106 void AbcCodeProcessor::AddDummyEndIns()
107 {
108     uint32_t inst_idx = static_cast<uint32_t>(function_.ins.size());
109     pandasm::Ins dummy_end_ins{};
110     dummy_end_ins.opcode = pandasm::Opcode::INVALID;
111     dummy_end_ins.label = AbcFileUtils::GetLabelNameByInstIdx(inst_idx);
112     dummy_end_ins.set_label = true;
113     inst_pc_idx_map_.emplace(ins_size_, inst_idx);
114     inst_idx_pc_map_.emplace(inst_idx, ins_size_);
115     function_.AddInstruction(dummy_end_ins);
116 }
117 
GetInstIdxByInstPc(uint32_t inst_pc) const118 uint32_t AbcCodeProcessor::GetInstIdxByInstPc(uint32_t inst_pc) const
119 {
120     if (inst_pc_idx_map_.empty()) {
121         return 0;
122     }
123     auto it = inst_pc_idx_map_.find(inst_pc);
124     // Locate the end of ins list of current function
125     if (it == inst_pc_idx_map_.end()) {
126         return (inst_pc_idx_map_.rbegin()->second) + 1;
127     }
128     return it->second;
129 }
130 
GetInstPcByInstIdx(uint32_t inst_idx) const131 uint32_t AbcCodeProcessor::GetInstPcByInstIdx(uint32_t inst_idx) const
132 {
133     auto it = inst_idx_pc_map_.find(inst_idx);
134     ASSERT(it != inst_idx_pc_map_.end());
135     return it->second;
136 }
137 
AddJumpLabels() const138 void AbcCodeProcessor::AddJumpLabels() const
139 {
140     for (uint32_t jump_inst_idx : jump_inst_idx_vec_) {
141         pandasm::Ins &jump_pa_ins = function_.ins[jump_inst_idx];
142         AddJumpLabel4InsAtIndex(jump_inst_idx, jump_pa_ins);
143     }
144 }
145 
AddJumpLabel4InsAtIndex(uint32_t curr_inst_idx,pandasm::Ins & curr_pa_ins) const146 void AbcCodeProcessor::AddJumpLabel4InsAtIndex(uint32_t curr_inst_idx, pandasm::Ins &curr_pa_ins) const
147 {
148     uint32_t curr_inst_pc = GetInstPcByInstIdx(curr_inst_idx);
149     const int32_t jmp_offset = std::get<int64_t>(curr_pa_ins.imms.at(0));
150     uint32_t dst_inst_pc = curr_inst_pc + jmp_offset;
151     uint32_t dst_inst_idx = GetInstIdxByInstPc(dst_inst_pc);
152     pandasm::Ins &dst_pa_ins = function_.ins[dst_inst_idx];
153     AddLabel4InsAtIndex(dst_inst_idx);
154     curr_pa_ins.imms.clear();
155     curr_pa_ins.ids.emplace_back(dst_pa_ins.label);
156 }
157 
FillCatchBlocks()158 void AbcCodeProcessor::FillCatchBlocks()
159 {
160     code_data_accessor_->EnumerateTryBlocks([&](panda_file::CodeDataAccessor::TryBlock &try_block) {
161         HandleTryBlock(try_block);
162         try_block.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catch_block) {
163             HandleCatchBlock(catch_block);
164             return true;
165         });
166         return true;
167     });
168 }
169 
HandleTryBlock(panda_file::CodeDataAccessor::TryBlock & try_block)170 void AbcCodeProcessor::HandleTryBlock(panda_file::CodeDataAccessor::TryBlock &try_block)
171 {
172     curr_try_begin_inst_pc_ = try_block.GetStartPc();
173     curr_try_end_inst_pc_ = try_block.GetStartPc() + try_block.GetLength();
174     AddLabel4InsAtPc(curr_try_begin_inst_pc_);
175     AddLabel4InsAtPc(curr_try_end_inst_pc_);
176 }
177 
HandleCatchBlock(panda_file::CodeDataAccessor::CatchBlock & catch_block)178 void AbcCodeProcessor::HandleCatchBlock(panda_file::CodeDataAccessor::CatchBlock &catch_block)
179 {
180     curr_catch_begin_pc_ = catch_block.GetHandlerPc();
181     curr_catch_end_pc_ = catch_block.GetHandlerPc() + catch_block.GetCodeSize();
182     AddLabel4InsAtPc(curr_catch_begin_pc_);
183     AddLabel4InsAtPc(curr_catch_end_pc_);
184     pandasm::Function::CatchBlock pa_catch_block{};
185     FillCatchBlockLabels(pa_catch_block);
186     FillExceptionRecord(catch_block, pa_catch_block);
187     function_.catch_blocks.emplace_back(pa_catch_block);
188 }
189 
FillCatchBlockLabels(pandasm::Function::CatchBlock & pa_catch_block) const190 void AbcCodeProcessor::FillCatchBlockLabels(pandasm::Function::CatchBlock &pa_catch_block) const
191 {
192     pa_catch_block.try_begin_label = GetLabelNameAtPc(curr_try_begin_inst_pc_);
193     pa_catch_block.try_end_label = GetLabelNameAtPc(curr_try_end_inst_pc_);
194     pa_catch_block.catch_begin_label = GetLabelNameAtPc(curr_catch_begin_pc_);
195     pa_catch_block.catch_end_label = GetLabelNameAtPc(curr_catch_end_pc_);
196 }
197 
FillExceptionRecord(panda_file::CodeDataAccessor::CatchBlock & catch_block,pandasm::Function::CatchBlock & pa_catch_block) const198 void AbcCodeProcessor::FillExceptionRecord(panda_file::CodeDataAccessor::CatchBlock &catch_block,
199                                            pandasm::Function::CatchBlock &pa_catch_block) const
200 {
201     uint32_t class_idx = catch_block.GetTypeIdx();
202     if (class_idx == panda_file::INVALID_INDEX) {
203         pa_catch_block.exception_record = "";
204     } else {
205         const panda_file::File::EntityId class_id = file_->ResolveClassIndex(method_id_, class_idx);
206         pa_catch_block.exception_record = entity_container_.GetFullRecordNameById(class_id);
207     }
208 }
209 
AddLabel4InsAtIndex(uint32_t inst_idx) const210 void AbcCodeProcessor::AddLabel4InsAtIndex(uint32_t inst_idx) const
211 {
212     pandasm::Ins &pa_ins = function_.ins[inst_idx];
213     if (pa_ins.set_label) {
214         return;
215     }
216     pa_ins.label = AbcFileUtils::GetLabelNameByInstIdx(inst_idx);
217     pa_ins.set_label = true;
218 }
219 
AddLabel4InsAtPc(uint32_t inst_pc) const220 void AbcCodeProcessor::AddLabel4InsAtPc(uint32_t inst_pc) const
221 {
222     uint32_t inst_idx = GetInstIdxByInstPc(inst_pc);
223     AddLabel4InsAtIndex(inst_idx);
224 }
225 
GetLabelNameAtPc(uint32_t inst_pc) const226 std::string AbcCodeProcessor::GetLabelNameAtPc(uint32_t inst_pc) const
227 {
228     uint32_t inst_idx = GetInstIdxByInstPc(inst_pc);
229     return AbcFileUtils::GetLabelNameByInstIdx(inst_idx);
230 }
231 
FillLocalVariableTable()232 void AbcCodeProcessor::FillLocalVariableTable()
233 {
234     const std::vector<panda_file::LocalVariableInfo>& variables =
235         debug_info_extractor_.GetLocalVariableTable(method_id_);
236 
237     for (const auto &variable : variables) {
238         uint32_t start_idx = GetInstIdxByInstPc(variable.start_offset);
239         uint32_t end_idx = GetInstIdxByInstPc(variable.end_offset);
240         uint32_t length = end_idx - start_idx;
241         panda::pandasm::debuginfo::LocalVariable local_var = {variable.name,
242                                                               variable.type,
243                                                               variable.type_signature,
244                                                               variable.reg_number,
245                                                               start_idx,
246                                                               length};
247         function_.local_variable_debug.push_back(local_var);
248     }
249 }
250 
FillInsDebug()251 void AbcCodeProcessor::FillInsDebug()
252 {
253     constexpr size_t DEFAULT_LINE = -1;
254     constexpr uint32_t DEFAULT_COLUMN = -1;
255     uint32_t line_idx = 0;
256     uint32_t column_idx = 0;
257     uint32_t offset_start = UINT_MAX;
258     uint32_t offset_end = UINT_MAX;
259     size_t line = DEFAULT_LINE;
260     uint32_t column = DEFAULT_COLUMN;
261     const std::vector<panda::panda_file::LineTableEntry>& line_table =
262         debug_info_extractor_.GetLineNumberTable(method_id_);
263     const std::vector<panda::panda_file::ColumnTableEntry>& column_table =
264         debug_info_extractor_.GetColumnNumberTable(method_id_);
265 
266     for (uint32_t inst_idx = 0; inst_idx < function_.ins.size(); inst_idx++) {
267         SkipToNextEntryIfNeeded(line_idx, offset_start, offset_end, inst_idx, line_table);
268         line = line_idx > 0 ? line_table[line_idx - 1].line : line;
269         function_.ins[inst_idx].ins_debug.line_number = line;
270     }
271 
272     // Column table may be empty if all ins of current function has default column number
273     // The first entry of column table is the offset of the first ins for which column number differs from the default
274     offset_start = 0;
275     offset_end = column_table.size() > 0 ? column_table[0].offset : UINT_MAX;
276     for (uint32_t inst_idx = 0; inst_idx < function_.ins.size(); inst_idx++) {
277         SkipToNextEntryIfNeeded(column_idx, offset_start, offset_end, inst_idx, column_table);
278         column = column_idx > 0 ? column_table[column_idx - 1].column : column;
279         function_.ins[inst_idx].ins_debug.column_number = column;
280     }
281 }
282 
283 template <typename T>
SkipToNextEntryIfNeeded(uint32_t & idx,uint32_t & offset_start,uint32_t & offset_end,uint32_t inst_idx,const T & table)284 void AbcCodeProcessor::SkipToNextEntryIfNeeded(uint32_t &idx,
285                                                uint32_t &offset_start,
286                                                uint32_t &offset_end,
287                                                uint32_t inst_idx,
288                                                const T &table)
289 {
290     uint32_t ins_offset = GetInstPcByInstIdx(inst_idx);
291     ASSERT(ins_offset < UINT_MAX);
292     while (idx < table.size() && (ins_offset < offset_start || ins_offset >= offset_end)) {
293         offset_start = table[idx].offset;
294         ++idx;
295         offset_end = idx < table.size() ? table[idx].offset : UINT_MAX;
296     }
297 }
298 
299 }  // namespace panda::abc2program
300