Coverage Report

Created: 2025-11-09 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: 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.02M
      : Offset(Off), Code(Byte) {
57
8.02M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
58
8.02M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
59
8.02M
    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.02M
    Flags.IsAllocLabelList = false;
65
8.02M
    Flags.IsAllocValTypeList = false;
66
8.02M
    Flags.IsAllocBrCast = false;
67
8.02M
    Flags.IsAllocTryCatch = false;
68
8.02M
  }
69
70
  /// Copy constructor.
71
  Instruction(const Instruction &Instr) noexcept
72
2.57M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
73
2.57M
        Flags(Instr.Flags) {
74
2.57M
    if (Flags.IsAllocLabelList) {
75
2.17k
      Data.BrTable.LabelList = new JumpDescriptor[Data.BrTable.LabelListSize];
76
2.17k
      std::copy_n(Instr.Data.BrTable.LabelList, Data.BrTable.LabelListSize,
77
2.17k
                  Data.BrTable.LabelList);
78
2.56M
    } else if (Flags.IsAllocValTypeList) {
79
708
      Data.SelectT.ValTypeList = new ValType[Data.SelectT.ValTypeListSize];
80
708
      std::copy_n(Instr.Data.SelectT.ValTypeList, Data.SelectT.ValTypeListSize,
81
708
                  Data.SelectT.ValTypeList);
82
2.56M
    } else if (Flags.IsAllocBrCast) {
83
127
      Data.BrCast = new BrCastDescriptor(*Instr.Data.BrCast);
84
2.56M
    } else if (Flags.IsAllocTryCatch) {
85
671
      Data.TryCatch = new TryDescriptor(*Instr.Data.TryCatch);
86
671
    }
87
2.57M
  }
88
89
  /// Move constructor.
90
  Instruction(Instruction &&Instr) noexcept
91
11.7M
      : Data(Instr.Data), Offset(Instr.Offset), Code(Instr.Code),
92
11.7M
        Flags(Instr.Flags) {
93
11.7M
    Instr.Flags.IsAllocLabelList = false;
94
11.7M
    Instr.Flags.IsAllocValTypeList = false;
95
11.7M
    Instr.Flags.IsAllocBrCast = false;
96
11.7M
    Instr.Flags.IsAllocTryCatch = false;
97
11.7M
  }
98
99
  /// Destructor.
100
22.3M
  ~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
12.8M
  OpCode getOpCode() const noexcept { return Code; }
113
114
  /// Getter of Offset.
115
1.81k
  uint32_t getOffset() const noexcept { return Offset; }
116
117
  /// Getter and setter of block type.
118
15.9k
  const BlockType &getBlockType() const noexcept { return Data.Blocks.ResType; }
119
262k
  BlockType &getBlockType() noexcept { return Data.Blocks.ResType; }
120
121
  /// Getter and setter of jump count to End instruction.
122
9.32k
  uint32_t getJumpEnd() const noexcept { return Data.Blocks.JumpEnd; }
123
28.7k
  void setJumpEnd(const uint32_t Cnt) noexcept { Data.Blocks.JumpEnd = Cnt; }
124
125
  /// Getter and setter of jump count to Else instruction.
126
18.9k
  uint32_t getJumpElse() const noexcept { return Data.Blocks.JumpElse; }
127
10.9k
  void setJumpElse(const uint32_t Cnt) noexcept { Data.Blocks.JumpElse = Cnt; }
128
129
  /// Getter and setter of value type.
130
29.2k
  const ValType &getValType() const noexcept { return Data.VType; }
131
11.5k
  void setValType(const ValType &VType) noexcept { Data.VType = VType; }
132
133
  /// Getter and setter of label list.
134
5.98k
  void setLabelListSize(uint32_t Size) {
135
5.98k
    reset();
136
5.98k
    if (Size > 0) {
137
5.98k
      Data.BrTable.LabelListSize = Size;
138
5.98k
      Data.BrTable.LabelList = new JumpDescriptor[Size];
139
5.98k
      Flags.IsAllocLabelList = true;
140
5.98k
    }
141
5.98k
  }
142
887
  Span<const JumpDescriptor> getLabelList() const noexcept {
143
887
    return Span<const JumpDescriptor>(
144
887
        Data.BrTable.LabelList,
145
887
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
146
887
  }
147
54.7k
  Span<JumpDescriptor> getLabelList() noexcept {
148
54.7k
    return Span<JumpDescriptor>(
149
54.7k
        Data.BrTable.LabelList,
150
54.7k
        Flags.IsAllocLabelList ? Data.BrTable.LabelListSize : 0);
151
54.7k
  }
152
153
  /// Getter and setter of expression end for End instruction.
154
0
  bool isExprLast() const noexcept { return Data.EndFlags.IsExprLast; }
155
56.2k
  void setExprLast(bool Last = true) noexcept {
156
56.2k
    Data.EndFlags.IsExprLast = Last;
157
56.2k
  }
158
159
  /// Getter and setter of try block end for End instruction.
160
0
  bool isTryBlockLast() const noexcept { return Data.EndFlags.IsTryBlockLast; }
161
27.0k
  void setTryBlockLast(bool Last = true) noexcept {
162
27.0k
    Data.EndFlags.IsTryBlockLast = Last;
163
27.0k
  }
164
165
  /// Getter and setter of Jump for Br* instruction.
166
4.74k
  const JumpDescriptor &getJump() const noexcept { return Data.Jump; }
167
15.6k
  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.58k
      Data.SelectT.ValTypeListSize = Size;
174
1.58k
      Data.SelectT.ValTypeList = new ValType[Size];
175
1.58k
      Flags.IsAllocValTypeList = true;
176
1.58k
    }
