Coverage Report

Created: 2026-06-30 06:10

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: Copyright The WasmEdge Authors
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
11.0M
      : Offset(Off), Code(Byte) {
57
11.0M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
58
11.0M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
59
11.0M
    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
11.0M
    Flags.IsAllocLabelList = false;
65
11.0M
    Flags.IsAllocValTypeList = false;
66
11.0M
    Flags.IsAllocBrCast = false;
67
11.0M
    Flags.IsAllocTryCatch = false;
68
11.0M
  }
69
70
  /// Copy constructor.
71
  Instruction(const Instruction &Instr) noexcept
72
3.00M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
73
3.00M
        Flags(Instr.Flags) {
74
3.00M
    if (Flags.IsAllocLabelList) {
75
4.99k
      Data.BrTable.LabelList = new JumpDescriptor[Data.BrTable.LabelListSize];
76
4.99k
      std::copy_n(Instr.Data.BrTable.LabelList, Data.BrTable.LabelListSize,
77
4.99k
                  Data.BrTable.LabelList);
78
2.99M
    } else if (Flags.IsAllocValTypeList) {
79
606
      Data.SelectT.ValTypeList = new ValType[Data.SelectT.ValTypeListSize];
80
606
      std::copy_n(Instr.Data.SelectT.ValTypeList, Data.SelectT.ValTypeListSize,
81
606
                  Data.SelectT.ValTypeList);
82
2.99M
    } else if (Flags.IsAllocBrCast) {
83
751
      Data.BrCast = new BrCastDescriptor(*Instr.Data.BrCast);
84
2.99M
    } else if (Flags.IsAllocTryCatch) {
85
12.2k
      Data.TryCatch = new TryDescriptor(*Instr.Data.TryCatch);
86
12.2k
    }
87
3.00M
  }
88
89
  /// Move constructor.
90
  Instruction(Instruction &&Instr) noexcept
91
15.9M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
92
15.9M
        Flags(Instr.Flags) {
93
15.9M
    Instr.Flags.IsAllocLabelList = false;
94
15.9M
    Instr.Flags.IsAllocValTypeList = false;
95
15.9M
    Instr.Flags.IsAllocBrCast = false;
96
15.9M
    Instr.Flags.IsAllocTryCatch = false;
97
15.9M
  }
98
99
  /// Destructor.
100
29.9M
  ~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 for OpCode.
112
16.1M
  OpCode getOpCode() const noexcept { return Code; }
113
114
  /// Getter for Offset.
115
1.73k
  uint32_t getOffset() const noexcept { return Offset; }
116
117
  /// Getter and setter for block type.
118
18.3k
  const BlockType &getBlockType() const noexcept { return Data.Blocks.ResType; }
119
828k
  BlockType &getBlockType() noexcept { return Data.Blocks.ResType; }
120
121
  /// Getter and setter for jump count to End instruction.
122
10.9k
  uint32_t getJumpEnd() const noexcept { return Data.Blocks.JumpEnd; }
123
34.1k
  void setJumpEnd(const uint32_t Cnt) noexcept { Data.Blocks.JumpEnd = Cnt; }
124
125
  /// Getter and setter for jump count to Else instruction.
126
21.2k
  uint32_t getJumpElse() const noexcept { return Data.Blocks.JumpElse; }
127
12.0k
  void setJumpElse(const uint32_t Cnt) noexcept { Data.Blocks.JumpElse = Cnt; }
128
129
  /// Getter and setter for value type.
130
46.0k
  const ValType &getValType() const noexcept { return Data.VType; }
131
37.4k
  void setValType(const ValType &VType) noexcept { Data.VType = VType; }
132
133
  /// Getter and setter for label list.
134
11.1k
  void setLabelListSize(uint32_t Size) {
135
11.1k
    reset();
136
11.1k
    if (Size > 0) {
137
11.1k
      Data.BrTable.LabelListSize = Size;
138
11.1k
      Data.BrTable.LabelList = new JumpDescriptor[Size];
139
11.1k
      Flags.IsAllocLabelList = true;
140
11.1k
    }
141
11.1k
  }
