Coverage Report

Created: 2025-07-11 06:21

/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.62M
      : Offset(Off), Code(Byte) {
66
8.62M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
67
8.62M
    (defined(__riscv) && __riscv_xlen == 64)
68
8.62M
    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.62M
    Flags.IsAllocLabelList = false;
74
8.62M
    Flags.IsAllocValTypeList = false;
75
8.62M
    Flags.IsAllocBrCast = false;
76
8.62M
    Flags.IsAllocTryCatch = false;
77
8.62M
  }
78
79
  /// Copy constructor.
80
  Instruction(const Instruction &Instr) noexcept
81
2.92M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
82
2.92M
        Flags(Instr.Flags) {
83
2.92M
    if (Flags.IsAllocLabelList) {
84
2.63k
      Data.BrTable.LabelList = new JumpDescriptor[Data.BrTable.LabelListSize];
85
2.63k
      std::copy_n(Instr.Data.BrTable.LabelList, Data.BrTable.LabelListSize,
86
2.63k
                  Data.BrTable.LabelList);
87
2.92M
    } else if (Flags.IsAllocValTypeList) {
88
658
      Data.SelectT.ValTypeList = new ValType[Data.SelectT.ValTypeListSize];
89
658
      std::copy_n(Instr.Data.SelectT.ValTypeList, Data.SelectT.ValTypeListSize,
90
658
                  Data.SelectT.ValTypeList);
91
2.91M
    } else if (Flags.IsAllocBrCast) {
92
0
      Data.BrCast = new BrCastDescriptor(*Instr.Data.BrCast);
93
2.91M
    } else if (Flags.IsAllocTryCatch) {
94
0
      Data.TryCatch = new TryDescriptor(*Instr.Data.TryCatch);
95
0
    }
96
2.92M
  }
97
98
  /// Move constructor.
99
  Instruction(Instruction &&Instr) noexcept
100
12.3M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
101
12.3M
        Flags(Instr.Flags) {
102
12.3M
    Instr.Flags.IsAllocLabelList = false;
103
12.3M
    Instr.Flags.IsAllocValTypeList = false;
104
12.3M
    Instr.Flags.IsAllocBrCast = false;
105
12.3M
    Instr.Flags.IsAllocTryCatch = false;
106
12.3M
  }
107
108
  /// Destructor.
109
23.8M
  ~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
13.0M
  OpCode getOpCode() const noexcept { return Code; }
122
123
  /// Getter of Offset.
124
1.44k
  uint32_t getOffset() const noexcept { return Offset; }
125
126
  /// Getter and setter of block type.
127
17.0k
  const BlockType &getBlockType() const noexcept { return Data.Blocks.ResType; }
128
245k
  BlockType &getBlockType() noexcept { return Data.Blocks.ResType; }
129
130
  /// Getter and setter of jump count to End instruction.
131
10.1k
  uint32_t getJumpEnd() const noexcept { return Data.Blocks.JumpEnd; }
132
40.8k
  void setJumpEnd(const uint32_t Cnt) noexcept { Data.Blocks.JumpEnd = Cnt; }
133
134
  /// Getter and setter of jump count to Else instruction.
135
25.6k
  uint32_t getJumpElse() const noexcept { return Data.Blocks.JumpElse; }
136
18.0k
  void setJumpElse(const uint32_t Cnt) noexcept { Data.Blocks.JumpElse = Cnt; }
137
138
  /// Getter and setter of value type.
139
5.42k
  const ValType &getValType() const noexcept { return Data.VType; }
140
3.55k
  void setValType(const ValType &VType) noexcept { Data.VType = VType; }
141
142
  /// Getter and setter of label list.
143
8.95k
  void setLabelListSize(uint32_t Size) {
144
8.95k
    reset();
145
8.95k
    if (Size > 0) {
146
8.95k
      Data.BrTable.LabelListSize = Size;
147
8.95k
      Data.BrTable.LabelList = new JumpDescriptor[Size];
148
8.95k
      Flags.IsAllocLabelList = true;
149
8.95k
    }
150
8.95k
  }
