Coverage Report

Created: 2025-11-16 06:42

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: 2019-2024 Second State INC
3
4
//===-- wasmedge/common/configure.h - Configuration class -----------------===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contents 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
16.3k
  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.89k
  OptimizationLevel getOptimizationLevel() const noexcept {
64
6.89k
    return OptLevel.load(std::memory_order_relaxed);
65
6.89k
  }
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.59k
  OutputFormat getOutputFormat() const noexcept {
78
4.59k
    return OFormat.load(std::memory_order_relaxed);
79
4.59k
  }
80
81
0
  void setDumpIR(bool IsDump) noexcept {
82
0
    DumpIR.store(IsDump, std::memory_order_relaxed);
83
0
  }
84
85
4.59k
  bool isDumpIR() const noexcept {
86
4.59k
    return DumpIR.load(std::memory_order_relaxed);
87
4.59k
  }
88
89
0
  void setGenericBinary(bool IsGenericBinary) noexcept {
90
0
    GenericBinary.store(IsGenericBinary, std::memory_order_relaxed);
91
0
  }
92
93
4.60k
  bool isGenericBinary() const noexcept {
94
4.60k
    return GenericBinary.load(std::memory_order_relaxed);
95
4.60k
  }
96
97
0
  void setInterruptible(bool IsInterruptible) noexcept {
98
0
    Interruptible.store(IsInterruptible, std::memory_order_relaxed);
99
0
  }
100
101
11.4k
  bool isInterruptible() const noexcept {
102
11.4k
    return Interruptible.load(std::memory_order_relaxed);
103
11.4k
  }
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
16.3k
  RuntimeConfigure() noexcept = default;
116
  RuntimeConfigure(const RuntimeConfigure &RHS) noexcept
117
18.3k
      : MaxMemPage(RHS.MaxMemPage.load(std::memory_order_relaxed)),
