@@ -433,6 +433,22 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
433433 self . alias_region ( func, key)
434434 }
435435
436+ /// Get the alias region for the storage of a bulk-copy entity.
437+ fn bulk_copy_alias_region (
438+ & mut self ,
439+ func : & mut Function ,
440+ entity : CheckedEntity ,
441+ ) -> Option < ir:: AliasRegion > {
442+ match entity {
443+ CheckedEntity :: Memory ( index) => Some ( self . memory_alias_region ( func, index) ) ,
444+ CheckedEntity :: Table { table, .. } => Some ( self . table_alias_region ( func, table) ) ,
445+ CheckedEntity :: Array { .. } => Some ( self . gc_heap_alias_region ( func) ) ,
446+ CheckedEntity :: Data { .. } | CheckedEntity :: RuntimeData ( _) | CheckedEntity :: Elem ( _) => {
447+ None
448+ }
449+ }
450+ }
451+
436452 pub ( crate ) fn vmctx_alias_region (
437453 & mut self ,
438454 func : & mut Function ,
@@ -3687,14 +3703,18 @@ impl FuncEnvironment<'_> {
36873703 dst,
36883704 src,
36893705 const_len : Some ( bytes) ,
3706+ src_entity,
3707+ dst_entity,
36903708 ..
36913709 } = op
36923710 {
36933711 if bytes <= INLINE_COPY_MAX_BYTES {
36943712 if self . tunables . consume_fuel {
36953713 self . fuel_consumed += bytes as i64 ;
36963714 }
3697- self . emit_inline_memcpy ( builder, dst, src, bytes) ;
3715+ let src_region = self . bulk_copy_alias_region ( builder. func , src_entity) ;
3716+ let dst_region = self . bulk_copy_alias_region ( builder. func , dst_entity) ;
3717+ self . emit_inline_memcpy ( builder, dst, src, bytes, src_region, dst_region) ;
36983718 return ;
36993719 }
37003720 }
@@ -4347,6 +4367,8 @@ impl FuncEnvironment<'_> {
43474367 src : src_raw_addr,
43484368 len : len_ptr,
43494369 const_len : const_count,
4370+ src_entity,
4371+ dst_entity,
43504372 } ,
43514373 ) ;
43524374 Ok ( ( ) )
@@ -4704,6 +4726,8 @@ impl FuncEnvironment<'_> {
47044726 src : src_elem_addr,
47054727 len : dst_copy_byte_len,
47064728 const_len,
4729+ src_entity,
4730+ dst_entity,
47074731 } ,
47084732 ) ;
47094733 return Ok ( ( ) ) ;
@@ -4823,14 +4847,27 @@ impl FuncEnvironment<'_> {
48234847 dst_addr : ir:: Value ,
48244848 src_addr : ir:: Value ,
48254849 bytes : u64 ,
4850+ src_region : Option < ir:: AliasRegion > ,
4851+ dst_region : Option < ir:: AliasRegion > ,
48264852 ) {
48274853 // `trusted()` (notrap + aligned) is sound: the range is already
48284854 // bounds-checked, and each load feeds only its paired store, so the
48294855 // backend selects unaligned moves regardless of the `aligned` flag.
48304856 // Endianness is pinned to `Little` because Pulley's `v128` load/store
48314857 // only encode the little-endian variant, and matching load/store
48324858 // endianness preserves the destination bytes either way.
4833- let flags = ir:: MemFlagsData :: trusted ( ) . with_endianness ( Endianness :: Little ) ;
4859+ //
4860+ // The loads/stores carry the source/destination entity's alias region
4861+ // so alias analysis keeps them in the same disjoint memory category as
4862+ // the entity's other (region-tagged) accesses; otherwise a region-less
4863+ // load here could be forwarded a stale value across an intervening
4864+ // region-tagged store to the same address.
4865+ let load_flags = ir:: MemFlagsData :: trusted ( )
4866+ . with_endianness ( Endianness :: Little )
4867+ . with_alias_region ( src_region) ;
4868+ let store_flags = ir:: MemFlagsData :: trusted ( )
4869+ . with_endianness ( Endianness :: Little )
4870+ . with_alias_region ( dst_region) ;
48344871 const WIDTHS : & [ ( u64 , ir:: Type ) ] = & [
48354872 ( 16 , ir:: types:: I8X16 ) ,
48364873 ( 8 , ir:: types:: I64 ) ,
@@ -4853,10 +4890,10 @@ impl FuncEnvironment<'_> {
48534890 }
48544891 let vals: SmallVec < [ ir:: Value ; 12 ] > = chunks
48554892 . iter ( )
4856- . map ( |& ( off, ty) | builder. ins ( ) . load ( ty, flags , src_addr, off) )
4893+ . map ( |& ( off, ty) | builder. ins ( ) . load ( ty, load_flags , src_addr, off) )
48574894 . collect ( ) ;
48584895 for ( & ( off, _) , val) in chunks. iter ( ) . zip ( vals) {
4859- builder. ins ( ) . store ( flags , val, dst_addr, off) ;
4896+ builder. ins ( ) . store ( store_flags , val, dst_addr, off) ;
48604897 }
48614898 }
48624899
@@ -6319,11 +6356,20 @@ enum BulkOp {
63196356 /// must have type `env.pointer_type()`. `const_len`, when set, is the
63206357 /// statically-known byte length (from a constant wasm length); the inline
63216358 /// fast path in `raw_bulk_memory_operation` uses it to expand small copies.
6359+ ///
6360+ /// `src_entity`/`dst_entity` are the source and destination entities,
6361+ /// carried so that the inline fast path can tag its loads/stores with each
6362+ /// entity's alias region (the per-memory or GC-heap region); otherwise a
6363+ /// region-less inline load could be forwarded a stale value across an
6364+ /// intervening region-tagged store to the same address. They are unused on
6365+ /// the libcall path.
63226366 MemoryCopy {
63236367 dst : ir:: Value ,
63246368 src : ir:: Value ,
63256369 len : ir:: Value ,
63266370 const_len : Option < u64 > ,
6371+ src_entity : CheckedEntity ,
6372+ dst_entity : CheckedEntity ,
63276373 } ,
63286374
63296375 /// A `memory.fill` operation, setting all bytes of `dst` to `val`.
0 commit comments