@@ -612,6 +612,17 @@ trait NameKeyExt {
612
612
FunctionOrigin :: EntryPoint ( idx) => NameKey :: EntryPointLocal ( idx, local_handle) ,
613
613
}
614
614
}
615
+
616
+ /// Return the name key for a local variable used by ReadZeroSkipWrite bounds-check
617
+ /// policy when it needs to produce a pointer-typed result for an OOB access. These
618
+ /// are unique per accessed type, so the second argument is a type handle. See docs
619
+ /// for [`crate::back::msl`].
620
+ fn oob_local_for_type ( origin : FunctionOrigin , ty : Handle < crate :: Type > ) -> NameKey {
621
+ match origin {
622
+ FunctionOrigin :: Handle ( handle) => NameKey :: FunctionOobLocal ( handle, ty) ,
623
+ FunctionOrigin :: EntryPoint ( idx) => NameKey :: EntryPointOobLocal ( idx, ty) ,
624
+ }
625
+ }
615
626
}
616
627
617
628
impl NameKeyExt for NameKey { }
@@ -722,6 +733,11 @@ impl<'a> ExpressionContext<'a> {
722
733
index:: bounds_check_iter ( chain, self . module , self . function , self . info )
723
734
}
724
735
736
+ /// See docs for [`proc::index::oob_local_types`].
737
+ fn oob_local_types ( & self ) -> FastHashSet < Handle < crate :: Type > > {
738
+ index:: oob_local_types ( self . module , self . function , self . info , self . policies )
739
+ }
740
+
725
741
fn get_packed_vec_kind ( & self , expr_handle : Handle < crate :: Expression > ) -> Option < crate :: Scalar > {
726
742
match self . function . expressions [ expr_handle] {
727
743
crate :: Expression :: AccessIndex { base, index } => {
@@ -929,8 +945,18 @@ impl<W: Write> Writer<W> {
929
945
Ok ( ( ) )
930
946
}
931
947
932
- /// Writes the local variables of the given function.
948
+ /// Writes the local variables of the given function, as well as any extra
949
+ /// out-of-bounds locals that are needed.
950
+ ///
951
+ /// The names of the OOB locals are also added to `self.names` at the same
952
+ /// time.
933
953
fn put_locals ( & mut self , context : & ExpressionContext ) -> BackendResult {
954
+ let oob_local_types = context. oob_local_types ( ) ;
955
+ for & ty in oob_local_types. iter ( ) {
956
+ let name_key = NameKey :: oob_local_for_type ( context. origin , ty) ;
957
+ self . names . insert ( name_key, self . namer . call ( "oob" ) ) ;
958
+ }
959
+
934
960
for ( name_key, ty, init) in context
935
961
. function
936
962
. local_variables
@@ -939,6 +965,10 @@ impl<W: Write> Writer<W> {
939
965
let name_key = NameKey :: local ( context. origin , local_handle) ;
940
966
( name_key, local. ty , local. init )
941
967
} )
968
+ . chain ( oob_local_types. iter ( ) . map ( |& ty| {
969
+ let name_key = NameKey :: oob_local_for_type ( context. origin , ty) ;
970
+ ( name_key, ty, None )
971
+ } ) )
942
972
{
943
973
let ty_name = TypeContext {
944
974
handle : ty,
@@ -1761,7 +1791,42 @@ impl<W: Write> Writer<W> {
1761
1791
{
1762
1792
write ! ( self . out, " ? " ) ?;
1763
1793
self . put_access_chain ( expr_handle, policy, context) ?;
1764
- write ! ( self . out, " : DefaultConstructible()" ) ?;
1794
+ write ! ( self . out, " : " ) ?;
1795
+
1796
+ if context. resolve_type ( base) . pointer_space ( ) . is_some ( ) {
1797
+ // We can't just use `DefaultConstructible` if this is a pointer.
1798
+ // Instead, we create a dummy local variable to serve as pointer
1799
+ // target if the access is out of bounds.
1800
+ let result_ty = context. info [ expr_handle]
1801
+ . ty
1802
+ . inner_with ( & context. module . types )
1803
+ . pointer_base_type ( ) ;
1804
+ let result_ty_handle = match result_ty {
1805
+ Some ( TypeResolution :: Handle ( handle) ) => handle,
1806
+ Some ( TypeResolution :: Value ( _) ) => {
1807
+ // As long as the result of a pointer access expression is
1808
+ // passed to a function or stored in a let binding, the
1809
+ // type will be in the arena. If additional uses of
1810
+ // pointers become valid, this assumption might no longer
1811
+ // hold. Note that the LHS of a load or store doesn't
1812
+ // take this path -- there is dedicated code in `put_load`
1813
+ // and `put_store`.
1814
+ unreachable ! (
1815
+ "Expected type {result_ty:?} of access through pointer type {base:?} to be in the arena" ,
1816
+ ) ;
1817
+ }
1818
+ None => {
1819
+ unreachable ! (
1820
+ "Expected access through pointer type {base:?} to return a pointer, but got {result_ty:?}" ,
1821
+ )
1822
+ }
1823
+ } ;
1824
+ let name_key =
1825
+ NameKey :: oob_local_for_type ( context. origin , result_ty_handle) ;
1826
+ self . out . write_str ( & self . names [ & name_key] ) ?;
1827
+ } else {
1828
+ write ! ( self . out, "DefaultConstructible()" ) ?;
1829
+ }
1765
1830
1766
1831
if !is_scoped {
1767
1832
write ! ( self . out, ")" ) ?;
0 commit comments