11use core:: fmt;
22
3- use alloc:: { borrow:: ToOwned , collections:: VecDeque , string:: ToString } ;
3+ use alloc:: {
4+ borrow:: ToOwned ,
5+ boxed:: Box ,
6+ collections:: VecDeque ,
7+ format,
8+ string:: { String , ToString } ,
9+ } ;
410
511use cranelift_entity:: SecondaryMap ;
612use fx_utils:: FxHashMap ;
7- use itertools:: izip;
13+ use itertools:: { izip, Itertools } ;
814use log:: trace;
15+ use smallvec:: { smallvec, SmallVec } ;
916
1017use crate :: {
1118 cfg:: { Block , CfgContext } ,
@@ -18,7 +25,7 @@ use crate::{
1825
1926use super :: { Assignment , AssignmentCopy , OperandAssignment } ;
2027
21- #[ derive( Clone , Copy ) ]
28+ #[ derive( Clone ) ]
2229pub enum VerifierError {
2330 BadUseCount ( Instr ) ,
2431 BadDefCount ( Instr ) ,
@@ -33,7 +40,7 @@ pub enum VerifierError {
3340 BadUse {
3441 instr : Instr ,
3542 op : u32 ,
36- found_vreg : Option < VirtReg > ,
43+ found_vregs : Option < Box < [ VirtReg ] > > ,
3744 } ,
3845 IllegalSpillCopy {
3946 copy_idx : u32 ,
@@ -49,7 +56,7 @@ pub enum VerifierError {
4956 } ,
5057 BadGhostCopySource {
5158 ghost_copy_idx : u32 ,
52- found_vreg : Option < VirtReg > ,
59+ found_vregs : Option < Box < [ VirtReg ] > > ,
5360 } ,
5461}
5562
@@ -111,15 +118,13 @@ impl<M: MachineCore> fmt::Display for DisplayVerifierError<'_, M> {
111118 VerifierError :: BadUse {
112119 instr,
113120 op,
114- found_vreg ,
121+ ref found_vregs ,
115122 } => {
116- let found_vreg = found_vreg. map_or ( "garbage" . to_owned ( ) , |found_vreg : VirtReg | {
117- found_vreg. to_string ( )
118- } ) ;
123+ let found_vregs = display_found_vregs ( found_vregs. as_deref ( ) ) ;
119124 let expected_vreg = self . lir . instr_uses ( instr) [ op as usize ] . reg ( ) ;
120125 write ! (
121126 f,
122- "expected {instr} use {op} to be of {expected_vreg}, found {found_vreg }"
127+ "expected {instr} use {op} to be of {expected_vreg}, found {found_vregs }"
123128 )
124129 }
125130 VerifierError :: IllegalSpillCopy { copy_idx } => {
@@ -158,16 +163,15 @@ impl<M: MachineCore> fmt::Display for DisplayVerifierError<'_, M> {
158163 }
159164 VerifierError :: BadGhostCopySource {
160165 ghost_copy_idx,
161- found_vreg ,
166+ ref found_vregs ,
162167 } => {
163- let found_vreg =
164- found_vreg. map_or ( "garbage" . to_owned ( ) , |found_vreg| found_vreg. to_string ( ) ) ;
168+ let found_vregs = display_found_vregs ( found_vregs. as_deref ( ) ) ;
165169 let ghost_copy = & self . assignment . block_exit_ghost_copies [ ghost_copy_idx as usize ] ;
166170 let block = ghost_copy. block ;
167171 let expected_vreg = ghost_copy. from_vreg ;
168172 write ! (
169173 f,
170- "expected ghost copy for '{}' at end of '{block}' to be from {expected_vreg}, found {found_vreg }" ,
174+ "expected ghost copy for '{}' at end of '{block}' to be from {expected_vreg}, found {found_vregs }" ,
171175 ghost_copy. assignment. display:: <M >( ) ,
172176 )
173177 }
@@ -183,9 +187,10 @@ pub fn verify<M: MachineCore>(
183187 let mut block_entry_reg_states = BlockKnownRegState :: with_capacity ( cfg_ctx. block_order . len ( ) ) ;
184188
185189 let entry = cfg_ctx. block_order [ 0 ] ;
190+
186191 let mut reg_state = KnownRegState :: default ( ) ;
187192 for ( & live_in, & preg) in lir. block_params ( entry) . iter ( ) . zip ( lir. live_in_regs ( ) ) {
188- reg_state. insert ( OperandAssignment :: Reg ( preg) , live_in) ;
193+ reg_state. insert ( OperandAssignment :: Reg ( preg) , smallvec ! [ live_in] ) ;
189194 }
190195
191196 let mut worklist = VecDeque :: new ( ) ;
@@ -212,7 +217,17 @@ pub fn verify<M: MachineCore>(
212217 Ok ( ( ) )
213218}
214219
215- type KnownRegState = FxHashMap < OperandAssignment , VirtReg > ;
220+ fn display_found_vregs ( found_vregs : Option < & [ VirtReg ] > ) -> String {
221+ match found_vregs {
222+ Some ( & [ found_vreg] ) => found_vreg. to_string ( ) ,
223+ Some ( found_vregs) => {
224+ format ! ( "{{{}}}" , found_vregs. iter( ) . format( ", " ) )
225+ }
226+ None => "garbage" . to_owned ( ) ,
227+ }
228+ }
229+
230+ type KnownRegState = FxHashMap < OperandAssignment , SmallVec < [ VirtReg ; 2 ] > > ;
216231type BlockKnownRegState = SecondaryMap < Block , Option < KnownRegState > > ;
217232
218233fn verify_block_ghost_copies (
@@ -229,13 +244,20 @@ fn verify_block_ghost_copies(
229244 for ghost_copy_idx in ghost_copy_range {
230245 let ghost_copy = & assignment. block_exit_ghost_copies [ ghost_copy_idx] ;
231246 let operand = ghost_copy. assignment ;
232- check_assigned_vreg ( operand, ghost_copy. from_vreg , & * reg_state) . map_err ( |found_vreg | {
247+ check_assigned_vreg ( operand, ghost_copy. from_vreg , reg_state) . map_err ( |found_vregs | {
233248 VerifierError :: BadGhostCopySource {
234249 ghost_copy_idx : ghost_copy_idx. try_into ( ) . unwrap ( ) ,
235- found_vreg ,
250+ found_vregs ,
236251 }
237252 } ) ?;
238- reg_state. insert ( operand, ghost_copy. to_vreg ) ;
253+
254+ // Ghost copies are special in that they copy between vregs in the same physical assignment
255+ // rather than between physical assignments in the same vreg. That means the same physical
256+ // assignment may now simultaneously house an additional vreg.
257+ let vregs = reg_state. get_mut ( & operand) . unwrap ( ) ;
258+ if !vregs. contains ( & ghost_copy. to_vreg ) {
259+ vregs. push ( ghost_copy. to_vreg ) ;
260+ }
239261 }
240262
241263 Ok ( ( ) )
@@ -273,10 +295,13 @@ fn verify_copy<M: MachineCore>(
273295 return Err ( VerifierError :: IllegalSpillCopy { copy_idx } ) ;
274296 }
275297
276- let from_vreg = match copy. from {
277- CopySourceAssignment :: Operand ( from) => * reg_state
278- . get ( & from)
279- . ok_or ( VerifierError :: UndefCopySource { copy_idx } ) ?,
298+ match copy. from {
299+ CopySourceAssignment :: Operand ( from) => {
300+ let from = reg_state
301+ . get ( & from)
302+ . ok_or ( VerifierError :: UndefCopySource { copy_idx } ) ?;
303+ reg_state. insert ( copy. to , from. clone ( ) ) ;
304+ }
280305 CopySourceAssignment :: Remat ( instr) => {
281306 if !lir. instr_uses ( instr) . is_empty ( ) {
282307 return Err ( VerifierError :: BadRematOperands { copy_idx } ) ;
@@ -293,12 +318,10 @@ fn verify_copy<M: MachineCore>(
293318 return Err ( VerifierError :: BadRematOperands { copy_idx } ) ;
294319 }
295320
296- def. reg ( )
321+ reg_state . insert ( copy . to , smallvec ! [ def. reg( ) ] ) ;
297322 }
298323 } ;
299324
300- reg_state. insert ( copy. to , from_vreg) ;
301-
302325 Ok ( ( ) )
303326}
304327
@@ -324,11 +347,11 @@ fn verify_instr<M: MachineCore>(
324347 if !use_matches_constraint ( use_op, use_assignment, def_assignments) {
325348 return Err ( VerifierError :: UseConstraintViolation { instr, op : i } ) ;
326349 }
327- check_use_assignment ( use_op, use_assignment, reg_state) . map_err ( |found_vreg | {
350+ check_use_assignment ( use_op, use_assignment, reg_state) . map_err ( |found_vregs | {
328351 VerifierError :: BadUse {
329352 instr,
330353 op : i,
331- found_vreg ,
354+ found_vregs ,
332355 }
333356 } ) ?;
334357 }
@@ -341,7 +364,7 @@ fn verify_instr<M: MachineCore>(
341364 if !def_matches_constraint ( def_op, def_assignment) {
342365 return Err ( VerifierError :: DefConstraintViolation { instr, op : i } ) ;
343366 }
344- reg_state. insert ( def_assignment, def_op. reg ( ) ) ;
367+ reg_state. insert ( def_assignment, smallvec ! [ def_op. reg( ) ] ) ;
345368 }
346369
347370 Ok ( ( ) )
@@ -351,20 +374,25 @@ fn check_use_assignment(
351374 use_op : UseOperand ,
352375 use_assignment : OperandAssignment ,
353376 reg_state : & KnownRegState ,
354- ) -> Result < ( ) , Option < VirtReg > > {
377+ ) -> Result < ( ) , Option < Box < [ VirtReg ] > > > {
355378 check_assigned_vreg ( use_assignment, use_op. reg ( ) , reg_state)
356379}
357380
358381fn check_assigned_vreg (
359382 assignment : OperandAssignment ,
360383 expected_vreg : VirtReg ,
361384 reg_state : & KnownRegState ,
362- ) -> Result < ( ) , Option < VirtReg > > {
363- let found_vreg = reg_state. get ( & assignment) . copied ( ) ;
364- if found_vreg != Some ( expected_vreg) {
365- return Err ( found_vreg) ;
385+ ) -> Result < ( ) , Option < Box < [ VirtReg ] > > > {
386+ match reg_state. get ( & assignment) {
387+ Some ( found_vregs) => {
388+ if !found_vregs. contains ( & expected_vreg) {
389+ return Err ( Some ( found_vregs. as_slice ( ) . into ( ) ) ) ;
390+ }
391+
392+ Ok ( ( ) )
393+ }
394+ None => Err ( None ) ,
366395 }
367- Ok ( ( ) )
368396}
369397
370398fn use_matches_constraint (
@@ -415,10 +443,19 @@ fn merge_reg_state(
415443) -> bool {
416444 let mut changed = false ;
417445
418- existing_reg_state. retain ( |operand, known_reg| {
419- let retain = incoming_reg_state
420- . get ( operand)
421- . is_some_and ( |incoming| incoming == known_reg) ;
446+ existing_reg_state. retain ( |operand, known_regs| {
447+ let retain = match incoming_reg_state. get ( operand) {
448+ Some ( incoming) => {
449+ // This can end up quadratic in the worst case, but these lists will almost always
450+ // have 1 or 2 elements in practice. We also don't care that much about the
451+ // performance of the verifier as long as it isn't unbearably slow - it's just a
452+ // debugging aid.
453+ known_regs. retain ( |reg| incoming. contains ( reg) ) ;
454+ !known_regs. is_empty ( )
455+ }
456+ None => false ,
457+ } ;
458+
422459 if !retain {
423460 changed = true ;
424461 }
0 commit comments