Coverage Report

Created: 2025-07-01 06:18

/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