@@ -32,7 +32,7 @@ use stable_mir::mir::{
32
32
Statement , StatementKind , Terminator , TerminatorKind ,
33
33
} ;
34
34
use stable_mir:: target:: { MachineInfo , MachineSize } ;
35
- use stable_mir:: ty:: { AdtKind , IndexedVal , MirConst , RigidTy , Ty , TyKind , UintTy } ;
35
+ use stable_mir:: ty:: { AdtKind , IndexedVal , MirConst , RigidTy , Span , Ty , TyKind , UintTy } ;
36
36
use std:: fmt:: Debug ;
37
37
use strum_macros:: AsRefStr ;
38
38
use tracing:: { debug, trace} ;
@@ -164,7 +164,18 @@ pub struct ValidValueReq {
164
164
/// Size of this requirement.
165
165
size : MachineSize ,
166
166
/// The range restriction is represented by a Scalar.
167
- valid_range : WrappingRange ,
167
+ valid_range : ValidityRange ,
168
+ }
169
+
170
+ #[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
171
+ enum ValidityRange {
172
+ /// The value validity fits in a single value range.
173
+ /// This includes cases where the full range is covered.
174
+ Single ( WrappingRange ) ,
175
+ /// The validity includes more than one value range.
176
+ /// Currently, this is only the case for `char`, which has two ranges.
177
+ /// If more cases come up, we could turn this into a vector instead.
178
+ Multiple ( [ WrappingRange ; 2 ] ) ,
168
179
}
169
180
170
181
// TODO: Optimize checks by merging requirements whenever possible.
@@ -180,44 +191,88 @@ impl ValidValueReq {
180
191
/// It's not possible to define a `rustc_layout_scalar_valid_range_*` to any other structure.
181
192
/// Note that this annotation only applies to the first scalar in the layout.
182
193
pub fn try_from_ty ( machine_info : & MachineInfo , ty : Ty ) -> Option < ValidValueReq > {
183
- let shape = ty. layout ( ) . unwrap ( ) . shape ( ) ;
184
- match shape. abi {
185
- ValueAbi :: Scalar ( Scalar :: Initialized { value, valid_range } )
186
- | ValueAbi :: ScalarPair ( Scalar :: Initialized { value, valid_range } , _) => {
187
- Some ( ValidValueReq { offset : 0 , size : value. size ( machine_info) , valid_range } )
194
+ if ty. kind ( ) . is_char ( ) {
195
+ Some ( ValidValueReq {
196
+ offset : 0 ,
197
+ size : MachineSize :: from_bits ( size_of :: < char > ( ) * 8 ) ,
198
+ valid_range : ValidityRange :: Multiple ( [
199
+ WrappingRange { start : 0 , end : 0xD7FF } ,
200
+ WrappingRange { start : 0xE000 , end : char:: MAX . into ( ) } ,
201
+ ] ) ,
202
+ } )
203
+ } else {
204
+ let shape = ty. layout ( ) . unwrap ( ) . shape ( ) ;
205
+ match shape. abi {
206
+ ValueAbi :: Scalar ( Scalar :: Initialized { value, valid_range } )
207
+ | ValueAbi :: ScalarPair ( Scalar :: Initialized { value, valid_range } , _) => {
208
+ Some ( ValidValueReq {
209
+ offset : 0 ,
210
+ size : value. size ( machine_info) ,
211
+ valid_range : ValidityRange :: Single ( valid_range) ,
212
+ } )
213
+ }
214
+ ValueAbi :: Scalar ( _)
215
+ | ValueAbi :: ScalarPair ( _, _)
216
+ | ValueAbi :: Uninhabited
217
+ | ValueAbi :: Vector { .. }
218
+ | ValueAbi :: Aggregate { .. } => None ,
188
219
}
189
- ValueAbi :: Scalar ( _)
190
- | ValueAbi :: ScalarPair ( _, _)
191
- | ValueAbi :: Uninhabited
192
- | ValueAbi :: Vector { .. }
193
- | ValueAbi :: Aggregate { .. } => None ,
194
220
}
195
221
}
196
222
197
223
/// Check if range is full.
198
224
pub fn is_full ( & self ) -> bool {
199
- self . valid_range . is_full ( self . size ) . unwrap ( )
225
+ if let ValidityRange :: Single ( valid_range) = self . valid_range {
226
+ valid_range. is_full ( self . size ) . unwrap ( )
227
+ } else {
228
+ false
229
+ }
200
230
}
201
231
202
232
/// Check if this range contains `other` range.
203
233
///
204
234
/// I.e., `scalar_2` ⊆ `scalar_1`
205
235
pub fn contains ( & self , other : & ValidValueReq ) -> bool {
206
236
assert_eq ! ( self . size, other. size) ;
207
- match ( self . valid_range . wraps_around ( ) , other. valid_range . wraps_around ( ) ) {
208
- ( true , true ) | ( false , false ) => {
209
- self . valid_range . start <= other. valid_range . start
210
- && self . valid_range . end >= other. valid_range . end
237
+ match ( & self . valid_range , & other. valid_range ) {
238
+ ( ValidityRange :: Single ( this_range) , ValidityRange :: Single ( other_range) ) => {
239
+ range_contains ( this_range, other_range, self . size )
240
+ }
241
+ ( ValidityRange :: Multiple ( this_ranges) , ValidityRange :: Single ( other_range) ) => {
242
+ range_contains ( & this_ranges[ 0 ] , other_range, self . size )
243
+ || range_contains ( & this_ranges[ 1 ] , other_range, self . size )
244
+ }
245
+ ( ValidityRange :: Single ( this_range) , ValidityRange :: Multiple ( other_ranges) ) => {
246
+ range_contains ( this_range, & other_ranges[ 0 ] , self . size )
247
+ && range_contains ( this_range, & other_ranges[ 1 ] , self . size )
211
248
}
212
- ( true , false ) => {
213
- self . valid_range . start <= other. valid_range . start
214
- || self . valid_range . end >= other. valid_range . end
249
+ ( ValidityRange :: Multiple ( this_ranges) , ValidityRange :: Multiple ( other_ranges) ) => {
250
+ let contains = ( range_contains ( & this_ranges[ 0 ] , & other_ranges[ 0 ] , self . size )
251
+ || range_contains ( & this_ranges[ 1 ] , & other_ranges[ 0 ] , self . size ) )
252
+ && ( range_contains ( & this_ranges[ 0 ] , & other_ranges[ 1 ] , self . size )
253
+ || range_contains ( & this_ranges[ 1 ] , & other_ranges[ 1 ] , self . size ) ) ;
254
+ // Multiple today only cover `char` case.
255
+ debug_assert ! (
256
+ contains,
257
+ "Expected validity of `char` for Multiple ranges. Found: {self:?}, {other:?}"
258
+ ) ;
259
+ contains
215
260
}
216
- ( false , true ) => self . is_full ( ) ,
217
261
}
218
262
}
219
263
}
220
264
265
+ /// Check if range `r1` contains range `r2`.
266
+ ///
267
+ /// I.e., `r2` ⊆ `r1`
268
+ fn range_contains ( r1 : & WrappingRange , r2 : & WrappingRange , sz : MachineSize ) -> bool {
269
+ match ( r1. wraps_around ( ) , r2. wraps_around ( ) ) {
270
+ ( true , true ) | ( false , false ) => r1. start <= r2. start && r1. end >= r2. end ,
271
+ ( true , false ) => r1. start <= r2. start || r1. end >= r2. end ,
272
+ ( false , true ) => r1. is_full ( sz) . unwrap ( ) ,
273
+ }
274
+ }
275
+
221
276
#[ derive( AsRefStr , Clone , Debug ) ]
222
277
enum SourceOp {
223
278
/// Validity checks are done on a byte level when the Rvalue can generate invalid value.
@@ -763,8 +818,6 @@ pub fn build_limits(
763
818
let span = source. span ( body. blocks ( ) ) ;
764
819
debug ! ( ?req, ?rvalue_ptr, ?span, "build_limits" ) ;
765
820
let primitive_ty = uint_ty ( req. size . bytes ( ) ) ;
766
- let start_const = body. new_uint_operand ( req. valid_range . start , primitive_ty, span) ;
767
- let end_const = body. new_uint_operand ( req. valid_range . end , primitive_ty, span) ;
768
821
let orig_ptr = if req. offset != 0 {
769
822
let start_ptr =
770
823
move_local ( body. insert_assignment ( rvalue_ptr, source, InsertPosition :: Before ) ) ;
@@ -799,6 +852,35 @@ pub fn build_limits(
799
852
InsertPosition :: Before ,
800
853
) ;
801
854
let value = Operand :: Copy ( Place { local : value_ptr, projection : vec ! [ ProjectionElem :: Deref ] } ) ;
855
+ match & req. valid_range {
856
+ ValidityRange :: Single ( range) => {
857
+ build_single_limit ( body, range, source, span, primitive_ty, value)
858
+ }
859
+ ValidityRange :: Multiple ( [ range1, range2] ) => {
860
+ // Build `let valid = range1.contains(value) || range2.contains(value);
861
+ let cond1 = build_single_limit ( body, range1, source, span, primitive_ty, value. clone ( ) ) ;
862
+ let cond2 = build_single_limit ( body, range2, source, span, primitive_ty, value) ;
863
+ body. insert_binary_op (
864
+ BinOp :: BitOr ,
865
+ move_local ( cond1) ,
866
+ move_local ( cond2) ,
867
+ source,
868
+ InsertPosition :: Before ,
869
+ )
870
+ }
871
+ }
872
+ }
873
+
874
+ fn build_single_limit (
875
+ body : & mut MutableBody ,
876
+ range : & WrappingRange ,
877
+ source : & mut SourceInstruction ,
878
+ span : Span ,
879
+ primitive_ty : UintTy ,
880
+ value : Operand ,
881
+ ) -> Local {
882
+ let start_const = body. new_uint_operand ( range. start , primitive_ty, span) ;
883
+ let end_const = body. new_uint_operand ( range. end , primitive_ty, span) ;
802
884
let start_result = body. insert_binary_op (
803
885
BinOp :: Ge ,
804
886
value. clone ( ) ,
@@ -808,7 +890,7 @@ pub fn build_limits(
808
890
) ;
809
891
let end_result =
810
892
body. insert_binary_op ( BinOp :: Le , value, end_const, source, InsertPosition :: Before ) ;
811
- if req . valid_range . wraps_around ( ) {
893
+ if range . wraps_around ( ) {
812
894
// valid >= start || valid <= end
813
895
body. insert_binary_op (
814
896
BinOp :: BitOr ,
0 commit comments