177
1.86k
  }
178
1.24k
  Span<const ValType> getValTypeList() const noexcept {
179
1.24k
    return Span<const ValType>(Data.SelectT.ValTypeList,
180
1.24k
                               Data.SelectT.ValTypeListSize);
181
1.24k
  }
182
2.59k
  Span<ValType> getValTypeList() noexcept {
183
2.59k
    return Span<ValType>(Data.SelectT.ValTypeList,
184
2.59k
                         Data.SelectT.ValTypeListSize);
185
2.59k
  }
186
187
  /// Getter and setter of target index.
188
194k
  uint32_t getTargetIndex() const noexcept { return Data.Indices.TargetIdx; }
189
279k
  uint32_t &getTargetIndex() noexcept { return Data.Indices.TargetIdx; }
190
191
  /// Getter and setter of source index.
192
4.45k
  uint32_t getSourceIndex() const noexcept { return Data.Indices.SourceIdx; }
193
24.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
25.2k
  uint32_t &getStackOffset() noexcept { return Data.Indices.StackOffset; }
198
199
  /// Getter and setter of memory alignment.
200
119k
  uint32_t getMemoryAlign() const noexcept { return Data.Memories.MemAlign; }
201
465k
  uint32_t &getMemoryAlign() noexcept { return Data.Memories.MemAlign; }
202
203
  /// Getter of memory offset.
204
22.2k
  uint32_t getMemoryOffset() const noexcept { return Data.Memories.MemOffset; }
205
154k
  uint32_t &getMemoryOffset() noexcept { return Data.Memories.MemOffset; }
206
207
  /// Getter of memory lane.
208
16.4k
  uint8_t getMemoryLane() const noexcept { return Data.Memories.MemLane; }
209
49.1k
  uint8_t &getMemoryLane() noexcept { return Data.Memories.MemLane; }
210
211
  /// Getter and setter of the constant value.
212
672k
  ValVariant getNum() const noexcept {
213
672k
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
214
672k
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
215
672k
    return ValVariant(Data.Num);
216
#else
217
    uint128_t N{Data.Num.High, Data.Num.Low};
218
    return ValVariant(N);
219
#endif
220
672k
  }
221
2.32M
  void setNum(ValVariant N) noexcept {
222
2.32M
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
223
2.32M
    (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__)
224
2.32M
    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.32M
  }
231
232
  /// Getter and setter of BrCast info for Br_cast instructions.
233
635
  void setBrCast(uint32_t LabelIdx) {
234
635
    reset();
235
635
    Data.BrCast = new BrCastDescriptor();
236
635
    Data.BrCast->Jump.TargetIndex = LabelIdx;
237
635
    Flags.IsAllocBrCast = true;
238
635
  }
239
75
  const BrCastDescriptor &getBrCast() const noexcept { return *Data.BrCast; }
240
1.26k
  BrCastDescriptor &getBrCast() noexcept { return *Data.BrCast; }
241
242
  /// Getter and setter of try block info for try_table instruction.
243
1.33k
  void setTryCatch() {
244
1.33k
    reset();
245
1.33k
    Data.TryCatch = new TryDescriptor();
246
1.33k
    Flags.IsAllocTryCatch = true;
247
1.33k
  }
248
82
  const TryDescriptor &getTryCatch() const noexcept { return *Data.TryCatch; }
249
27.3k
  TryDescriptor &getTryCatch() noexcept { return *Data.TryCatch; }
250
251
private:
252
  /// Release allocated resources.
253
22.3M
  void reset() noexcept {
254
22.3M
    if (Flags.IsAllocLabelList) {
255
8.15k
      Data.BrTable.LabelListSize = 0;
256
8.15k
      delete[] Data.BrTable.LabelList;
257
22.3M
    } else if (Flags.IsAllocValTypeList) {
258
2.29k
      Data.SelectT.ValTypeListSize = 0;
259
2.29k
      delete[] Data.SelectT.ValTypeList;
260
22.3M
    } else if (Flags.IsAllocBrCast) {
261
762
      delete Data.BrCast;
262
22.3M
    } else if (Flags.IsAllocTryCatch) {
263
2.00k
      delete Data.TryCatch;
264
2.00k
    }
265
22.3M
    Flags.IsAllocLabelList = false;
266
22.3M
    Flags.IsAllocValTypeList = false;
267
22.3M
    Flags.IsAllocBrCast = false;
268
22.3M
    Flags.IsAllocTryCatch = false;
269
22.3M
  }
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