@@ -140,10 +140,14 @@ use clap::Parser;
140140use std:: os:: raw:: { c_int, c_void} ;
141141use std:: slice;
142142use std:: { env, path:: PathBuf } ;
143- use wasmtime:: { Engine , Instance , Linker , Module , Result , Store , error:: Context as _} ;
143+ use wasmtime:: component:: ResourceTable ;
144+ use wasmtime:: {
145+ CodeBuilder , CodeHint , Engine , Instance , Linker , Module , Result , Store , error:: Context as _,
146+ format_err,
147+ } ;
144148use wasmtime_cli_flags:: CommonOptions ;
145149use wasmtime_wasi:: cli:: { InputFile , OutputFile } ;
146- use wasmtime_wasi:: { DirPerms , FilePerms , I32Exit , WasiCtx , p1:: WasiP1Ctx } ;
150+ use wasmtime_wasi:: { DirPerms , FilePerms , I32Exit , WasiCtx , WasiCtxView , WasiView , p1:: WasiP1Ctx } ;
147151
148152pub type ExitCode = c_int ;
149153pub const OK : ExitCode = 0 ;
@@ -271,12 +275,19 @@ pub extern "C" fn wasm_bench_create(
271275 out_bench_ptr : * mut * mut c_void ,
272276) -> ExitCode {
273277 let result = ( || -> Result < _ > {
278+ // Clone paths for the core Wasm WASI closure.
274279 let working_dir = config. working_dir ( ) ?;
275280 let stdout_path = config. stdout_path ( ) ?;
276281 let stderr_path = config. stderr_path ( ) ?;
277282 let stdin_path = config. stdin_path ( ) ?;
278283 let options = config. execution_flags ( ) ?;
279284
285+ // Clone paths for the component WASI closure.
286+ let working_dir2 = working_dir. clone ( ) ;
287+ let stdout_path2 = stdout_path. clone ( ) ;
288+ let stderr_path2 = stderr_path. clone ( ) ;
289+ let stdin_path2 = stdin_path. clone ( ) ;
290+
280291 let state = Box :: new ( BenchState :: new (
281292 options,
282293 config. compilation_timer ,
@@ -317,6 +328,34 @@ pub extern "C" fn wasm_bench_create(
317328
318329 Ok ( cx. build_p1 ( ) )
319330 } ,
331+ move || {
332+ let mut cx = WasiCtx :: builder ( ) ;
333+
334+ let stdout = std:: fs:: File :: create ( & stdout_path2)
335+ . with_context ( || format ! ( "failed to create {}" , stdout_path2. display( ) ) ) ?;
336+ cx. stdout ( OutputFile :: new ( stdout) ) ;
337+
338+ let stderr = std:: fs:: File :: create ( & stderr_path2)
339+ . with_context ( || format ! ( "failed to create {}" , stderr_path2. display( ) ) ) ?;
340+ cx. stderr ( OutputFile :: new ( stderr) ) ;
341+
342+ if let Some ( stdin_path) = & stdin_path2 {
343+ let stdin = std:: fs:: File :: open ( stdin_path)
344+ . with_context ( || format ! ( "failed to open {}" , stdin_path. display( ) ) ) ?;
345+ cx. stdin ( InputFile :: new ( stdin) ) ;
346+ }
347+
348+ cx. preopened_dir ( working_dir2. clone ( ) , "." , DirPerms :: READ , FilePerms :: READ ) ?;
349+
350+ if let Ok ( val) = env:: var ( "WASM_BENCH_USE_SMALL_WORKLOAD" ) {
351+ cx. env ( "WASM_BENCH_USE_SMALL_WORKLOAD" , & val) ;
352+ }
353+
354+ Ok ( ComponentHostState {
355+ wasi : cx. build ( ) ,
356+ table : ResourceTable :: new ( ) ,
357+ } )
358+ } ,
320359 ) ?) ;
321360 Ok ( Box :: into_raw ( state) as _ )
322361 } ) ( ) ;
@@ -386,15 +425,20 @@ fn to_exit_code<T>(result: impl Into<Result<T>>) -> ExitCode {
386425/// to manage the Wasmtime engine between calls.
387426struct BenchState {
388427 linker : Linker < HostState > ,
428+ component_linker : wasmtime:: component:: Linker < ComponentHostState > ,
389429 compilation_timer : * mut u8 ,
390430 compilation_start : extern "C" fn ( * mut u8 ) ,
391431 compilation_end : extern "C" fn ( * mut u8 ) ,
392432 instantiation_timer : * mut u8 ,
393433 instantiation_start : extern "C" fn ( * mut u8 ) ,
394434 instantiation_end : extern "C" fn ( * mut u8 ) ,
395435 make_wasi_cx : Box < dyn FnMut ( ) -> Result < WasiP1Ctx > > ,
436+ make_component_wasi_cx : Box < dyn FnMut ( ) -> Result < ComponentHostState > > ,
396437 module : Option < Module > ,
438+ component : Option < wasmtime:: component:: Component > ,
397439 store_and_instance : Option < ( Store < HostState > , Instance ) > ,
440+ component_store_and_instance :
441+ Option < ( Store < ComponentHostState > , wasmtime:: component:: Instance ) > ,
398442 epoch_interruption : bool ,
399443 fuel : Option < u64 > ,
400444}
@@ -405,6 +449,20 @@ struct HostState {
405449 wasi_nn : wasmtime_wasi_nn:: witx:: WasiNnCtx ,
406450}
407451
452+ struct ComponentHostState {
453+ wasi : WasiCtx ,
454+ table : ResourceTable ,
455+ }
456+
457+ impl WasiView for ComponentHostState {
458+ fn ctx ( & mut self ) -> WasiCtxView < ' _ > {
459+ WasiCtxView {
460+ ctx : & mut self . wasi ,
461+ table : & mut self . table ,
462+ }
463+ }
464+ }
465+
408466impl BenchState {
409467 fn new (
410468 mut options : CommonOptions ,
@@ -418,12 +476,14 @@ impl BenchState {
418476 execution_start : extern "C" fn ( * mut u8 ) ,
419477 execution_end : extern "C" fn ( * mut u8 ) ,
420478 make_wasi_cx : impl FnMut ( ) -> Result < WasiP1Ctx > + ' static ,
479+ make_component_wasi_cx : impl FnMut ( ) -> Result < ComponentHostState > + ' static ,
421480 ) -> Result < Self > {
422481 let mut config = options. config ( None ) ?;
423482 // NB: always disable the compilation cache.
424483 config. cache ( None ) ;
425484 let engine = Engine :: new ( & config) ?;
426485 let mut linker = Linker :: < HostState > :: new ( & engine) ;
486+ let mut component_linker = wasmtime:: component:: Linker :: < ComponentHostState > :: new ( & engine) ;
427487
428488 // Define the benchmarking start/end functions.
429489 let execution_timer = unsafe {
@@ -440,11 +500,29 @@ impl BenchState {
440500 Ok ( ( ) )
441501 } ) ?;
442502
503+ // Define the same benchmarking functions on the component linker.
504+ let mut bench_instance = component_linker. instance ( "bench" ) ?;
505+ bench_instance. func_wrap (
506+ "start" ,
507+ move |_: wasmtime:: StoreContextMut < ' _ , ComponentHostState > , ( ) : ( ) | {
508+ execution_start ( * execution_timer. get ( ) ) ;
509+ Ok ( ( ) )
510+ } ,
511+ ) ?;
512+ bench_instance. func_wrap (
513+ "end" ,
514+ move |_: wasmtime:: StoreContextMut < ' _ , ComponentHostState > , ( ) : ( ) | {
515+ execution_end ( * execution_timer. get ( ) ) ;
516+ Ok ( ( ) )
517+ } ,
518+ ) ?;
519+
443520 let epoch_interruption = options. wasm . epoch_interruption . unwrap_or ( false ) ;
444521 let fuel = options. wasm . fuel ;
445522
446523 if options. wasi . common != Some ( false ) {
447524 wasmtime_wasi:: p1:: add_to_linker_sync ( & mut linker, |cx| & mut cx. wasi ) ?;
525+ wasmtime_wasi:: p2:: add_to_linker_sync ( & mut component_linker) ?;
448526 }
449527
450528 #[ cfg( feature = "wasi-nn" ) ]
@@ -454,86 +532,139 @@ impl BenchState {
454532
455533 Ok ( Self {
456534 linker,
535+ component_linker,
457536 compilation_timer,
458537 compilation_start,
459538 compilation_end,
460539 instantiation_timer,
461540 instantiation_start,
462541 instantiation_end,
463542 make_wasi_cx : Box :: new ( make_wasi_cx) as _ ,
543+ make_component_wasi_cx : Box :: new ( make_component_wasi_cx) as _ ,
464544 module : None ,
545+ component : None ,
465546 store_and_instance : None ,
547+ component_store_and_instance : None ,
466548 epoch_interruption,
467549 fuel,
468550 } )
469551 }
470552
471553 fn compile ( & mut self , bytes : & [ u8 ] ) -> Result < ( ) > {
472554 self . module = None ;
555+ self . component = None ;
473556
474- ( self . compilation_start ) ( self . compilation_timer ) ;
475- let module = Module :: from_binary ( self . linker . engine ( ) , bytes) ?;
476- ( self . compilation_end ) ( self . compilation_timer ) ;
557+ let mut builder = CodeBuilder :: new ( self . linker . engine ( ) ) ;
558+ builder. wasm_binary ( bytes, None ) ?;
559+
560+ match builder. hint ( ) {
561+ Some ( CodeHint :: Component ) => {
562+ ( self . compilation_start ) ( self . compilation_timer ) ;
563+ let component = builder. compile_component ( ) ?;
564+ ( self . compilation_end ) ( self . compilation_timer ) ;
565+ self . component = Some ( component) ;
566+ }
567+ Some ( CodeHint :: Module ) | None => {
568+ ( self . compilation_start ) ( self . compilation_timer ) ;
569+ let module = builder. compile_module ( ) ?;
570+ ( self . compilation_end ) ( self . compilation_timer ) ;
571+ self . module = Some ( module) ;
572+ }
573+ }
477574
478- self . module = Some ( module) ;
479575 Ok ( ( ) )
480576 }
481577
482578 fn instantiate ( & mut self ) -> Result < ( ) > {
483579 self . store_and_instance = None ;
580+ self . component_store_and_instance = None ;
484581
485- let module = self
486- . module
487- . as_ref ( )
488- . expect ( "compile the module before instantiating it" ) ;
489-
490- let host = HostState {
491- wasi : ( self . make_wasi_cx ) ( ) . context ( "failed to create a WASI context" ) ?,
492- #[ cfg( feature = "wasi-nn" ) ]
493- wasi_nn : {
494- let ( backends, registry) = wasmtime_wasi_nn:: preload ( & [ ] ) ?;
495- wasmtime_wasi_nn:: witx:: WasiNnCtx :: new ( backends, registry)
496- } ,
497- } ;
582+ if let Some ( component) = & self . component {
583+ let host = ( self . make_component_wasi_cx ) ( )
584+ . context ( "failed to create a WASI context for component" ) ?;
498585
499- // NB: Start measuring instantiation time *after* we've created the WASI
500- // context, since that needs to do file I/O to setup
501- // stdin/stdout/stderr.
502- ( self . instantiation_start ) ( self . instantiation_timer ) ;
503- let mut store = Store :: new ( self . linker . engine ( ) , host) ;
504- if self . epoch_interruption {
505- store. set_epoch_deadline ( 1 ) ;
506- }
507- if let Some ( fuel) = self . fuel {
508- store. set_fuel ( fuel) . unwrap ( ) ;
509- }
586+ // NB: Start measuring instantiation time *after* we've created the
587+ // WASI context, since that needs to do file I/O to setup
588+ // stdin/stdout/stderr.
589+ ( self . instantiation_start ) ( self . instantiation_timer ) ;
510590
511- let instance = self . linker . instantiate ( & mut store, & module) ?;
512- ( self . instantiation_end ) ( self . instantiation_timer ) ;
591+ let mut store = Store :: new ( self . component_linker . engine ( ) , host) ;
592+ if self . epoch_interruption {
593+ store. set_epoch_deadline ( 1 ) ;
594+ }
595+ if let Some ( fuel) = self . fuel {
596+ store. set_fuel ( fuel) . unwrap ( ) ;
597+ }
513598
514- self . store_and_instance = Some ( ( store, instance) ) ;
599+ let instance = self . component_linker . instantiate ( & mut store, component) ?;
600+
601+ ( self . instantiation_end ) ( self . instantiation_timer ) ;
602+
603+ self . component_store_and_instance = Some ( ( store, instance) ) ;
604+ } else {
605+ let module = self
606+ . module
607+ . as_ref ( )
608+ . expect ( "compile the module before instantiating it" ) ;
609+
610+ let host = HostState {
611+ wasi : ( self . make_wasi_cx ) ( ) . context ( "failed to create a WASI context" ) ?,
612+ #[ cfg( feature = "wasi-nn" ) ]
613+ wasi_nn : {
614+ let ( backends, registry) = wasmtime_wasi_nn:: preload ( & [ ] ) ?;
615+ wasmtime_wasi_nn:: witx:: WasiNnCtx :: new ( backends, registry)
616+ } ,
617+ } ;
618+
619+ // NB: Start measuring instantiation time *after* we've created the
620+ // WASI context, since that needs to do file I/O to setup
621+ // stdin/stdout/stderr.
622+ ( self . instantiation_start ) ( self . instantiation_timer ) ;
623+
624+ let mut store = Store :: new ( self . linker . engine ( ) , host) ;
625+ if self . epoch_interruption {
626+ store. set_epoch_deadline ( 1 ) ;
627+ }
628+ if let Some ( fuel) = self . fuel {
629+ store. set_fuel ( fuel) . unwrap ( ) ;
630+ }
631+
632+ let instance = self . linker . instantiate ( & mut store, & module) ?;
633+
634+ ( self . instantiation_end ) ( self . instantiation_timer ) ;
635+
636+ self . store_and_instance = Some ( ( store, instance) ) ;
637+ }
515638 Ok ( ( ) )
516639 }
517640
518641 fn execute ( & mut self ) -> Result < ( ) > {
519- let ( mut store, instance) = self
520- . store_and_instance
521- . take ( )
522- . expect ( "instantiate the module before executing it" ) ;
523-
524- let start_func = instance. get_typed_func :: < ( ) , ( ) > ( & mut store, "_start" ) ?;
525- match start_func. call ( & mut store, ( ) ) {
526- Ok ( _) => Ok ( ( ) ) ,
527- Err ( trap) => {
528- // Since _start will likely return by using the system `exit` call, we must
529- // check the trap code to see if it actually represents a successful exit.
530- if let Some ( exit) = trap. downcast_ref :: < I32Exit > ( ) {
531- if exit. 0 == 0 {
532- return Ok ( ( ) ) ;
642+ if let Some ( ( mut store, instance) ) = self . component_store_and_instance . take ( ) {
643+ let command = wasmtime_wasi:: p2:: bindings:: sync:: Command :: new ( & mut store, & instance) ?;
644+ match command. wasi_cli_run ( ) . call_run ( & mut store) ? {
645+ Ok ( ( ) ) => Ok ( ( ) ) ,
646+ Err ( ( ) ) => Err ( format_err ! ( "calling `run` failed" ) ) ,
647+ }
648+ } else {
649+ let ( mut store, instance) = self
650+ . store_and_instance
651+ . take ( )
652+ . expect ( "instantiate the module before executing it" ) ;
653+
654+ let start_func = instance. get_typed_func :: < ( ) , ( ) > ( & mut store, "_start" ) ?;
655+ match start_func. call ( & mut store, ( ) ) {
656+ Ok ( _) => Ok ( ( ) ) ,
657+ Err ( trap) => {
658+ // Since _start will likely return by using the system `exit` call, we must
659+ // check the trap code to see if it actually represents a successful exit.
660+ if let Some ( exit) = trap. downcast_ref :: < I32Exit > ( ) {
661+ if exit. 0 == 0 {
662+ return Ok ( ( ) ) ;
663+ }
533664 }
534- }
535665
536- Err ( trap)
666+ Err ( trap)
667+ }
537668 }
538669 }
539670 }
0 commit comments