Coverage Report

Created: 2026-08-14 06:41

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
41.7M
      : Offset(Off), Code(Byte) {
57
41.7M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
58
41.7M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
59
41.7M
    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
41.7M
    Flags.IsAllocLabelList = false;
65
41.7M
    Flags.IsAllocValTypeList = false;
66
41.7M
    Flags.IsAllocBrCast = false;
67
41.7M
    Flags.IsAllocTryCatch = false;
68
41.7M
  }
69
70
  /// Copy constructor.
71
  Instruction(const Instruction &Instr) noexcept
72
9.64M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
73
9.64M
        Flags(Instr.Flags) {
74
9.64M
    if (Flags.IsAllocLabelList) {
75
30.8k
      Data.BrTable.LabelList = new JumpDescriptor[Data.BrTable.LabelListSize];
76
30.8k
      std::copy_n(Instr.Data.BrTable.LabelList, Data.BrTable.LabelListSize,
77
30.8k
                  Data.BrTable.LabelList);
78
9.60M
    } else if (Flags.IsAllocValTypeList) {
79
1.08k
      Data.SelectT.ValTypeList = new ValType[Data.SelectT.ValTypeListSize];
80
1.08k
      std::copy_n(Instr.Data.SelectT.ValTypeList, Data.SelectT.ValTypeListSize,
81
1.08k
                  Data.SelectT.ValTypeList);
82
9.60M
    } else if (Flags.IsAllocBrCast) {
83
294
      Data.BrCast = new BrCastDescriptor(*Instr.Data.BrCast);
84
9.60M
    } else if (Flags.IsAllocTryCatch) {
85
6.24k
      Data.TryCatch = new TryDescriptor(*Instr.Data.TryCatch);
86
6.24k
    }
87
9.64M
  }
88
89
  /// Move constructor.
90
  Instruction(Instruction &&Instr) noexcept
91
56.6M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
92
56.6M
        Flags(Instr.Flags) {
93
56.6M
    Instr.Flags.IsAllocLabelList = false;
94
56.6M
    Instr.Flags.IsAllocValTypeList = false;
95
56.6M
    Instr.Flags.IsAllocBrCast = false;
96
56.6M
    Instr.Flags.IsAllocTryCatch = false;
97
56.6M
  }
98
99
  /// Destructor.
100
108M
  ~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
47.5M
  OpCode getOpCode() const noexcept { return Code; }
113
114
  /// Getter for Offset.
115
1.96k
  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
1.48M
  BlockType &getBlockType() noexcept { return Data.Blocks.ResType; }
120
121
  /// Getter and setter for jump count to End instruction.
122
10.7k
  uint32_t getJumpEnd() const noexcept { return Data.Blocks.JumpEnd; }
123
49.1k
  void setJumpEnd(const uint32_t Cnt) noexcept { Data.Blocks.JumpEnd = Cnt; }
124
125
  /// Getter and setter for jump count to Else instruction.
126
30.5k
  uint32_t getJumpElse() const noexcept { return Data.Blocks.JumpElse; }
127
20.7k
  void setJumpElse(const uint32_t Cnt) noexcept { Data.Blocks.JumpElse = Cnt; }
128
129
  /// Getter and setter for value type.
130
41.0k
  const ValType &getValType() const noexcept { return Data.VType; }
131
44.7k
  void setValType(const ValType &VType) noexcept { Data.VType = VType; }
132
133
  /// Getter and setter for label list.
134
47.9k
  void setLabelListSize(uint32_t Size) {
135
47.9k
    reset();
136
47.9k
    if (Size > 0) {
137
47.9k
      Data.BrTable.LabelListSize = Size;
138
47.9k
      Data.BrTable.LabelList = new JumpDescriptor[Size];
139
47.9k
      Flags.IsAllocLabelList = true;
140
47.9k
    }
141
47.9k
  }
