Coverage Report

Created: 2025-11-24 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/ast/instruction.h
Line
Count
Source
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
    bool IsAll : 1;
41
    bool IsRef : 1;
42
    uint32_t TagIndex;
43
    uint32_t LabelIndex;
44
    struct JumpDescriptor Jump;
45
  };
46
  struct TryDescriptor {
47
    BlockType ResType;
48
    uint32_t BlockParamNum;
49
    uint32_t JumpEnd;
50
    std::vector<CatchDescriptor> Catch;
51
  };
52
53
public:
54
  /// Constructor assigns the OpCode and the Offset.
55
  Instruction(OpCode Byte, uint32_t Off = 0) noexcept
56
8.54M
      : Offset(Off), Code(Byte) {
57
8.54M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
58
8.54M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
59
8.54M
    Data.Num = static_cast<uint128_t>(0);
60
#else
61
    Data.Num.Low = static_cast<uint64_t>(0);
62
    Data.Num.High = static_cast<uint64_t>(0);
63
#endif
64
8.54M
    Flags.IsAllocLabelList = false;
65
8.54M
    Flags.IsAllocValTypeList = false;
66
8.54M
    Flags.IsAllocBrCast = false;
67
8.54M
    Flags.IsAllocTryCatch = false;
68
8.54M
  }
69
70
  /// Copy constructor.
71
  Instruction(const Instruction &Instr) noexcept
72
2.59M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
73
2.59M
        Flags(Instr.Flags) {
74
2.59M
    if (Flags.IsAllocLabelList) {
75
2.19k
      Data.BrTable.LabelList = new JumpDescriptor[Data.BrTable.LabelListSize];
76
2.19k
      std::copy_n(Instr.Data.BrTable.LabelList, Data.BrTable.LabelListSize,
77
2.19k
                  Data.BrTable.LabelList);
78
2.58M
    } else if (Flags.IsAllocValTypeList) {
79
683
      Data.SelectT.ValTypeList = new ValType[Data.SelectT.ValTypeListSize];
80
683
      std::copy_n(Instr.Data.SelectT.ValTypeList, Data.SelectT.ValTypeListSize,
81
683
                  Data.SelectT.ValTypeList);
82
2.58M
    } else if (Flags.IsAllocBrCast) {
83
616
      Data.BrCast = new BrCastDescriptor(*Instr.Data.BrCast);
84
2.58M
    } else if (Flags.IsAllocTryCatch) {
85
699
      Data.TryCatch = new TryDescriptor(*Instr.Data.TryCatch);
86
699
    }
87
2.59M
  }
88
89
  /// Move constructor.
90
  Instruction(Instruction &&Instr) noexcept
91
12.5M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
92
12.5M
        Flags(Instr.Flags) {
93
12.5M
    Instr.Flags.IsAllocLabelList = false;
94
12.5M
    Instr.Flags.IsAllocValTypeList = false;
95
12.5M
    Instr.Flags.IsAllocBrCast = false;
96
12.5M
    Instr.Flags.IsAllocTryCatch = false;
97
12.5M
  }
98
99
  /// Destructor.
100
23.6M
  ~Instruction() { reset(); }
101
102
  /// Copy assignment.
103
0
  Instruction &operator=(const Instruction &Instr) {
104
0
    if (this != &Instr) {
105
0
      Instruction Tmp(Instr);
106
0
      Tmp.swap(*this);
107
0
    }
108
0
    return *this;
109
0
  }
110
111
  /// Getter of OpCode.
112
13.4M
  OpCode getOpCode() const noexcept { return Code; }
113
114
  /// Getter of Offset.
115
1.79k
  uint32_t getOffset() const noexcept { return Offset; }
116
117
  /// Getter and setter of block type.
118
19.2k
  const BlockType &getBlockType() const noexcept { return Data.Blocks.ResType; }
119
304k
  BlockType &getBlockType() noexcept { return Data.Blocks.ResType; }
120
121
  /// Getter and setter of jump count to End instruction.
122
11.6k
  uint32_t getJumpEnd() const noexcept { return Data.Blocks.JumpEnd; }
123
36.9k
  void setJumpEnd(const uint32_t Cnt) noexcept { Data.Blocks.JumpEnd = Cnt; }
124
125
  /// Getter and setter of jump count to Else instruction.
126
22.5k
  uint32_t getJumpElse() const noexcept { return Data.Blocks.JumpElse; }
127
12.7k
  void setJumpElse(const uint32_t Cnt) noexcept { Data.Blocks.JumpElse = Cnt; }
128
129
  /// Getter and setter of value type.
130
36.9k
  const ValType &getValType() const noexcept { return Data.VType; }
131
22.3k
  void setValType(const ValType &VType) noexcept { Data.VType = VType; }
132
133
  /// Getter and setter of label list.
