/src/WasmEdge/include/ast/instruction.h
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
3 | | |
4 | | //===-- wasmedge/ast/instruction.h - Instruction class definition ---------===// |
5 | | // |
6 | | // Part of the WasmEdge Project. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | /// |
10 | | /// \file |
11 | | /// This file contains the declaration of the Instruction node class. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | #pragma once |
15 | | |
16 | | #include "common/enum_ast.hpp" |
17 | | #include "common/span.h" |
18 | | #include "common/types.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <vector> |
22 | | |
23 | | namespace WasmEdge { |
24 | | namespace AST { |
25 | | |
26 | | /// Instruction node class. |
27 | | class Instruction { |
28 | | public: |
29 | | struct JumpDescriptor { |
30 | | uint32_t TargetIndex; |
31 | | uint32_t StackEraseBegin; |
32 | | uint32_t StackEraseEnd; |
33 | | int32_t PCOffset; |
34 | | }; |
35 | | struct BrCastDescriptor { |
36 | | struct JumpDescriptor Jump; |
37 | | ValType RType1, RType2; |
38 | | }; |
39 | | struct CatchDescriptor { |
40 | | // LEGACY-EH: remove this flag after deprecating legacy EH. |
41 | | bool IsLegacy : 1; |
42 | | bool IsAll : 1; |
43 | | bool IsRef : 1; |
44 | | uint32_t TagIndex; |
45 | | uint32_t LabelIndex; |
46 | | struct JumpDescriptor Jump; |
47 | | }; |
48 | | struct TryDescriptor { |
49 | | BlockType ResType; |
50 | | uint32_t BlockParamNum; |
51 | | uint32_t JumpEnd; |
52 | | std::vector<CatchDescriptor> Catch; |
53 | | }; |
54 | | // LEGACY-EH: remove this struct after deprecating legacy EH. |
55 | | struct CatchDescriptorLegacy { |
56 | | uint32_t TagIndex; |
57 | | uint32_t LabelIndex; |
58 | | uint32_t CatchIndex; |
59 | | uint32_t CatchPCOffset; |
60 | | }; |
61 | | |
62 | | public: |
63 | | /// Constructor assigns the OpCode and the Offset. |
64 | | Instruction(OpCode Byte, uint32_t Off = 0) noexcept |
65 | 8.65M | : Offset(Off), Code(Byte) { |
66 | 8.65M | #if defined(__x86_64__) || defined(__aarch64__) || \ |
67 | 8.65M | (defined(__riscv) && __riscv_xlen == 64) |
68 | 8.65M | Data.Num = static_cast<uint128_t>(0); |
69 | | #else |
70 | | Data.Num.Low = static_cast<uint64_t>(0); |
71 | | Data.Num.High = static_cast<uint64_t>(0); |
72 | | #endif |
73 | 8.65M | Flags.IsAllocLabelList = false; |
74 | 8.65M | Flags.IsAllocValTypeList = false; |
75 | 8.65M | Flags.IsAllocBrCast = false; |
76 | 8.65M | Flags.IsAllocTryCatch = false; |
77 | 8.65M | } |
78 | | |
79 | | /// Copy constructor. |
80 | | Instruction(const Instruction &Instr) noexcept |
81 | 2.89M | : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code), |
82 | 2.89M | Flags(Instr.Flags) { |
83 | 2.89M | if (Flags.IsAllocLabelList) { |
84 | 2.64k | Data.BrTable.LabelList = new JumpDescriptor[Data.BrTable.LabelListSize]; |
85 | 2.64k | std::copy_n(Instr.Data.BrTable.LabelList, Data.BrTable.LabelListSize, |
86 | 2.64k | Data.BrTable.LabelList); |
87 | 2.89M | } else if (Flags.IsAllocValTypeList) { |
88 | 634 | Data.SelectT.ValTypeList = new ValType[Data.SelectT.ValTypeListSize]; |
89 | 634 | std::copy_n(Instr.Data.SelectT.ValTypeList, Data.SelectT.ValTypeListSize, |
90 | 634 | Data.SelectT.ValTypeList); |
91 | 2.89M | } else if (Flags.IsAllocBrCast) { |
92 | 0 | Data.BrCast = new BrCastDescriptor(*Instr.Data.BrCast); |
93 | 2.89M | } else if (Flags.IsAllocTryCatch) { |
94 | 0 | Data.TryCatch = new TryDescriptor(*Instr.Data.TryCatch); |
95 | 0 | } |
96 | 2.89M | } |
97 | | |
98 | | /// Move constructor. |
99 | | Instruction(Instruction &&Instr) noexcept |
100 | 12.4M | : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code), |
101 | 12.4M | Flags(Instr.Flags) { |
102 | 12.4M | Instr.Flags.IsAllocLabelList = false; |
103 | 12.4M | Instr.Flags.IsAllocValTypeList = false; |
104 | 12.4M | Instr.Flags.IsAllocBrCast = false; |
105 | 12.4M | Instr.Flags.IsAllocTryCatch = false; |
106 | 12.4M | } |
107 | | |
108 | | /// Destructor. |
109 | 23.9M | ~Instruction() { reset(); } |
110 | | |
111 | | /// Copy assignment. |
112 | 0 | Instruction &operator=(const Instruction &Instr) { |
113 | 0 | if (this != &Instr) { |
114 | 0 | Instruction Tmp(Instr); |
115 | 0 | Tmp.swap(*this); |
116 | 0 | } |
117 | 0 | return *this; |
118 | 0 | } |
119 | | |
120 | | /// Getter of OpCode. |
121 | 12.9M | OpCode getOpCode() const noexcept { return Code; } |
122 | | |
123 | | /// Getter of Offset. |
124 | 1.46k | uint32_t getOffset() const noexcept { return Offset; } |
125 | | |
126 | | /// Getter and setter of block type. |
127 | 17.8k | const BlockType &getBlockType() const noexcept { return Data.Blocks.ResType; } |
128 | 290k | BlockType &getBlockType() noexcept { return Data.Blocks.ResType; } |
129 | | |
130 | | /// Getter and setter of jump count to End instruction. |
131 | 10.9k | uint32_t getJumpEnd() const noexcept { return Data.Blocks.JumpEnd; } |
132 | 38.9k | void setJumpEnd(const uint32_t Cnt) noexcept { Data.Blocks.JumpEnd = Cnt; } |
133 | | |
134 | | /// Getter and setter of jump count to Else instruction. |
135 | 25.1k | uint32_t getJumpElse() const noexcept { return Data.Blocks.JumpElse; } |
136 | 17.5k | void setJumpElse(const uint32_t Cnt) noexcept { Data.Blocks.JumpElse = Cnt; } |
137 | | |
138 | | /// Getter and setter of value type. |
139 | 4.80k | const ValType &getValType() const noexcept { return Data.VType; } |
140 | 3.38k | void setValType(const ValType &VType) noexcept { Data.VType = VType; } |
141 | | |
142 | | /// Getter and setter of label list. |
143 | 6.72k | void setLabelListSize(uint32_t Size) { |
144 | 6.72k | reset(); |
145 | 6.72k | if (Size > 0) { |
146 | 6.72k | Data.BrTable.LabelListSize = Size; |
147 | 6.72k | Data.BrTable.LabelList = new JumpDescriptor[Size]; |
148 | 6.72k | Flags.IsAllocLabelList = true; |
149 | 6.72k | } |
150 | 6.72k | } |
151 | 998 | Span<const JumpDescriptor> getLabelList() const noexcept { |
152 | 998 | return Span<const JumpDescriptor>( |
153 | 998 | Data.BrTable.LabelList, |
154 | 998 | Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0); |
155 | 998 | } |
156 | 96.0k | Span<JumpDescriptor> getLabelList() noexcept { |
157 | 96.0k | return Span<JumpDescriptor>( |
158 | 96.0k | Data.BrTable.LabelList, |
159 | 96.0k | Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0); |
160 | 96.0k | } |
161 | | |
162 | | /// Getter and setter of expression end for End instruction. |
163 | 0 | bool isExprLast() const noexcept { return Data.EndFlags.IsExprLast; } |
164 | 67.8k | void setExprLast(bool Last = true) noexcept { |
165 | 67.8k | Data.EndFlags.IsExprLast = Last; |
166 | 67.8k | } |
167 | | |
168 | | /// Getter and setter of try block end for End instruction. |
169 | 0 | bool isTryBlockLast() const noexcept { return Data.EndFlags.IsTryBlockLast; } |
170 | 36.6k | void setTryBlockLast(bool Last = true) noexcept { |
171 | 36.6k | Data.EndFlags.IsTryBlockLast = Last; |
172 | 36.6k | } |
173 | | |
174 | | // LEGACY-EH: remove these functions after deprecating legacy EH. |
175 | | /// Getter and setter of try block end for End instruction. |
176 | 0 | bool isLegacyTryBlockLast() const noexcept { |
177 | 0 | return Data.EndFlags.IsLegacyTryBlockLast; |
178 | 0 | } |
179 | 36.6k | void setLegacyTryBlockLast(bool Last = true) noexcept { |
180 | 36.6k | Data.EndFlags.IsLegacyTryBlockLast = Last; |
181 | 36.6k | } |
182 | | |
183 | | /// Getter and setter of Jump for Br* instruction. |
184 | 4.81k | const JumpDescriptor &getJump() const noexcept { return Data.Jump; } |
185 | 16.1k | JumpDescriptor &getJump() noexcept { return Data.Jump; } |
186 | | |
187 | | /// Getter and setter of selecting value types list. |
188 | 1.74k | void setValTypeListSize(uint32_t Size) { |
189 | 1.74k | reset(); |
190 | 1.74k | if (Size > 0) { |
191 | 1.58k | Data.SelectT.ValTypeListSize = Size; |
192 | 1.58k | Data.SelectT.ValTypeList = new ValType[Size]; |
193 | 1.58k | Flags.IsAllocValTypeList = true; |
194 | 1.58k | } |
195 | 1.74k | } |
196 | 1.15k | Span<const ValType> getValTypeList() const noexcept { |
197 | 1.15k | return Span<const ValType>(Data.SelectT.ValTypeList, |
198 | 1.15k | Data.SelectT.ValTypeListSize); |
199 | 1.15k | } |
200 | 1.79k | Span<ValType> getValTypeList() noexcept { |
201 | 1.79k | return Span<ValType>(Data.SelectT.ValTypeList, |
202 | 1.79k | Data.SelectT.ValTypeListSize); |
203 | 1.79k | } |
204 | | |
205 | | /// Getter and setter of target index. |
206 | 211k | uint32_t getTargetIndex() const noexcept { return Data.Indices.TargetIdx; } |
207 | 318k | uint32_t &getTargetIndex() noexcept { return Data.Indices.TargetIdx; } |
208 | | |
209 | | /// Getter and setter of source index. |
210 | 2.97k | uint32_t getSourceIndex() const noexcept { return Data.Indices.SourceIdx; } |
211 | 9.43k | uint32_t &getSourceIndex() noexcept { return Data.Indices.SourceIdx; } |
212 | | |
213 | | /// Getter and setter of stack offset. |
214 | 0 | uint32_t getStackOffset() const noexcept { return Data.Indices.StackOffset; } |
215 | 32.3k | uint32_t &getStackOffset() noexcept { return Data.Indices.StackOffset; } |
216 | | |
217 | | /// Getter and setter of memory alignment. |
218 | 123k | uint32_t getMemoryAlign() const noexcept { return Data.Memories.MemAlign; } |
219 | 364k | uint32_t &getMemoryAlign() noexcept { return Data.Memories.MemAlign; } |
220 | | |
221 | | /// Getter of memory offset. |
222 | 22.0k | uint32_t getMemoryOffset() const noexcept { return Data.Memories.MemOffset; } |
223 | 181k | uint32_t &getMemoryOffset() noexcept { return Data.Memories.MemOffset; } |
224 | | |
225 | | /// Getter of memory lane. |
226 | 14.2k | uint8_t getMemoryLane() const noexcept { return Data.Memories.MemLane; } |
227 | 48.5k | uint8_t &getMemoryLane() noexcept { return Data.Memories.MemLane; } |
228 | | |
229 | | // LEGACY-EH: remove these functions after deprecating legacy EH. |
230 | | /// Getter and setter of legacy Catch for Catch* instructions. |
231 | 0 | const CatchDescriptorLegacy &getCatchLegacy() const noexcept { |
232 | 0 | return Data.CatchLegacy; |
233 | 0 | } |
234 | 0 | CatchDescriptorLegacy &getCatchLegacy() noexcept { return Data.CatchLegacy; } |
235 | | |
236 | | /// Getter and setter of the constant value. |
237 | 588k | ValVariant getNum() const noexcept { |
238 | 588k | #if defined(__x86_64__) || defined(__aarch64__) || \ |
239 | 588k | (defined(__riscv) && __riscv_xlen == 64) |
240 | 588k | return ValVariant(Data.Num); |
241 | | #else |
242 | | uint128_t N{Data.Num.High, Data.Num.Low}; |
243 | | return ValVariant(N); |
244 | | #endif |
245 | 588k | } |
246 | 2.21M | void setNum(ValVariant N) noexcept { |
247 | 2.21M | #if defined(__x86_64__) || defined(__aarch64__) || \ |
248 | 2.21M | (defined(__riscv) && __riscv_xlen == 64) |
249 | 2.21M | Data.Num = N.get<uint128_t>(); |
250 | | #else |
251 | | uint128_t V = N.get<uint128_t>(); |
252 | | Data.Num.Low = V.low(); |
253 | | Data.Num.High = V.high(); |
254 | | #endif |
255 | 2.21M | } |
256 | | |
257 | | /// Getter and setter of BrCast info for Br_cast instructions. |
258 | 0 | void setBrCast(uint32_t LabelIdx) { |
259 | 0 | reset(); |
260 | 0 | Data.BrCast = new BrCastDescriptor(); |
261 | 0 | Data.BrCast->Jump.TargetIndex = LabelIdx; |
262 | 0 | Flags.IsAllocBrCast = true; |
263 | 0 | } |
264 | 0 | const BrCastDescriptor &getBrCast() const noexcept { return *Data.BrCast; } |
265 | 0 | BrCastDescriptor &getBrCast() noexcept { return *Data.BrCast; } |
266 | | |
267 | | /// Getter and setter of try block info for try_table instruction. |
268 | 0 | void setTryCatch() { |
269 | 0 | reset(); |
270 | 0 | Data.TryCatch = new TryDescriptor(); |
271 | 0 | Flags.IsAllocTryCatch = true; |
272 | 0 | } |
273 | 0 | const TryDescriptor &getTryCatch() const noexcept { return *Data.TryCatch; } |
274 | 0 | TryDescriptor &getTryCatch() noexcept { return *Data.TryCatch; } |
275 | | |
276 | | private: |
277 | | /// Release allocated resources. |
278 | 23.9M | void reset() noexcept { |
279 | 23.9M | if (Flags.IsAllocLabelList) { |
280 | 9.36k | Data.BrTable.LabelListSize = 0; |
281 | 9.36k | delete[] Data.BrTable.LabelList; |
282 | 23.9M | } else if (Flags.IsAllocValTypeList) { |
283 | 2.21k | Data.SelectT.ValTypeListSize = 0; |
284 | 2.21k | delete[] Data.SelectT.ValTypeList; |
285 | 23.9M | } else if (Flags.IsAllocBrCast) { |
286 | 0 | delete Data.BrCast; |
287 | 23.9M | } else if (Flags.IsAllocTryCatch) { |
288 | 0 | delete Data.TryCatch; |
289 | 0 | } |
290 | 23.9M | Flags.IsAllocLabelList = false; |
291 | 23.9M | Flags.IsAllocValTypeList = false; |
292 | 23.9M | Flags.IsAllocBrCast = false; |
293 | 23.9M | Flags.IsAllocTryCatch = false; |
294 | 23.9M | } |
295 | | |
296 | | /// Swap function. |
297 | 0 | void swap(Instruction &Instr) noexcept { |
298 | 0 | std::swap(Data, Instr.Data); |
299 | 0 | std::swap(Offset, Instr.Offset); |
300 | 0 | std::swap(Code, Instr.Code); |
301 | 0 | std::swap(Flags, Instr.Flags); |
302 | 0 | } |
303 | | |
304 | | /// \name Data of instructions. |
305 | | /// @{ |
306 | | union Inner { |
307 | | // Type 1: BlockType, JumpEnd, and JumpElse. |
308 | | struct { |
309 | | uint32_t JumpEnd; |
310 | | uint32_t JumpElse; |
311 | | BlockType ResType; |
312 | | } Blocks; |
313 | | // Type 2: TargetIdx, SourceIdx and StackOffset. |
314 | | struct { |
315 | | uint32_t TargetIdx; |
316 | | uint32_t SourceIdx; |
317 | | uint32_t StackOffset; |
318 | | } Indices; |
319 | | // Type 3: Jump. |
320 | | JumpDescriptor Jump; |
321 | | // Type 4: LabelList. |
322 | | struct { |
323 | | uint32_t LabelListSize; |
324 | | JumpDescriptor *LabelList; |
325 | | } BrTable; |
326 | | // Type 5: ValType. |
327 | | ValType VType; |
328 | | // Type 6: ValTypeList. |
329 | | struct { |
330 | | uint32_t ValTypeListSize; |
331 | | ValType *ValTypeList; |
332 | | } SelectT; |
333 | | // Type 7: TargetIdx, MemAlign, MemOffset, and MemLane. |
334 | | struct { |
335 | | uint32_t TargetIdx; |
336 | | uint32_t MemAlign; |
337 | | uint32_t MemOffset; |
338 | | uint8_t MemLane; |
339 | | } Memories; |
340 | | // Type 8: Num. |
341 | | #if defined(__x86_64__) || defined(__aarch64__) || \ |
342 | | (defined(__riscv) && __riscv_xlen == 64) |
343 | | uint128_t Num; |
344 | | #else |
345 | | struct { |
346 | | uint64_t Low; |
347 | | uint64_t High; |
348 | | } Num; |
349 | | #endif |
350 | | // Type 9: End flags. |
351 | | struct { |
352 | | bool IsExprLast : 1; |
353 | | bool IsTryBlockLast : 1; |
354 | | // LEGACY-EH: remove this flag after deprecating legacy EH. |
355 | | bool IsLegacyTryBlockLast : 1; |
356 | | } EndFlags; |
357 | | // Type 10: TypeCastBranch. |
358 | | BrCastDescriptor *BrCast; |
359 | | // Type 11: Try Block. |
360 | | TryDescriptor *TryCatch; |
361 | | // LEGACY-EH: remove this case after deprecating legacy EH. |
362 | | // Type 12: Legacy Catch descriptor. |
363 | | CatchDescriptorLegacy CatchLegacy; |
364 | | } Data; |
365 | | uint32_t Offset = 0; |
366 | | OpCode Code = OpCode::End; |
367 | | struct { |
368 | | bool IsAllocLabelList : 1; |
369 | | bool IsAllocValTypeList : 1; |
370 | | bool IsAllocBrCast : 1; |
371 | | bool IsAllocTryCatch : 1; |
372 | | } Flags; |
373 | | /// @} |
374 | | }; |
375 | | |
376 | | // Type aliasing |
377 | | using InstrVec = std::vector<Instruction>; |
378 | | using InstrView = Span<const Instruction>; |
379 | | |
380 | | } // namespace AST |
381 | | } // namespace WasmEdge |