@@ -38,6 +38,15 @@ use core::{cmp, iter, mem};
3838#[ cfg( feature = "simd" ) ]
3939mod simd;
4040
41+ #[ cfg( feature = "try-op" ) ]
42+ mod transaction;
43+ #[ cfg( not( feature = "try-op" ) ) ]
44+ mod transaction_disabled;
45+ #[ cfg( not( feature = "try-op" ) ) ]
46+ use transaction_disabled as transaction;
47+
48+ use transaction:: { RollbackLogAllocations , Transaction } ;
49+
4150#[ derive( Clone , PartialEq ) ]
4251pub ( crate ) struct OperatorValidator {
4352 pub ( super ) locals : Locals ,
@@ -65,128 +74,12 @@ pub(crate) struct OperatorValidator {
6574 #[ cfg( debug_assertions) ]
6675 pub ( crate ) pop_push_log : Vec < bool > ,
6776
68- /// When "atomic " validation of an operator is pending, this is a trace
77+ /// When "try-op " validation of an operator is pending, this is a trace
6978 /// of discarded info that can restore the OperatorValidator to its
7079 /// pre-operator state if necessary.
71- #[ cfg( feature = "atomic" ) ]
7280 transaction : Transaction ,
7381}
7482
75- #[ cfg( feature = "atomic" ) ]
76- #[ derive( Clone , PartialEq ) ]
77- enum Transaction {
78- Active ( RollbackLog ) ,
79- Inactive ( RollbackLogAllocations ) ,
80- }
81-
82- #[ cfg( feature = "atomic" ) ]
83- #[ derive( Clone , Default , PartialEq ) ]
84- struct RollbackLog {
85- /// A trace of operands popped. Pushes are recorded as `None`.
86- operands : Vec < Option < MaybeType > > ,
87-
88- /// A trace of frames popped. Pushes are recorded as `None`.
89- frames : Vec < Option < Frame > > ,
90-
91- /// A trace of local init indices reset at the end of a frame.
92- inits : Vec < u32 > ,
93-
94- /// The local init height when transaction was begun.
95- init_height : usize ,
96-
97- /// Whether the current frame was made unreachable.
98- unreachable : bool ,
99- }
100-
101- #[ cfg( feature = "atomic" ) ]
102- #[ derive( Clone , Default , PartialEq ) ]
103- struct RollbackLogAllocations {
104- operands : Vec < Option < MaybeType > > ,
105- frames : Vec < Option < Frame > > ,
106- inits : Vec < u32 > ,
107- }
108-
109- #[ cfg( feature = "atomic" ) ]
110- impl Transaction {
111- fn begin ( & mut self , init_height : usize ) {
112- match self {
113- Transaction :: Active ( _) => panic ! ( "transaction already in progress" ) ,
114- Transaction :: Inactive ( allocs) => {
115- * self = Transaction :: Active ( RollbackLog :: new ( init_height, mem:: take ( allocs) ) )
116- }
117- }
118- }
119-
120- fn end ( & mut self ) {
121- if let Transaction :: Active ( log) = self {
122- * self = Transaction :: Inactive ( mem:: take ( log) . into_allocations ( ) )
123- }
124- }
125-
126- fn into_allocations ( self ) -> RollbackLogAllocations {
127- match self {
128- Transaction :: Active ( log) => log. into_allocations ( ) ,
129- Transaction :: Inactive ( allocs) => allocs,
130- }
131- }
132-
133- fn map ( & mut self , f : impl FnOnce ( & mut RollbackLog ) ) {
134- if let Transaction :: Active ( log) = self {
135- f ( log) ;
136- }
137- }
138- }
139-
140- #[ cfg( feature = "atomic" ) ]
141- impl RollbackLog {
142- fn new ( init_height : usize , allocs : RollbackLogAllocations ) -> Self {
143- let RollbackLogAllocations {
144- operands,
145- frames,
146- inits,
147- } = allocs;
148- debug_assert ! ( operands. is_empty( ) ) ;
149- debug_assert ! ( frames. is_empty( ) ) ;
150- debug_assert ! ( inits. is_empty( ) ) ;
151- Self {
152- operands,
153- frames,
154- inits,
155- init_height,
156- unreachable : false ,
157- }
158- }
159-
160- fn into_allocations ( self ) -> RollbackLogAllocations {
161- fn clear < T > ( mut tmp : Vec < T > ) -> Vec < T > {
162- tmp. clear ( ) ;
163- tmp
164- }
165- RollbackLogAllocations {
166- operands : clear ( self . operands ) ,
167- frames : clear ( self . frames ) ,
168- inits : clear ( self . inits ) ,
169- }
170- }
171-
172- fn record_push ( & mut self ) {
173- self . operands . push ( None ) ;
174- }
175-
176- fn record_pop ( & mut self , ty : MaybeType ) {
177- self . operands . push ( Some ( ty) ) ;
178- }
179-
180- fn push_ctrl ( & mut self ) {
181- self . frames . push ( None ) ;
182- }
183-
184- fn pop_ctrl ( & mut self , frame : Frame , inits : Vec < u32 > ) {
185- self . frames . push ( Some ( frame) ) ;
186- self . inits . extend ( inits) ;
187- }
188- }
189-
19083/// Captures the initialization of non-defaultable locals.
19184#[ derive( Clone , PartialEq ) ]
19285struct LocalInits {
@@ -353,7 +246,6 @@ pub struct OperatorValidatorAllocations {
353246 local_inits : LocalInits ,
354247 locals_first : Vec < ValType > ,
355248 locals_uncached : Vec < ( u32 , ValType ) > ,
356- #[ cfg( feature = "atomic" ) ]
357249 rollback_log : RollbackLogAllocations ,
358250}
359251
@@ -460,7 +352,6 @@ impl OperatorValidator {
460352 local_inits,
461353 locals_first,
462354 locals_uncached,
463- #[ cfg( feature = "atomic" ) ]
464355 rollback_log,
465356 } = allocs;
466357 debug_assert ! ( popped_types_tmp. is_empty( ) ) ;
@@ -483,8 +374,7 @@ impl OperatorValidator {
483374 shared : false ,
484375 #[ cfg( debug_assertions) ]
485376 pop_push_log : vec ! [ ] ,
486- #[ cfg( feature = "atomic" ) ]
487- transaction : Transaction :: Inactive ( rollback_log) ,
377+ transaction : Transaction :: new ( rollback_log) ,
488378 }
489379 }
490380
@@ -672,39 +562,46 @@ impl OperatorValidator {
672562 } ,
673563 locals_first : clear ( self . locals . first ) ,
674564 locals_uncached : clear ( self . locals . uncached ) ,
675- #[ cfg( feature = "atomic" ) ]
676565 rollback_log : self . transaction . into_allocations ( ) ,
677566 }
678567 }
679568
680- fn record_pop ( & mut self ) {
569+ // records a pop that mutated the operand stack
570+ fn record_pop ( & mut self , ty : MaybeType ) {
571+ self . transaction . map ( |log| log. record_pop ( ty) ) ;
572+ self . record_any_pop ( ) ;
573+ }
574+
575+ // records any pop, including a Bottom synthesized from an empty polymorphic operand stack
576+ fn record_any_pop ( & mut self ) {
681577 #[ cfg( debug_assertions) ]
682578 {
683579 self . pop_push_log . push ( false ) ;
684580 }
685581 }
686582
687583 fn record_push ( & mut self ) {
584+ self . transaction . map ( |log| log. record_push ( ) ) ;
688585 #[ cfg( debug_assertions) ]
689586 {
690587 self . pop_push_log . push ( true ) ;
691588 }
692589 }
693590
694- #[ cfg ( feature = "atomic" ) ]
695- pub ( super ) fn begin_atomic_op ( & mut self ) {
591+ #[ allow ( dead_code ) ]
592+ pub ( super ) fn begin_try_op ( & mut self ) {
696593 self . transaction . begin ( self . local_inits . height ( ) ) ;
697594 }
698595
699- #[ cfg ( feature = "atomic" ) ]
596+ #[ allow ( dead_code ) ]
700597 pub ( super ) fn commit ( & mut self ) {
701598 self . transaction . end ( ) ;
702599 }
703600
704- /// Reverse the actions in the rollback log. This is used by `FuncValidator::atomic_op ()`
601+ /// Reverse the actions in the rollback log. This is used by `FuncValidator::try_op ()`
705602 /// if validating the operator fails. The rollback log is sufficient to handle
706603 /// the mutations of any individual operator (but not necessarily multiple operators).
707- #[ cfg( feature = "atomic " ) ]
604+ #[ cfg( feature = "try-op " ) ]
708605 pub ( super ) fn rollback ( & mut self ) {
709606 let Transaction :: Active ( rollback_log) = & self . transaction else {
710607 panic ! ( "no transaction pending" ) ;
@@ -793,8 +690,6 @@ where
793690
794691 self . operands . push ( maybe_ty) ;
795692 self . record_push ( ) ;
796- #[ cfg( feature = "atomic" ) ]
797- self . transaction . map ( |log| log. record_push ( ) ) ;
798693 Ok ( ( ) )
799694 }
800695
@@ -919,10 +814,7 @@ where
919814 if Some ( actual_ty) == expected {
920815 if let Some ( control) = self . control . last ( ) {
921816 if self . operands . len ( ) >= control. height {
922- #[ cfg( feature = "atomic" ) ]
923- self . transaction
924- . map ( |log| log. record_pop ( MaybeType :: Known ( actual_ty) ) ) ;
925- self . record_pop ( ) ;
817+ self . record_pop ( MaybeType :: Known ( actual_ty) ) ;
926818 return Ok ( MaybeType :: Known ( actual_ty) ) ;
927819 }
928820 }
@@ -947,6 +839,7 @@ where
947839 self . operands . extend ( popped) ;
948840 let control = self . control . last ( ) . unwrap ( ) ;
949841 let actual = if self . operands . len ( ) == control. height && control. unreachable {
842+ self . record_any_pop ( ) ;
950843 MaybeType :: Bottom
951844 } else {
952845 if self . operands . len ( ) == control. height {
@@ -960,8 +853,7 @@ where
960853 )
961854 } else {
962855 let ty = self . operands . pop ( ) . unwrap ( ) ;
963- #[ cfg( feature = "atomic" ) ]
964- self . transaction . map ( |log| log. record_pop ( ty) ) ;
856+ self . record_pop ( ty) ;
965857 ty
966858 }
967859 } ;
@@ -1022,7 +914,6 @@ where
1022914 }
1023915 }
1024916 }
1025- self . record_pop ( ) ;
1026917 Ok ( actual)
1027918 }
1028919
@@ -1135,24 +1026,20 @@ where
11351026 /// Flags the current control frame as unreachable, additionally truncating
11361027 /// the currently active operand stack.
11371028 fn unreachable ( & mut self ) -> Result < ( ) > {
1138- #[ cfg( feature = "atomic" ) ]
11391029 if !self . control . last ( ) . unwrap ( ) . unreachable {
1140- self . transaction . map ( |log| log. unreachable = true ) ;
1030+ self . transaction . map ( |log| log. set_unreachable ( ) ) ;
11411031 }
11421032
11431033 let control = self . control . last_mut ( ) . unwrap ( ) ;
11441034 control. unreachable = true ;
11451035 let new_height = control. height ;
11461036
1147- #[ cfg( feature = "atomic" ) ]
1148- {
1149- let ops = self . operands . split_off ( new_height) ;
1150- self . transaction . map ( |log| {
1151- for op in ops. iter ( ) . rev ( ) {
1152- log. record_pop ( * op) ;
1153- }
1154- } ) ;
1155- }
1037+ let operands = self . operands . split_off ( new_height) ;
1038+ self . transaction . map ( |log| {
1039+ for op in operands. iter ( ) . rev ( ) {
1040+ log. record_pop ( * op) ;
1041+ }
1042+ } ) ;
11561043
11571044 self . operands . truncate ( new_height) ;
11581045 Ok ( ( ) )
@@ -1188,7 +1075,6 @@ where
11881075 unreachable : false ,
11891076 init_height,
11901077 } ) ;
1191- #[ cfg( feature = "atomic" ) ]
11921078 self . transaction . map ( |log| log. push_ctrl ( ) ) ;
11931079 }
11941080
@@ -1221,7 +1107,6 @@ where
12211107 // And then we can remove it and reset_locals.
12221108 let frame = self . control . pop ( ) . unwrap ( ) ;
12231109 let _inits = self . local_inits . pop_ctrl ( frame. init_height ) ;
1224- #[ cfg( feature = "atomic" ) ]
12251110 self . transaction . map ( |log| log. pop_ctrl ( frame, _inits) ) ;
12261111
12271112 Ok ( frame)
0 commit comments