134
7.23k
  void setLabelListSize(uint32_t Size) {
135
7.23k
    reset();
136
7.23k
    if (Size > 0) {
137
7.23k
      Data.BrTable.LabelListSize = Size;
138
7.23k
      Data.BrTable.LabelList = new JumpDescriptor[Size];
139
7.23k
      Flags.IsAllocLabelList = true;
140
7.23k
    }
141
7.23k
  }
142
1.00k
  Span<const JumpDescriptor> getLabelList() const noexcept {
143
1.00k
    return Span<const JumpDescriptor>(
144
1.00k
        Data.BrTable.LabelList,
145
1.00k
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
146
1.00k
  }
147
68.9k
  Span<JumpDescriptor> getLabelList() noexcept {
148
68.9k
    return Span<JumpDescriptor>(
149
68.9k
        Data.BrTable.LabelList,
150
68.9k
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
151
68.9k
  }
152
153
  /// Getter and setter of expression end for End instruction.
154
0
  bool isExprLast() const noexcept { return Data.EndFlags.IsExprLast; }
155
62.0k
  void setExprLast(bool Last = true) noexcept {
156
62.0k
    Data.EndFlags.IsExprLast = Last;
157
62.0k
  }
158
159
  /// Getter and setter of try block end for End instruction.
160
0
  bool isTryBlockLast() const noexcept { return Data.EndFlags.IsTryBlockLast; }
161
34.8k
  void setTryBlockLast(bool Last = true) noexcept {
162
34.8k
    Data.EndFlags.IsTryBlockLast = Last;
163
34.8k
  }
164
165
  /// Getter and setter of Jump for Br* instruction.
166
5.66k
  const JumpDescriptor &getJump() const noexcept { return Data.Jump; }
167
19.9k
  JumpDescriptor &getJump() noexcept { return Data.Jump; }
168
169
  /// Getter and setter of selecting value types list.
170
1.86k
  void setValTypeListSize(uint32_t Size) {
171
1.86k
    reset();
172
1.86k
    if (Size > 0) {
173
1.60k
      Data.SelectT.ValTypeListSize = Size;
174
1.60k
      Data.SelectT.ValTypeList = new ValType[Size];
175
1.60k
      Flags.IsAllocValTypeList = true;
176
1.60k
    }
177
1.86k
  }
178
1.27k
  Span<const ValType> getValTypeList() const noexcept {
179
1.27k
    return Span<const ValType>(Data.SelectT.ValTypeList,
180
1.27k
                               Data.SelectT.ValTypeListSize);
181
1.27k
  }
182
6.19k
  Span<ValType> getValTypeList() noexcept {
183
6.19k
    return Span<ValType>(Data.SelectT.ValTypeList,
184
6.19k
                         Data.SelectT.ValTypeListSize);
185
6.19k
  }
186
187
  /// Getter and setter of target index.
188
203k
  uint32_t getTargetIndex() const noexcept { return Data.Indices.TargetIdx; }
189
301k
  uint32_t &getTargetIndex() noexcept { return Data.Indices.TargetIdx; }
190
191
  /// Getter and setter of source index.
192
4.76k
  uint32_t getSourceIndex() const noexcept { return Data.Indices.SourceIdx; }
193
37.1k
  uint32_t &getSourceIndex() noexcept { return Data.Indices.SourceIdx; }
194
195
  /// Getter and setter of stack offset.
196
0
  uint32_t getStackOffset() const noexcept { return Data.Indices.StackOffset; }
197
28.0k
  uint32_t &getStackOffset() noexcept { return Data.Indices.StackOffset; }
198
199
  /// Getter and setter of memory alignment.
200
118k
  uint32_t getMemoryAlign() const noexcept { return Data.Memories.MemAlign; }
201
456k
  uint32_t &getMemoryAlign() noexcept { return Data.Memories.MemAlign; }
202
203
  /// Getter of memory offset.
204
23.2k
  uint32_t getMemoryOffset() const noexcept { return Data.Memories.MemOffset; }
205
151k
  uint32_t &getMemoryOffset() noexcept { return Data.Memories.MemOffset; }
206
207
  /// Getter of memory lane.
208
16.6k
  uint8_t getMemoryLane() const noexcept { return Data.Memories.MemLane; }
209
51.4k
  uint8_t &getMemoryLane() noexcept { return Data.Memories.MemLane; }
210
211
  /// Getter and setter of the constant value.
212
703k
  ValVariant getNum() const noexcept {
213
703k
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
214
703k
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
215
703k
    return ValVariant(Data.Num);
216
#else
217
    uint128_t N{Data.Num.High, Data.Num.Low};
218
    return ValVariant(N);
219
#endif
220
703k
  }
221
2.33M
  void setNum(ValVariant N) noexcept {
222
2.33M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
223
2.33M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
224
2.33M
    Data.Num = N.get<uint128_t>();
225
#else
226
    uint128_t V = N.get<uint128_t>();
227
    Data.Num.Low = V.low();
228
    Data.Num.High = V.high();
229
#endif
230
2.33M
  }
231
232
  /// Getter and setter of BrCast info for Br_cast instructions.
233
1.82k
  void setBrCast(uint32_t LabelIdx) {
234
1.82k
    reset();
235
1.82k
    Data.BrCast = new BrCastDescriptor();
236
1.82k
    Data.BrCast->Jump.TargetIndex = LabelIdx;
237
1.82k
    Flags.IsAllocBrCast = true;
238
1.82k
  }
239
96
  const BrCastDescriptor &getBrCast() const noexcept { return *Data.BrCast; }
240
3.64k
  BrCastDescriptor &getBrCast() noexcept { return *Data.BrCast; }
241
242
  /// Getter and setter of try block info for try_table instruction.
243
1.83k
  void setTryCatch() {
244
1.83k
    reset();
245
1.83k
    Data.TryCatch = new TryDescriptor();
246
1.83k
    Flags.IsAllocTryCatch = true;
247
1.83k
  }
248
251
  const TryDescriptor &getTryCatch() const noexcept { return *Data.TryCatch; }
249
45.9k
  TryDescriptor &getTryCatch() noexcept { return *Data.TryCatch; }
250
251
private:
252
  /// Release allocated resources.
253
23.6M
  void reset() noexcept {
254
23.6M
    if (Flags.IsAllocLabelList) {
255
9.42k
      Data.BrTable.LabelListSize = 0;
256
9.42k
      delete[] Data.BrTable.LabelList;
257
23.6M
    } else if (Flags.IsAllocValTypeList) {
258
2.28k
      Data.SelectT.ValTypeListSize = 0;
259
2.28k
      delete[] Data.SelectT.ValTypeList;
260
23.6M
    } else if (Flags.IsAllocBrCast) {
261
2.43k
      delete Data.BrCast;
262
23.6M
    } else if (Flags.IsAllocTryCatch) {
263
2.53k
      delete Data.TryCatch;
264
2.53k
    }
265
23.6M
    Flags.IsAllocLabelList = false;
266
23.6M
    Flags.IsAllocValTypeList = false;
267
23.6M
    Flags.IsAllocBrCast = false;
268
23.6M
    Flags.IsAllocTryCatch = false;
269
23.6M
  }
270
271
  /// Swap function.
272
0
  void swap(Instruction &Instr) noexcept {
273
0
    std::swap(Data, Instr.Data);
274
0
    std::swap(Offset, Instr.Offset);
275
0
    std::swap(Code, Instr.Code);
276
0
    std::swap(Flags, Instr.Flags);
277
0
  }
278
279
  /// \name Data of instructions.
280
  /// @{
281
  union Inner {
282
    // Type 1: BlockType, JumpEnd, and JumpElse.
283
    struct {
284
      uint32_t JumpEnd;
285
      uint32_t JumpElse;
286
      BlockType ResType;
287
    } Blocks;
288
    // Type 2: TargetIdx, SourceIdx and StackOffset.
289
    struct {
290
      uint32_t TargetIdx;
291
      uint32_t SourceIdx;
292
      uint32_t StackOffset;
293
    } Indices;
294
    // Type 3: Jump.
295
    JumpDescriptor Jump;
296
    // Type 4: LabelList.
297
    struct {
298
      uint32_t LabelListSize;
299
      JumpDescriptor *LabelList;
300
    } BrTable;
301
    // Type 5: ValType.
302
    ValType VType;
303
    // Type 6: ValTypeList.
304
    struct {
305
      uint32_t ValTypeListSize;
306
      ValType *ValTypeList;
307
    } SelectT;
308
    // Type 7: TargetIdx, MemAlign, MemOffset, and MemLane.
309
    struct {
310
      uint32_t TargetIdx;
311
      uint32_t MemAlign;
312
      uint32_t MemOffset;
313
      uint8_t MemLane;
314
    } Memories;
315
    // Type 8: Num.
316
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
317
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
318
    uint128_t Num;
319
#else
320
    struct {
321
      uint64_t Low;
322
      uint64_t High;
323
    } Num;
324
#endif
325
    // Type 9: End flags.
326
    struct {
327
      bool IsExprLast : 1;
328
      bool IsTryBlockLast : 1;
329
    } EndFlags;
330
    // Type 10: TypeCastBranch.
331
    BrCastDescriptor *BrCast;
332
    // Type 11: Try Block.
333
    TryDescriptor *TryCatch;
334
  } Data;
335
  uint32_t Offset = 0;
336
  OpCode Code = OpCode::End;
337
  struct {
338
    bool IsAllocLabelList : 1;
339
    bool IsAllocValTypeList : 1;
340
    bool IsAllocBrCast : 1;
341
    bool IsAllocTryCatch : 1;
342
  } Flags;
343
  /// @}
344
};
345
346
// Type aliasing
347
using InstrVec = std::vector<Instruction>;
348
using InstrView = Span<const Instruction>;
349
350
} // namespace AST
351
} // namespace WasmEdge