Coverage Report

Created: 2025-11-16 06:42

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.24M
      : Offset(Off), Code(Byte) {
57
8.24M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
58
8.24M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
59
8.24M
    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.24M
    Flags.IsAllocLabelList = false;
65
8.24M
    Flags.IsAllocValTypeList = false;
66
8.24M
    Flags.IsAllocBrCast = false;
67
8.24M
    Flags.IsAllocTryCatch = false;
68
8.24M
  }
69
70
  /// Copy constructor.
71
  Instruction(const Instruction &Instr) noexcept
72
2.58M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
73
2.58M
        Flags(Instr.Flags) {
74
2.58M
    if (Flags.IsAllocLabelList) {
75
2.63k
      Data.BrTable.LabelList = new JumpDescriptor[Data.BrTable.LabelListSize];
76
2.63k
      std::copy_n(Instr.Data.BrTable.LabelList, Data.BrTable.LabelListSize,
77
2.63k
                  Data.BrTable.LabelList);
78
2.57M
    } else if (Flags.IsAllocValTypeList) {
79
663
      Data.SelectT.ValTypeList = new ValType[Data.SelectT.ValTypeListSize];
80
663
      std::copy_n(Instr.Data.SelectT.ValTypeList, Data.SelectT.ValTypeListSize,
81
663
                  Data.SelectT.ValTypeList);
82
2.57M
    } else if (Flags.IsAllocBrCast) {
83
301
      Data.BrCast = new BrCastDescriptor(*Instr.Data.BrCast);
84
2.57M
    } else if (Flags.IsAllocTryCatch) {
85
1.66k
      Data.TryCatch = new TryDescriptor(*Instr.Data.TryCatch);
86
1.66k
    }
87
2.58M
  }
88
89
  /// Move constructor.
90
  Instruction(Instruction &&Instr) noexcept
91
12.1M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
92
12.1M
        Flags(Instr.Flags) {
93
12.1M
    Instr.Flags.IsAllocLabelList = false;
94
12.1M
    Instr.Flags.IsAllocValTypeList = false;
95
12.1M
    Instr.Flags.IsAllocBrCast = false;
96
12.1M
    Instr.Flags.IsAllocTryCatch = false;
97
12.1M
  }
98
99
  /// Destructor.
100
22.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 of OpCode.
112
13.1M
  OpCode getOpCode() const noexcept { return Code; }
113
114
  /// Getter of Offset.
115
1.80k
  uint32_t getOffset() const noexcept { return Offset; }
116
117
  /// Getter and setter of block type.
118
17.0k
  const BlockType &getBlockType() const noexcept { return Data.Blocks.ResType; }
119
271k
  BlockType &getBlockType() noexcept { return Data.Blocks.ResType; }
120
121
  /// Getter and setter of jump count to End instruction.
122
10.3k
  uint32_t getJumpEnd() const noexcept { return Data.Blocks.JumpEnd; }
123
30.9k
  void setJumpEnd(const uint32_t Cnt) noexcept { Data.Blocks.JumpEnd = Cnt; }
124
125
  /// Getter and setter of jump count to Else instruction.
126
20.2k
  uint32_t getJumpElse() const noexcept { return Data.Blocks.JumpElse; }
127
11.4k
  void setJumpElse(const uint32_t Cnt) noexcept { Data.Blocks.JumpElse = Cnt; }
128
129
  /// Getter and setter of value type.
130
24.3k
  const ValType &getValType() const noexcept { return Data.VType; }
131
14.5k
  void setValType(const ValType &VType) noexcept { Data.VType = VType; }
132
133
  /// Getter and setter of label list.
134
9.18k
  void setLabelListSize(uint32_t Size) {
135
9.18k
    reset();
136
9.18k
    if (Size > 0) {
137
9.18k
      Data.BrTable.LabelListSize = Size;
138
9.18k
      Data.BrTable.LabelList = new JumpDescriptor[Size];
139
9.18k
      Flags.IsAllocLabelList = true;
140
9.18k
    }
141
9.18k
  }
