Coverage Report

Created: 2025-07-01 06:18

/src/WasmEdge/include/common/configure.h
Line
Count
Source (jump to first uncovered line)
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
15.7k
  CompilerConfigure() noexcept = default;
36
  CompilerConfigure(const CompilerConfigure &RHS) noexcept
37
17.3k
      : OptLevel(RHS.OptLevel.load(std::memory_order_relaxed)),
38
17.3k
        OFormat(RHS.OFormat.load(std::memory_order_relaxed)),
39
17.3k
        DumpIR(RHS.DumpIR.load(std::memory_order_relaxed)),
40
17.3k
        GenericBinary(RHS.GenericBinary.load(std::memory_order_relaxed)),
41
17.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.42k
  OptimizationLevel getOptimizationLevel() const noexcept {
64
6.42k
    return OptLevel.load(std::memory_order_relaxed);
65
6.42k
  }
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.28k
  OutputFormat getOutputFormat() const noexcept {
78
4.28k
    return OFormat.load(std::memory_order_relaxed);
79
4.28k
  }
80
81
0
  void setDumpIR(bool IsDump) noexcept {
82
0
    DumpIR.store(IsDump, std::memory_order_relaxed);
83
0
  }
84
85
4.28k
  bool isDumpIR() const noexcept {
86
4.28k
    return DumpIR.load(std::memory_order_relaxed);
87
4.28k
  }
88
89
0
  void setGenericBinary(bool IsGenericBinary) noexcept {
90
0
    GenericBinary.store(IsGenericBinary, std::memory_order_relaxed);
91
0
  }
92
93
4.28k
  bool isGenericBinary() const noexcept {
94
4.28k
    return GenericBinary.load(std::memory_order_relaxed);
95
4.28k
  }
96
97
0
  void setInterruptible(bool IsInterruptible) noexcept {
98
0
    Interruptible.store(IsInterruptible, std::memory_order_relaxed);
99
0
  }
100
101
11.7k
  bool isInterruptible() const noexcept {
102
11.7k
    return Interruptible.load(std::memory_order_relaxed);
103
11.7k
  }
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
15.7k
  RuntimeConfigure() noexcept = default;
116
  RuntimeConfigure(const RuntimeConfigure &RHS) noexcept
117
17.3k
      : MaxMemPage(RHS.MaxMemPage.load(std::memory_order_relaxed)),
118
17.3k
        EnableJIT(RHS.EnableJIT.load(std::memory_order_relaxed)),
119
17.3k
        EnableCoredump(RHS.EnableCoredump.load(std::memory_order_relaxed)),
120
17.3k
        CoredumpWasmgdb(RHS.CoredumpWasmgdb.load(std::memory_order_relaxed)),
121
17.3k
        ForceInterpreter(RHS.ForceInterpreter.load(std::memory_order_relaxed)),
122
17.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.30k
  void setForceInterpreter(bool IsForceInterpreter) noexcept {
157
9.30k
    ForceInterpreter.store(IsForceInterpreter, std::memory_order_relaxed);
158
9.30k
  }
159
160
35.3k
  bool isForceInterpreter() const noexcept {
161
35.3k
    return ForceInterpreter.load(std::memory_order_relaxed);
162
35.3k
  }
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
15.7k
  StatisticsConfigure() noexcept = default;
184
  StatisticsConfigure(const StatisticsConfigure &RHS) noexcept
185
17.3k
      : InstrCounting(RHS.InstrCounting.load(std::memory_order_relaxed)),
186
17.3k
        CostMeasuring(RHS.CostMeasuring.load(std::memory_order_relaxed)),
187
17.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.7k
  bool isInstructionCounting() const noexcept {
194
11.7k
    return InstrCounting.load(std::memory_order_relaxed);
195
11.7k
  }
196
197
0
  void setCostMeasuring(bool IsMeasure) noexcept {
198
0
    CostMeasuring.store(IsMeasure, std::memory_order_relaxed);
199
0
  }
