• 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 "bytecode_emitter.h"
17 #include <bytecode_instruction-inl.h>
18 
19 namespace panda {
20 
21 using Opcode = BytecodeInstruction::Opcode;
22 using Format = BytecodeInstruction::Format;
23 using BitImmSize = BytecodeEmitter::BitImmSize;
24 
GetBitLengthUnsigned(uint32_t val)25 static inline constexpr BitImmSize GetBitLengthUnsigned(uint32_t val)
26 {
27     constexpr size_t BIT_4 = 4;
28     constexpr size_t BIT_8 = 8;
29 
30     auto bitlen = MinimumBitsToStore(val);
31     if (bitlen <= BIT_4) {
32         return BitImmSize::BITSIZE_4;
33     }
34     if (bitlen <= BIT_8) {
35         return BitImmSize::BITSIZE_8;
36     }
37     return BitImmSize::BITSIZE_16;
38 }
39 
GetBitLengthSigned(int32_t val)40 static inline constexpr BitImmSize GetBitLengthSigned(int32_t val)
41 {
42     constexpr int32_t INT4T_MIN = -8;
43     constexpr int32_t INT4T_MAX = 7;
44     constexpr int32_t INT8T_MIN = std::numeric_limits<int8_t>::min();
45     constexpr int32_t INT8T_MAX = std::numeric_limits<int8_t>::max();
46     constexpr int32_t INT16T_MIN = std::numeric_limits<int16_t>::min();
47     constexpr int32_t INT16T_MAX = std::numeric_limits<int16_t>::max();
48     if (INT4T_MIN <= val && val <= INT4T_MAX) {
49         return BitImmSize::BITSIZE_4;
50     }
51     if (INT8T_MIN <= val && val <= INT8T_MAX) {
52         return BitImmSize::BITSIZE_8;
53     }
54     if (INT16T_MIN <= val && val <= INT16T_MAX) {
55         return BitImmSize::BITSIZE_16;
56     }
57     return BitImmSize::BITSIZE_32;
58 }
59 
EmitImpl(Span<uint8_t> buf,Span<const uint8_t> offsets)60 static inline void EmitImpl([[maybe_unused]] Span<uint8_t> buf, [[maybe_unused]] Span<const uint8_t> offsets) {}
61 
62 template <typename Type, typename... Types>
EmitImpl(Span<uint8_t> buf,Span<const uint8_t> offsets,Type arg,Types...args)63 static void EmitImpl(Span<uint8_t> buf, Span<const uint8_t> offsets, Type arg, Types... args)
64 {
65     static constexpr uint8_t BYTEMASK = 0xFF;
66     static constexpr uint8_t BITMASK_4 = 0xF;
67     static constexpr size_t BIT_4 = 4;
68     static constexpr size_t BIT_8 = 8;
69     static constexpr size_t BIT_16 = 16;
70     static constexpr size_t BIT_32 = 32;
71     static constexpr size_t BIT_64 = 64;
72 
73     uint8_t offset = offsets[0];
74     size_t bitlen = offsets[1] - offsets[0];
75     size_t byte_offset = offset / BIT_8;
76     size_t bit_offset = offset % BIT_8;
77     switch (bitlen) {
78         case BIT_4: {
79             auto val = static_cast<uint8_t>(arg);
80             buf[byte_offset] |= static_cast<uint8_t>(static_cast<uint8_t>(val & BITMASK_4) << bit_offset);
81             break;
82         }
83         case BIT_8: {
84             auto val = static_cast<uint8_t>(arg);
85             buf[byte_offset] = val;
86             break;
87         }
88         case BIT_16: {
89             auto val = static_cast<uint16_t>(arg);
90             buf[byte_offset] = val & BYTEMASK;
91             buf[byte_offset + 1] = val >> BIT_8;
92             break;
93         }
94         case BIT_32: {
95             auto val = static_cast<uint32_t>(arg);
96             for (size_t i = 0; i < sizeof(uint32_t); i++) {
97                 buf[byte_offset + i] = (val >> (i * BIT_8)) & BYTEMASK;
98             }
99             break;
100         }
101         case BIT_64: {
102             auto val = static_cast<uint64_t>(arg);
103             for (size_t i = 0; i < sizeof(uint64_t); i++) {
104                 buf[byte_offset + i] = (val >> (i * BIT_8)) & BYTEMASK;
105             }
106             break;
107         }
108         default: {
109             UNREACHABLE();
110             break;
111         }
112     }
113     EmitImpl(buf, offsets.SubSpan(1), args...);
114 }
115 
116 #ifndef WITH_MOCK
117 template <Format format, typename It, typename... Types>
118 static size_t Emit(It out, Types... args);
119 
Bind(const Label & label)120 void BytecodeEmitter::Bind(const Label &label)
121 {
122     *label.pc_ = pc_;
123     targets_.insert(label);
124 }
125 
Build(std::vector<uint8_t> * output)126 BytecodeEmitter::ErrorCode BytecodeEmitter::Build(std::vector<uint8_t> *output)
127 {
128     ErrorCode res = CheckLabels();
129     if (res != ErrorCode::SUCCESS) {
130         return res;
131     }
132     res = ReserveSpaceForOffsets();
133     if (res != ErrorCode::SUCCESS) {
134         return res;
135     }
136     res = UpdateBranches();
137     if (res != ErrorCode::SUCCESS) {
138         return res;
139     }
140     *output = bytecode_;
141     return ErrorCode::SUCCESS;
142 }
143 
144 /*
145  * NB! All conditional jumps with displacements not fitting into imm16
146  * are transformed into two instructions:
147  * jcc far   # cc is any condiitonal code
148  *      =>
149  * jCC next  # CC is inverted cc
150  * jmp far
151  * next:     # This label is inserted just after previous instruction.
152  */
ReserveSpaceForOffsets()153 BytecodeEmitter::ErrorCode BytecodeEmitter::ReserveSpaceForOffsets()
154 {
155     uint32_t bias = 0;
156     std::map<uint32_t, Label> new_branches;
157     auto it = branches_.begin();
158     while (it != branches_.end()) {
159         uint32_t insn_pc = it->first + bias;
160         auto label = it->second;
161 
162         BytecodeInstruction insn(&bytecode_[insn_pc]);
163         auto opcode = insn.GetOpcode();
164         const auto ENCODED_IMM_SIZE = GetBitImmSizeByOpcode(opcode);
165         const auto REAL_IMM_SIZE = GetBitLengthSigned(EstimateMaxDistance(insn_pc, label.GetPc(), bias));
166 
167         auto new_target = insn_pc;
168         size_t extra_bytes = 0;
169 
170         if (REAL_IMM_SIZE > ENCODED_IMM_SIZE) {
171             auto res = DoReserveSpaceForOffset(insn, insn_pc, REAL_IMM_SIZE, &extra_bytes, &new_target);
172             if (res != ErrorCode::SUCCESS) {
173                 return res;
174             }
175         }
176 
177         new_branches.insert(std::make_pair(new_target, label));
178         if (extra_bytes > 0) {
179             bias += extra_bytes;
180             UpdateLabelTargets(insn_pc, extra_bytes);
181         }
182         it = branches_.erase(it);
183     }
184     branches_ = std::move(new_branches);
185     return ErrorCode::SUCCESS;
186 }
187 
DoReserveSpaceForOffset(const BytecodeInstruction & insn,uint32_t insn_pc,BitImmSize expected_imm_size,size_t * extra_bytes_ptr,uint32_t * target_ptr)188 BytecodeEmitter::ErrorCode BytecodeEmitter::DoReserveSpaceForOffset(const BytecodeInstruction &insn, uint32_t insn_pc,
189                                                                     BitImmSize expected_imm_size,
190                                                                     size_t *extra_bytes_ptr, uint32_t *target_ptr)
191 {
192     auto opcode = insn.GetOpcode();
193     const auto INSN_SIZE = GetSizeByOpcode(opcode);
194 
195     auto upd_op = GetSuitableJump(opcode, expected_imm_size);
196     if (upd_op != Opcode::LAST) {
197         *extra_bytes_ptr = GetSizeByOpcode(upd_op) - INSN_SIZE;
198         bytecode_.insert(bytecode_.begin() + insn_pc + INSN_SIZE, *extra_bytes_ptr, 0);
199     } else {
200         *extra_bytes_ptr = GetSizeByOpcode(Opcode::JMP_IMM32);
201         bytecode_.insert(bytecode_.begin() + insn_pc + INSN_SIZE, *extra_bytes_ptr, 0);
202 
203         upd_op = RevertConditionCode(opcode);
204         if (upd_op == Opcode::LAST) {
205             UNREACHABLE();  // no revcc and no far opcode
206             return ErrorCode::INTERNAL_ERROR;
207         }
208         UpdateBranchOffs(&bytecode_[insn_pc], INSN_SIZE + GetSizeByOpcode(Opcode::JMP_IMM32));
209         *target_ptr = insn_pc + INSN_SIZE;
210         Emit<Format::IMM32>(bytecode_.begin() + *target_ptr, Opcode::JMP_IMM32, 0);
211     }
212     if (BytecodeInstruction(reinterpret_cast<uint8_t *>(&upd_op)).IsPrefixed()) {
213         Emit<BytecodeInstruction::Format::PREF_NONE>(bytecode_.begin() + insn_pc, upd_op);
214     } else {
215         Emit<BytecodeInstruction::Format::NONE>(bytecode_.begin() + insn_pc, upd_op);
216     }
217     return ErrorCode::SUCCESS;
218 }
219 
UpdateBranches()220 BytecodeEmitter::ErrorCode BytecodeEmitter::UpdateBranches()
221 {
222     for (std::pair<const uint32_t, Label> &branch : branches_) {
223         uint32_t insn_pc = branch.first;
224         Label label = branch.second;
225         auto offset = static_cast<int32_t>(label.GetPc()) - static_cast<int32_t>(insn_pc);
226         UpdateBranchOffs(&bytecode_[insn_pc], offset);
227     }
228     return ErrorCode::SUCCESS;
229 }
230 
UpdateLabelTargets(uint32_t pc,size_t bias)231 void BytecodeEmitter::UpdateLabelTargets(uint32_t pc, size_t bias)
232 {
233     pc_list_.push_front(pc);
234     Label fake(pc_list_.begin());
235     std::list<Label> updated_labels;
236     auto it = targets_.upper_bound(fake);
237     while (it != targets_.end()) {
238         Label label = *it;
239         it = targets_.erase(it);
240         *label.pc_ += bias;
241         updated_labels.push_back(label);
242     }
243     targets_.insert(updated_labels.begin(), updated_labels.end());
244     pc_list_.pop_front();
245 }
246 
EstimateMaxDistance(uint32_t insn_pc,uint32_t target_pc,uint32_t bias) const247 int32_t BytecodeEmitter::EstimateMaxDistance(uint32_t insn_pc, uint32_t target_pc, uint32_t bias) const
248 {
249     int32_t distance = 0;
250     uint32_t end_pc = 0;
251     std::map<uint32_t, Label>::const_iterator it;
252     if (static_cast<int64_t>(target_pc) < static_cast<int64_t>(insn_pc) + INT32_MIN ||
253         static_cast<int64_t>(target_pc) > static_cast<int64_t>(insn_pc) + INT32_MAX) {
254         LOG(ERROR, ASSEMBLER) << "Failed to emit jmp/jcc with displacement not fitting into imm32";
255     }
256 
257     if (target_pc > insn_pc) {
258         it = branches_.lower_bound(insn_pc - bias);
259         distance = static_cast<int32_t>(target_pc - insn_pc);
260         end_pc = target_pc - bias;
261     } else if (target_pc < insn_pc) {
262         it = branches_.lower_bound(target_pc - bias);
263         distance = static_cast<int32_t>(static_cast<int64_t>(target_pc) - static_cast<int64_t>(insn_pc));
264         end_pc = insn_pc - bias;
265     } else {
266         // Do we support branch to itself?
267         return 0;
268     }
269 
270     while (it != branches_.end() && it->first < end_pc) {
271         auto insn = BytecodeInstruction(&bytecode_[it->first + bias]);
272         auto longest = GetSizeByOpcode(GetLongestJump(insn.GetOpcode()));
273         distance += static_cast<int32_t>(longest - insn.GetSize());
274         ++it;
275     }
276     return distance;
277 }
278 
CheckLabels()279 BytecodeEmitter::ErrorCode BytecodeEmitter::CheckLabels()
280 {
281     for (const std::pair<const uint32_t, Label> &branch : branches_) {
282         const Label &label = branch.second;
283         if (targets_.find(label) == targets_.end()) {
284             return ErrorCode::UNBOUND_LABELS;
285         }
286     }
287     return ErrorCode::SUCCESS;
288 }
289 
290 #include <bytecode_emitter_gen.h>
291 #endif  // WITH_MOCK
292 
293 }  // namespace panda
294