118
18.3k
        EnableJIT(RHS.EnableJIT.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
        ForceInterpreter(RHS.ForceInterpreter.load(std::memory_order_relaxed)),
122
18.3k
        AllowAFUNIX(RHS.AllowAFUNIX.load(std::memory_order_relaxed)) {}
123
124
0
  void setMaxMemoryPage(const uint32_t Page) noexcept {
125
0
    MaxMemPage.store(Page, std::memory_order_relaxed);
126
0
  }
127
128
0
  uint32_t getMaxMemoryPage() const noexcept {
129
0
    return MaxMemPage.load(std::memory_order_relaxed);
130
0
  }
131
132
0
  void setEnableJIT(bool IsEnableJIT) noexcept {
133
0
    EnableJIT.store(IsEnableJIT, std::memory_order_relaxed);
134
0
  }
135
136
0
  bool isEnableJIT() const noexcept {
137
0
    return EnableJIT.load(std::memory_order_relaxed);
138
0
  }
139
140
0
  void setEnableCoredump(bool IsEnableCoredump) noexcept {
141
0
    EnableCoredump.store(IsEnableCoredump, std::memory_order_relaxed);
142
0
  }
143
144
0
  bool isEnableCoredump() const noexcept {
145
0
    return EnableCoredump.load(std::memory_order_relaxed);
146
0
  }
147
148
0
  void setCoredumpWasmgdb(bool IsCoredumpWasmgdb) noexcept {
149
0
    CoredumpWasmgdb.store(IsCoredumpWasmgdb, std::memory_order_relaxed);
150
0
  }
151
152
0
  bool isCoredumpWasmgdb() const noexcept {
153
0
    return CoredumpWasmgdb.load(std::memory_order_relaxed);
154
0
  }
155
156
9.39k
  void setForceInterpreter(bool IsForceInterpreter) noexcept {
157
9.39k
    ForceInterpreter.store(IsForceInterpreter, std::memory_order_relaxed);
158
9.39k
  }
159
160
33.9k
  bool isForceInterpreter() const noexcept {
161
33.9k
    return ForceInterpreter.load(std::memory_order_relaxed);
162
33.9k
  }
163
164
0
  void setAllowAFUNIX(bool IsAllowAFUNIX) noexcept {
165
0
    AllowAFUNIX.store(IsAllowAFUNIX, std::memory_order_relaxed);
166
0
  }
167
168
0
  bool isAllowAFUNIX() const noexcept {
169
0
    return AllowAFUNIX.load(std::memory_order_relaxed);
170
0
  }
171
172
private:
173
  std::atomic<uint32_t> MaxMemPage = 65536;
174
  std::atomic<bool> EnableJIT = false;
175
  std::atomic<bool> EnableCoredump = false;
176
  std::atomic<bool> CoredumpWasmgdb = false;
177
  std::atomic<bool> ForceInterpreter = false;
178
  std::atomic<bool> AllowAFUNIX = false;
179
};
180
181
class StatisticsConfigure {
182
public:
183
16.3k
  StatisticsConfigure() noexcept = default;
184
  StatisticsConfigure(const StatisticsConfigure &RHS) noexcept
185
18.3k
      : InstrCounting(RHS.InstrCounting.load(std::memory_order_relaxed)),
186
18.3k
        CostMeasuring(RHS.CostMeasuring.load(std::memory_order_relaxed)),
187
18.3k
        TimeMeasuring(RHS.TimeMeasuring.load(std::memory_order_relaxed)) {}
188
189
0
  void setInstructionCounting(bool IsCount) noexcept {
190
0
    InstrCounting.store(IsCount, std::memory_order_relaxed);
191
0
  }
192
193
11.4k
  bool isInstructionCounting() const noexcept {
194
11.4k
    return InstrCounting.load(std::memory_order_relaxed);
195
11.4k
  }
196
197
0
  void setCostMeasuring(bool IsMeasure) noexcept {
198
0
    CostMeasuring.store(IsMeasure, std::memory_order_relaxed);
199
0
  }
200
201
11.4k
  bool isCostMeasuring() const noexcept {
202
11.4k
    return CostMeasuring.load(std::memory_order_relaxed);
203
11.4k
  }
204
205
0
  void setTimeMeasuring(bool IsTimeMeasure) noexcept {
206
0
    TimeMeasuring.store(IsTimeMeasure, std::memory_order_relaxed);
207
0
  }
208
209
0
  bool isTimeMeasuring() const noexcept {
210
0
    return TimeMeasuring.load(std::memory_order_relaxed);
211
0
  }
212
213
0
  void setCostLimit(uint64_t Cost) noexcept {
214
0
    CostLimit.store(Cost, std::memory_order_relaxed);
215
0
  }
216
217
0
  uint64_t getCostLimit() const noexcept {
218
0
    return CostLimit.load(std::memory_order_relaxed);
219
0
  }
220
221
private:
222
  std::atomic<bool> InstrCounting = false;
223
  std::atomic<bool> CostMeasuring = false;
224
  std::atomic<bool> TimeMeasuring = false;
225
226
  std::atomic<uint64_t> CostLimit = std::numeric_limits<uint64_t>::max();
227
};
228
229
class Configure {
230
public:
231
16.3k
  Configure() noexcept {
232
16.3k
    unsafeAddProposal(Proposal::ImportExportMutGlobals);
233
16.3k
    unsafeAddProposal(Proposal::NonTrapFloatToIntConversions);
234
16.3k
    unsafeAddProposal(Proposal::SignExtensionOperators);
235
16.3k
    unsafeAddProposal(Proposal::MultiValue);
236
16.3k
    unsafeAddProposal(Proposal::BulkMemoryOperations);
237
16.3k
    unsafeAddProposal(Proposal::ReferenceTypes);
238
16.3k
    unsafeAddProposal(Proposal::SIMD);
239
16.3k
    unsafeAddProposal(Proposal::TailCall);
240
16.3k
    unsafeAddProposal(Proposal::ExtendedConst);
241
16.3k
    unsafeAddProposal(Proposal::FunctionReferences);
242
16.3k
    unsafeAddProposal(Proposal::GC);
243
16.3k
    unsafeAddProposal(Proposal::MultiMemories);
244
16.3k
    unsafeAddProposal(Proposal::RelaxSIMD);
245
    // unsafeAddProposal(Proposal::Annotations);    Not implemented
246
16.3k
    unsafeAddProposal(Proposal::ExceptionHandling);
247
    // unsafeAddProposal(Proposal::Memory64);       Not implemented
248
16.3k
  }
249
  template <typename... ArgsT> Configure(ArgsT... Args) noexcept : Configure() {
250
    (unsafeAddSet(Args), ...);
251
  }
252
  Configure(const Configure &RHS) noexcept
253
18.3k
      : Proposals(RHS.Proposals), Hosts(RHS.Hosts),
254
18.3k
        ForbiddenPlugins(RHS.ForbiddenPlugins), CompilerConf(RHS.CompilerConf),
255
18.3k
        RuntimeConf(RHS.RuntimeConf), StatisticsConf(RHS.StatisticsConf) {}
256
257
0
  void addProposal(const Proposal Type) noexcept {
258
0
    std::unique_lock Lock(Mutex);
259
0
    unsafeAddProposal(Type);
260
0
  }
261
262
0
  void removeProposal(const Proposal Type) noexcept {
263
0
    std::unique_lock Lock(Mutex);
264
0
    if (Type == Proposal::FunctionReferences &&
265
0
        Proposals.test(static_cast<uint8_t>(Proposal::GC))) {
266
      // Proposal dependency: GC depends FunctionReferences.
267
0
      return;
268
0
    }
269
0
    if (Type == Proposal::ReferenceTypes &&
270
0
        (Proposals.test(static_cast<uint8_t>(Proposal::GC)) ||
271
0
         Proposals.test(static_cast<uint8_t>(Proposal::FunctionReferences)))) {
272
      // Proposal dependency: GC and FunctionReferences depend on
273
      // ReferenceTypes.
274
0
      return;
275
0
    }
276
0
    Proposals.reset(static_cast<uint8_t>(Type));
277
0
  }
278
279
0
  void setWASMStandard(const Standard Std) noexcept {
280
0
    std::unique_lock Lock(Mutex);
281
0
    switch (Std) {
282
0
    case Standard::WASM_1:
283
0
      Proposals.reset();
284
0
      unsafeAddProposal(Proposal::ImportExportMutGlobals);
285
0
      break;
286
0
    case Standard::WASM_2:
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
      break;
296
0
    case Standard::WASM_3:
297
0
      Proposals.reset();
298
0
      unsafeAddProposal(Proposal::ImportExportMutGlobals);
299
0
      unsafeAddProposal(Proposal::NonTrapFloatToIntConversions);
300
0
      unsafeAddProposal(Proposal::SignExtensionOperators);
301
0
      unsafeAddProposal(Proposal::MultiValue);
302
0
      unsafeAddProposal(Proposal::BulkMemoryOperations);
303
0
      unsafeAddProposal(Proposal::ReferenceTypes);
304
0
      unsafeAddProposal(Proposal::SIMD);
305
0
      unsafeAddProposal(Proposal::TailCall);
306
0
      unsafeAddProposal(Proposal::ExtendedConst);
307
0
      unsafeAddProposal(Proposal::FunctionReferences);
308
0
      unsafeAddProposal(Proposal::GC);
309
0
      unsafeAddProposal(Proposal::MultiMemories);
310
0
      unsafeAddProposal(Proposal::RelaxSIMD);
311
      // unsafeAddProposal(Proposal::Annotations);    Not implemented
312
0
      unsafeAddProposal(Proposal::ExceptionHandling);
313
      // unsafeAddProposal(Proposal::Memory64);       Not implemented
314
0
      break;
315
0
    default:
316
0
      break;
317
0
    }
318
0
  }
319
320
1.89M
  bool hasProposal(const Proposal Type) const noexcept {
321
1.89M
    std::shared_lock Lock(Mutex);
322
1.89M
    return Proposals.test(static_cast<uint8_t>(Type));
323
1.89M
  }
324
325
0
  void addHostRegistration(const HostRegistration Host) noexcept {
326
0
    std::unique_lock Lock(Mutex);
327
0
    Hosts.set(static_cast<uint8_t>(Host));
328
0
  }
329
330
0
  void removeHostRegistration(const HostRegistration Host) noexcept {
331
0
    std::unique_lock Lock(Mutex);
332
0
    Hosts.reset(static_cast<uint8_t>(Host));
333
0
  }
334
335
0
  void addForbiddenPlugins(std::string PluginName) noexcept {
336
0
    std::unique_lock Lock(Mutex);
337
0
    ForbiddenPlugins.emplace(std::move(PluginName));
338
0
  }
339
340
0
  bool isForbiddenPlugins(const std::string &PluginName) const noexcept {
341
0
    std::shared_lock Lock(Mutex);
342
0
    return ForbiddenPlugins.find(PluginName) != ForbiddenPlugins.end();
343
0
  }
344
345
0
  bool hasHostRegistration(const HostRegistration Host) const noexcept {
346
0
    std::shared_lock Lock(Mutex);
347
0
    return Hosts.test(static_cast<uint8_t>(Host));
348
0
  }
349
350
32.1k
  const CompilerConfigure &getCompilerConfigure() const noexcept {
351
32.1k
    return CompilerConf;
352
32.1k
  }
353
0
  CompilerConfigure &getCompilerConfigure() noexcept { return CompilerConf; }
354
355
33.9k
  const RuntimeConfigure &getRuntimeConfigure() const noexcept {
356
33.9k
    return RuntimeConf;
357
33.9k
  }
358
9.39k
  RuntimeConfigure &getRuntimeConfigure() noexcept { return RuntimeConf; }
359
360
22.9k
  const StatisticsConfigure &getStatisticsConfigure() const noexcept {
361
22.9k
    return StatisticsConf;
362
22.9k
  }
363
0
  StatisticsConfigure &getStatisticsConfigure() noexcept {
364
0
    return StatisticsConf;
365
0
  }
366
367
  /// Helper function of checking the proposal of instructions.
368
8.23M
  std::optional<Proposal> isInstrNeedProposal(OpCode Code) const noexcept {
369
8.23M
    if (Code >= OpCode::I32__trunc_sat_f32_s &&
370
879k
        Code <= OpCode::I64__trunc_sat_f64_u) {
371
      // These instructions are for NonTrapFloatToIntConversions proposal.
372
20.8k
      if (unlikely(!hasProposal(Proposal::NonTrapFloatToIntConversions))) {
373
0
        return Proposal::NonTrapFloatToIntConversions;
374
0
      }
375
8.21M
    } else if (Code >= OpCode::I32__extend8_s &&
376
948k
               Code <= OpCode::I64__extend32_s) {
377
      // These instructions are for SignExtensionOperators proposal.
378
54.3k
      if (unlikely(!hasProposal(Proposal::SignExtensionOperators))) {
379
0
        return Proposal::SignExtensionOperators;
380
0
      }
381
8.15M
    } else if ((Code >= OpCode::Ref__null && Code <= OpCode::Ref__func) ||
382
8.14M
               (Code >= OpCode::Table__init && Code <= OpCode::Table__copy) ||
383
8.13M
               (Code >= OpCode::Memory__init && Code <= OpCode::Memory__fill)) {
384
      // These instructions are for ReferenceTypes or BulkMemoryOperations
385
      // proposal.
386
27.5k
      if (unlikely(!hasProposal(Proposal::ReferenceTypes)) &&
387
0
          unlikely(!hasProposal(Proposal::BulkMemoryOperations))) {
388
0
        return Proposal::ReferenceTypes;
389
0
      }
390
8.13M
    } else if (Code == OpCode::Select_t ||
391
8.12M
               (Code >= OpCode::Table__get && Code <= OpCode::Table__set) ||
392
8.12M
               (Code >= OpCode::Table__grow && Code <= OpCode::Table__fill)) {
393
      // These instructions are for ReferenceTypes proposal.
394
15.6k
      if (unlikely(!hasProposal(Proposal::ReferenceTypes))) {
395
0
        return Proposal::ReferenceTypes;
396
0
      }
397
8.11M
    } else if (Code >= OpCode::V128__load &&
398
840k
               Code <= OpCode::F64x2__convert_low_i32x4_u) {
399
      // These instructions are for SIMD proposal.
400
827k
      if (unlikely(!hasProposal(Proposal::SIMD))) {
401
0
        return Proposal::SIMD;
402
0
      }
403
7.28M
    } else if (Code >= OpCode::I8x16__relaxed_swizzle &&
404
12.5k
               Code <= OpCode::I32x4__relaxed_dot_i8x16_i7x16_add_s) {
405
      // These instructions are for Relaxed SIMD proposal.
406
5.90k
      if (unlikely(!hasProposal(Proposal::RelaxSIMD))) {
407
0
        return Proposal::RelaxSIMD;
408
0
      }
409
7.28M
    } else if (Code == OpCode::Return_call ||
410
7.28M
               Code == OpCode::Return_call_indirect) {
411
      // These instructions are for TailCall proposal.
412
1.10k
      if (unlikely(!hasProposal(Proposal::TailCall))) {
413
0
        return Proposal::TailCall;
414
0
      }
415
7.28M
    } else if (Code >= OpCode::I32__atomic__load &&
416
79
               Code <= OpCode::I64__atomic__rmw32__cmpxchg_u) {
417
      // These instructions are for Thread proposal.
418
79
      if (!hasProposal(Proposal::Threads)) {
419
79
        return Proposal::Threads;
420
79
      }
421
7.28M
    } else if (Code == OpCode::Call_ref || Code == OpCode::Return_call_ref ||
422
7.27M
               Code == OpCode::Ref__as_non_null || Code == OpCode::Br_on_null ||
423
7.27M
               Code == OpCode::Br_on_non_null) {
424
      // These instructions are for TypedFunctionReferences proposal.
425
8.51k
      if (unlikely(!hasProposal(Proposal::FunctionReferences))) {
426
0
        return Proposal::FunctionReferences;
427
0
      }
428
8.51k
      if (Code == OpCode::Return_call_ref && !hasProposal(Proposal::TailCall)) {
429
0
        return Proposal::TailCall;
430
0
      }
431
7.27M
    } else if (Code == OpCode::Ref__eq ||
432
7.26M
               (Code >= OpCode::Struct__new && Code <= OpCode::I31__get_u)) {
433
      // These instructions are for GC proposal.
434
16.3k
      if (unlikely(!hasProposal(Proposal::GC))) {
435
0
        return Proposal::GC;
436
0
      }
437
7.25M
    } else if (Code == OpCode::Throw || Code == OpCode::Throw_ref ||
438
7.24M
               Code == OpCode::Try_table) {
439
      // These instructions are for ExceptionHandling proposal.
440
8.38k
      if (unlikely(!hasProposal(Proposal::ExceptionHandling))) {
441
0
        return Proposal::ExceptionHandling;
442
0
      }
443
8.38k
    }
444
8.23M
    return {};
445
8.23M
  }
446
447
private:
448
0
  void unsafeAddSet(const Proposal P) noexcept { unsafeAddProposal(P); }
449
0
  void unsafeAddSet(const HostRegistration H) noexcept {
450
0
    unsafeAddHostRegistration(H);
451
0
  }
452
453
228k
  void unsafeAddProposal(const Proposal Type) noexcept {
454
228k
    Proposals.set(static_cast<uint8_t>(Type));
455
    // Proposal dependency: FunctionReferences depends on ReferenceTypes.
456
228k
    if (Type == Proposal::FunctionReferences) {
457
16.3k
      Proposals.set(static_cast<uint8_t>(Proposal::ReferenceTypes));
458
16.3k
    }
459
    // Proposal dependency: GC depends on FunctionReferences and ReferenceTypes.
460
228k
    if (Type == Proposal::GC) {
461
16.3k
      Proposals.set(static_cast<uint8_t>(Proposal::FunctionReferences));
462
16.3k
      Proposals.set(static_cast<uint8_t>(Proposal::ReferenceTypes));
463
16.3k
    }
464
228k
  }
465
466
0
  void unsafeAddHostRegistration(const HostRegistration Host) noexcept {
467
0
    Hosts.set(static_cast<uint8_t>(Host));
468
0
  }
469
470
  mutable std::shared_mutex Mutex;
471
  std::bitset<static_cast<uint8_t>(Proposal::Max)> Proposals;
472
  std::bitset<static_cast<uint8_t>(HostRegistration::Max)> Hosts;
473
  std::unordered_set<std::string, Hash::Hash> ForbiddenPlugins;
474
475
  CompilerConfigure CompilerConf;
476
  RuntimeConfigure RuntimeConf;
477
  StatisticsConfigure StatisticsConf;
478
};
479
480
} // namespace WasmEdge