200
201
11.7k
  bool isCostMeasuring() const noexcept {
202
11.7k
    return CostMeasuring.load(std::memory_order_relaxed);
203
11.7k
  }
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
15.7k
  Configure() noexcept {
232
15.7k
    unsafeAddProposal(Proposal::ImportExportMutGlobals);
233
15.7k
    unsafeAddProposal(Proposal::NonTrapFloatToIntConversions);
234
15.7k
    unsafeAddProposal(Proposal::SignExtensionOperators);
235
15.7k
    unsafeAddProposal(Proposal::MultiValue);
236
15.7k
    unsafeAddProposal(Proposal::BulkMemoryOperations);
237
15.7k
    unsafeAddProposal(Proposal::ReferenceTypes);
238
15.7k
    unsafeAddProposal(Proposal::SIMD);
239
15.7k
  }
240
  template <typename... ArgsT> Configure(ArgsT... Args) noexcept : Configure() {
241
    (unsafeAddSet(Args), ...);
242
  }
243
  Configure(const Configure &RHS) noexcept
244
17.3k
      : Proposals(RHS.Proposals), Hosts(RHS.Hosts),
245
17.3k
        ForbiddenPlugins(RHS.ForbiddenPlugins), CompilerConf(RHS.CompilerConf),
246
17.3k
        RuntimeConf(RHS.RuntimeConf), StatisticsConf(RHS.StatisticsConf) {}
247
248
0
  void addProposal(const Proposal Type) noexcept {
249
0
    std::unique_lock Lock(Mutex);
250
0
    unsafeAddProposal(Type);
251
0
  }
252
253
0
  void removeProposal(const Proposal Type) noexcept {
254
0
    std::unique_lock Lock(Mutex);
255
0
    if (Type == Proposal::FunctionReferences &&
256
0
        Proposals.test(static_cast<uint8_t>(Proposal::GC))) {
257
      // Proposal dependency: GC depends FunctionReferences.
258
0
      return;
259
0
    }
260
0
    if (Type == Proposal::ReferenceTypes &&
261
0
        (Proposals.test(static_cast<uint8_t>(Proposal::GC)) ||
262
0
         Proposals.test(static_cast<uint8_t>(Proposal::FunctionReferences)))) {
263
      // Proposal dependency: GC and FunctionReferences depend on
264
      // ReferenceTypes.
265
0
      return;
266
0
    }
267
0
    Proposals.reset(static_cast<uint8_t>(Type));
268
0
  }
269
270
1.43M
  bool hasProposal(const Proposal Type) const noexcept {
271
1.43M
    std::shared_lock Lock(Mutex);
272
1.43M
    return Proposals.test(static_cast<uint8_t>(Type));
273
1.43M
  }
274
275
0
  void addHostRegistration(const HostRegistration Host) noexcept {
276
0
    std::unique_lock Lock(Mutex);
277
0
    Hosts.set(static_cast<uint8_t>(Host));
278
0
  }
279
280
0
  void removeHostRegistration(const HostRegistration Host) noexcept {
281
0
    std::unique_lock Lock(Mutex);
282
0
    Hosts.reset(static_cast<uint8_t>(Host));
283
0
  }
284
285
0
  void addForbiddenPlugins(std::string PluginName) noexcept {
286
0
    std::unique_lock Lock(Mutex);
287
0
    ForbiddenPlugins.emplace(std::move(PluginName));
288
0
  }
289
290
0
  bool isForbiddenPlugins(const std::string &PluginName) const noexcept {
291
0
    std::shared_lock Lock(Mutex);
292
0
    return ForbiddenPlugins.find(PluginName) != ForbiddenPlugins.end();
293
0
  }
294
295
0
  bool hasHostRegistration(const HostRegistration Host) const noexcept {
296
0
    std::shared_lock Lock(Mutex);
297
0
    return Hosts.test(static_cast<uint8_t>(Host));
298
0
  }