142
984
  Span<const JumpDescriptor> getLabelList() const noexcept {
143
984
    return Span<const JumpDescriptor>(
144
984
        Data.BrTable.LabelList,
145
984
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
146
984
  }
147
86.0k
  Span<JumpDescriptor> getLabelList() noexcept {
148
86.0k
    return Span<JumpDescriptor>(
149
86.0k
        Data.BrTable.LabelList,
150
86.0k
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
151
86.0k
  }
152
153
  /// Getter and setter of expression end for End instruction.
154
0
  bool isExprLast() const noexcept { return Data.EndFlags.IsExprLast; }
155
59.4k
  void setExprLast(bool Last = true) noexcept {
156
59.4k
    Data.EndFlags.IsExprLast = Last;
157
59.4k
  }
158
159
  /// Getter and setter of try block end for End instruction.
160
0
  bool isTryBlockLast() const noexcept { return Data.EndFlags.IsTryBlockLast; }
161
30.5k
  void setTryBlockLast(bool Last = true) noexcept {
162
30.5k
    Data.EndFlags.IsTryBlockLast = Last;
163
30.5k
  }
164
165
  /// Getter and setter of Jump for Br* instruction.
166
4.97k
  const JumpDescriptor &getJump() const noexcept { return Data.Jump; }
167
17.7k
  JumpDescriptor &getJump() noexcept { return Data.Jump; }
168
169
  /// Getter and setter of selecting value types list.
170
2.21k
  void setValTypeListSize(uint32_t Size) {
171
2.21k
    reset();
172
2.21k
    if (Size > 0) {
173
1.76k
      Data.SelectT.ValTypeListSize = Size;
174
1.76k
      Data.SelectT.ValTypeList = new ValType[Size];
175
1.76k
      Flags.IsAllocValTypeList = true;
176
1.76k
    }
177
2.21k
  }
178
1.19k
  Span<const ValType> getValTypeList() const noexcept {
179
1.19k
    return Span<const ValType>(Data.SelectT.ValTypeList,
180
1.19k
                               Data.SelectT.ValTypeListSize);
181
1.19k
  }
182
3.33k
  Span<ValType> getValTypeList() noexcept {
183
3.33k
    return Span<ValType>(Data.SelectT.ValTypeList,
184
3.33k
                         Data.SelectT.ValTypeListSize);
185
3.33k
  }
186
187
  /// Getter and setter of target index.
188
196k
  uint32_t getTargetIndex() const noexcept { return Data.Indices.TargetIdx; }
189
296k
  uint32_t &getTargetIndex() noexcept { return Data.Indices.TargetIdx; }
190
191
  /// Getter and setter of source index.
192
4.69k
  uint32_t getSourceIndex() const noexcept { return Data.Indices.SourceIdx; }
193
33.6k
  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
25.6k
  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
470k
  uint32_t &getMemoryAlign() noexcept { return Data.Memories.MemAlign; }
202
203
  /// Getter of memory offset.
204
22.9k
  uint32_t getMemoryOffset() const noexcept { return Data.Memories.MemOffset; }
205
156k
  uint32_t &getMemoryOffset() noexcept { return Data.Memories.MemOffset; }
206
207
  /// Getter of memory lane.
208
16.2k
  uint8_t getMemoryLane() const noexcept { return Data.Memories.MemLane; }
209
48.7k
  uint8_t &getMemoryLane() noexcept { return Data.Memories.MemLane; }
210
211
  /// Getter and setter of the constant value.
212
707k
  ValVariant getNum() const noexcept {
213
707k
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
214
707k
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
215
707k
    return ValVariant(Data.Num);
216
#else
217
    uint128_t N{Data.Num.High, Data.Num.Low};
218
    return ValVariant(N);
219
#endif
220
707k
  }
221
2.25M
  void setNum(ValVariant N) noexcept {
222
2.25M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
223
2.25M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
224
2.25M
    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.25M
  }
231
232
  /// Getter and setter of BrCast info for Br_cast instructions.
233
1.36k
  void setBrCast(uint32_t LabelIdx) {
234
1.36k
    reset();
235
1.36k
    Data.BrCast = new BrCastDescriptor();
236
1.36k
    Data.BrCast->Jump.TargetIndex = LabelIdx;
237
1.36k
    Flags.IsAllocBrCast = true;
238
1.36k
  }
239
84
  const BrCastDescriptor &getBrCast() const noexcept { return *Data.BrCast; }
240
2.74k
  BrCastDescriptor &getBrCast() noexcept { return *Data.BrCast; }
241
242
  /// Getter and setter of try block info for try_table instruction.
243
2.79k
  void setTryCatch() {
244
2.79k
    reset();
245
2.79k
    Data.TryCatch = new TryDescriptor();
246
2.79k
    Flags.IsAllocTryCatch = true;
247
2.79k
  }
248
132
  const TryDescriptor &getTryCatch() const noexcept { return *Data.TryCatch; }
249
30.9k
  TryDescriptor &getTryCatch() noexcept { return *Data.TryCatch; }
250
251
private:
252
  /// Release allocated resources.
253
22.9M
  void reset() noexcept {
254
22.9M
    if (Flags.IsAllocLabelList) {
255
11.8k
      Data.BrTable.LabelListSize = 0;
256
11.8k
      delete[] Data.BrTable.LabelList;
257
22.9M
    } else if (Flags.IsAllocValTypeList) {
258
2.42k
      Data.SelectT.ValTypeListSize = 0;
259
2.42k
      delete[] Data.SelectT.ValTypeList;
260
22.9M
    } else if (Flags.IsAllocBrCast) {
261
1.66k
      delete Data.BrCast;
262
22.9M
    } else if (Flags.IsAllocTryCatch) {
263
4.46k
      delete Data.TryCatch;
264
4.46k
    }
265
22.9M
    Flags.IsAllocLabelList = false;
266
22.9M
    Flags.IsAllocValTypeList = false;
267
22.9M
    Flags.IsAllocBrCast = false;
268
22.9M
    Flags.IsAllocTryCatch = false;
269
22.9M
  }
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