142
954
  Span<const JumpDescriptor> getLabelList() const noexcept {
143
954
    return Span<const JumpDescriptor>(
144
954
        Data.BrTable.LabelList,
145
954
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
146
954
  }
147
92.8k
  Span<JumpDescriptor> getLabelList() noexcept {
148
92.8k
    return Span<JumpDescriptor>(
149
92.8k
        Data.BrTable.LabelList,
150
92.8k
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
151
92.8k
  }
152
153
  /// Getter and setter for expression end for End instruction.
154
0
  bool isExprLast() const noexcept { return Data.EndFlags.IsExprLast; }
155
76.7k
  void setExprLast(bool Last = true) noexcept {
156
76.7k
    Data.EndFlags.IsExprLast = Last;
157
76.7k
  }
158
159
  /// Getter and setter for the try block end for End instruction.
160
0
  bool isTryBlockLast() const noexcept { return Data.EndFlags.IsTryBlockLast; }
161
45.6k
  void setTryBlockLast(bool Last = true) noexcept {
162
45.6k
    Data.EndFlags.IsTryBlockLast = Last;
163
45.6k
  }
164
165
  /// Getter and setter for Jump for Br* instructions.
166
5.67k
  const JumpDescriptor &getJump() const noexcept { return Data.Jump; }
167
20.7k
  JumpDescriptor &getJump() noexcept { return Data.Jump; }
168
169
  /// Getter and setter for the selected value type list.
170
1.65k
  void setValTypeListSize(uint32_t Size) {
171
1.65k
    reset();
172
1.65k
    if (Size > 0) {
173
1.27k
      Data.SelectT.ValTypeListSize = Size;
174
1.27k
      Data.SelectT.ValTypeList = new ValType[Size];
175
1.27k
      Flags.IsAllocValTypeList = true;
176
1.27k
    }
177
1.65k
  }
178
1.12k
  Span<const ValType> getValTypeList() const noexcept {
179
1.12k
    return Span<const ValType>(Data.SelectT.ValTypeList,
180
1.12k
                               Data.SelectT.ValTypeListSize);
181
1.12k
  }
182
59.2k
  Span<ValType> getValTypeList() noexcept {
183
59.2k
    return Span<ValType>(Data.SelectT.ValTypeList,
184
59.2k
                         Data.SelectT.ValTypeListSize);
185
59.2k
  }
186
187
  /// Getter and setter for target index.
188
267k
  uint32_t getTargetIndex() const noexcept { return Data.Indices.TargetIdx; }
189
331k
  uint32_t &getTargetIndex() noexcept { return Data.Indices.TargetIdx; }
190
191
  /// Getter and setter for source index.
192
5.39k
  uint32_t getSourceIndex() const noexcept { return Data.Indices.SourceIdx; }
193
51.1k
  uint32_t &getSourceIndex() noexcept { return Data.Indices.SourceIdx; }
194
195
  /// Getter and setter for stack offset.
196
0
  uint32_t getStackOffset() const noexcept { return Data.Indices.StackOffset; }
197
23.5k
  uint32_t &getStackOffset() noexcept { return Data.Indices.StackOffset; }
198
199
  /// Getter and setter for memory alignment.
200
124k
  uint32_t getMemoryAlign() const noexcept { return Data.Memories.MemAlign; }
201
503k
  uint32_t &getMemoryAlign() noexcept { return Data.Memories.MemAlign; }
202
203
  /// Getter for memory offset.
204
73.6k
  uint64_t getMemoryOffset() const noexcept { return Data.Memories.MemOffset; }
205
167k
  uint64_t &getMemoryOffset() noexcept { return Data.Memories.MemOffset; }
206
207
  /// Getter for memory lane.
208
15.6k
  uint8_t getMemoryLane() const noexcept { return Flags.MemLane; }
209
53.8k
  uint8_t &getMemoryLane() noexcept { return Flags.MemLane; }
210
211
  /// Getter and setter for the constant value.
212
716k
  ValVariant getNum() const noexcept {
213
716k
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
214
716k
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
215
716k
    return ValVariant(Data.Num);
216
#else
217
    uint128_t N{Data.Num.High, Data.Num.Low};
218
    return ValVariant(N);
219
#endif
220
716k
  }
221
5.45M
  void setNum(ValVariant N) noexcept {
222
5.45M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
223
5.45M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
224
5.45M
    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
5.45M
  }
231
232
  /// Getter and setter for BrCast info for Br_cast instructions.
233
1.65k
  void setBrCast(uint32_t LabelIdx) {
234
1.65k
    reset();
235
1.65k
    Data.BrCast = new BrCastDescriptor();
236
1.65k
    Data.BrCast->Jump.TargetIndex = LabelIdx;
237
1.65k
    Flags.IsAllocBrCast = true;
238
1.65k
  }
239
157
  const BrCastDescriptor &getBrCast() const noexcept { return *Data.BrCast; }
240
3.31k
  BrCastDescriptor &getBrCast() noexcept { return *Data.BrCast; }
241
242
  /// Getter and setter for try block info for try_table instruction.
243
14.5k
  void setTryCatch() {
244
14.5k
    reset();
245
14.5k
    Data.TryCatch = new TryDescriptor();
246
14.5k
    Flags.IsAllocTryCatch = true;
247
14.5k
  }
248
297
  const TryDescriptor &getTryCatch() const noexcept { return *Data.TryCatch; }
249
46.4k
  TryDescriptor &getTryCatch() noexcept { return *Data.TryCatch; }
250
251
private:
252
  /// Release allocated resources.
253
30.0M
  void reset() noexcept {
254
30.0M
    if (Flags.IsAllocLabelList) {
255
16.1k
      Data.BrTable.LabelListSize = 0;
256
16.1k
      delete[] Data.BrTable.LabelList;
257
30.0M
    } else if (Flags.IsAllocValTypeList) {
258
1.88k
      Data.SelectT.ValTypeListSize = 0;
259
1.88k
      delete[] Data.SelectT.ValTypeList;
260
30.0M
    } else if (Flags.IsAllocBrCast) {
261
2.40k
      delete Data.BrCast;
262
30.0M
    } else if (Flags.IsAllocTryCatch) {
263
26.7k
      delete Data.TryCatch;
264
26.7k
    }
265
30.0M
    Flags.IsAllocLabelList = false;
266
30.0M
    Flags.IsAllocValTypeList = false;
267
30.0M
    Flags.IsAllocBrCast = false;
268
30.0M
    Flags.IsAllocTryCatch = false;
269
30.0M
  }
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.
309
    struct {
310
      uint32_t TargetIdx;
311
      uint32_t MemAlign;
312
      uint64_t MemOffset;
313
      // To keep the inner data union at 16 bytes and avoid allocating this
314
      // struct (memory instructions may have high density in instruction
315
      // sequences), the `MemLane` member is kept outside this struct.
316
    } Memories;
317
    // Type 8: Num.
318
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
319
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
320
    uint128_t Num;
321
#else
322
    struct {
323
      uint64_t Low;
324
      uint64_t High;
325
    } Num;
326
#endif
327
    // Type 9: End flags.
328
    struct {
329
      bool IsExprLast : 1;
330
      bool IsTryBlockLast : 1;
331
    } EndFlags;
332
    // Type 10: TypeCastBranch.
333
    BrCastDescriptor *BrCast;
334
    // Type 11: Try Block.
335
    TryDescriptor *TryCatch;
336
  } Data;
337
  uint32_t Offset = 0;
338
  OpCode Code = OpCode::End;
339
  struct {
340
    // Memory lane data for memory instructions.
341
    uint8_t MemLane;
342
    // Flags of if allocating something in this instance.
343
    bool IsAllocLabelList : 1;
344
    bool IsAllocValTypeList : 1;
345
    bool IsAllocBrCast : 1;
346
    bool IsAllocTryCatch : 1;
347
  } Flags;
348
  /// @}
349
};
350
351
// Type aliasing
352
using InstrVec = std::vector<Instruction>;
353
using InstrView = Span<const Instruction>;
354
355
} // namespace AST
356
} // namespace WasmEdge