@@ -426,10 +426,12 @@ class Component:
426
426
427
427
def write_body (self , out : Formatter , cache_adjust : int ) -> None :
428
428
with out .block ("" ):
429
+ input_names = {ieffect .name for _ , ieffect in self .input_mapping }
429
430
for var , ieffect in self .input_mapping :
430
431
out .declare (ieffect , var )
431
432
for _ , oeffect in self .output_mapping :
432
- out .declare (oeffect , None )
433
+ if oeffect .name not in input_names :
434
+ out .declare (oeffect , None )
433
435
434
436
self .instr .write_body (out , dedent = - 4 , cache_adjust = cache_adjust )
435
437
@@ -565,10 +567,10 @@ def analyze(self) -> None:
565
567
Raises SystemExit if there is an error.
566
568
"""
567
569
self .find_predictions ()
568
- self .map_families ()
569
- self .check_families ()
570
570
self .analyze_register_instrs ()
571
571
self .analyze_supers_and_macros ()
572
+ self .map_families ()
573
+ self .check_families ()
572
574
573
575
def find_predictions (self ) -> None :
574
576
"""Find the instructions that need PREDICTED() labels."""
@@ -587,11 +589,30 @@ def find_predictions(self) -> None:
587
589
)
588
590
589
591
def map_families (self ) -> None :
590
- """Make instruction names back to their family, if they have one."""
592
+ """Link instruction names back to their family, if they have one."""
591
593
for family in self .families .values ():
592
594
for member in family .members :
593
595
if member_instr := self .instrs .get (member ):
594
- member_instr .family = family
596
+ if member_instr .family not in (family , None ):
597
+ self .error (
598
+ f"Instruction { member } is a member of multiple families "
599
+ f"({ member_instr .family .name } , { family .name } )." ,
600
+ family
601
+ )
602
+ else :
603
+ member_instr .family = family
604
+ elif member_macro := self .macro_instrs .get (member ):
605
+ for part in member_macro .parts :
606
+ if isinstance (part , Component ):
607
+ if part .instr .family not in (family , None ):
608
+ self .error (
609
+ f"Component { part .instr .name } of macro { member } "
610
+ f"is a member of multiple families "
611
+ f"({ part .instr .family .name } , { family .name } )." ,
612
+ family
613
+ )
614
+ else :
615
+ part .instr .family = family
595
616
else :
596
617
self .error (
597
618
f"Unknown instruction { member !r} referenced in family { family .name !r} " ,
@@ -608,32 +629,50 @@ def check_families(self) -> None:
608
629
for family in self .families .values ():
609
630
if len (family .members ) < 2 :
610
631
self .error (f"Family { family .name !r} has insufficient members" , family )
611
- members = [member for member in family .members if member in self .instrs ]
632
+ members = [member for member in family .members if member in self .instrs or member in self . macro_instrs ]
612
633
if members != family .members :
613
634
unknown = set (family .members ) - set (members )
614
635
self .error (
615
636
f"Family { family .name !r} has unknown members: { unknown } " , family
616
637
)
617
638
if len (members ) < 2 :
618
639
continue
619
- head = self .instrs [members [0 ]]
620
- cache = head .cache_offset
621
- input = len (head .input_effects )
622
- output = len (head .output_effects )
640
+ expected_effects = self .effect_counts (members [0 ])
623
641
for member in members [1 :]:
624
- instr = self .instrs [member ]
625
- c = instr .cache_offset
626
- i = len (instr .input_effects )
627
- o = len (instr .output_effects )
628
- if (c , i , o ) != (cache , input , output ):
642
+ member_effects = self .effect_counts (member )
643
+ if member_effects != expected_effects :
629
644
self .error (
630
645
f"Family { family .name !r} has inconsistent "
631
- f"(cache, inputs, outputs ) effects:\n "
632
- f" { family .members [0 ]} = { ( cache , input , output ) } ; "
633
- f"{ member } = { ( c , i , o ) } " ,
646
+ f"(cache, input, output ) effects:\n "
647
+ f" { family .members [0 ]} = { expected_effects } ; "
648
+ f"{ member } = { member_effects } " ,
634
649
family ,
635
650
)
636
651
652
+ def effect_counts (self , name : str ) -> tuple [int , int , int ]:
653
+ if instr := self .instrs .get (name ):
654
+ cache = instr .cache_offset
655
+ input = len (instr .input_effects )
656
+ output = len (instr .output_effects )
657
+ elif macro := self .macro_instrs .get (name ):
658
+ cache , input , output = 0 , 0 , 0
659
+ for part in macro .parts :
660
+ if isinstance (part , Component ):
661
+ cache += part .instr .cache_offset
662
+ # A component may pop what the previous component pushed,
663
+ # so we offset the input/output counts by that.
664
+ delta_i = len (part .instr .input_effects )
665
+ delta_o = len (part .instr .output_effects )
666
+ offset = min (delta_i , output )
667
+ input += delta_i - offset
668
+ output += delta_o - offset
669
+ else :
670
+ assert isinstance (part , parser .CacheEffect ), part
671
+ cache += part .size
672
+ else :
673
+ assert False , f"Unknown instruction { name !r} "
674
+ return cache , input , output
675
+
637
676
def analyze_register_instrs (self ) -> None :
638
677
for instr in self .instrs .values ():
639
678
if instr .register :
0 commit comments