1
1
use crate :: signatures:: SignatureRegistry ;
2
2
use crate :: { Config , Trap } ;
3
3
use anyhow:: Result ;
4
+ use once_cell:: sync:: OnceCell ;
4
5
#[ cfg( feature = "parallel-compilation" ) ]
5
6
use rayon:: prelude:: * ;
6
7
use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
7
8
use std:: sync:: Arc ;
8
9
#[ cfg( feature = "cache" ) ]
9
10
use wasmtime_cache:: CacheConfig ;
11
+ use wasmtime_environ:: FlagValue ;
10
12
use wasmtime_runtime:: { debug_builtins, CompiledModuleIdAllocator , InstanceAllocator } ;
11
13
12
14
/// An `Engine` which is a global context for compilation and management of wasm
@@ -44,6 +46,10 @@ struct EngineInner {
44
46
signatures : SignatureRegistry ,
45
47
epoch : AtomicU64 ,
46
48
unique_id_allocator : CompiledModuleIdAllocator ,
49
+
50
+ // One-time check of whether the compiler's settings, if present, are
51
+ // compatible with the native host.
52
+ compatible_with_native_host : OnceCell < Result < ( ) , String > > ,
47
53
}
48
54
49
55
impl Engine {
@@ -70,6 +76,7 @@ impl Engine {
70
76
signatures : registry,
71
77
epoch : AtomicU64 :: new ( 0 ) ,
72
78
unique_id_allocator : CompiledModuleIdAllocator :: new ( ) ,
79
+ compatible_with_native_host : OnceCell :: new ( ) ,
73
80
} ) ,
74
81
} )
75
82
}
@@ -217,6 +224,211 @@ impl Engine {
217
224
. map ( |a| f ( a) )
218
225
. collect :: < Result < Vec < B > , E > > ( )
219
226
}
227
+
228
+ /// Returns the target triple which this engine is compiling code for
229
+ /// and/or running code for.
230
+ pub ( crate ) fn target ( & self ) -> target_lexicon:: Triple {
231
+ // If a compiler is configured, use that target.
232
+ #[ cfg( compiler) ]
233
+ return self . compiler ( ) . triple ( ) . clone ( ) ;
234
+
235
+ // ... otherwise it's the native target
236
+ #[ cfg( not( compiler) ) ]
237
+ return target_lexicon:: Triple :: host ( ) ;
238
+ }
239
+
240
+ /// Verify that this engine's configuration is compatible with loading
241
+ /// modules onto the native host platform.
242
+ ///
243
+ /// This method is used as part of `Module::new` to ensure that this
244
+ /// engine can indeed load modules for the configured compiler (if any).
245
+ /// Note that if cranelift is disabled this trivially returns `Ok` because
246
+ /// loaded serialized modules are checked separately.
247
+ pub ( crate ) fn check_compatible_with_native_host ( & self ) -> Result < ( ) > {
248
+ self . inner
249
+ . compatible_with_native_host
250
+ . get_or_init ( || self . _check_compatible_with_native_host ( ) )
251
+ . clone ( )
252
+ . map_err ( anyhow:: Error :: msg)
253
+ }
254
+ fn _check_compatible_with_native_host ( & self ) -> Result < ( ) , String > {
255
+ #[ cfg( compiler) ]
256
+ {
257
+ let compiler = self . compiler ( ) ;
258
+
259
+ // Check to see that the config's target matches the host
260
+ let target = compiler. triple ( ) ;
261
+ if * target != target_lexicon:: Triple :: host ( ) {
262
+ return Err ( format ! (
263
+ "target '{}' specified in the configuration does not match the host" ,
264
+ target
265
+ ) ) ;
266
+ }
267
+
268
+ // Also double-check all compiler settings
269
+ for ( key, value) in compiler. flags ( ) . iter ( ) {
270
+ self . check_compatible_with_shared_flag ( key, value) ?;
271
+ }
272
+ for ( key, value) in compiler. isa_flags ( ) . iter ( ) {
273
+ self . check_compatible_with_isa_flag ( key, value) ?;
274
+ }
275
+ }
276
+ Ok ( ( ) )
277
+ }
278
+
279
+ /// Checks to see whether the "shared flag", something enabled for
280
+ /// individual compilers, is compatible with the native host platform.
281
+ ///
282
+ /// This is used both when validating an engine's compilation settings are
283
+ /// compatible with the host as well as when deserializing modules from
284
+ /// disk to ensure they're compatible with the current host.
285
+ ///
286
+ /// Note that most of the settings here are not configured by users that
287
+ /// often. While theoretically possible via `Config` methods the more
288
+ /// interesting flags are the ISA ones below. Typically the values here
289
+ /// represent global configuration for wasm features. Settings here
290
+ /// currently rely on the compiler informing us of all settings, including
291
+ /// those disabled. Settings then fall in a few buckets:
292
+ ///
293
+ /// * Some settings must be enabled, such as `avoid_div_traps`.
294
+ /// * Some settings must have a particular value, such as
295
+ /// `libcall_call_conv`.
296
+ /// * Some settings do not matter as to their value, such as `opt_level`.
297
+ pub ( crate ) fn check_compatible_with_shared_flag (
298
+ & self ,
299
+ flag : & str ,
300
+ value : & FlagValue ,
301
+ ) -> Result < ( ) , String > {
302
+ let ok = match flag {
303
+ // These settings must all have be enabled, since their value
304
+ // can affect the way the generated code performs or behaves at
305
+ // runtime.
306
+ "avoid_div_traps" => * value == FlagValue :: Bool ( true ) ,
307
+ "unwind_info" => * value == FlagValue :: Bool ( true ) ,
308
+ "libcall_call_conv" => * value == FlagValue :: Enum ( "isa_default" . into ( ) ) ,
309
+
310
+ // Features wasmtime doesn't use should all be disabled, since
311
+ // otherwise if they are enabled it could change the behavior of
312
+ // generated code.
313
+ "baldrdash_prologue_words" => * value == FlagValue :: Num ( 0 ) ,
314
+ "enable_llvm_abi_extensions" => * value == FlagValue :: Bool ( false ) ,
315
+ "emit_all_ones_funcaddrs" => * value == FlagValue :: Bool ( false ) ,
316
+ "enable_pinned_reg" => * value == FlagValue :: Bool ( false ) ,
317
+ "enable_probestack" => * value == FlagValue :: Bool ( false ) ,
318
+ "use_colocated_libcalls" => * value == FlagValue :: Bool ( false ) ,
319
+ "use_pinned_reg_as_heap_base" => * value == FlagValue :: Bool ( false ) ,
320
+
321
+ // If reference types are enabled this must be enabled, otherwise
322
+ // this setting can have any value.
323
+ "enable_safepoints" => {
324
+ if self . config ( ) . features . reference_types {
325
+ * value == FlagValue :: Bool ( true )
326
+ } else {
327
+ return Ok ( ( ) )
328
+ }
329
+ }
330
+
331
+ // These settings don't affect the interface or functionality of
332
+ // the module itself, so their configuration values shouldn't
333
+ // matter.
334
+ "enable_heap_access_spectre_mitigation"
335
+ | "enable_nan_canonicalization"
336
+ | "enable_jump_tables"
337
+ | "enable_float"
338
+ | "enable_simd"
339
+ | "enable_verifier"
340
+ | "is_pic"
341
+ | "machine_code_cfg_info"
342
+ | "tls_model" // wasmtime doesn't use tls right now
343
+ | "opt_level" // opt level doesn't change semantics
344
+ | "probestack_func_adjusts_sp" // probestack above asserted disabled
345
+ | "probestack_size_log2" // probestack above asserted disabled
346
+ | "regalloc" // shouldn't change semantics
347
+ | "enable_atomics" => return Ok ( ( ) ) ,
348
+
349
+ // Everything else is unknown and needs to be added somewhere to
350
+ // this list if encountered.
351
+ _ => {
352
+ return Err ( format ! ( "unknown shared setting {:?} configured to {:?}" , flag, value) )
353
+ }
354
+ } ;
355
+
356
+ if !ok {
357
+ return Err ( format ! (
358
+ "setting {:?} is configured to {:?} which is not supported" ,
359
+ flag, value,
360
+ ) ) ;
361
+ }
362
+ Ok ( ( ) )
363
+ }
364
+
365
+ /// Same as `check_compatible_with_native_host` except used for ISA-specific
366
+ /// flags. This is used to test whether a configured ISA flag is indeed
367
+ /// available on the host platform itself.
368
+ pub ( crate ) fn check_compatible_with_isa_flag (
369
+ & self ,
370
+ flag : & str ,
371
+ value : & FlagValue ,
372
+ ) -> Result < ( ) , String > {
373
+ match value {
374
+ // ISA flags are used for things like CPU features, so if they're
375
+ // disabled then it's compatible with the native host.
376
+ FlagValue :: Bool ( false ) => return Ok ( ( ) ) ,
377
+
378
+ // Fall through below where we test at runtime that features are
379
+ // available.
380
+ FlagValue :: Bool ( true ) => { }
381
+
382
+ // Only `bool` values are supported right now, other settings would
383
+ // need more support here.
384
+ _ => {
385
+ return Err ( format ! (
386
+ "isa-specific feature {:?} configured to unknown value {:?}" ,
387
+ flag, value
388
+ ) )
389
+ }
390
+ }
391
+ #[ cfg( target_arch = "x86_64" ) ]
392
+ {
393
+ let enabled = match flag {
394
+ "has_sse3" => Some ( std:: is_x86_feature_detected!( "sse3" ) ) ,
395
+ "has_ssse3" => Some ( std:: is_x86_feature_detected!( "ssse3" ) ) ,
396
+ "has_sse41" => Some ( std:: is_x86_feature_detected!( "sse4.1" ) ) ,
397
+ "has_sse42" => Some ( std:: is_x86_feature_detected!( "sse4.2" ) ) ,
398
+ "has_popcnt" => Some ( std:: is_x86_feature_detected!( "popcnt" ) ) ,
399
+ "has_avx" => Some ( std:: is_x86_feature_detected!( "avx" ) ) ,
400
+ "has_avx2" => Some ( std:: is_x86_feature_detected!( "avx2" ) ) ,
401
+ "has_bmi1" => Some ( std:: is_x86_feature_detected!( "bmi1" ) ) ,
402
+ "has_bmi2" => Some ( std:: is_x86_feature_detected!( "bmi2" ) ) ,
403
+ "has_avx512bitalg" => Some ( std:: is_x86_feature_detected!( "avx512bitalg" ) ) ,
404
+ "has_avx512dq" => Some ( std:: is_x86_feature_detected!( "avx512dq" ) ) ,
405
+ "has_avx512f" => Some ( std:: is_x86_feature_detected!( "avx512f" ) ) ,
406
+ "has_avx512vl" => Some ( std:: is_x86_feature_detected!( "avx512vl" ) ) ,
407
+ "has_avx512vbmi" => Some ( std:: is_x86_feature_detected!( "avx512vbmi" ) ) ,
408
+ "has_lzcnt" => Some ( std:: is_x86_feature_detected!( "lzcnt" ) ) ,
409
+
410
+ // fall through to the very bottom to indicate that support is
411
+ // not enabled to test whether this feature is enabled on the
412
+ // host.
413
+ _ => None ,
414
+ } ;
415
+ match enabled {
416
+ Some ( true ) => return Ok ( ( ) ) ,
417
+ Some ( false ) => {
418
+ return Err ( format ! (
419
+ "compilation setting {:?} is enabled but not available on the host" ,
420
+ flag
421
+ ) )
422
+ }
423
+ // fall through
424
+ None => { }
425
+ }
426
+ }
427
+ Err ( format ! (
428
+ "cannot test if target-specific flag {:?} is available at runtime" ,
429
+ flag
430
+ ) )
431
+ }
220
432
}
221
433
222
434
impl Default for Engine {
0 commit comments