142
938
  Span<const JumpDescriptor> getLabelList() const noexcept {
143
938
    return Span<const JumpDescriptor>(
144
938
        Data.BrTable.LabelList,
145
938
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
146
938
  }
147
643k
  Span<JumpDescriptor> getLabelList() noexcept {
148
643k
    return Span<JumpDescriptor>(
149
643k
        Data.BrTable.LabelList,
150
643k
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
151
643k
  }
152
153
  /// Getter and setter for expression end for End instruction.
154
0
  bool isExprLast() const noexcept { return Data.EndFlags.IsExprLast; }
155
91.8k
  void setExprLast(bool Last = true) noexcept {
156
91.8k
    Data.EndFlags.IsExprLast = Last;
157
91.8k
  }
158
159
  /// Getter and setter for the try block end for End instruction.
160
0
  bool isTryBlockLast() const noexcept { return Data.EndFlags.IsTryBlockLast; }
161
52.8k
  void setTryBlockLast(bool Last = true) noexcept {
162
52.8k
    Data.EndFlags.IsTryBlockLast = Last;
163
52.8k
  }
164
165
  /// Getter and setter for Jump for Br* instructions.
166
5.58k
  const JumpDescriptor &getJump() const noexcept { return Data.Jump; }
167
232k
  JumpDescriptor &getJump() noexcept { return Data.Jump; }
168
169
  /// Getter and setter for the selected value type list.
170
6.11k
  void setValTypeListSize(uint32_t Size) {
171
6.11k
    reset();
172
6.11k
    if (Size > 0) {
173
2.39k
      Data.SelectT.ValTypeListSize = Size;
174
2.39k
      Data.SelectT.ValTypeList = new ValType[Size];
175
2.39k
      Flags.IsAllocValTypeList = true;
176
2.39k
    }
177
6.11k
  }
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
80.6k
  Span<ValType> getValTypeList() noexcept {
183
80.6k
    return Span<ValType>(Data.SelectT.ValTypeList,
184
80.6k
                         Data.SelectT.ValTypeListSize);
185
80.6k
  }
186
187
  /// Getter and setter for target index.
188
260k
  uint32_t getTargetIndex() const noexcept { return Data.Indices.TargetIdx; }
189
1.43M
  uint32_t &getTargetIndex() noexcept { return Data.Indices.TargetIdx; }
190
191
  /// Getter and setter for source index.
192
4.59k
  uint32_t getSourceIndex() const noexcept { return Data.Indices.SourceIdx; }
193
106k
  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
22.9k
  uint32_t &getStackOffset() noexcept { return Data.Indices.StackOffset; }
198
199
  /// Getter and setter for memory alignment.
200
121k
  uint32_t getMemoryAlign() const noexcept { return Data.Memories.MemAlign; }
201
2.62M
  uint32_t &getMemoryAlign() noexcept { return Data.Memories.MemAlign; }
202
203
  /// Getter for memory offset.
204
71.5k
  uint64_t getMemoryOffset() const noexcept { return Data.Memories.MemOffset; }
205
859k
  uint64_t &getMemoryOffset() noexcept { return Data.Memories.MemOffset; }
206
207
  /// Getter for memory lane.
208
15.5k
  uint8_t getMemoryLane() const noexcept { return Flags.MemLane; }
209
80.0k
  uint8_t &getMemoryLane() noexcept { return Flags.MemLane; }
210
211
  /// Getter and setter for the constant value.
212
655k
  ValVariant getNum() const noexcept {
213
655k
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
214
655k
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
215
655k
    return ValVariant(Data.Num);
216
#else
217
    uint128_t N{Data.Num.High, Data.Num.Low};
218
    return ValVariant(N);
219
#endif
220
655k
  }
221
7.68M
  void setNum(ValVariant N) noexcept {
222
7.68M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
223
7.68M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
224
7.68M
    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
7.68M
  }
231
232
  /// Getter and setter for BrCast info for Br_cast instructions.
233
995
  void setBrCast(uint32_t LabelIdx) {
234
995
    reset();
235
995
    Data.BrCast = new BrCastDescriptor();
236
995
    Data.BrCast->Jump.TargetIndex = LabelIdx;
237
995
    Flags.IsAllocBrCast = true;
238
995
  }
239
11
  const BrCastDescriptor &getBrCast() const noexcept { return *Data.BrCast; }
240
1.97k
  BrCastDescriptor &getBrCast() noexcept { return *Data.BrCast; }
241
242
  /// Getter and setter for try block info for try_table instruction.
243
42.1k
  void setTryCatch() {
244
42.1k
    reset();
245
42.1k
    Data.TryCatch = new TryDescriptor();
246
42.1k
    Flags.IsAllocTryCatch = true;
247
42.1k
  }
248
1.94k
  const TryDescriptor &getTryCatch() const noexcept { return *Data.TryCatch; }
249
95.7k
  TryDescriptor &getTryCatch() noexcept { return *Data.TryCatch; }
250
251
private:
252
  /// Release allocated resources.
253
108M
  void reset() noexcept {
254
108M
    if (Flags.IsAllocLabelList) {
255
78.7k
      Data.BrTable.LabelListSize = 0;
256
78.7k
      delete[] Data.BrTable.LabelList;
257
108M
    } else if (Flags.IsAllocValTypeList) {
258
3.48k
      Data.SelectT.ValTypeListSize = 0;
259
3.48k
      delete[] Data.SelectT.ValTypeList;
260
108M
    } else if (Flags.IsAllocBrCast) {
261
1.28k
      delete Data.BrCast;
262
108M
    } else if (Flags.IsAllocTryCatch) {
263
48.3k
      delete Data.TryCatch;
264
48.3k
    }
265
108M
    Flags.IsAllocLabelList = false;
266
108M
    Flags.IsAllocValTypeList = false;
267
108M
    Flags.IsAllocBrCast = false;
268
108M
    Flags.IsAllocTryCatch = false;
269
108M
  }
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