151
988
  Span<const JumpDescriptor> getLabelList() const noexcept {
152
988
    return Span<const JumpDescriptor>(
153
988
        Data.BrTable.LabelList,
154
988
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
155
988
  }
156
86.5k
  Span<JumpDescriptor> getLabelList() noexcept {
157
86.5k
    return Span<JumpDescriptor>(
158
86.5k
        Data.BrTable.LabelList,
159
86.5k
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
160
86.5k
  }
161
162
  /// Getter and setter of expression end for End instruction.
163
0
  bool isExprLast() const noexcept { return Data.EndFlags.IsExprLast; }
164
67.3k
  void setExprLast(bool Last = true) noexcept {
165
67.3k
    Data.EndFlags.IsExprLast = Last;
166
67.3k
  }
167
168
  /// Getter and setter of try block end for End instruction.
169
0
  bool isTryBlockLast() const noexcept { return Data.EndFlags.IsTryBlockLast; }
170
38.3k
  void setTryBlockLast(bool Last = true) noexcept {
171
38.3k
    Data.EndFlags.IsTryBlockLast = Last;
172
38.3k
  }
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
38.3k
  void setLegacyTryBlockLast(bool Last = true) noexcept {
180
38.3k
    Data.EndFlags.IsLegacyTryBlockLast = Last;
181
38.3k
  }
182
183
  /// Getter and setter of Jump for Br* instruction.
184
4.63k
  const JumpDescriptor &getJump() const noexcept { return Data.Jump; }
185
19.2k
  JumpDescriptor &getJump() noexcept { return Data.Jump; }
186
187
  /// Getter and setter of selecting value types list.
188
1.77k
  void setValTypeListSize(uint32_t Size) {
189
1.77k
    reset();
190
1.77k
    if (Size > 0) {
191
1.60k
      Data.SelectT.ValTypeListSize = Size;
192
1.60k
      Data.SelectT.ValTypeList = new ValType[Size];
193
1.60k
      Flags.IsAllocValTypeList = true;
194
1.60k
    }
195
1.77k
  }
196
1.19k
  Span<const ValType> getValTypeList() const noexcept {
197
1.19k
    return Span<const ValType>(Data.SelectT.ValTypeList,
198
1.19k
                               Data.SelectT.ValTypeListSize);
199
1.19k
  }
200
1.82k
  Span<ValType> getValTypeList() noexcept {
201
1.82k
    return Span<ValType>(Data.SelectT.ValTypeList,
202
1.82k
                         Data.SelectT.ValTypeListSize);
203
1.82k
  }
204
205
  /// Getter and setter of target index.
206
190k
  uint32_t getTargetIndex() const noexcept { return Data.Indices.TargetIdx; }
207
336k
  uint32_t &getTargetIndex() noexcept { return Data.Indices.TargetIdx; }
208
209
  /// Getter and setter of source index.
210
2.87k
  uint32_t getSourceIndex() const noexcept { return Data.Indices.SourceIdx; }
211
36.3k
  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
27.5k
  uint32_t &getStackOffset() noexcept { return Data.Indices.StackOffset; }
216
217
  /// Getter and setter of memory alignment.
218
113k
  uint32_t getMemoryAlign() const noexcept { return Data.Memories.MemAlign; }
219
378k
  uint32_t &getMemoryAlign() noexcept { return Data.Memories.MemAlign; }
220
221
  /// Getter of memory offset.
222
21.5k
  uint32_t getMemoryOffset() const noexcept { return Data.Memories.MemOffset; }
223
189k
  uint32_t &getMemoryOffset() noexcept { return Data.Memories.MemOffset; }
224
225
  /// Getter of memory lane.
226
14.7k
  uint8_t getMemoryLane() const noexcept { return Data.Memories.MemLane; }
227
47.8k
  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
625k
  ValVariant getNum() const noexcept {
238
625k
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
239
625k
    (defined(__riscv) && __riscv_xlen == 64)
240
625k
    return ValVariant(Data.Num);
241
#else
242
    uint128_t N{Data.Num.High, Data.Num.Low};
243
    return ValVariant(N);
244
#endif
245
625k
  }
246
2.19M
  void setNum(ValVariant N) noexcept {
247
2.19M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
248
2.19M
    (defined(__riscv) && __riscv_xlen == 64)
249
2.19M
    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.19M
  }
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
11.5k
      Data.BrTable.LabelListSize = 0;
281
11.5k
      delete[] Data.BrTable.LabelList;
282
23.8M
    } else if (Flags.IsAllocValTypeList) {
283
2.26k
      Data.SelectT.ValTypeListSize = 0;
284
2.26k
      delete[] Data.SelectT.ValTypeList;
285
23.8M
    } else if (Flags.IsAllocBrCast) {
286
0
      delete Data.BrCast;
287
23.8M
    } 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