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