299
300
31.0k
  const CompilerConfigure &getCompilerConfigure() const noexcept {
301
31.0k
    return CompilerConf;
302
31.0k
  }
303
0
  CompilerConfigure &getCompilerConfigure() noexcept { return CompilerConf; }
304
305
35.3k
  const RuntimeConfigure &getRuntimeConfigure() const noexcept {
306
35.3k
    return RuntimeConf;
307
35.3k
  }
308
9.30k
  RuntimeConfigure &getRuntimeConfigure() noexcept { return RuntimeConf; }
309
310
23.4k
  const StatisticsConfigure &getStatisticsConfigure() const noexcept {
311
23.4k
    return StatisticsConf;
312
23.4k
  }
313
0
  StatisticsConfigure &getStatisticsConfigure() noexcept {
314
0
    return StatisticsConf;
315
0
  }
316
317
  /// Helper function of checking the proposal of instructions.
318
8.63M
  std::optional<Proposal> isInstrNeedProposal(OpCode Code) const noexcept {
319
8.63M
    if (Code >= OpCode::I32__trunc_sat_f32_s &&
320
8.63M
        Code <= OpCode::I64__trunc_sat_f64_u) {
321
      // These instructions are for NonTrapFloatToIntConversions proposal.
322
19.1k
      if (unlikely(!hasProposal(Proposal::NonTrapFloatToIntConversions))) {
323
0
        return Proposal::NonTrapFloatToIntConversions;
324
0
      }
325
8.61M
    } else if (Code >= OpCode::I32__extend8_s &&
326
8.61M
               Code <= OpCode::I64__extend32_s) {
327
      // These instructions are for SignExtensionOperators proposal.
328
46.5k
      if (unlikely(!hasProposal(Proposal::SignExtensionOperators))) {
329
0
        return Proposal::SignExtensionOperators;
330
0
      }
331
8.56M
    } else if ((Code >= OpCode::Ref__null && Code <= OpCode::Ref__func) ||
332
8.56M
               (Code >= OpCode::Table__init && Code <= OpCode::Table__copy) ||
333
8.56M
               (Code >= OpCode::Memory__init && Code <= OpCode::Memory__fill)) {
334
      // These instructions are for ReferenceTypes or BulkMemoryOperations
335
      // proposal.
336
13.1k
      if (unlikely(!hasProposal(Proposal::ReferenceTypes)) &&
337
13.1k
          unlikely(!hasProposal(Proposal::BulkMemoryOperations))) {
338
0
        return Proposal::ReferenceTypes;
339
0
      }
340
8.55M
    } else if (Code == OpCode::Select_t ||
341
8.55M
               (Code >= OpCode::Table__get && Code <= OpCode::Table__set) ||
342
8.55M
               (Code >= OpCode::Table__grow && Code <= OpCode::Table__fill)) {
343
      // These instructions are for ReferenceTypes proposal.
344
7.47k
      if (unlikely(!hasProposal(Proposal::ReferenceTypes))) {
345
0
        return Proposal::ReferenceTypes;
346
0
      }
347
8.54M
    } else if (Code >= OpCode::V128__load &&
348
8.54M
               Code <= OpCode::F64x2__convert_low_i32x4_u) {
349
      // These instructions are for SIMD proposal.
350
819k
      if (!hasProposal(Proposal::SIMD)) {
351
0
        return Proposal::SIMD;
352
0
      }
353
7.72M
    } else if (Code >= OpCode::I8x16__relaxed_swizzle &&
354
7.72M
               Code <= OpCode::I32x4__relaxed_dot_i8x16_i7x16_add_s) {
355
      // These instructions are for Relaxed SIMD proposal.
356
28
      if (!hasProposal(Proposal::RelaxSIMD)) {
357
28
        return Proposal::RelaxSIMD;
358
28
      }
359
7.72M
    } else if (Code == OpCode::Return_call ||
360
7.72M
               Code == OpCode::Return_call_indirect) {
361
      // These instructions are for TailCall proposal.
362
10
      if (!hasProposal(Proposal::TailCall)) {
363
10
        return Proposal::TailCall;
364
10
      }
365
7.72M
    } else if (Code >= OpCode::I32__atomic__load &&
366
7.72M
               Code <= OpCode::I64__atomic__rmw32__cmpxchg_u) {
367
      // These instructions are for Thread proposal.
368
80
      if (!hasProposal(Proposal::Threads)) {
369
80
        return Proposal::Threads;
370
80
      }
371
7.72M
    } else if (Code == OpCode::Call_ref || Code == OpCode::Return_call_ref ||
372
7.72M
               Code == OpCode::Ref__as_non_null || Code == OpCode::Br_on_null ||
373
7.72M
               Code == OpCode::Br_on_non_null) {
374
      // These instructions are for TypedFunctionReferences proposal.
375
13
      if (!hasProposal(Proposal::FunctionReferences)) {
376
13
        return Proposal::FunctionReferences;
377
13
      }
378
0
      if (Code == OpCode::Return_call_ref && !hasProposal(Proposal::TailCall)) {
379
0
        return Proposal::TailCall;
380
0
      }
381
7.72M
    } else if (Code == OpCode::Ref__eq ||
382
7.72M
               (Code >= OpCode::Struct__new && Code <= OpCode::I31__get_u)) {
383
      // These instructions are for GC proposal.
384
49
      if (!hasProposal(Proposal::GC)) {
385
49
        return Proposal::GC;
386
49
      }
387
7.72M
    } else if ((Code >= OpCode::Try && Code <= OpCode::Throw_ref) ||
388
7.72M
               Code == OpCode::Delegate || Code == OpCode::Catch_all ||
389
7.72M
               Code == OpCode::Try_table) {
390
      // LEGACY-EH: remove the old instructions after deprecating legacy EH.
391
      // These instructions are for ExceptionHandling proposal.
392
136
      if (!hasProposal(Proposal::ExceptionHandling)) {
393
136
        return Proposal::ExceptionHandling;
394
136
      }
395
136
    }
396
8.63M
    return {};
397
8.63M
  }
