Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/common/configure.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- wasmedge/common/configure.h - Configuration class -----------------===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the configuration class of proposals, pre-registration
12
/// host functions, etc.
13
///
14
//===----------------------------------------------------------------------===//
15
#pragma once
16
17
#include "common/enum_ast.hpp"
18
#include "common/enum_configure.hpp"
19
#include "common/errcode.h"
20
#include "common/hash.h"
21
22
#include <atomic>
23
#include <bitset>
24
#include <cstdint>
25
#include <initializer_list>
26
#include <mutex>
27
#include <optional>
28
#include <shared_mutex>
29
#include <unordered_set>
30
31
namespace WasmEdge {
32
33
class CompilerConfigure {
34
public:
35
9.46k
  CompilerConfigure() noexcept = default;
36
  CompilerConfigure(const CompilerConfigure &RHS) noexcept
37
18.3k
      : OptLevel(RHS.OptLevel.load(std::memory_order_relaxed)),
38
18.3k
        OFormat(RHS.OFormat.load(std::memory_order_relaxed)),
39
18.3k
        DumpIR(RHS.DumpIR.load(std::memory_order_relaxed)),
40
18.3k
        GenericBinary(RHS.GenericBinary.load(std::memory_order_relaxed)),
41
18.3k
        Interruptible(RHS.Interruptible.load(std::memory_order_relaxed)) {}
42
43
  /// AOT compiler optimization level enum class.
44
  enum class OptimizationLevel : uint8_t {
45
    // Disable as many optimizations as possible.
46
    O0,
47
    // Optimize quickly without destroying debuggability.
48
    O1,
49
    // Optimize for fast execution as much as possible without triggering
50
    // significant incremental compile time or code size growth.
51
    O2,
52
    // Optimize for fast execution as much as possible.
53
    O3,
54
    // Optimize for small code size as much as possible without triggering
55
    // significant incremental compile time or execution time slowdowns.
56
    Os,
57
    // Optimize for small code size as much as possible.
58
    Oz
59
  };
60
0
  void setOptimizationLevel(OptimizationLevel Level) noexcept {
61
0
    OptLevel.store(Level, std::memory_order_relaxed);
62
0
  }
63
6.88k
  OptimizationLevel getOptimizationLevel() const noexcept {
64
6.88k
    return OptLevel.load(std::memory_order_relaxed);
65
6.88k
  }
66
67
  /// AOT compiler output binary format.
68
  enum class OutputFormat : uint8_t {
69
    // Native dynamic library format.
70
    Native,
71
    // WebAssembly with AOT compiled codes in custom sections.
72
    Wasm,
73
  };
74
0
  void setOutputFormat(OutputFormat Format) noexcept {
75
0
    OFormat.store(Format, std::memory_order_relaxed);
76
0
  }
77
4.58k
  OutputFormat getOutputFormat() const noexcept {
78
4.58k
    return OFormat.load(std::memory_order_relaxed);
79
4.58k
  }
80
81
0
  void setDumpIR(bool IsDump) noexcept {
82
0
    DumpIR.store(IsDump, std::memory_order_relaxed);
83
0
  }
84
85
4.58k
  bool isDumpIR() const noexcept {
86
4.58k
    return DumpIR.load(std::memory_order_relaxed);
87
4.58k
  }
88
89
0
  void setGenericBinary(bool IsGenericBinary) noexcept {
90
0
    GenericBinary.store(IsGenericBinary, std::memory_order_relaxed);
91
0
  }
92
93
4.59k
  bool isGenericBinary() const noexcept {
94
4.59k
    return GenericBinary.load(std::memory_order_relaxed);
95
4.59k
  }
96
97
0
  void setInterruptible(bool IsInterruptible) noexcept {
98
0
    Interruptible.store(IsInterruptible, std::memory_order_relaxed);
99
0
  }
100
101
11.2k
  bool isInterruptible() const noexcept {
102
11.2k
    return Interruptible.load(std::memory_order_relaxed);
103
11.2k
  }
104
105
private:
106
  std::atomic<OptimizationLevel> OptLevel = OptimizationLevel::O3;
107
  std::atomic<OutputFormat> OFormat = OutputFormat::Wasm;
108
  std::atomic<bool> DumpIR = false;
109
  std::atomic<bool> GenericBinary = false;
110
  std::atomic<bool> Interruptible = false;
111
};
112
113
class RuntimeConfigure {
114
public:
115
9.46k
  RuntimeConfigure() noexcept = default;
116
  RuntimeConfigure(const RuntimeConfigure &RHS) noexcept
117
18.3k
      : MaxMemPage(RHS.MaxMemPage.load(std::memory_order_relaxed)),
118
18.3k
        Mode(RHS.Mode.load(std::memory_order_relaxed)),
119
18.3k
        EnableCoredump(RHS.EnableCoredump.load(std::memory_order_relaxed)),
120
18.3k
        CoredumpWasmgdb(RHS.CoredumpWasmgdb.load(std::memory_order_relaxed)),
121
18.3k
        AllowAFUNIX(RHS.AllowAFUNIX.load(std::memory_order_relaxed)) {}
122
123
0
  void setMaxMemoryPage(const uint64_t Page) noexcept {
124
0
    MaxMemPage.store(Page, std::memory_order_relaxed);
125
0
  }
126
127
0
  uint64_t getMaxMemoryPage() const noexcept {
128
0
    return MaxMemPage.load(std::memory_order_relaxed);
129
0
  }
130
131
9.46k
  void setRunMode(RunMode M) noexcept {
132
9.46k
    Mode.store(M, std::memory_order_relaxed);
133
9.46k
  }
134
135
44.0k
  RunMode getRunMode() const noexcept {
136
44.0k
    return Mode.load(std::memory_order_relaxed);
137
44.0k
  }
138
139
0
  void setEnableCoredump(bool IsEnableCoredump) noexcept {
140
0
    EnableCoredump.store(IsEnableCoredump, std::memory_order_relaxed);
141
0
  }
142
143
0
  bool isEnableCoredump() const noexcept {
144
0
    return EnableCoredump.load(std::memory_order_relaxed);
145
0
  }
146
147
0
  void setCoredumpWasmgdb(bool IsCoredumpWasmgdb) noexcept {
148
0
    CoredumpWasmgdb.store(IsCoredumpWasmgdb, std::memory_order_relaxed);
149
0
  }
150
151
0
  bool isCoredumpWasmgdb() const noexcept {
152
0
    return CoredumpWasmgdb.load(std::memory_order_relaxed);
153
0
  }
154
155
0
  void setAllowAFUNIX(bool IsAllowAFUNIX) noexcept {
156
0
    AllowAFUNIX.store(IsAllowAFUNIX, std::memory_order_relaxed);
157
0
  }
158
159
0
  bool isAllowAFUNIX() const noexcept {
160
0
    return AllowAFUNIX.load(std::memory_order_relaxed);
161
0
  }
162
163
private:
164
  std::atomic<uint64_t> MaxMemPage = 65536;
165
  std::atomic<RunMode> Mode = RunMode::Interpreter;
166
  std::atomic<bool> EnableCoredump = false;
167
  std::atomic<bool> CoredumpWasmgdb = false;
168
  std::atomic<bool> AllowAFUNIX = false;
169
};
170
171
class StatisticsConfigure {
172
public:
173
9.46k
  StatisticsConfigure() noexcept = default;
174
  StatisticsConfigure(const StatisticsConfigure &RHS) noexcept
175
18.3k
      : InstrCounting(RHS.InstrCounting.load(std::memory_order_relaxed)),
176
18.3k
        CostMeasuring(RHS.CostMeasuring.load(std::memory_order_relaxed)),
177
18.3k
        TimeMeasuring(RHS.TimeMeasuring.load(std::memory_order_relaxed)) {}
178
179
0
  void setInstructionCounting(bool IsCount) noexcept {
180
0
    InstrCounting.store(IsCount, std::memory_order_relaxed);
181
0
  }
182
183
11.2k
  bool isInstructionCounting() const noexcept {
184
11.2k
    return InstrCounting.load(std::memory_order_relaxed);
185
11.2k
  }
186
187
0
  void setCostMeasuring(bool IsMeasure) noexcept {
188
0
    CostMeasuring.store(IsMeasure, std::memory_order_relaxed);
189
0
  }
190
191
11.2k
  bool isCostMeasuring() const noexcept {
192
11.2k
    return CostMeasuring.load(std::memory_order_relaxed);
193
11.2k
  }
194
195
0
  void setTimeMeasuring(bool IsTimeMeasure) noexcept {
196
0
    TimeMeasuring.store(IsTimeMeasure, std::memory_order_relaxed);
197
0
  }
198
199
0
  bool isTimeMeasuring() const noexcept {
200
0
    return TimeMeasuring.load(std::memory_order_relaxed);
201
0
  }
202
203
0
  void setCostLimit(uint64_t Cost) noexcept {
204
0
    CostLimit.store(Cost, std::memory_order_relaxed);
205
0
  }
206
207
0
  uint64_t getCostLimit() const noexcept {
208
0
    return CostLimit.load(std::memory_order_relaxed);
209
0
  }
210
211
private:
212
  std::atomic<bool> InstrCounting = false;
213
  std::atomic<bool> CostMeasuring = false;
214
  std::atomic<bool> TimeMeasuring = false;
215
216
  std::atomic<uint64_t> CostLimit = std::numeric_limits<uint64_t>::max();
217
};
218
219
class Configure {
220
public:
221
9.46k
  Configure() noexcept {
222
9.46k
    unsafeAddProposal(Proposal::ImportExportMutGlobals);
223
9.46k
    unsafeAddProposal(Proposal::NonTrapFloatToIntConversions);
224
9.46k
    unsafeAddProposal(Proposal::SignExtensionOperators);
225
9.46k
    unsafeAddProposal(Proposal::MultiValue);
226
9.46k
    unsafeAddProposal(Proposal::BulkMemoryOperations);
227
9.46k
    unsafeAddProposal(Proposal::ReferenceTypes);
228
9.46k
    unsafeAddProposal(Proposal::SIMD);
229
9.46k
    unsafeAddProposal(Proposal::TailCall);
230
9.46k
    unsafeAddProposal(Proposal::ExtendedConst);
231
9.46k
    unsafeAddProposal(Proposal::FunctionReferences);
232
9.46k
    unsafeAddProposal(Proposal::GC);
233
9.46k
    unsafeAddProposal(Proposal::MultiMemories);
234
9.46k
    unsafeAddProposal(Proposal::RelaxSIMD);
235
    // unsafeAddProposal(Proposal::Annotations);    Not implemented
236
9.46k
    unsafeAddProposal(Proposal::ExceptionHandling);
237
9.46k
    unsafeAddProposal(Proposal::Memory64);
238
9.46k
  }
239
  template <typename... ArgsT> Configure(ArgsT... Args) noexcept : Configure() {
240
    (unsafeAddSet(Args), ...);
241
  }
242
  Configure(const Configure &RHS) noexcept
243
18.3k
      : Proposals(RHS.Proposals), Hosts(RHS.Hosts),
244
18.3k
        ForbiddenPlugins(RHS.ForbiddenPlugins), CompilerConf(RHS.CompilerConf),
245
18.3k
        RuntimeConf(RHS.RuntimeConf), StatisticsConf(RHS.StatisticsConf) {}
246
247
0
  void addProposal(const Proposal Type) noexcept {
248
0
    std::unique_lock Lock(Mutex);
249
0
    unsafeAddProposal(Type);
250
0
  }
251
252
0
  void removeProposal(const Proposal Type) noexcept {
253
0
    std::unique_lock Lock(Mutex);
254
0
    if (Type == Proposal::FunctionReferences &&
255
0
        Proposals.test(static_cast<uint8_t>(Proposal::GC))) {
256
      // Proposal dependency: GC depends FunctionReferences.
257
0
      return;
258
0
    }
259
0
    if (Type == Proposal::ReferenceTypes &&
260
0
        (Proposals.test(static_cast<uint8_t>(Proposal::GC)) ||
261
0
         Proposals.test(static_cast<uint8_t>(Proposal::FunctionReferences)))) {
262
      // Proposal dependency: GC and FunctionReferences depend on
263
      // ReferenceTypes.
264
0
      return;
265
0
    }
266
0
    Proposals.reset(static_cast<uint8_t>(Type));
267
0
  }
268
269
0
  void setWASMStandard(const Standard Std) noexcept {
270
0
    std::unique_lock Lock(Mutex);
271
0
    switch (Std) {
272
0
    case Standard::WASM_1:
273
0
      Proposals.reset();
274
0
      unsafeAddProposal(Proposal::ImportExportMutGlobals);
275
0
      break;
276
0
    case Standard::WASM_2:
277
0
      Proposals.reset();
278
0
      unsafeAddProposal(Proposal::ImportExportMutGlobals);
279
0
      unsafeAddProposal(Proposal::NonTrapFloatToIntConversions);
280
0
      unsafeAddProposal(Proposal::SignExtensionOperators);
281
0
      unsafeAddProposal(Proposal::MultiValue);
282
0
      unsafeAddProposal(Proposal::BulkMemoryOperations);
283
0
      unsafeAddProposal(Proposal::ReferenceTypes);
284
0
      unsafeAddProposal(Proposal::SIMD);
285
0
      break;
286
0
    case Standard::WASM_3:
287
0
      Proposals.reset();
288
0
      unsafeAddProposal(Proposal::ImportExportMutGlobals);
289
0
      unsafeAddProposal(Proposal::NonTrapFloatToIntConversions);
290
0
      unsafeAddProposal(Proposal::SignExtensionOperators);
291
0
      unsafeAddProposal(Proposal::MultiValue);
292
0
      unsafeAddProposal(Proposal::BulkMemoryOperations);
293
0
      unsafeAddProposal(Proposal::ReferenceTypes);
294
0
      unsafeAddProposal(Proposal::SIMD);
295
0
      unsafeAddProposal(Proposal::TailCall);
296
0
      unsafeAddProposal(Proposal::ExtendedConst);
297
0
      unsafeAddProposal(Proposal::FunctionReferences);
298
0
      unsafeAddProposal(Proposal::GC);
299
0
      unsafeAddProposal(Proposal::MultiMemories);
300
0
      unsafeAddProposal(Proposal::RelaxSIMD);
301
      // unsafeAddProposal(Proposal::Annotations);    Not implemented
302
0
      unsafeAddProposal(Proposal::ExceptionHandling);
303
0
      unsafeAddProposal(Proposal::Memory64);
304
0
      break;
305
0
    default:
306
0
      break;
307
0
    }
308
0
  }
309
310
2.90M
  bool hasProposal(const Proposal Type) const noexcept {
311
2.90M
    std::shared_lock Lock(Mutex);
312
2.90M
    return Proposals.test(static_cast<uint8_t>(Type));
313
2.90M
  }
314
315
0
  void addHostRegistration(const HostRegistration Host) noexcept {
316
0
    std::unique_lock Lock(Mutex);
317
0
    Hosts.set(static_cast<uint8_t>(Host));
318
0
  }
319
320
0
  void removeHostRegistration(const HostRegistration Host) noexcept {
321
0
    std::unique_lock Lock(Mutex);
322
0
    Hosts.reset(static_cast<uint8_t>(Host));
323
0
  }
324
325
0
  void addForbiddenPlugins(std::string PluginName) noexcept {
326
0
    std::unique_lock Lock(Mutex);
327
0
    ForbiddenPlugins.emplace(std::move(PluginName));
328
0
  }
329
330
0
  bool isForbiddenPlugins(const std::string &PluginName) const noexcept {
331
0
    std::shared_lock Lock(Mutex);
332
0
    return ForbiddenPlugins.find(PluginName) != ForbiddenPlugins.end();
333
0
  }
334
335
0
  bool hasHostRegistration(const HostRegistration Host) const noexcept {
336
0
    std::shared_lock Lock(Mutex);
337
0
    return Hosts.test(static_cast<uint8_t>(Host));
338
0
  }
339
340
31.8k
  const CompilerConfigure &getCompilerConfigure() const noexcept {
341
31.8k
    return CompilerConf;
342
31.8k
  }
343
0
  CompilerConfigure &getCompilerConfigure() noexcept { return CompilerConf; }
344
345
44.0k
  const RuntimeConfigure &getRuntimeConfigure() const noexcept {
346
44.0k
    return RuntimeConf;
347
44.0k
  }
348
9.46k
  RuntimeConfigure &getRuntimeConfigure() noexcept { return RuntimeConf; }
349
350
22.4k
  const StatisticsConfigure &getStatisticsConfigure() const noexcept {
351
22.4k
    return StatisticsConf;
352
22.4k
  }
353
0
  StatisticsConfigure &getStatisticsConfigure() noexcept {
354
0
    return StatisticsConf;
355
0
  }
356
357
  /// Helper function for checking instruction proposals.
358
10.9M
  std::optional<Proposal> isInstrNeedProposal(OpCode Code) const noexcept {
359
10.9M
    if (Code >= OpCode::I32__trunc_sat_f32_s &&
360
1.01M
        Code <= OpCode::I64__trunc_sat_f64_u) {
361
      // These instructions are for NonTrapFloatToIntConversions proposal.
362
25.7k
      if (unlikely(!hasProposal(Proposal::NonTrapFloatToIntConversions))) {
363
0
        return Proposal::NonTrapFloatToIntConversions;
364
0
      }
365
10.9M
    } else if (Code >= OpCode::I32__extend8_s &&
366
1.13M
               Code <= OpCode::I64__extend32_s) {
367
      // These instructions are for SignExtensionOperators proposal.
368
61.5k
      if (unlikely(!hasProposal(Proposal::SignExtensionOperators))) {
369
0
        return Proposal::SignExtensionOperators;
370
0
      }
371
10.9M
    } else if ((Code >= OpCode::Ref__null && Code <= OpCode::Ref__func) ||
372
10.8M
               (Code >= OpCode::Table__init && Code <= OpCode::Table__copy) ||
373
10.8M
               (Code >= OpCode::Memory__init && Code <= OpCode::Memory__fill)) {
374
      // These instructions are for ReferenceTypes or BulkMemoryOperations
375
      // proposal.
376
50.1k
      if (unlikely(!hasProposal(Proposal::ReferenceTypes)) &&
377
0
          unlikely(!hasProposal(Proposal::BulkMemoryOperations))) {
378
0
        return Proposal::ReferenceTypes;
379
0
      }
380
10.8M
    } else if (Code == OpCode::Select_t ||
381
10.8M
               (Code >= OpCode::Table__get && Code <= OpCode::Table__set) ||
382
10.8M
               (Code >= OpCode::Table__grow && Code <= OpCode::Table__fill)) {
383
      // These instructions are for ReferenceTypes proposal.
384
9.42k
      if (unlikely(!hasProposal(Proposal::ReferenceTypes))) {
385
0
        return Proposal::ReferenceTypes;
386
0
      }
387
10.8M
    } else if (Code >= OpCode::V128__load &&
388
976k
               Code <= OpCode::F64x2__convert_low_i32x4_u) {
389
      // These instructions are for SIMD proposal.
390
956k
      if (unlikely(!hasProposal(Proposal::SIMD))) {
391
0
        return Proposal::SIMD;
392
0
      }
393
9.89M
    } else if (Code >= OpCode::I8x16__relaxed_swizzle &&
394
19.5k
               Code <= OpCode::I32x4__relaxed_dot_i8x16_i7x16_add_s) {
395
      // These instructions are for Relaxed SIMD proposal.
396
10.2k
      if (unlikely(!hasProposal(Proposal::RelaxSIMD))) {
397
0
        return Proposal::RelaxSIMD;
398
0
      }
399
9.88M
    } else if (Code == OpCode::Return_call ||
400
9.88M
               Code == OpCode::Return_call_indirect) {
401
      // These instructions are for TailCall proposal.
402
948
      if (unlikely(!hasProposal(Proposal::TailCall))) {
403
0
        return Proposal::TailCall;
404
0
      }
405
9.88M
    } else if (Code >= OpCode::I32__atomic__load &&
406
118
               Code <= OpCode::I64__atomic__rmw32__cmpxchg_u) {
407
      // These instructions are for Thread proposal.
408
118
      if (!hasProposal(Proposal::Threads)) {
409
118
        return Proposal::Threads;
410
118
      }
411
9.88M
    } else if (Code == OpCode::Call_ref || Code == OpCode::Return_call_ref ||
412
9.87M
               Code == OpCode::Ref__as_non_null || Code == OpCode::Br_on_null ||
413
9.87M
               Code == OpCode::Br_on_non_null) {
414
      // These instructions are for TypedFunctionReferences proposal.
415
4.77k
      if (unlikely(!hasProposal(Proposal::FunctionReferences))) {
416
0
        return Proposal::FunctionReferences;
417
0
      }
418
4.77k
      if (Code == OpCode::Return_call_ref && !hasProposal(Proposal::TailCall)) {
419
0
        return Proposal::TailCall;
420
0
      }
421
9.87M
    } else if (Code == OpCode::Ref__eq ||
422
9.87M
               (Code >= OpCode::Struct__new && Code <= OpCode::I31__get_u)) {
423
      // These instructions are for GC proposal.
424
43.2k
      if (unlikely(!hasProposal(Proposal::GC))) {
425
0
        return Proposal::GC;
426
0
      }
427
9.83M
    } else if (Code == OpCode::Throw || Code == OpCode::Throw_ref ||
428
9.82M
               Code == OpCode::Try_table) {
429
      // These instructions are for ExceptionHandling proposal.
430
22.9k
      if (unlikely(!hasProposal(Proposal::ExceptionHandling))) {
431
0
        return Proposal::ExceptionHandling;
432
0
      }
433
22.9k
    }
434
10.9M
    return {};
435
10.9M
  }
436
437
private:
438
0
  void unsafeAddSet(const Proposal P) noexcept { unsafeAddProposal(P); }
439
0
  void unsafeAddSet(const HostRegistration H) noexcept {
440
0
    unsafeAddHostRegistration(H);
441
0
  }
442
443
141k
  void unsafeAddProposal(const Proposal Type) noexcept {
444
141k
    Proposals.set(static_cast<uint8_t>(Type));
445
    // Proposal dependency: FunctionReferences depends on ReferenceTypes.
446
141k
    if (Type == Proposal::FunctionReferences) {
447
9.46k
      Proposals.set(static_cast<uint8_t>(Proposal::ReferenceTypes));
448
9.46k
    }
449
    // Proposal dependency: GC depends on FunctionReferences and ReferenceTypes.
450
141k
    if (Type == Proposal::GC) {
451
9.46k
      Proposals.set(static_cast<uint8_t>(Proposal::FunctionReferences));
452
9.46k
      Proposals.set(static_cast<uint8_t>(Proposal::ReferenceTypes));
453
9.46k
    }
454
141k
  }
455
456
0
  void unsafeAddHostRegistration(const HostRegistration Host) noexcept {
457
0
    Hosts.set(static_cast<uint8_t>(Host));
458
0
  }
459
460
  mutable std::shared_mutex Mutex;
461
  std::bitset<static_cast<uint8_t>(Proposal::Max)> Proposals;
462
  std::bitset<static_cast<uint8_t>(HostRegistration::Max)> Hosts;
463
  std::unordered_set<std::string, Hash::Hash> ForbiddenPlugins;
464
465
  CompilerConfigure CompilerConf;
466
  RuntimeConfigure RuntimeConf;
467
  StatisticsConfigure StatisticsConf;
468
};
469
470
} // namespace WasmEdge