398
399
private:
400
0
  void unsafeAddSet(const Proposal P) noexcept { unsafeAddProposal(P); }
401
0
  void unsafeAddSet(const HostRegistration H) noexcept {
402
0
    unsafeAddHostRegistration(H);
403
0
  }
404
405
110k
  void unsafeAddProposal(const Proposal Type) noexcept {
406
110k
    Proposals.set(static_cast<uint8_t>(Type));
407
    // Proposal dependency: FunctionReferences depends on ReferenceTypes.
408
110k
    if (Type == Proposal::FunctionReferences) {
409
0
      Proposals.set(static_cast<uint8_t>(Proposal::ReferenceTypes));
410
0
    }
411
    // Proposal dependency: GC depends on FunctionReferences and ReferenceTypes.
412
110k
    if (Type == Proposal::GC) {
413
0
      Proposals.set(static_cast<uint8_t>(Proposal::FunctionReferences));
414
0
      Proposals.set(static_cast<uint8_t>(Proposal::ReferenceTypes));
415
0
    }
416
110k
  }
417
418
0
  void unsafeAddHostRegistration(const HostRegistration Host) noexcept {
419
0
    Hosts.set(static_cast<uint8_t>(Host));
420
0
  }
421
422
  mutable std::shared_mutex Mutex;
423
  std::bitset<static_cast<uint8_t>(Proposal::Max)> Proposals;
424
  std::bitset<static_cast<uint8_t>(HostRegistration::Max)> Hosts;
425
  std::unordered_set<std::string, Hash::Hash> ForbiddenPlugins;
426
427
  CompilerConfigure CompilerConf;
428
  RuntimeConfigure RuntimeConf;
429
  StatisticsConfigure StatisticsConf;
430
};
431
432
} // namespace WasmEdge