/

'>main Soul of a tiny new machine. More thorough tests → More comprehensible and rewrite-friendly software → More resilient society.Kartik K. Agaram <vc@akkartik.com>
about summary refs log blame commit diff stats
path: root/apps/mu.subx
blob: c801e329528622427f7c797cebccac534a30b558 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                     


                                     
                                                          
 


                                                                              

                                                                         
                                                                         




                                                        

                         
                                                           







                                                                             


                                                                             
                           
                                                        
                                                                 
 


                                                                           



                                                                            

                                                                            
 
                                                                           

















                                                                            

                                
                                                                      
                                                                                        

                                                
                                       


                                                                              

                                       





                                                                            
 



                                                                              


                                           
                               
                                                                             

                           
                                  


                           
                        
                               
                                 


                       
                                       

                                             


                     
                                    
                                                    


                                 


                                    


                                             
                               
                                 


                                              
                               
                                 
                              
 
                                    
                                                                         

                                                                            





                                                                                   
                                                                     







                                                                                       
               

                            
                               
                                 
                
                                       
                                   

                                       
                                                                   

                                                                            
 









                                                                                
                               


                                         
                                    
 





























                                                                                  
                               
                                             
                                                                   
                                    


                                   
                                    
                                   




                          
 
                        











                                                                    


                              

       


                                        
                            
         
                                    
         

                        
 











                                                                              
         
                                     
         
                                      
            

                                
                                   
            
                            
               
 
                                      
         
                                      
         

                                       
                                           
            
                                         
            
                                        
            
                                          
            
                                                                 
            
                                          
            
                                    
            
                             
               
 
                

         
                                  
         
                          
           
 
                                       
         
                                  
           

                                   
 
                           
         
 
                                           
         
                                      
           

                                                                        
 
                        
            
 
                                
         
                                  
         

                                                                   
                                                              
            
                                                          
            
                       
            
 


                                    




             
                         
         
                             
         

                        
 



                                                                    
         

                             
                            
            
 
                                                                  
                                                                        

                                                                                 
 
                        
         

                      
         



                                  
           

                        
            
 

       
                                                
                                      
                  
              
                  





                      
                      
                                                                                  
                                                                                     









                                                                 
                     
                                                                 


                                                             
                                                                              
         




                                                      
                                    
           
                                   
            

                            
 


                                                                          
            










                                                                                                   
         
                                          
           

                                  
 




                        
                                 



                                        
                               
                                    
                                                      
                                      
                              














                                         
                                                                  


                        



                                                       
                                                        
                                           
                                                    

                       
                            














                                     
                                                     
                                      
                                                      









                                                                              




                                     
                                                     
                                      
                                                      












                                                                     







                                                                                                                       





                                         




                                     
                                                     
                                      
                                                      














                                                                     







                                                                                                                                 
                           







                                                                                                                                  




                        
                               




                                     
                                                     
                                      
                                                      
     
                                                  










                                                                     







                                                                                                                       





                                        




                                     
                                                     
                                      
                                                      
     
                                                  











                                                                     




                                                                                                                                







                                                                                                                                                    




                        
                                         





















                                                                     




                                                                                                                                 







                                                                                                                                                     




                        
                                     








                                                      
                                                                             












                                                                     




                                                                                                                             
                                                                                                                             
                                                                                                                                                 






                                                                                                                              




                        
                                       






















                                                                             





                                                                                                                               
                                                                                                                                                   






                                                                                                                                  




                        
                                         






















                                                                             





                                                                                                                                 
                                                                                                                                                     






                                                                                                                                          




                        
                                            

























                                                                                




                                                                                                                                    













                                                                                                                                       
                                                                                                                                                         






                                                                                                                                                          




                        
                                            









                                                      

                                                










                                                                     




                                                                                                                                    









                                                                                                                                                        




                        











































                                                                                                                                                                              
                                            









                                                      

                                                             










                                                                     




                                                                                                                                    










                                                                                                                                        




                        








                                                        





































                                                                                                                                                    








                                                      



































                                                                                                                                                 








                                                      



































                                                                                                                                                
                                              
























                                                                     




                                                                                                                                      













                                                                                                                                                            




                        
                                                    









                                                      
                                            













                                                                     




                                                                                                                                            













                                                                                                                                                                  




                        








                                                      










































                                                                                                                                              


















































                                                                                                                                           






















































                                                                                                                      





















































                                                                                                                                                          





















                                                                                                                           
                                                                                                                              










                                                                                                                        
                                             


























                                                                     




                                                                                                                                     














                                                                                                                                                            




                        
                                                   


























                                                                     




                                                                                                                                           














                                                                                                                                                                  




                        
                                               


























                                                                     




                                                                                                                                       

















                                                                                                                                                                





                                                          



























                                                                     

























                                                                                                                                                                            




                        
                                                   


                                                                           






























                                                                                                                                           




                                                                                                                                           













                                                                                                                                                                  




                        



                                                                              







                                                      

















                                                                     








































                                                                                                                                                                           





































                                                                                                                                                                          




                        








                                                             
















































                                                                                                                                                                            








                                                                      


























































                                                                                                                                                                                       








                                                                       




























































                                                                                                                                                                                      


























































                                                                                                                                                                             



















































                                                                                                                                                                                     













































                                                                                                                                                            













































                                                                                                                                           








                                                      





















                                                                                                                       

                                                                                                                                         











                                                                                                                       

































                                                                                                                                          
                                                                                                                                                  












                                                                                                                                                 








                                                      















                                                                            









                                                                                                                                          
                                                                                                                                                                







                                                                                                                                           




                        








                                                      






















                                                                                                                                                       
                                                                                                                                  












                                                                                                                                                                   











































































































                                                                                                                                                                                  








                                                      


























                                                                                                                                                       
                                                                                                                                                                  













                                                                                                                                                                 








                                                      


























                                                                                                                                                                
                                                                                                                                                                                        














                                                                                                                                                                          
                                          







                                                      



















                                                                       






                                                                                                                                    
                                                                                                                                                      

                                                                                                                                                                  
                                                                                                                                                                   







                                                                                                                                   




                        








                                                            





































                                                                                                                                                                      





























































                                                                                                                                                                        





























































                                                                                                                                                                        
































































                                                                                                                                                                                            
                                                                                                                                                                      












                                                                                                                                                                      























































                                                                                                                                                        








                                                      





























                                                                                                                                                     
                                                                                                                                                                           












                                                                                                                                                      



                                                       
                                     
                

                                                                      

                                   



                                                                 
                                          



                                                                 
                                                
                                                                    
                                              
                                                                 

                                                             

                                               





                                                                              

                   







                        
               
               
               
                                     
                                        
                            


                         
                               


                         
                                                   
                                           


                                            

                        





                                        
                                  
                        
                                



                              
                               
                                            
                                   
                                      
                                  






                                               
                                 


                                                            
                                          
                                        
                                    





                                                            

                                                    
                          

                                                      
                                       
                            
                            

                                
                                             





                                                               
         

                                          


                                                                                     
                                            




                                                    








                                             

                                                           



                                   

                                          
                       
                                     


                      
                                   

                         
                 
                 







                        
                 

                                                             
                                      







                                












                                                                                






                        

                     

                                   
                                                                                                                     
                 
                       
                                       




                                         
                                          


                                    
                                                                     
                                     
                                          
                                              
                       


                  
                                          
                                          
                                                                     
                                     
                                                

             





                        

               


                              
                               


                         



                             
                        
                                 
                    
                                                       

                                      
                                    
                                                               

                                       
                                    
                                                               

                                      
                                    
                                                               
                        
                                                     
                          
     
                                             
                                   
                                        
                                        
                                      
                                                               

                                         
                                      
                                   

                                        
                                      
                                                                 

                                                       
                                   


                                                            
                                                                 
                                      
       


                                                                                                      
                     

                                 
       

                         
                           
     

                                           

                                        
                                      
                                   

                                         
                                      
                                                                 

                                        
                                      
                                                                 

                                                       
                                     


                                                            
                                                                
                                              
                                                               

                                                                                                            

                         
                                  
                                   

                                 
                                  

                         

                 






                        
                                    
                                                        

                                                                                                  









                                 














                                                               



                                                                       










                                 
                              




                                     
                                               
                              
                                        

                                  


                                           

                        
             
                                                              














                                                                                                    





                                        





                                                              
                          
                                        

                                  


                                           

                        
             
                                                              





                                                                                       
                                                










                                                                                                              
                                                













                                                                                                              
                                                













                                                                                                              




                        
                                                    




                                     
                                                                                        
                          
                                        

                                  


                                           

                        
             
                                                              



















































































                                                                                                                           




                        
                                 
          
           



                                    
                                                                                                  
                 
                    


                                        
                                                             
                                  
                   
                                                        
                             
                                            
               
                                                                
                        




                        
               




               










                                                      
                      


                         
                                
                                                                                  
                                


                                              
                                   
                                                   



                                    
                                                                                 
                                                                
     
                                    


                                      


                                                           
     
                               

                                                  
                         

                               





                         
                 





                           
                                                                











                                                                    
                                                                                                        






                                        

                            
                   
                                                   

                                                      
                

                                            
     



                        
               

               

                                       










                                         
                                    
                                    
                                             
                     
                                     
                                    
                                             
                     
                                     
                                    
                                             
                      
                                      
                                    
                                             
                          
                                     
                                    
                                           
                    




                                                          
     
                           
                                       
                                      
                               

                                                                         
                          


                                        
                
                                                 

                                              
       
                 
                                 
                                                       
                                                    
                                                  
                                            
                                    
     
                     
                        





                                                      


                               
                         

                 
                 




                        











                                                                   
                                                                                                             
                 


                                                           


                                            




                        
               
               
               









                                         
                                           

                                              
                                                  

                                    
                        







                                                      
                     

                               

                         
                 
                 




                        


                                                           
                                          




                                                         



                                                             
                                                              












                                                              

                                        
                 



                              
                             
                 
                             
                 
                             
                 
                    


                                      



                        
               
               






                              
                                         




                                                    
                               





                                              
                                                  
                              

                                        













                                         
                  






























                                               
     
                                             



                                        
                                 

                                            
                                



                                          





                                                  





                                               


                                         

                                   








                                            
                         

                 
                 
                 




                        
                                                                                               





                                             
                                                
                            






                                          


                                             

                               




                        
                                                                       
                                             
                                                                                     







                        



                                               









                                                                    



                                           

                                  

                                            

                                              
                                      
                               



                                 

                        
     
                  

                        


                                         









                         








                                          
                                       




                                       
                                    



                             
     











                                                                                       








                                      

                                 


                                          
                                       


                        
                                       
                                     
                                    



                             
     














                                                                                                    




                        



                                        

                             


                                          
                                       





                                        



                             
     













                                                                                                           




                        








                                                     
                                       





                                        



                             
     














                                                                                                                   




                        








                                          
                                       





                                              



                             
     












                                                                                                     

                                                                                            
                              
                                                            

                                                                                              
                                    

                                                                   

                                                                                              
                                
                                                                                                




                        


                                                              
                                                          




                                        
                                    
                                             
                                  





                                   
                                           

                                   
                                           



                                   
                                            

                                   
                                            




















                                          
                                       



















                                                             
                                       



















                                                                 
                                       



















                                                        
                                       



















                                                        
                                       



















                                                        
                                       










                                                        
                      
                                     







                                          
                                       










                                                        









                                          
                                       




















                                                        
                                       




















                                                               
                                       



















                                                                       
                                       




















                                                                        
                                       










                                                             
                                                                                                             





                        
               

                            

                              


                                                          

                               
                 







                                                                                   
                                                                                                                           
                 

                                   
                                      
                                                 
                               

                                        



                                                                 
                                          




                                                                 
                                      
                                                
                                            


                                                   
                                                  
                                         
                                                                        
                                                  
                                                 
                                                  
                                              
              
                                                
                                           
                 
     



                        
               


               
               
                                     
                                        
                            


                         
                               


                         

                                          
                                                   

                                    
                        




                                                     

                                              
     
                          


                                        



                                         

                                     
                                  

                                        



                                           
                                            
                         
                                      
                                  






                                                        
                                 
                                        
       

                                
                                        
                                
                                   
                                    









                                                             
                                                
       


                                              
                                      
                                   


                                                                         
                                      
                                
                        


                                      
                                           
                                     
                                              


                                                  








                                                                        
         





                                                
                                        
                                








                                                  
         



                                                









                                                      
                         
                     

                              
                              
                    


                                   
                 


                 
                 




                        
                      











                                                                             
                                                              



                        
               

               
                                                                 

                                         
                                       
                                              

                                   
                                   












                                                                






                                                                                 
                            



                        

                                      



                                                          
                            


                         
                 




                        







                                                               
                                                 





                        
                      



                         
                                 

                               
                                    
                                                 






                                           
                                                





















                                                                             
                                                                                                                                                    
                 

                            
                     
                                         
                 

                        




                        
               
               
               
                             




                                    
                   




                                                                   
                               











                                                  
                          

                               
                         
                 
                 
                 




                        
                                                                                                       



                        
               

               


                               
                               


                         




                                                  
                                 
                                            
     

                              
                                                               

                                               

                              
                               
                                                           
                                             



                                                       
                              
                                         
                                   
                                        
                                      
                                                
       

                                                 

                                                                 

                      
                                  
                         
                 

                 
                 


























                                                                                                    
                                

                   
                        


                                         



                        
                                                   
                                   

                                    
     

                                                                                    
                                                              

                        
                                                      



                                                                                             
                                                            

                                                                                      
                                                                                        












                                                                                        
                                

                   
                        


                                         



                        


















                                                                                                

                                                                                    




                                                                                               




                        
                                                                                                                         
                 
                       


                                                

                                  
                                      

                                        
                                                                                                                     

                                                              




                        
               
               
               
               
               
                         


                         
                                        
                                








                                                  
                        
                            
                                                  

                                  
                                      
                                  

                            

                                     
                                           
                                     
                                        
                                     
                                 
                                          
                                        












                                                                            
                                      
                                       
                                        
                                                   

                                                               
                                                              

                                                                                                   


                           
                                                               

                      
                                  
                         
                 
                 
                 
                 
                 
















                                                  
                                                                                                                 
                 
                                                         
                  
                                    
                                         
                                                




                        
               
               
               
               
               


                            
                         


                         

                                        



                             
                                                 
                                   

                                                                                                                      


                                                

                                              

                                     

                                         
                                      


                                   
                                      
                                                                     














                                                                               








                                                                          
                                                 


                                   
                                                   
                                           


                                                                                                                   



                                      
                                  

                         
                 
                 
                 
                 
                 





                                        


                                                  

                             







                                 
                                                                     




                        
                               






                                
                                   

                                         
                                      
                                                                                            
                                   







                                                                                            
                                  

                                                     
                                      
                             












                                 

                                                                                    
                                                                                                     



                        
               





                                 
                                    
                                                        
                                    


                                  
                                                  
     
                                        
                                      
                              
                                                 
                                              
     
                                             
     
                                               
                               
                                         



                                              





                                            
                 













                                             
                                                                          
                                                                                          


                        

                      
     



                                                       
                                        
                

                         















                                                
                                                                            
                                                                                                 
                 
                                                                

                            
                                     
                            

                   
     



                        
               
               


               

                                       



                              
                                       
                                   
                                                    
                                                 
                                      

                                                                  
     
                              
                               
                                      
                                             
                                      




                                                        
                                      











                                            

                                      

                         
                       
                         


                 
                 
                 




                        
                          









                                                               
                                                                                                     
                                                                                                                         



                        
               
     
                                                       
     


                                     
                               

                                                 

                                                                   
                         
                                                



                          
                 




                        
                                                                                            



                        
               
               
                                                         
                            

                                              


                                     
                              





                                                            
                                      













                                                   
                         

                                                                
       





                              
                 




                        
                   





                                              


                                         


                        


                             
                        



                                    
                      








                                     
             

















                                                                                           




                        
                              





                                               


                                         


                        


                             
                        






                                     
     





                                
             

















                                                                                                    




                        
                                                                                               



                        
               
               
               
                               
     


                                      
                           



                                        


                         
                 




                        
                                                                                                    



                        
               


                                      
                                    
                                                     

                                                        
                                                 

                                    






                                                            
                         

                               

                         
                 















                                                                  
                                                                                            



                        
               
               


                                    
                        


                                          
                                                




                                                  
                                                            
                                                   
                                                     
                 

                               

                         
                 




                        
                                                                                                   




                        







                                                
                        

                               






                         
                                                                                            



                        
               

               




                                                  
                                                             
                       
                              
                                          

                                          
                 

                         
                 




                        
                                                                                                



                        






                                                  
             
                                                                
                

                                                                
                     
                         
                 




                        
                                                                                                                                 



                        
               
               


                               
                     
                                       


                                                        
                             





                                          

                                       
                            

                                                    

                                                  
                                                      
                              
                                 

                                                                 

                        

                               

                        
                                         
                            
                                           




                               
                 
                         
                 
                 
                 




                        
                                                                                                                                               



                        
               
               

















                                                      
                                           
                      

                                                  

                                                  
                                                          
                              


                                                                         

                        









                                               
                     
                         
                 
                 
                 




                        
                                                                                           



                        
               


                              
                                            
                                                     
                                                                                                                  

                         
                 
                 




                        





                                                                           
                                                                                                          



                        
               


                                       
                        






                                                                              
                                                                       
                                                                    
                               

                               

                         
                 




                        



                                            
                                                                    






                                          
     
                                                       
                              

                                            
     
                                          





                        
                                                                   



                        
               
               









                                                                                
     

                                     
                                   
                                















                                                           
                                     







                                                  


                             

                 
                 
                 




                        
                                                         



                        
               
               

               

                            


                              
                                   


                                            
     

                                     
                              
                                                   

                                      
                                              





                                                  




                         

                 
                 
                 




                        
                                                                                                  



                        
               
               
               

                                                                                    

                   





                                                             

                                            
                                                                      
                                   









                                                      
                            
                                 




                                                              
                                                     

                                      
                                       
                                                    






                                                             

                                                                      
     
                                        

                               
                         
                 
                 
                 




                        
                                                                                                         



                        
               
               
               


                                               
                                                                                   
                                                                                  
                        
                                       
     
                                     
                               

                                                       
     


                               
                        


                            
                                    
                         
                 
                 
                 




                        
                                                               

                                   
                      










                                                                     
                                                                                                 
                                            

                               


                                       
                                           
                                                       



                        

                                     










                                        
                            






                               














                                                    
                                   





                                              
                                


                                                                             
                                         
                                          
                                                          
                          










                                                      
                                 
                        
                                                      
       
                                     
                                                                             
                                                                        
                                 
                                         
                                                             
       

                         




                                                                                   









                                   

                               


















































                                                                                             
                                                       



















                                                                         
                              


                                                 












                                                       











                                                                
                                                     






































































                                                                                       
                                                         


                        

                      
                                                      
                            


                                                       
     

                                                           

                                                              
     

                                                           
                         

                         
































                                                                                           


                                                              
                                                



                        
               
               





                                  
                                                                                        
                            


                                                                        
                                        
                                               





                               
                                        


                                                            


                                                                                                   
                                            


                                                                                        


















                              
                                                                                                                                                   










                            
                                                                                        







                                                                     

                                            
                                     





                                                              

                        

                          
                                      
                         



                 





                        










                                                                
                                                       


                                                       




                        
                    




                        
                                            


                        

                      
                                                      
                            

                                                            







                                                
                                           
     

                                                           

                                                              
     

                                                   







                         
                                                  




                        
                                                      
                            

                                                            
                                


                                                                   







                                                
                                           
     

                                                           

                                                              
     

                                                   







                         
                                                              




                        
             
                            
                    
                                








                                                                   
                      







                         
                                                           








                                          
                                          
                                                  

                                          
                                                            


                                          


                                            
                                           








                         





                                                 
                                 
                              
                                                                                   

                                                   
                                  


                                
                              
                                                              
                                        

                                    
                          
                     




                        
                                                                                     























                                                      
                                                                  


                                            
                                                                    








                         



                                                       



                               

                                      


       
                                       








                            


                                                                       
     
                               
                                     
                                  
                                    
                         
                                                  











                         
                                                                    


                        

                                                   


                      
               




                              
                                          

                                        

                        


                               
                                  
                                             
                                                    
     
                             
                                                            
                             
                                                                             
                     
                        

                                 

                         
                 






                        
                                                         























                                                 
                              















                                           
                                                                                                           





                        
               
               


                              

                                              






                                        
       

                                                         
                                 

                                                   

       

                                                         
                                     
                           
         
                                        
                                          
                                      
                                 


                                               
                                              



                                                                     
                                            
                                        
                                        
                                                               
                                                         

                                           
                                                 
                                                                                    


                                                             
                                                                                                         
             
                                               

                                          
                                                                                             
                                                         
             

               

                                    
                                                                      






                                                                                                            

                                                                                           
                                                       

               
                                                            
                                                             

                                         
                                                 
                                                                 
                                             






                                                    
                                                                     

                                                                    
                                       
                                                  
                                            



                                                                                  
                                                                                             



                                                                                 



                                                    


                                                        
               
                                                            

                                        
                                                    





                                                    
                                                                                           



                                                    


                                                        
               
         
                            
                                                 
                                                    

       

                                                           
                                 
                             

                                                 

                                   
                                                   

       

                                                               
                                     
                                 
                                                      
                                                                    
                                       
                                                 

                                   
                                                   
       
                              
                                                     


                                           
                                  
                                                                          
                             



                                                   
                 







                        
                                                                                                                         






                              







                                                                                  

                                        



                                                                  
                                                      
                                                            
                                    
                                                             
                                        

                                                        


                                                 

                                                                        
                                     
                                      






                         
                                        










                                                                                            











                                                                                                                                      
                                          



























                                                                                     









                                                                   
                                    










                                                                    


























                                                                                                        
                 























                                                      
                                                                                                                                            











                              
                                                 
                               
                                                                  

















                                                          
                                     








                                              
                                     




                                                  


                                             

















                                                           

                                                                              
                                                                                                                    











                              
                                                 
                               
                                                                  



                                        
     
                                    







                                                      
                             
                                                            
       



                                                        
                                                                        

                                          
                                                       






                                                             
       
                                   

                                 
                                                    

                                



                                    
                                                


                                                        
                     
       
                                        

                                  
                         
     

























                                                                                                                                   
                                                 
                               
                                                                  










                                                
                                      




                                                            





                                                                        
                                                        






                                                              














                                                        
                                         




                                      
                         
                 
                 
                 
                 




                        

                                                                                           
                                                                                                     





                        
               





                              
                                                 
                               
                                                                  

                                        


                                               

                                                       
                                               






                                          



                                                         


                                      
                                       



















                                                      
                 










                                                                                       
                                                                                                                                 














                                                    
                                                 



                                                                
                                             







                                      




                                                      


                                                                



                                                             
                                        

















                                      
                                                         
















                                                                           
                                           



                                                      
                                                            




                                                            
                                                    












                         
 
                                                                  



                        

               
               

                              
                             
                                          


                                               


                                         
                                   
                                                       
                                          






                                                
                                                  










                                                     

                 
                                     
                               
                                              
                                                   
              
                                      




                         
                 






                        
                                                                                             


                        


                      
                                                                                            

                              
                




                                                                 
                                                    
                                        
     
                      




                                                                
                                                   
                                        
     








                                                                         
                           




                                                              
                                                 

                                        
                                            
     
                                    
                                                                                      
                                
                              
                          
                                                                       
                                        
     

                             
                     
                                   
                    
                         






                        
                                                                                   
                                                                         









                                            
                                       
                                            

                                                                
                                                  














                                                              



                                               
                                                                      










                                        
                                                                        



                        














































                                                                                                                                           
                                                                                               



                        







                                                                  
                                                          








                                                         
                                             


                                             
                        


                                                          

                                                                    



                                                             


                                                                                

                                                             
                                              




                                              
                                                                                               
       
                                               
                                                              

                                                                        
                                      
                                                                 


                                      

                                                                           

                                                             
                                                                         
                                        
                                                                                
     
                                     
                                                      


                                    

                                                                   

                                             
                                                         








                                                       
                                                                                

                     

                                                             
                       
                              

                                               
                                                                      

                                        
                                                    
                         

                 






                        
                                                                                            

















































                                                                                            
                                                              

                                                                        
                                      
                                                                 
























                                                                             
                                               














                                                                             
                                                                      











                                                 
 
                                                                                









                                            
                                           



                                                              



                                                              

                                          
                                        


                                          
                                       



                                               
                                                                      












                                        
                                                                      






                        

                                            
                                                

                              



                                                  

                                            

















                                                           
                                               




                                                      


                                               
                                                                      



                                        







                        
                                                              







                                        
                                          

                                                    
                                          
                                                      

                                          





                           


























                                                   
                                                      


                        
                                                                  
                            



                                                            

                                            
                     




                        
                                                                                                   








                              



                                                 

                                                           

     
                             
                                
                                  
                                              
                                     
                               
                                           
       
                                                
                                          
                                            
                             
                                                     
                                 

                                              
                                     
                                                
                                           
     
                     








                         
                      
                                                                                  

           
                       
                   
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                

                                 
                                              






                                       
                     
                                
                                 
                   

                                                     
                                      




                                          
                     
                                
                                 



                                                      
                                                 



                                          
                     
                                



                                                     
                                      




                                          
                     
                                









                                                      
                     
                                


                                    
                                                   






                                       
                     
                                

                                        
                                                              


                                                 
                               


                                
                     
                                

                                        
                                                     
                       
                                           
                   
                               


                               
                     
                                

                                        
                                                       
                    
                                      
                                                 
                            


                               
                     
                                









                                                               
                     
                                


                                                            
                       




                                    
                                 
                     
                                


                                           
                                                               






                                          
                     
                                

                                               
                                                                          


                                                 
                                      


                                
                     
                                

                                               
                                                                   
                              
                                           
                   
                                      


                               
                     
                                



                                                                 
                                      




                                                 
                     
                                









                                                                         
                     
                                









                                                                        
                     
                                










                                                     
                     
                                

                                          
                                                                


                                                 
                                 


                                
                     
                                

                                          
                                                         
                         
                                           
                   
                                 


                               
                     
                                



                                                       
                                      




                                                 
                     
                                









                                                               
                     
                                









                                                              
                     
                                










                                                   
                     
                                

                                         
                                                              


                                                 
                                


                                
                     
                                

                                         
                                                       
                        
                                           
                   
                                


                               
                     
                                



                                                     
                                      




                                                 
                     
                                





                                                             
                                   


                                
                     
                                





                                                            
                                   

                               
                                 
                     
                                










                                                     
                     
                                

                                          
                                                                


                                                 
                                 


                                
                     
                                

                                          
                                                         
                         
                                           
                   
                                 


                               
                     
                                



                                                       
                                      




                                                 
                     
                                





                                                               
                                    


                                
                     
                                





                                                              
                                    


                                
                     
                                










                                                     
                     










                                                     
                     










                                                     
                     










                                                     
                     










                                                     
                     










                                                     
                     


                                         
                                                           


                                                 
                           


                                
                     


                                         
                                                  
                        
                                           
                   
                           


                               
                     


                                         
                                                       
                     
                                      
                                                 
                           


                               
                     










                                                                 
                     










                                                              
                     
                                












                                                                    


                                              
                                                            


                                           
                                  






                                              
                                                              


                                           
                                  
















                                                       













                                                           









                                                             




                                                                    
                                      






                                                 
                                        
            







                                                      
                     









                                                       
                     









                                                  
                     









                                                   
                     









                                                       
                     









                                                      
                     









                                                  
                     









                                                   
                     









                                                   
                     









                                                  
                     
                     










                                          








                                                     
                     









                                                      
                     









                                                 
                     









                                                  
                     









                                                      
                     









                                                     
                     









                                                 
                     









                                                  
                     









                                                  
                     









                                                 
                     









                                                                               
                     
                     














































































































                                               










                                     
























































































































                                                                                     

                

                        

                
               
                     
                  



                             
                           
                        
                                              
 

                                  
                                    




                             
                    
                        

                             



                                  
                                


                                  



                                   

                         
                  
                            
                           
                      
 






                            





                        
                  









                            
                  









                            
                  









                            
                  









                            
                  









                            
                  



                            





                        
                      



                            
         
                        



                      
                        


                        
          
                        


                      
       
                                                                                                





                        
                     
                               
                         
                                            


                                                                                      

                                                                                    

                                                                                        

                                                                                          
                                     








                         
                                                                                






                                         
                                          

                                                                         
                                         







                         
                                                                                                     








                              
                                     
     
                                
                               
                                      
                                                                  
                                                           
     
                                           

                                
                               
                                      
                                                                  
                                               




                                                           
                               
                                      
                                                 















                                                            
                                   






                                
                                                                               





                        
                               
                                         
                                             


                                                                         
                                                                                       


                                        








                         
                                                                                 





                        

                                         
                                           


                                                                         


                                              
                     







                         
                                                                                  








                                                


                                                                         







                                                                       
                                      





                                                                     
                                      












                                       
                                                          












                                                         
                                                    





                                            
                                            
                         
                                               



                                   
               

                         




                        
                                                                        
                                            




                        
               
               



                                              
                                                        
     
                                                  


                                                                    


                                        


                                                           
                                                            
     
                                                    
                                                            
                              
                                                                    

                                          
                                                              



                                                      
                                                         
                              
                              
                                                  

                                                

                                                  
                                          
                                                      
                               

                                     
                                    


                            
                 
                 





                        
                                                                                     








                              


                                          




























                                                           
                                                                         













                                               
                            























                                          
                                                                       




                        
               
               





                                                       
                                                   








                                                                    
     
                                                 
                                                            
                              


                                                                    
                                    
                                                           


                                                      
                                                         
                              
                             

                                        
                                                              

                                   
                           
                         
                 
                 





                        
                                                                                                           



                        
               
               
                                                 

                            
                              

                                     
                                  


                                                              
                                        
                                
                            
                                                  
       
                                        
                         


                                                                              
                         





                             
                 




                        
                                                                                                    




                                                                            




                        




                
                            




                                                            
                                                                             
                                      
                               



                                                    
                                                                    
                                                                

                                                
                                                             

                                       
                                 
                                               

                                         
                                   

                                                                  

                      
                                    




                                                      
                                 

                                               



                                                      
                                                                   
                                        
                                 
                                    


                                                      
                                               
                         
                                               
                        

                                          
                                                                      
                                               



                                                 
                                         
                                       
                                 

                                         
                                   










                                                        
                                 




                                                      
                                                                   
                                        
                                 



                                                      
                                               
                         
                                               
                        


                                        

                                



                 





                        
                                                                                              



                        
               

               



                                           
                    
                              
                                       
                                                   
                                                                     
                                    
                                
                                                          
     



                                                                              
                                     

                                                              
                                                                      


                                                              









                                                                                   









                                                                                                            
                                                                    






                                                                                   
                                                                   
       
     
                                        
                               


                                                 
                                


                         
                 




                        
                                                                                          




                        
                                                 
                                             

                                                 
                                               

                             
                           
                                  
                      
                         





                        
                                                                                


                        




                              
                            
                                               
                           
                                  


                         




                        
                              
                                                     



                                     
                                                     

                   
                        
     
                                         
                         
                       

                                     
                                   





                                      
                                                      
                                               


                              
                          

                                 
                               
               

                        

                                        

                        
                        
                             

                           
                        
                                       
                   
                        
                                   
                        
                                        
                             



                                                  
                           
                                                                                                      


                                  
                                             
                                                         







                                           
                                                                                                                                   




                        
                                       











                                                                    
                    








                                      
                                                      
                                               


                              
                                 


                                   
               

                        

                                        

                        
                        
                             
                        
                       

                                       
                   
                        
                                             


                                   
                                        

                         

                                        


                          
                                   
                        
                                        
                             








                                                  
                                             
                                                         







                                           
                                                                                                                               




                        
                                     
























                                                                    
                                                      
                                               


                              
                                 


                                   
               

                        

                                             


                        
                             



                                       
                   
                        
                                             


                                   
                                        

                         

                                               


                          
                                   
                        
                                        
                             







                                                  
                                   
                    
                                        
                             








                                                                                                                   
                                             
                                                         







                                           
                                                                                                                             




                        
                                       
























                                                                    
                                                      
                                               


                              
                                 


                                   
               

                        

                                       


                        
                             



                                       
                   
                        
                                             


                                   
                                        

                         

                                        


                          
                                   
                        
                                        
                             







                                                  
                                   
                    
                                        
                             








                                                                                                                   
                                             
                                                         







                                           
                                                                                                                               




                        
                        
                                                  

                        
                        














                                                     
                                                      
                                               

                              
                           
                        
                                 


                                   
               

                        

                                             


                        
                             



                                       
                                

                        
                                             
                                                               







                                           
                                                                                                       




                        
                   


















                                                                    
                                                      
                                               

                              
                           
                        
                                 


                                   
               

                        

                                       


                        
                             
                        

                          
                                       
                   

                        
                                             
                                                               







                                           
                                                                                                  







                                
                            





                                      
                                                      
                                               

                              
                           
                        
                                  


                                   
               

                        
                                  


                                   
                                        

                        

                                                


                        

                                                       


                        
                             



                                 
                   

                        
                                             
                                                               







                                           
                                                                                                                




                        


                            
                                





                                      
                                                      
                                               

                              
                           
                        
                           


                                
               

                        
                                  


                                   
                                        

                        

                                                


                        
                                                   


                        
                             






                                    
                                             
                                                               







                                           
                                                                                                                             







                            
                             





                                      
                                                      
                                               

                              
                           
                        
                                  


                                   
               

                        
                           


                                
                                        

                        

                                                


                        

                                                  


                        
                             






                                 
                                             
                                                               







                                           
                                                                                                                          




                        
                        

                            
                                





                                      
                                                      
                                               

                              
                           
                        
                                  


                                   
               

                        
                                                   


                                
                                   


                                   
               

                        

                                                


                        

                                                 


                        
                             



                                 
                   

                        
                                             
                                                               







                                           
















                                                                                                               
                                               

                              
                           
                        
                                  


                                   
               

                        
                                                   


                                
                                   


                                   
               

                        

                                                


                        

                                                 


                        
                             






                                 
                                             
                                                               








                                                                                                                     




                        









                                      
                                                      
                                               

                              
                           
                        
                           


                                
               

                        
                                                   


                                
                                   


                                   
               

                        

                                                


                        

                                                   


                        
                             






                                    
                                             
                                                               













                                                                                                                                  













                                                      
                           














                                        

                                                


                        

                                  










                                     
                                             
                                                               







                                           
                                                                                                                                      







                              
                                         









                                                      
                           














                                        

                                                


                        

                                  










                                     
                                             
                                                               







                                           
                                                                                                                                      


















                                                      
                           


















                                                   

                                                


                        

                                  










                                     
                                             
                                                               



























                                                                                                                                            
                           


















                                                   

                                                


                        

                                  










                                     
                                             
                                                               



























                                                                                                                           
                           


















                                                   

                                                


                        

                                  










                                     
                                             
                                                               













                                                                                                                               
                                  
                                                 
             
        
                    

                                                                              

                                                     
                   
                 
                        
     

                                    
                                                            





                                      
                                                      
                                               

                              
                           
                        
                          

                                 
                               
               
                       
                        

                                       


                        
                             

                           
                      
                               
                   
                        
             
                                             
                                                      
                                      





                                           
                  
                                                                                                                      




                        
                                                   









                                      
                                                      
                                                   


                                
                                  


                                   
               

                        

                                       


                        
                             



                               
                   
                        
             
                                             
                                                           







                                           
                                                                                                                        




                        























                                                 
                                                



                        


                                                        
                        




                        
                                                



                        



                                                        
                        



                        
# The Mu computer's level-2 language, also called Mu.
# http://akkartik.name/post/mu-2019-2
#
# To run:
#   $ ./translate_subx init.linux [0-9]*.subx apps/mu.subx

# == Goals
# 1. Be memory safe. It should be impossible to corrupt the heap, or to create
# a bad pointer. (Requires strong type safety.)
# 2. Do as little as possible to achieve goal 1. The translator should be
# implementable in machine code.
#   - minimize impedance mismatch between source language and SubX target
#     (e.g. programmer manages registers manually)
#   - checks over syntax
#     (e.g. programmer's register allocation is checked)
#   - runtime checks to avoid complex static analysis
#     (e.g. array indexing always checks bounds)

# == Language description
# A program is a sequence of function and type definitions.
#
# Function example:
#   fn foo n: int -> result/eax: int {
#     ...
#   }
#
# Functions consist of a name, optional inputs, optional outputs and a block.
#
# Function inputs and outputs are variables. All variables have a type and
# storage specifier. They can be placed either in memory (on the stack) or in
# one of 6 named registers.
#   eax ecx edx ebx esi edi
# Variables in registers must be primitive 32-bit types.
# Variables not explicitly placed in a register are on the stack.
#
# Function inputs are always passed in memory (on the stack), while outputs
# are always returned in registers.
#
# Blocks mostly consist of statements.
#
# Statements mostly consist of a name, optional inputs and optional outputs.
#
# Statement inputs are variables or literals. Variables need to specify type
# (and storage) the first time they're mentioned but not later.
#
# Statement outputs, like function outputs, must be variables in registers.
#
# Statement names must be either primitives or user-defined functions.
#
# Primitives can write to any register.
# User-defined functions only write to hard-coded registers. Outputs of each
# call must have the same registers as in the function definition.
#
# There are some other statement types:
#   - blocks. Multiple statements surrounded by '{...}' and optionally
#     prefixed with a label name and ':'
#       - {
#           ...
#         }
#       - foo: {
#           ...
#         }
#
#   - variable definitions on the stack. E.g.:
#       - var foo: int
#       - var bar: (array int 3)
#     There's no initializer; variables are automatically initialized.
#     The type of a local variable is either word-length (4 bytes) or starts with 'ref'.
#
#   - variables definitions in a register. E.g.:
#       - var foo/eax: int <- add bar 1
#     The initializer is mandatory and must be a valid instruction that writes
#     a single output to the right register. In practice registers will
#     usually be either initialized by primitives or copied from eax.
#       - var eax: int <- foo bar quux
#         var floo/ecx: int <- copy eax
#
# Still todo:
#   global variables
#   heap allocations (planned name: 'handle')
#   user-defined types: 'type' for structs, 'choice' for unions
#   short-lived 'address' type for efficiently writing inside nested structs
#
# We don't have 'handle' types yet, but we try to distinguish 'ref', 'handle'
# and 'address' in comments. Their definitions are in layer 50, but really you
# can ignore the distinctions on a first reading of this program.
#
# Formal types:
#   A program is a linked list of functions
#   A function contains:
#     name: (handle array byte)
#     inouts: linked list of vars  <-- 'inouts' is more precise than 'inputs'
#       data: (handle var)
#       next: (handle list)
#     outputs: linked list of vars
#       data: (handle var)
#       next: (handle list)
#     body: (handle block)
#   A var-type contains:
#     name: (handle array byte)
#     type: (handle tree type-id)
#
#   A statement can be:
#     tag 0: a block
#     tag 1: a simple statement (stmt1)
#     tag 2: a variable defined on the stack
#     tag 3: a variable defined in a register
#
#   A block contains:
#     tag: 0
#     statements: (handle list stmt)
#     name: (handle array byte) -- starting with '$'
#
#   A regular statement contains:
#     tag: 1
#     operation: (handle array byte)
#     inouts: (handle list operand)
#     outputs: (handle list var)
#
#   A variable defined on the stack contains:
#     tag: 2
#     name: (handle array byte)
#     type: (handle tree type-id)
#
#   A variable defined in a register contains:
#     tag: 3
#     name: (handle array byte)
#     type: (handle tree type-id)
#     reg: (handle array byte)

# == Translation: managing the stack
# Now that we know what the language looks like in the large, let's think
# about how translation happens from the bottom up. One crucial piece of the
# puzzle is how Mu will clean up variables defined on the stack for you.
#
# Assume that we maintain a 'functions' list while parsing source code. And a
# 'primitives' list is a global constant. Both these contain enough information
# to perform type-checking on function calls or primitive statements, respectively.
#
# Defining variables pushes them on a stack with the current block depth and
# enough information about their location (stack offset or register).
# Starting a block increments the current block id.
# Each statement now has enough information to emit code for it.
# Ending a block is where the magic happens:
#   pop all variables at the current block depth
#   emit code to restore all register variables introduced at the current depth
#   emit code to clean up all stack variables at the current depth (just increment esp)
#   decrement the current block depth
#
# Formal types:
#   live-vars: stack of vars
#   var:
#     name: (handle array byte)
#     type: (handle tree type-id)
#     block: int
#     stack-offset: int  (added to ebp)
#     register: (handle array byte)
#       either usual register names
#       or '*' to indicate any register
#   At most one of stack-offset or register-index must be non-zero.
#   A register of '*' designates a variable _template_. Only legal in formal
#   parameters for primitives.

# == Translating a single function call
# This one's easy. Assuming we've already checked things, we just drop the
# outputs (which use hard-coded registers) and emit inputs in a standard format.
#
# out1, out2, out3, ... <- name inout1, inout2, inout3, ...
# =>
# (subx-name inout1 inout2 inout3)
#
# Formal types:
#   functions: linked list of info
#     name: (handle array byte)
#     inouts: linked list of vars
#     outputs: linked list of vars
#     body: block (singleton linked list)
#     subx-name: (handle array byte)

# == Translating a single primitive instruction
# A second crucial piece of the puzzle is how Mu converts fairly regular
# primitives with their uniform syntax to SubX instructions with their gnarly
# x86 details.
#
# Mu instructions have inputs and outputs. Primitives can have up to 2 of
# them.
# SubX instructions have rm32 and r32 operands.
# The translation between them covers almost all the possibilities.
#   Instructions with 1 inout may turn into ones with 1 rm32
#     (e.g. incrementing a var on the stack)
#   Instructions with 1 output may turn into ones with 1 rm32
#     (e.g. incrementing a var in a register)
#   1 inout and 1 output may turn into 1 rm32 and 1 r32
#     (e.g. adding a var to a reg)
#   2 inouts may turn into 1 rm32 and 1 r32
#     (e.g. adding a reg to a var)
#   1 inout and 1 literal may turn into 1 rm32 and 1 imm32
#     (e.g. adding a constant to a var)
#   1 output and 1 literal may turn into 1 rm32 and 1 imm32
#     (e.g. adding a constant to a reg)
#   2 outputs to hardcoded registers and 1 inout may turn into 1 rm32
#     (special-case: divide edx:eax by a var or reg)
# Observations:
#   We always emit rm32. It may be the first inout or the first output.
#   We may emit r32 or imm32 or neither.
#   When we emit r32 it may come from first inout or second inout or first output.
#
# Accordingly, the formal data structure for a primitive looks like this:
#   primitives: linked list of info
#     name: (handle array byte)
#     mu-inouts: linked list of vars to check
#     mu-outputs: linked list of vars to check; at most a singleton
#     subx-name: (handle array byte)
#     subx-rm32: enum arg-location
#     subx-r32: enum arg-location
#     subx-imm32: enum arg-location
#     subx-disp32: enum arg-location
#     output-is-write-only: boolean
#   arg-location: enum
#     0 means none
#     1 means first inout
#     2 means second inout
#     3 means first output

# == Translating a block
# Emit block name if necessary
# Emit '{'
# When you encounter a statement, emit it as above
# When you encounter a variable declaration
#   emit any code needed for it (bzeros)
#   push it on the var stack
#   update register dict if necessary
# When you encounter '}'
#   While popping variables off the var stack until block id changes
#     Emit code needed to clean up the stack
#       either increment esp
#       or pop into appropriate register

# The rest is straightforward.

== data

Program:
_Program-functions:  # (handle function)
  0/imm32
_Program-functions->payload:
  0/imm32
_Program-types:  # (handle typeinfo)
  0/imm32
_Program-types->payload:
  0/imm32

# Some constants for simulating the data structures described above.
# Many constants here come with a type in a comment.
#
# Sometimes the type is of the value at that offset for the given type. For
# example, if you start at a function record and move forward Function-inouts
# bytes, you'll find a (handle list var).
#
# At other times, the type is of the constant itself. For example, the type of
# the constant Function-size is (addr int). To get the size of a function,
# look in *Function-size.

Function-name:  # (handle array byte)
  0/imm32
Function-inouts:  # (handle list var)
  8/imm32
Function-outputs:  # (handle list var)
  0x10/imm32
Function-body:  # (handle block)
  0x18/imm32
Function-next:  # (handle function)
  0x20/imm32
Function-size:  # (addr int)
  0x28/imm32/24

Primitive-name:  # (handle array byte)
  0/imm32
Primitive-inouts:  # (handle list var)
  8/imm32
Primitive-outputs:  # (handle list var)
  0x10/imm32
Primitive-subx-name:  # (handle array byte)
  0x18/imm32
Primitive-subx-rm32:  # enum arg-location
  0x20/imm32
Primitive-subx-r32:  # enum arg-location
  0x24/imm32
Primitive-subx-imm32:  # enum arg-location
  0x28/imm32
Primitive-subx-disp32:  # enum arg-location  -- only for branches
  0x2c/imm32
Primitive-output-is-write-only:  # boolean
  0x30/imm32
Primitive-next:  # (handle function)
  0x34/imm32
Primitive-size:  # (addr int)
  0x3c/imm32/36

Stmt-tag:  # int
  0/imm32

Block-stmts:  # (handle list stmt)
  4/imm32
Block-var:  # (handle var)
  0xc/imm32

Stmt1-operation:  # (handle array byte)
  4/imm32
Stmt1-inouts:  # (handle stmt-var)
  0xc/imm32
Stmt1-outputs:  # (handle stmt-var)
  0x14/imm32

Vardef-var:  # (handle var)
  4/imm32

Regvardef-operation:  # (handle array byte)
  4/imm32
Regvardef-inouts:  # (handle stmt-var)
  0xc/imm32
Regvardef-outputs:  # (handle stmt-var)  # will have exactly one element
  0x14/imm32

Stmt-size:  # (addr int)
  0x1c/imm32

Var-name:  # (handle array byte)
  0/imm32
Var-type:  # (handle tree type-id)
  8/imm32
Var-block-depth:  # int -- not available until code-generation time
  0x10/imm32
Var-offset:  # int -- not available until code-generation time
  0x14/imm32
Var-register:  # (handle array byte) -- name of a register
  0x18/imm32
Var-size:  # (addr int)
  0x20/imm32

Any-register:  # (handle array byte)
  # alloc-id
  0x11/imm32
  # size
  1/imm32
  # data
  2a/asterisk

List-value:  # (handle _)
  0/imm32
List-next:  # (handle list _)
  8/imm32
List-size:  # (addr int)
  0x10/imm32

# A stmt-var is like a list of vars with call-site specific metadata
Stmt-var-value:  # (handle var)
  0/imm32
Stmt-var-next:  # (handle stmt-var)
  8/imm32
Stmt-var-is-deref:  # boolean
  0x10/imm32
Stmt-var-size:  # (addr int)
  0x14/imm32

# Types are expressed as trees (s-expressions) of type-ids (ints).
# However, there's no need for singletons, so we can assume (int) == int
#   - if x->right == nil, x is an atom
#   - x->left contains either a pointer to a pair, or an atomic type-id directly.

Tree-is-atom:  # boolean
  0/imm32
# if left-is-atom?
Tree-value:  # type-id
  4/imm32
# unless left-is-atom?
Tree-left:  # (addr tree type-id)
  4/imm32
Tree-right:  # (addr tree type-id)
  0xc/imm32
#
Tree-size:  # (addr int)
  0x14/imm32

# Types

# TODO: heap allocations here can't be reclaimed
Type-id:  # (stream (addr array byte))
  0x1c/imm32/write
  0/imm32/read
  0x100/imm32/size
  # data
  "literal"/imm32  # 0
  "int"/imm32  # 1
  "addr"/imm32  # 2
  "array"/imm32  # 3
  "handle"/imm32  # 4
  "boolean"/imm32  # 5
  "constant"/imm32  # 6: like a literal, but replaced with its value in Var-offset
  "offset"/imm32  # 7: (offset T) is guaranteed to be a 32-bit multiple of size-of(T)
  0/imm32
  # 0x20
  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
  0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32

# == Type definitions
# Program->types contains some typeinfo for each type definition.
# Types contain vars with types, but can't specify registers.
Typeinfo-id:  # type-id
  0/imm32
Typeinfo-fields:  # (handle table (handle array byte) (handle typeinfo-entry))
  4/imm32
# Total size must be >= 0
# During parsing it may take on two additional values:
#   -2: not yet initialized
#   -1: in process of being computed
# See populate-mu-type-sizes for details.
Typeinfo-total-size-in-bytes:  # int
  0xc/imm32
Typeinfo-next:  # (handle typeinfo)
  0x10/imm32
Typeinfo-size:  # (addr int)
  0x18/imm32

# Each entry in the typeinfo->fields table has a pointer to a string and a
# pointer to a typeinfo-entry.
Typeinfo-fields-row-size:  # (addr int)
  0x10/imm32

# typeinfo-entry objects have information about a field in a single record type
#
# each field of a type is represented using two var's:
#   1. the input var: expected type of the field; convenient for creating using parse-var-with-type
#   2. the output var: a constant containing the byte offset; convenient for code-generation
# computing the output happens after parsing; in the meantime we preserve the
# order of fields in the 'index' field.
Typeinfo-entry-input-var:  # (handle var)
  0/imm32
Typeinfo-entry-index:  # int
  8/imm32
Typeinfo-entry-output-var:  # (handle var)
  0xc/imm32
Typeinfo-entry-size:  # (addr int)
  0x14/imm32

== code

Entry:
    # . prologue
    89/<- %ebp 4/r32/esp
    (new-segment *Heap-size Heap)
    # if (argv[1] == "test') run-tests()
    {
      # if (argc <= 1) break
      81 7/subop/compare *ebp 1/imm32
      7e/jump-if-<= break/disp8
      # if (argv[1] != "test") break
      (kernel-string-equal? *(ebp+8) "test")  # => eax
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= break/disp8
      #
      (run-tests)
      # syscall(exit, *Num-test-failures)
      8b/-> *Num-test-failures 3/r32/ebx
      eb/jump $mu-main:end/disp8
    }
    # otherwise convert Stdin
    (convert-mu Stdin Stdout)
    (flush Stdout)
    # syscall(exit, 0)
    bb/copy-to-ebx 0/imm32
$mu-main:end:
    b8/copy-to-eax 1/imm32/exit
    cd/syscall 0x80/imm8

convert-mu:  # in: (addr buffered-file), out: (addr buffered-file)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # initialize global data structures
    c7 0/subop/copy *Next-block-index 1/imm32
    c7 0/subop/copy *Type-id 0x1c/imm32  # stream-write
    c7 0/subop/copy *_Program-functions 0/imm32
    c7 0/subop/copy *_Program-functions->payload 0/imm32
    c7 0/subop/copy *_Program-types 0/imm32
    c7 0/subop/copy *_Program-types->payload 0/imm32
    #
    (parse-mu *(ebp+8))
    (populate-mu-type-sizes)
    (check-mu-types)
    (emit-subx *(ebp+0xc))
$convert-mu:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-empty-input:
    # empty input => empty output
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
    (check-stream-equal _test-output-stream "" "F - test-convert-empty-input")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-skeleton:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-skeleton/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-skeleton/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-skeleton/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-skeleton/3")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-skeleton/4")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-skeleton/5")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-skeleton/6")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-skeleton/7")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-multiple-function-skeletons:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "fn bar {\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check first function
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-multiple-function-skeletons/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/3")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/4")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/5")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/6")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/7")
    # check second function
    (check-next-stream-line-equal _test-output-stream "bar:"                    "F - test-convert-multiple-function-skeletons/10")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-multiple-function-skeletons/11")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-multiple-function-skeletons/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-multiple-function-skeletons/13")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-multiple-function-skeletons/14")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-multiple-function-skeletons/15")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-multiple-function-skeletons/16")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-multiple-function-skeletons/17")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-arg:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo n: int {\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg/3")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg/4")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg/5")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg/6")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg/7")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-arg-and-body:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo n: int {\n")
    (write _test-input-stream "  increment n\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-arg-and-body/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-arg-and-body/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-arg-and-body/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-arg-and-body/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-arg-and-body/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-arg-and-body/5")
    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-arg-and-body/6")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-arg-and-body/7")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-arg-and-body/8")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-arg-and-body/9")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-arg-and-body/10")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-arg-and-body/11")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-arg-and-body/12")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-distinguishes-args:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo a: int, b: int {\n")
    (write _test-input-stream "  increment b\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-distinguishes-args/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-distinguishes-args/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-distinguishes-args/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-distinguishes-args/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-distinguishes-args/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-distinguishes-args/5")
    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0x0000000c)"  "F - test-convert-function-distinguishes-args/6")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-distinguishes-args/7")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-distinguishes-args/8")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-distinguishes-args/9")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-distinguishes-args/10")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-distinguishes-args/11")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-distinguishes-args/12")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-returns-result:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
    (write _test-input-stream "  result <- copy a\n")
    (write _test-input-stream "  result <- increment\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-returns-result/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-returns-result/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-returns-result/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-returns-result/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-returns-result/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-returns-result/5")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-returns-result/6")
    (check-next-stream-line-equal _test-output-stream "    40/increment-eax"    "F - test-convert-function-returns-result/7")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-returns-result/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-returns-result/9")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-returns-result/10")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-returns-result/11")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-returns-result/12")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-returns-result/13")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-literal-arg:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo a: int, b: int -> result/eax: int {\n")
    (write _test-input-stream "  result <- copy a\n")
    (write _test-input-stream "  result <- add 1\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-literal-arg/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg/5")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-with-literal-arg/6")
    (check-next-stream-line-equal _test-output-stream "    05/add-to-eax 1/imm32"  "F - test-convert-function-with-literal-arg/7")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg/9")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg/10")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg/11")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg/12")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-literal-arg/13")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-literal-arg-2:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo a: int, b: int -> result/ebx: int {\n")
    (write _test-input-stream "  result <- copy a\n")
    (write _test-input-stream "  result <- add 1\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-literal-arg-2/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-literal-arg-2/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-literal-arg-2/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-literal-arg-2/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-literal-arg-2/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-literal-arg-2/5")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-with-literal-arg-2/6")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %ebx 1/imm32"  "F - test-convert-function-with-literal-arg-2/7")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-literal-arg-2/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-literal-arg-2/9")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-literal-arg-2/10")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-literal-arg-2/11")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-literal-arg-2/12")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-literal-arg-2/13")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-call-with-literal-arg:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn main -> result/ebx: int {\n")
    (write _test-input-stream "  result <- do-add 3 4\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "fn do-add a: int, b: int -> result/ebx: int {\n")
    (write _test-input-stream "  result <- copy a\n")
    (write _test-input-stream "  result <- add b\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "main:"                   "F - test-convert-function-call-with-literal-arg/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/4")
    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:loop:"  "F - test-convert-function-call-with-literal-arg/5")
    (check-next-stream-line-equal _test-output-stream "    (do-add 3 4)"        "F - test-convert-function-call-with-literal-arg/6")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/7")
    (check-next-stream-line-equal _test-output-stream "$main:0x00000001:break:" "F - test-convert-function-call-with-literal-arg/8")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/9")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/10")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/11")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/12")
    (check-next-stream-line-equal _test-output-stream "do-add:"                 "F - test-convert-function-call-with-literal-arg/13")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-literal-arg/14")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-literal-arg/15")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-literal-arg/16")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-literal-arg/17")
    (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:loop:"  "F - test-convert-function-call-with-literal-arg/18")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/19")
    (check-next-stream-line-equal _test-output-stream "    03/add *(ebp+0x0000000c) 0x00000003/r32"  "F - test-convert-function-call-with-literal-arg/20")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-literal-arg/21")
    (check-next-stream-line-equal _test-output-stream "$do-add:0x00000002:break:"  "F - test-convert-function-call-with-literal-arg/22")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-literal-arg/23")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-literal-arg/24")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-literal-arg/25")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-literal-arg/26")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-var-in-mem:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x: int\n")
    (write _test-input-stream "  increment x\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-mem/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-mem/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-mem/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-mem/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-mem/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-mem/5")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-in-mem/6")
    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-mem/7")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-mem/8")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-mem/9")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-mem/10")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-mem/11")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-mem/12")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-mem/13")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-mem/14")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-var-with-compound-type-in-mem:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x: (addr int)\n")
    (write _test-input-stream "  copy-to x, 0\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-compound-type-in-mem/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-compound-type-in-mem/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-compound-type-in-mem/5")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-compound-type-in-mem/6")
    (check-next-stream-line-equal _test-output-stream "    c7 0/subop/copy *(ebp+0xfffffffc) 0/imm32"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/7")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/8")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-compound-type-in-mem/9")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/10")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-compound-type-in-mem/11")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-with-compound-type-in-mem/12")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-compound-type-in-mem/13")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-compound-type-in-mem/14")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-var-in-reg:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
    (write _test-input-stream "  x <- increment\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-reg/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-reg/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-reg/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-reg/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-reg/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-reg/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-in-reg/6")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-local-var-in-reg/7")
    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-local-var-in-reg/8")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-in-reg/9")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-reg/10")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-reg/11")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-reg/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-reg/13")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-reg/14")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-reg/15")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-second-local-var-in-same-reg:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
    (write _test-input-stream "  var y/ecx: int <- copy 4\n")
    (write _test-input-stream "  y <- increment\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-second-local-var-in-same-reg/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-second-local-var-in-same-reg/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-second-local-var-in-same-reg/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-second-local-var-in-same-reg/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-second-local-var-in-same-reg/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-second-local-var-in-same-reg/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-second-local-var-in-same-reg/6")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-convert-function-with-second-local-var-in-same-reg/7")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-convert-function-with-second-local-var-in-same-reg/8")
    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-convert-function-with-second-local-var-in-same-reg/9")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-second-local-var-in-same-reg/10")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-second-local-var-in-same-reg/11")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-second-local-var-in-same-reg/12")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-second-local-var-in-same-reg/13")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-second-local-var-in-same-reg/14")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-second-local-var-in-same-reg/15")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-second-local-var-in-same-reg/16")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-var-dereferenced:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x/ecx: (addr int) <- copy 0\n")
    (write _test-input-stream "  increment *x\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-dereferenced/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-dereferenced/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-dereferenced/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-dereferenced/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-dereferenced/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-dereferenced/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-with-local-var-dereferenced/6")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-function-with-local-var-dereferenced/7")
    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-with-local-var-dereferenced/8")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-with-local-var-dereferenced/9")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-dereferenced/10")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-dereferenced/11")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-dereferenced/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-dereferenced/13")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-dereferenced/14")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-dereferenced/15")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-compare-register-with-literal:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x/ecx: int <- copy 0\n")
    (write _test-input-stream "  compare x, 0\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-compare-register-with-literal/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-compare-register-with-literal/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-compare-register-with-literal/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-compare-register-with-literal/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-compare-register-with-literal/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-compare-register-with-literal/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 0/imm32"  "F - test-convert-compare-register-with-literal/7")
    (check-next-stream-line-equal _test-output-stream "    81 7/subop/compare %ecx 0/imm32"  "F - test-convert-compare-register-with-literal/8")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-compare-register-with-literal/10")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-compare-register-with-literal/11")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-compare-register-with-literal/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-compare-register-with-literal/13")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-compare-register-with-literal/14")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-compare-register-with-literal/15")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-var-in-block:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-block/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-block/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-block/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-block/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-block/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-block/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-block/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-local-var-in-block/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-block/8")
    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-block/9")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-block/10")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-block/11")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-local-var-in-block/12")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-block/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-block/14")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-block/15")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-block/16")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-block/17")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-block/18")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-var-in-named-block:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  $bar: {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-in-named-block/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-in-named-block/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-in-named-block/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-in-named-block/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-in-named-block/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-in-named-block/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-local-var-in-named-block/6")
    (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-local-var-in-named-block/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-local-var-in-named-block/8")
    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-local-var-in-named-block/9")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-local-var-in-named-block/10")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-local-var-in-named-block/11")
    (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-local-var-in-named-block/12")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-in-named-block/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-in-named-block/14")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-in-named-block/15")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-in-named-block/16")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-in-named-block/17")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-in-named-block/18")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-always-shadow-outermost-reg-vars-in-function:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-always-shadow-outermost-reg-vars-in-function/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-always-shadow-outermost-reg-vars-in-function/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-always-shadow-outermost-reg-vars-in-function/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-always-shadow-outermost-reg-vars-in-function/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-always-shadow-outermost-reg-vars-in-function/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-always-shadow-outermost-reg-vars-in-function/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-compare-register-with-literal/6")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-always-shadow-outermost-reg-vars-in-function/8")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-always-shadow-outermost-reg-vars-in-function/12")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-always-shadow-outermost-reg-vars-in-function/13")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-always-shadow-outermost-reg-vars-in-function/14")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-always-shadow-outermost-reg-vars-in-function/15")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-always-shadow-outermost-reg-vars-in-function/16")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-always-shadow-outermost-reg-vars-in-function/17")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

_pending-test-clobber-dead-local:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var y/ecx: int <- copy 4\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-clobber-dead-local/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-clobber-dead-local/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-clobber-dead-local/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-clobber-dead-local/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-clobber-dead-local/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-clobber-dead-local/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-clobber-dead-local/6")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-clobber-dead-local/7")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-clobber-dead-local/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-clobber-dead-local/9")
    (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-clobber-dead-local/10")  # no push/pop here
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-clobber-dead-local/11")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-clobber-dead-local/12")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-clobber-dead-local/13")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-clobber-dead-local/14")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-clobber-dead-local/15")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-clobber-dead-local/16")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-clobber-dead-local/17")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-clobber-dead-local/18")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-clobber-dead-local/19")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-shadow-live-local:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x/ecx: int <- copy 3\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var y/ecx: int <- copy 4\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "  x <- increment\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-local/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-local/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-local/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-local/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-local/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-local/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-shadow-live-local/6")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-live-local/7")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-local/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-local/9")
    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-local/10")
    (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-local/11")
    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-local/12")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-local/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-local/14")
    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-local/15")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-shadow-live-local/16")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-local/17")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-local/18")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-local/19")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-local/20")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-local/21")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-local/21")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-shadow-live-output:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo -> x/ecx: int {\n")
    (write _test-input-stream "  x <- copy 3\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var y/ecx: int <- copy 4\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "  x <- increment\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-shadow-live-output/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-shadow-live-output/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-shadow-live-output/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-shadow-live-output/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-shadow-live-output/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-shadow-live-output/5")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"  "F - test-shadow-live-output/7")  # no push because it's an output reg
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-shadow-live-output/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-shadow-live-output/9")
    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %ecx"  "F - test-shadow-live-output/10")
    (check-next-stream-line-equal _test-output-stream "      b9/copy-to-ecx 4/imm32"  "F - test-shadow-live-output/11")
    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %ecx" "F - test-shadow-live-output/12")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-shadow-live-output/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-shadow-live-output/14")
    (check-next-stream-line-equal _test-output-stream "    41/increment-ecx"    "F - test-shadow-live-output/15")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-shadow-live-output/17")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-shadow-live-output/18")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-shadow-live-output/19")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-shadow-live-output/20")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-shadow-live-output/21")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-shadow-live-output/21")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

_pending-test-local-clobbered-by-output:
    # also doesn't spill
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo -> x/ecx: int {\n")
    (write _test-input-stream "  var y/ecx: int <- copy 4\n")
    (write _test-input-stream "  x <- copy y\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-local-clobbered-by-output/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-local-clobbered-by-output/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-local-clobbered-by-output/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-local-clobbered-by-output/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-local-clobbered-by-output/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-local-clobbered-by-output/5")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 4/imm32"  "F - test-local-clobbered-by-output/6")
    (check-next-stream-line-equal _test-output-stream "    89/<- %ecx 0x00000001/r32"  "F - test-local-clobbered-by-output/7")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-local-clobbered-by-output/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-local-clobbered-by-output/9")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-local-clobbered-by-output/10")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-local-clobbered-by-output/11")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-local-clobbered-by-output/12")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-local-clobbered-by-output/13")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-branches-in-block:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo x: int {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    break-if->=\n")
    (write _test-input-stream "    loop-if-addr<\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "    loop\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-block/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-block/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-block/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-block/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-block/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-block/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-block/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-in-block/7")
    (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-in-block/8")
    (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< loop/disp32"  "F - test-convert-function-with-branches-in-block/9")
    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-block/10")
    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-in-block/11")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-block/12")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-in-block/13")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-block/14")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-block/15")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-block/16")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-block/17")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-block/18")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-block/19")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-branches-in-named-block:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo x: int {\n")
    (write _test-input-stream "  $bar: {\n")
    (write _test-input-stream "    break-if->= $bar\n")
    (write _test-input-stream "    loop-if-addr< $bar\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "    loop\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-in-named-block/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-in-named-block/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-in-named-block/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-in-named-block/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-in-named-block/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-in-named-block/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-in-named-block/6")
    (check-next-stream-line-equal _test-output-stream "$bar:loop:"              "F - test-convert-function-with-branches-in-named-block/7")
    (check-next-stream-line-equal _test-output-stream "      0f 8d/jump-if->= $bar:break/disp32"  "F - test-convert-function-with-branches-in-named-block/8")
    (check-next-stream-line-equal _test-output-stream "      0f 82/jump-if-addr< $bar:loop/disp32"  "F - test-convert-function-with-branches-in-named-block/9")
    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0x00000008)"  "F - test-convert-function-with-branches-in-named-block/10")
    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"   "F - test-convert-function-with-branches-in-named-block/11")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-in-named-block/12")
    (check-next-stream-line-equal _test-output-stream "$bar:break:"             "F - test-convert-function-with-branches-in-named-block/13")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-in-named-block/14")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-in-named-block/15")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-in-named-block/16")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-in-named-block/17")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-in-named-block/18")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-in-named-block/19")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-var-in-nested-block:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo x: int {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    {\n")
    (write _test-input-stream "      var x: int\n")
    (write _test-input-stream "      increment x\n")
    (write _test-input-stream "    }\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-var-in-nested-block/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-var-in-nested-block/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-var-in-nested-block/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-var-in-nested-block/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-var-in-nested-block/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-var-in-nested-block/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-var-in-nested-block/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-var-in-nested-block/7")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-var-in-nested-block/8")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-var-in-nested-block/9")
    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-var-in-nested-block/10")
    (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-var-in-nested-block/11")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-var-in-nested-block/12")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-var-in-nested-block/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-var-in-nested-block/14")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-var-in-nested-block/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-var-in-nested-block/16")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-var-in-nested-block/17")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-var-in-nested-block/18")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-var-in-nested-block/19")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-var-in-nested-block/20")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-var-in-nested-block/21")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-var-in-nested-block/22")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-multiple-vars-in-nested-blocks:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo x: int {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var x/eax: int <- copy 0\n")
    (write _test-input-stream "    {\n")
    (write _test-input-stream "      var y: int\n")
    (write _test-input-stream "      x <- add y\n")
    (write _test-input-stream "    }\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-multiple-vars-in-nested-blocks/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-multiple-vars-in-nested-blocks/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/7")
    (check-next-stream-line-equal _test-output-stream "      ff 6/subop/push %eax"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/8")
    (check-next-stream-line-equal _test-output-stream "      b8/copy-to-eax 0/imm32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/9")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/10")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/11")
    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-multiple-vars-in-nested-blocks/12")
    (check-next-stream-line-equal _test-output-stream "        03/add *(ebp+0xfffffff8) 0x00000000/r32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/13")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/14")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-multiple-vars-in-nested-blocks/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/16")
    (check-next-stream-line-equal _test-output-stream "      8f 0/subop/pop %eax"   "F - test-convert-function-with-multiple-vars-in-nested-blocks/17")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-multiple-vars-in-nested-blocks/18")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/19")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-multiple-vars-in-nested-blocks/20")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/21")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-multiple-vars-in-nested-blocks/22")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-multiple-vars-in-nested-blocks/23")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-multiple-vars-in-nested-blocks/24")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-multiple-vars-in-nested-blocks/25")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-branches-and-local-vars:
    # A conditional 'break' after a 'var' in a block is converted into a
    # nested block that performs all necessary cleanup before jumping. This
    # results in some ugly code duplication.
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    break-if->=\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-local-vars/10")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/18")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-local-vars/19")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/20")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/21")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/22")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/23")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-conditional-loops-and-local-vars:
    # A conditional 'loop' after a 'var' in a block is converted into a nested
    # block that performs all necessary cleanup before jumping. This results
    # in some ugly code duplication.
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    loop-if->=\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-conditional-loops-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-conditional-loops-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-conditional-loops-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-conditional-loops-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-conditional-loops-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-conditional-loops-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-conditional-loops-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-conditional-loops-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-conditional-loops-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-conditional-loops-and-local-vars/10")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-conditional-loops-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:loop/disp32"  "F - test-convert-function-with-conditional-loops-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-conditional-loops-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-conditional-loops-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-conditional-loops-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-conditional-loops-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-conditional-loops-and-local-vars/18")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-conditional-loops-and-local-vars/19")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-conditional-loops-and-local-vars/20")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-conditional-loops-and-local-vars/21")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-conditional-loops-and-local-vars/22")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-conditional-loops-and-local-vars/23")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-unconditional-loops-and-local-vars:
    # An unconditional 'loop' after a 'var' in a block is emitted _after_ the
    # regular block cleanup. Any instructions after 'loop' are dead and
    # therefore skipped.
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    loop\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-loops-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-loops-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-loops-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-loops-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-unconditional-loops-and-local-vars/10")
    # not emitted:                                           ff 0/subop/increment *(ebp+0xfffffffc)
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-loops-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-loops-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-loops-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-loops-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-loops-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-loops-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-loops-and-local-vars/18")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-branches-and-loops-and-local-vars:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    break-if->=\n")
    (write _test-input-stream "    increment x\n")
    (write _test-input-stream "    loop\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-branches-and-loops-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-loops-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-loops-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-branches-and-loops-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-loops-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "        0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/10")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "        e9/jump $foo:0x00000002:break/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-loops-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "      ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-loops-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "      e9/jump loop/disp32"  "F - test-convert-function-with-branches-and-loops-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-loops-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/18")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-loops-and-local-vars/19")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-branches-and-loops-and-local-vars/20")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-loops-and-local-vars/21")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-loops-and-local-vars/22")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-loops-and-local-vars/23")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-loops-and-local-vars/24")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-nonlocal-branches-and-loops-and-local-vars:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  a: {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    {\n")
    (write _test-input-stream "      var y: int\n")
    (write _test-input-stream "      break-if->= a\n")
    (write _test-input-stream "      increment x\n")
    (write _test-input-stream "      loop\n")
    (write _test-input-stream "    }\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/10")
    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "        {"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "          0f 8c/jump-if-< break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "          81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "          81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "          e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "        }"               "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/18")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/19")
    (check-next-stream-line-equal _test-output-stream "        e9/jump loop/disp32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/20")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/21")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/22")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/23")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/24")
    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/25")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/26")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/27")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/28")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/29")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/30")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-branches-and-loops-and-local-vars/31")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-nonlocal-unconditional-break-and-local-vars:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  a: {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    {\n")
    (write _test-input-stream "      var y: int\n")
    (write _test-input-stream "      break a\n")
    (write _test-input-stream "      increment x\n")
    (write _test-input-stream "    }\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10")
    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "        e9/jump a:break/disp32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18")
    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/23")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-unconditional-break-and-local-vars:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    {\n")
    (write _test-input-stream "      var y: int\n")
    (write _test-input-stream "      break\n")
    (write _test-input-stream "      increment x\n")
    (write _test-input-stream "    }\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-unconditional-break-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-unconditional-break-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-unconditional-break-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-unconditional-break-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-unconditional-break-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-unconditional-break-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-unconditional-break-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-unconditional-break-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-unconditional-break-and-local-vars/10")
    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-break-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-unconditional-break-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-unconditional-break-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-unconditional-break-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-unconditional-break-and-local-vars/18")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-unconditional-break-and-local-vars/19")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-unconditional-break-and-local-vars/20")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-unconditional-break-and-local-vars/21")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-unconditional-break-and-local-vars/22")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-unconditional-break-and-local-vars/23")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-nonlocal-unconditional-loop-and-local-vars:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  a: {\n")
    (write _test-input-stream "    var x: int\n")
    (write _test-input-stream "    {\n")
    (write _test-input-stream "      var y: int\n")
    (write _test-input-stream "      loop a\n")
    (write _test-input-stream "      increment x\n")
    (write _test-input-stream "    }\n")
    (write _test-input-stream "  }\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5")
    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6")
    (check-next-stream-line-equal _test-output-stream "a:loop:"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7")
    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8")
    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:"   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10")
    (check-next-stream-line-equal _test-output-stream "        68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/12")
    (check-next-stream-line-equal _test-output-stream "        81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/13")
    (check-next-stream-line-equal _test-output-stream "        e9/jump a:loop/disp32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/14")
    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16")
    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/17")
    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18")
    (check-next-stream-line-equal _test-output-stream "a:break:"                "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/23")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-array-var-in-mem:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var x: (array int 3)\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-array-var-in-mem/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-array-var-in-mem/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-array-var-in-mem/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-array-var-in-mem/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-array-var-in-mem/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-array-var-in-mem/5")
    # define x
    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-function-with-local-array-var-in-mem/7")
    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-function-with-local-array-var-in-mem/8")
    # reclaim x
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-function-with-local-array-var-in-mem/9")
    #
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-array-var-in-mem/10")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-array-var-in-mem/11")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-array-var-in-mem/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-array-var-in-mem/13")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-array-var-in-mem/14")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-array-var-in-mem/15")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-address:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var a: int\n")
    (write _test-input-stream "  var b/eax: (addr int) <- address a\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-address/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-address/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-address/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-address/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-address/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-address/5")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-address/6")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-address/7")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000000/r32"  "F - test-convert-address/8")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-address/9")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-address/10")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-address/11")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-address/12")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-address/13")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-address/14")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-address/15")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-address/16")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-length-of-array:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo a: (addr array int) {\n")
    (write _test-input-stream "  var b/eax: (addr array int) <- copy a\n")
    (write _test-input-stream "  var c/eax: int <- length b\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array/6")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-length-of-array/7")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *eax 0x00000000/r32"  "F - test-convert-length-of-array/9")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array/11")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array/12")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array/13")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array/14")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array/15")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array/16")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array/17")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-length-of-array-on-stack:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var a: (array int 3)\n")
    (write _test-input-stream "  var b/eax: int <- length a\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-length-of-array-on-stack/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-length-of-array-on-stack/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-length-of-array-on-stack/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-length-of-array-on-stack/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-length-of-array-on-stack/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-length-of-array-on-stack/5")
    # define x
    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"  "F - test-convert-length-of-array-on-stack/6")
    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"  "F - test-convert-length-of-array-on-stack/7")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-length-of-array-on-stack/8")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0xfffffff0) 0x00000000/r32"  "F - test-convert-length-of-array-on-stack/9")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"  "F - test-convert-length-of-array-on-stack/10")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"  "F - test-convert-length-of-array-on-stack/11")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-length-of-array-on-stack/12")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-length-of-array-on-stack/13")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-length-of-array-on-stack/14")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-length-of-array-on-stack/15")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-length-of-array-on-stack/16")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-length-of-array-on-stack/17")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-index-into-array:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
    (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array/3")
    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array/6")
    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array/7")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array/8")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array/9")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000002 + 4) 0x00000000/r32"  "F - test-convert-index-into-array/11")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array/13")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array/14")
    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array/16")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array/17")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array/18")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array/19")
    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array/20")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-index-into-array-with-literal:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
    (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-with-literal/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-with-literal/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-with-literal/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-with-literal/3")
    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-with-literal/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-with-literal/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-with-literal/6")
    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-with-literal/7")
                                                                                 # 2 * 4 bytes/elem + 4 bytes for size = offset 12
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x0000000c) 0x00000000/r32"  "F - test-convert-index-into-array-with-literal/8")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-with-literal/9")
    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-with-literal/10")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-with-literal/11")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-with-literal/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-with-literal/13")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-with-literal/14")
    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-with-literal/15")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-index-into-array-on-stack:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var arr: (array int 3)\n")
    (write _test-input-stream "  var idx/eax: int <- copy 2\n")
    (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack/3")
    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack/5")
    # var arr
    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack/6")
    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack/7")
    # var idx
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack/8")
    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 2/imm32"                  "F - test-convert-index-into-array-on-stack/9")
    # var x is at (ebp-0x10) + idx<<2 + 4 = ebp + idx<<2 - 0xc
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp + eax<<0x00000002 + 0xfffffff4) 0x00000000/r32"  "F - test-convert-index-into-array-on-stack/10")
    # reclaim idx
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack/11")
    # reclaim arr
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack/12")
    #
    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack/13")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack/14")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack/15")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack/16")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack/17")
    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack/18")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-index-into-array-on-stack-with-literal:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var arr: (array int 3)\n")
    (write _test-input-stream "  var x/eax: (addr int) <- index arr, 2\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-on-stack-with-literal/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-on-stack-with-literal/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-on-stack-with-literal/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-on-stack-with-literal/3")
    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-on-stack-with-literal/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-on-stack-with-literal/5")
    # var arr
    (check-next-stream-line-equal _test-output-stream "    (push-n-zero-bytes 0x0000000c)"          "F - test-convert-index-into-array-on-stack-with-literal/6")
    (check-next-stream-line-equal _test-output-stream "    68/push 0x0000000c/imm32"                "F - test-convert-index-into-array-on-stack-with-literal/7")
    # var x
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-on-stack-with-literal/8")
    # x is at (ebp-0x10) + 4 + 2*4 = ebp-4
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp + 0xfffffffc) 0x00000000/r32"  "F - test-convert-index-into-array-on-stack-with-literal/9")
    # reclaim x
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-on-stack-with-literal/10")
    # reclaim arr
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000010/imm32"    "F - test-convert-index-into-array-on-stack-with-literal/11")
    #
    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-on-stack-with-literal/12")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-on-stack-with-literal/13")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-on-stack-with-literal/14")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-on-stack-with-literal/15")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-on-stack-with-literal/16")
    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-on-stack-with-literal/17")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-index-into-array-using-offset:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
    (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
    (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset/3")
    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset/6")
    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset/7")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset/8")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-index-into-array-using-offset/9")
    (check-next-stream-line-equal _test-output-stream "    69/multiply %ecx 0x00000004/imm32 0x00000001/r32"  "F - test-convert-index-into-array-using-offset/10")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx + 4) 0x00000000/r32"  "F - test-convert-index-into-array-using-offset/11")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset/12")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset/13")
    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset/14")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset/15")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset/16")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset/17")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset/18")
    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset/19")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-index-into-array-using-offset-on-stack:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var arr/eax: (addr array int) <- copy 0\n")
    (write _test-input-stream "  var idx: int\n")
    (write _test-input-stream "  var off/ecx: (offset int) <- compute-offset arr, idx\n")
    (write _test-input-stream "  var x/eax: (addr int) <- index arr, off\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-index-into-array-using-offset-on-stack/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-index-into-array-using-offset-on-stack/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-index-into-array-using-offset-on-stack/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-index-into-array-using-offset-on-stack/3")
    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-index-into-array-using-offset-on-stack/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-index-into-array-using-offset-on-stack/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-index-into-array-using-offset-on-stack/6")
    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-index-into-array-using-offset-on-stack/7")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"                         "F - test-convert-index-into-array-using-offset-on-stack/8")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-index-into-array-using-offset-on-stack/9")
    (check-next-stream-line-equal _test-output-stream "    69/multiply *(ebp+0xfffffff8) 0x00000004/imm32 0x00000001/r32"  "F - test-convert-index-into-array-using-offset-on-stack/10")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx + 4) 0x00000000/r32"  "F - test-convert-index-into-array-using-offset-on-stack/11")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-index-into-array-using-offset-on-stack/12")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000004/imm32"    "F - test-convert-index-into-array-using-offset-on-stack/13")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-index-into-array-using-offset-on-stack/14")
    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-index-into-array-using-offset-on-stack/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-index-into-array-using-offset-on-stack/16")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-index-into-array-using-offset-on-stack/17")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-index-into-array-using-offset-on-stack/18")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-index-into-array-using-offset-on-stack/19")
    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-index-into-array-using-offset-on-stack/20")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-and-type-definition:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo a: (addr t) {\n")
    (write _test-input-stream "  var _a/eax: (addr t) <- copy a\n")
    (write _test-input-stream "  var b/ecx: (addr int) <- get _a, x\n")
    (write _test-input-stream "  var c/ecx: (addr int) <- get _a, y\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "type t {\n")
    (write _test-input-stream "  x: int\n")
    (write _test-input-stream "  y: int\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-and-type-definition/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-and-type-definition/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-and-type-definition/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-and-type-definition/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-and-type-definition/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-and-type-definition/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-and-type-definition/6")
    (check-next-stream-line-equal _test-output-stream "    8b/-> *(ebp+0x00000008) 0x00000000/r32"  "F - test-convert-function-and-type-definition/7")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-and-type-definition/8")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000000) 0x00000001/r32"  "F - test-convert-function-and-type-definition/9")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + 0x00000004) 0x00000001/r32"  "F - test-convert-function-and-type-definition/11")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-and-type-definition/13")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-and-type-definition/14")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-and-type-definition/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-and-type-definition/16")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-and-type-definition/17")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-and-type-definition/18")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-and-type-definition/19")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-and-type-definition/20")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-with-local-var-with-user-defined-type:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var a: t\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "type t {\n")
    (write _test-input-stream "  x: int\n")
    (write _test-input-stream "  y: int\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-with-local-var-with-user-defined-type/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-local-var-with-user-defined-type/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-local-var-with-user-defined-type/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-local-var-with-user-defined-type/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-local-var-with-user-defined-type/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-function-with-local-var-with-user-defined-type/5")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/6")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-with-local-var-with-user-defined-type/7")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-function-with-local-var-with-user-defined-type/8")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-local-var-with-user-defined-type/9")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-function-with-local-var-with-user-defined-type/10")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-local-var-with-user-defined-type/11")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-local-var-with-user-defined-type/12")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-local-var-with-user-defined-type/13")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-local-var-with-user-defined-type/14")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-call-with-arg-of-user-defined-type:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn f {\n")
    (write _test-input-stream "  var a: t\n")
    (write _test-input-stream "  foo a\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "fn foo x: t {\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "type t {\n")
    (write _test-input-stream "  x: int\n")
    (write _test-input-stream "  y: int\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type/4")
    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type/5")
    # var a: t
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type/6")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type/7")
    # foo a
    (check-next-stream-line-equal _test-output-stream "    (foo *(ebp+0xfffffff8) *(ebp+0xfffffffc))"  "F - test-convert-function-call-with-arg-of-user-defined-type/8")
    #
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-function-call-with-arg-of-user-defined-type/9")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type/10")
    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type/11")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/13")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/14")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/15")
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type/16")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/17")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/18")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/19")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/20")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/21")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/22")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/23")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-function-call-with-arg-of-user-defined-type-register-indirect:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn f {\n")
    (write _test-input-stream "  var a/eax: (addr t) <- copy 0\n")
    (write _test-input-stream "  foo *a\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "fn foo x: t {\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "type t {\n")
    (write _test-input-stream "  x: int\n")
    (write _test-input-stream "  y: int\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type/4")
    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type/5")
    # var a
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-call-with-arg-of-user-defined-type/6")
    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"  "F - test-convert-function-call-with-arg-of-user-defined-type/7")
    # foo a
    (check-next-stream-line-equal _test-output-stream "    (foo *(eax+0x00000000) *(eax+0x00000004))"  "F - test-convert-function-call-with-arg-of-user-defined-type/8")
    #
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-call-with-arg-of-user-defined-type/9")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type/10")
    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type/11")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/12")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/13")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/14")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/15")
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type/16")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type/17")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type/18")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type/19")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type/20")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type/21")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type/22")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type/23")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# we don't have special support for call-by-reference; just explicitly create
# a new variable with the address of the arg
test-convert-function-call-with-arg-of-user-defined-type-by-reference:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn f {\n")
    (write _test-input-stream "  var a: t\n")
    (write _test-input-stream "  var b/eax: (addr t) <- address a\n")
    (write _test-input-stream "  foo b\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "fn foo x: (addr t) {\n")
    (write _test-input-stream "  var x/ecx: (addr int) <- copy x\n")
    (write _test-input-stream "  increment *x\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "type t {\n")
    (write _test-input-stream "  x: int\n")
    (write _test-input-stream "  y: int\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "f:"                      "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/4")
    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:loop:"     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/5")
    # var a: t
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/6")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/7")
    # var b/eax: (addr t)
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/8")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffff8) 0x00000000/r32"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/9")
    # foo a
    (check-next-stream-line-equal _test-output-stream "    (foo %eax)"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/10")
    #
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax" "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/11")
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/12")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/13")
    (check-next-stream-line-equal _test-output-stream "$f:0x00000001:break:"    "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/14")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/15")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/16")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/17")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/18")
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/19")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/20")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/21")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/22")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/23")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:"   "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/24")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/25")
    (check-next-stream-line-equal _test-output-stream "    89/<- %ecx 0x00000001/r32"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/26")
    (check-next-stream-line-equal _test-output-stream "    ff 0/subop/increment *ecx"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/27")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/28")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/29")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/30")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/31")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/32")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/33")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-call-with-arg-of-user-defined-type-by-reference/34")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-get-of-type-on-stack:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var a: t\n")
    (write _test-input-stream "  var c/ecx: (addr int) <- get a, y\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "type t {\n")
    (write _test-input-stream "  x: int\n")
    (write _test-input-stream "  y: int\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                    "F - test-convert-get-of-type-on-stack/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-get-of-type-on-stack/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-get-of-type-on-stack/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-get-of-type-on-stack/3")
    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-get-of-type-on-stack/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"   "F - test-convert-get-of-type-on-stack/5")
    # var a
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/6")
    (check-next-stream-line-equal _test-output-stream "    68/push 0/imm32"     "F - test-convert-get-of-type-on-stack/7")
    # var c
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"  "F - test-convert-get-of-type-on-stack/8")
    # get
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(ebp+0xfffffffc) 0x00000001/r32"  "F - test-convert-get-of-type-on-stack/9")
    # reclaim c
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx" "F - test-convert-get-of-type-on-stack/10")
    # reclaim a
    (check-next-stream-line-equal _test-output-stream "    81 0/subop/add %esp 0x00000008/imm32"  "F - test-convert-get-of-type-on-stack/11")
    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-get-of-type-on-stack/12")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"  "F - test-convert-get-of-type-on-stack/13")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-get-of-type-on-stack/14")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-get-of-type-on-stack/15")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-get-of-type-on-stack/16")
    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-get-of-type-on-stack/17")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-convert-array-of-user-defined-types:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (clear-stream $_test-input-buffered-file->buffer)
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    #
    (write _test-input-stream "type t {\n")  # each t is 8 bytes, which is a power of 2
    (write _test-input-stream "  x: int\n")
    (write _test-input-stream "  y: int\n")
    (write _test-input-stream "}\n")
    (write _test-input-stream "fn foo {\n")
    (write _test-input-stream "  var arr/eax: (addr array t) <- copy 0\n")
    (write _test-input-stream "  var idx/ecx: int <- copy 3\n")
    (write _test-input-stream "  var x/eax: (addr int) <- index arr, idx\n")
    (write _test-input-stream "}\n")
    # convert
    (convert-mu _test-input-buffered-file _test-output-buffered-file)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "foo:"                                        "F - test-convert-array-of-user-defined-types/0")
    (check-next-stream-line-equal _test-output-stream "  # . prologue"                              "F - test-convert-array-of-user-defined-types/1")
    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"                               "F - test-convert-array-of-user-defined-types/2")
    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"                      "F - test-convert-array-of-user-defined-types/3")
    (check-next-stream-line-equal _test-output-stream "  {"                                         "F - test-convert-array-of-user-defined-types/4")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:"                       "F - test-convert-array-of-user-defined-types/5")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %eax"                    "F - test-convert-array-of-user-defined-types/6")
    (check-next-stream-line-equal _test-output-stream "    b8/copy-to-eax 0/imm32"                  "F - test-convert-array-of-user-defined-types/7")
    (check-next-stream-line-equal _test-output-stream "    ff 6/subop/push %ecx"                    "F - test-convert-array-of-user-defined-types/8")
    (check-next-stream-line-equal _test-output-stream "    b9/copy-to-ecx 3/imm32"                  "F - test-convert-array-of-user-defined-types/9")
    (check-next-stream-line-equal _test-output-stream "    8d/copy-address *(eax + ecx<<0x00000003 + 4) 0x00000000/r32"  "F - test-convert-array-of-user-defined-types/11")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %ecx"                     "F - test-convert-array-of-user-defined-types/13")
    (check-next-stream-line-equal _test-output-stream "    8f 0/subop/pop %eax"                     "F - test-convert-array-of-user-defined-types/14")
    (check-next-stream-line-equal _test-output-stream "  }"                                         "F - test-convert-array-of-user-defined-types/15")
    (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:"                      "F - test-convert-array-of-user-defined-types/16")
    (check-next-stream-line-equal _test-output-stream "  # . epilogue"                              "F - test-convert-array-of-user-defined-types/17")
    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"                      "F - test-convert-array-of-user-defined-types/18")
    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"                             "F - test-convert-array-of-user-defined-types/19")
    (check-next-stream-line-equal _test-output-stream "  c3/return"                                 "F - test-convert-array-of-user-defined-types/20")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

#######################################################
# Parsing
#######################################################

parse-mu:  # in: (addr buffered-file)
    # pseudocode
    #   var curr-function: (addr handle function) = Program->functions
    #   var curr-type: (addr handle typeinfo) = Program->types
    #   var line: (stream byte 512)
    #   var word-slice: slice
    #   while true                                  # line loop
    #     clear-stream(line)
    #     read-line-buffered(in, line)
    #     if (line->write == 0) break               # end of file
    #     word-slice = next-mu-token(line)
    #     if slice-empty?(word-slice)               # end of line
    #       continue
    #     else if slice-starts-with?(word-slice, "#")  # comment
    #       continue                                # end of line
    #     else if slice-equal?(word-slice, "fn")
    #       var new-function: (handle function) = allocate(function)
    #       var vars: (stack (handle var) 256)
    #       populate-mu-function-header(line, new-function, vars)
    #       populate-mu-function-body(in, new-function, vars)
    #       assert(vars->top == 0)
    #       *curr-function = new-function
    #       curr-function = &new-function->next
    #     else if slice-equal?(word-slice, "type")
    #       word-slice = next-mu-token(line)
    #       type-id = pos-or-insert-slice(Type-id, word-slice)
    #       var new-type: (handle typeinfo) = find-or-create-typeinfo(type-id)
    #       assert(next-word(line) == "{")
    #       populate-mu-type(in, new-type)
    #     else
    #       abort()
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # var line/ecx: (stream byte 512)
    81 5/subop/subtract %esp 0x200/imm32
    68/push 0x200/imm32/size
    68/push 0/imm32/read
    68/push 0/imm32/write
    89/<- %ecx 4/r32/esp
    # var word-slice/edx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %edx 4/r32/esp
    # var curr-function/edi: (addr handle function)
    bf/copy-to-edi _Program-functions/imm32
    # var vars/ebx: (stack (handle var) 256)
    81 5/subop/subtract %esp 0x800/imm32
    68/push 0x800/imm32/size
    68/push 0/imm32/top
    89/<- %ebx 4/r32/esp
    {
$parse-mu:line-loop:
      (clear-stream %ecx)
      (read-line-buffered *(ebp+8) %ecx)
      # if (line->write == 0) break
      81 7/subop/compare *ecx 0/imm32
      0f 84/jump-if-= break/disp32
#?       # dump line {{{
#?       (write 2 "parse-mu: ^")
#?       (write-stream 2 %ecx)
#?       (write 2 "$\n")
#?       (rewind-stream %ecx)
#?       # }}}
      (next-mu-token %ecx %edx)
      # if slice-empty?(word-slice) continue
      (slice-empty? %edx)  # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= loop/disp32
      # if (*word-slice->start == "#") continue
      # . eax = *word-slice->start
      8b/-> *edx 0/r32/eax
      8a/copy-byte *eax 0/r32/AL
      81 4/subop/and %eax 0xff/imm32
      # . if (eax == '#') continue
      3d/compare-eax-and 0x23/imm32/hash
      0f 84/jump-if-= loop/disp32
      # if (slice-equal?(word-slice, "fn")) parse a function
      {
$parse-mu:fn:
        (slice-equal? %edx "fn")  # => eax
        3d/compare-eax-and 0/imm32/false
        0f 84/jump-if-= break/disp32
        # var new-function/esi: (handle function)
        68/push 0/imm32
        68/push 0/imm32
        89/<- %esi 4/r32/esp
        # populate-mu-function(line, in, vars, new-function)
        (allocate Heap *Function-size %esi)
        # var new-function-addr/eax: (addr function)
        (lookup *esi *(esi+4))  # => eax
        (clear-stack %ebx)
        (populate-mu-function-header %ecx %eax %ebx)
        (populate-mu-function-body *(ebp+8) %eax %ebx)
        # *curr-function = new-function
        8b/-> *esi 0/r32/eax
        89/<- *edi 0/r32/eax
        8b/-> *(esi+4) 0/r32/eax
        89/<- *(edi+4) 0/r32/eax
        # curr-function = &new-function->next
        # . var tmp/eax: (addr function) = lookup(new-function)
        (lookup *esi *(esi+4))  # => eax
        # . curr-function = &tmp->next
        8d/copy-address *(eax+0x28) 7/r32/edi  # Function-next
        # reclaim new-function
        81 0/subop/add %esp 8/imm32
        #
        e9/jump $parse-mu:line-loop/disp32
      }
      # if (slice-equal?(word-slice, "type")) parse a type (struct/record) definition
      {
$parse-mu:type:
        (slice-equal? %edx "type")  # => eax
        3d/compare-eax-and 0/imm32
        0f 84/jump-if-= break/disp32
        (next-mu-token %ecx %edx)
        # var type-id/eax: int
        (pos-or-insert-slice Type-id %edx)  # => eax
        # spill
        51/push-ecx
        # var new-type/ecx: (handle typeinfo)
        68/push 0/imm32
        68/push 0/imm32
        89/<- %ecx 4/r32/esp
        (find-or-create-typeinfo %eax %ecx)
        #
        (lookup *ecx *(ecx+4))  # => eax
        # TODO: ensure that 'line' has nothing else but '{'
        (populate-mu-type *(ebp+8) %eax)  # => eax
        # reclaim new-type
        81 0/subop/add %esp 8/imm32
        # restore
        59/pop-to-ecx
        e9/jump $parse-mu:line-loop/disp32
      }
      # otherwise abort
      e9/jump $parse-mu:error1/disp32
    } # end line loop
$parse-mu:end:
    # . reclaim locals
    81 0/subop/add %esp 0x630/imm32
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$parse-mu:error1:
    # error("unexpected top-level command: " word-slice "\n")
    (write-buffered Stderr "unexpected top-level command: ")
    (write-slice-buffered Stderr %edx)
    (write-buffered Stderr "\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

$parse-mu:error2:
    # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n")
    (print-int32-buffered Stderr *ebx)
    (write-buffered Stderr " vars not reclaimed after fn '")
    (write-slice-buffered Stderr *eax)  # Function-name
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

# scenarios considered:
# ✗ fn foo  # no block
# ✓ fn foo {
# ✗ fn foo { {
# ✗ fn foo { }
# ✗ fn foo { } {
# ✗ fn foo x {
# ✗ fn foo x: {
# ✓ fn foo x: int {
# ✓ fn foo x: int {
# ✓ fn foo x: int -> y/eax: int {
populate-mu-function-header:  # first-line: (addr stream byte), out: (addr function), vars: (addr stack (handle var))
    # pseudocode:
    #   var name: slice
    #   next-mu-token(first-line, name)
    #   assert(name not in '{' '}' '->')
    #   out->name = slice-to-string(name)
    #   ## inouts
    #   while true
    #     ## name
    #     name = next-mu-token(first-line)
    #     if (name == '{') goto done
    #     if (name == '->') break
    #     assert(name != '}')
    #     var v: (handle var) = parse-var-with-type(name, first-line)
    #     assert(v->register == null)
    #     # v->block-depth is implicitly 0
    #     out->inouts = append(v, out->inouts)
    #     push(vars, v)
    #   ## outputs
    #   while true
    #     ## name
    #     name = next-mu-token(first-line)
    #     assert(name not in '{' '}' '->')
    #     var v: (handle var) = parse-var-with-type(name, first-line)
    #     assert(v->register != null)
    #     out->outputs = append(v, out->outputs)
    #   done:
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    57/push-edi
    # edi = out
    8b/-> *(ebp+0xc) 7/r32/edi
    # var word-slice/ecx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %ecx 4/r32/esp
    # var v/ebx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ebx 4/r32/esp
    # read function name
    (next-mu-token *(ebp+8) %ecx)
    # error checking
    # TODO: error if name starts with 'break' or 'loop'
    # if (word-slice == '{') abort
    (slice-equal? %ecx "{")   # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
    # if (word-slice == '->') abort
    (slice-equal? %ecx "->")   # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
    # if (word-slice == '}') abort
    (slice-equal? %ecx "}")   # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
    # save function name
    (slice-to-string Heap %ecx %edi)  # Function-name
    # save function inouts
    {
$populate-mu-function-header:check-for-inout:
      (next-mu-token *(ebp+8) %ecx)
      # if (word-slice == '{') goto done
      (slice-equal? %ecx "{")   # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= $populate-mu-function-header:done/disp32
      # if (word-slice == '->') break
      (slice-equal? %ecx "->")   # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= break/disp32
      # if (word-slice == '}') abort
      (slice-equal? %ecx "}")   # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
      # v = parse-var-with-type(word-slice, first-line)
      (parse-var-with-type %ecx *(ebp+8) %ebx)
      # assert(v->register == null)
      # . eax: (addr var) = lookup(v)
      (lookup *ebx *(ebx+4))  # => eax
      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
      0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
      # v->block-depth is implicitly 0
      #
      # out->inouts = append(v, out->inouts)
      8d/copy-address *(edi+8) 0/r32/eax  # Function-inouts
      (append-list Heap  *ebx *(ebx+4)  *(edi+8) *(edi+0xc)  %eax)  # Function-inouts, Function-inouts
      # push(vars, v)
      (push *(ebp+0x10) *ebx)
      (push *(ebp+0x10) *(ebx+4))
      #
      e9/jump loop/disp32
    }
    # save function outputs
    {
$populate-mu-function-header:check-for-out:
      (next-mu-token *(ebp+8) %ecx)
      # if (word-slice == '{') break
      (slice-equal? %ecx "{")   # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= break/disp32
      # if (word-slice == '->') abort
      (slice-equal? %ecx "->")   # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
      # if (word-slice == '}') abort
      (slice-equal? %ecx "}")   # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= $populate-mu-function-header:error1/disp32
      # v = parse-var-with-type(word-slice, first-line)
      (parse-var-with-type %ecx *(ebp+8) %ebx)
      # assert(var->register != null)
      # . eax: (addr var) = lookup(v)
      (lookup *ebx *(ebx+4))  # => eax
      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
      0f 84/jump-if-= $populate-mu-function-header:error3/disp32
      # out->outputs = append(v, out->outputs)
      8d/copy-address *(edi+0x10) 0/r32/eax  # Function-outputs
      (append-list Heap  *ebx *(ebx+4)  *(edi+0x10) *(edi+0x14)  %eax)  # Function-outputs, Function-outputs
      #
      e9/jump loop/disp32
    }
$populate-mu-function-header:done:
    (check-no-tokens-left *(ebp+8))
$populate-mu-function-header:end:
    # . reclaim locals
    81 0/subop/add %esp 0x10/imm32
    # . restore registers
    5f/pop-to-edi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$populate-mu-function-header:error1:
    # error("function header not in form 'fn <name> {'")
    (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '")
    (flush Stderr)
    (rewind-stream *(ebp+8))
    (write-stream 2 *(ebp+8))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

$populate-mu-function-header:error2:
    # error("function input '" var "' cannot be in a register")
    (write-buffered Stderr "function input '")
    (write-buffered Stderr *ebx)  # Var-name
    (write-buffered Stderr "' cannot be in a register")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

$populate-mu-function-header:error3:
    # error("function input '" var "' must be in a register")
    (write-buffered Stderr "function input '")
    (lookup *ebx *(ebx+4))  # => eax
    (lookup *eax *(eax+4))  # Var-name Var-name => eax
    (write-buffered Stderr %eax)
    (write-buffered Stderr "' must be in a register, in instruction '")
    (flush Stderr)
    (rewind-stream *(ebp+8))
    (write-stream 2 *(ebp+8))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

test-function-header-with-arg:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (write _test-input-stream "foo n: int {\n")
    # var result/ecx: function
    2b/subtract *Function-size 4/r32/esp
    89/<- %ecx 4/r32/esp
    (zero-out %ecx *Function-size)
    # var vars/ebx: (stack (handle var) 16)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/size
    68/push 0/imm32/top
    89/<- %ebx 4/r32/esp
    # convert
    (populate-mu-function-header _test-input-stream %ecx %ebx)
    # check result->name
    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
    (check-strings-equal %eax "foo" "F - test-function-header-with-arg/name")
    # var v/edx: (addr var) = result->inouts->value
    (lookup *(ecx+8) *(ecx+0xc))  # Function-inouts Function-inouts => eax
    (lookup *eax *(eax+4))  # List-value List-value => eax
    89/<- %edx 0/r32/eax
    # check v->name
    (lookup *edx *(edx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "n" "F - test-function-header-with-arg/inout:0")
    # check v->type
    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-arg/inout:0/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-arg/inout:0/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-arg/inout:0/type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-function-header-with-multiple-args:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (write _test-input-stream "foo a: int, b: int c: int {\n")
    # result/ecx: function
    2b/subtract *Function-size 4/r32/esp
    89/<- %ecx 4/r32/esp
    (zero-out %ecx *Function-size)
    # var vars/ebx: (stack (handle var) 16)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/size
    68/push 0/imm32/top
    89/<- %ebx 4/r32/esp
    # convert
    (populate-mu-function-header _test-input-stream %ecx %ebx)
    # check result->name
    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
    (check-strings-equal %eax "foo" "F - test-function-header-with-multiple-args/name")
    # var inouts/edx: (addr list var) = lookup(result->inouts)
    (lookup *(ecx+8) *(ecx+0xc))  # Function-inouts Function-inouts => eax
    89/<- %edx 0/r32/eax
$test-function-header-with-multiple-args:inout0:
    # var v/ebx: (addr var) = lookup(inouts->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args/inout:0/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args/inout:0/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args/inout:0/type:2")  # Tree-right
$test-function-header-with-multiple-args:inout1:
    # inouts = lookup(inouts->next)
    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
    89/<- %edx 0/r32/eax
    # v = lookup(inouts->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "b" "F - test-function-header-with-multiple-args/inout:1")  # Var-name
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args/inout:1/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args/inout:1/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args/inout:1/type:2")  # Tree-right
$test-function-header-with-multiple-args:inout2:
    # inouts = lookup(inouts->next)
    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
    89/<- %edx 0/r32/eax
    # v = lookup(inouts->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args/inout:2/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args/inout:2/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args/inout:2/type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-function-header-with-multiple-args-and-outputs:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx: int {\n")
    # result/ecx: function
    2b/subtract *Function-size 4/r32/esp
    89/<- %ecx 4/r32/esp
    (zero-out %ecx *Function-size)
    # var vars/ebx: (stack (handle var) 16)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/size
    68/push 0/imm32/top
    89/<- %ebx 4/r32/esp
    # convert
    (populate-mu-function-header _test-input-stream %ecx %ebx)
    # check result->name
    (lookup *ecx *(ecx+4))  # Function-name Function-name => eax
    (check-strings-equal %eax "foo" "F - test-function-header-with-multiple-args-and-outputs/name")
    # var inouts/edx: (addr list var) = lookup(result->inouts)
    (lookup *(ecx+8) *(ecx+0xc))  # Function-inouts Function-inouts => eax
    89/<- %edx 0/r32/eax
$test-function-header-with-multiple-args-and-outputs:inout0:
    # var v/ebx: (addr var) = lookup(inouts->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "a" "F - test-function-header-with-multiple-args-and-outputs/inout:0")
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:0/type:2")  # Tree-right
$test-function-header-with-multiple-args-and-outputs:inout1:
    # inouts = lookup(inouts->next)
    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
    89/<- %edx 0/r32/eax
    # v = lookup(inouts->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "b" "F - test-function-header-with-multiple-args-and-outputs/inout:1")
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:1/type:2")  # Tree-right
$test-function-header-with-multiple-args-and-outputs:inout2:
    # inouts = lookup(inouts->next)
    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
    89/<- %edx 0/r32/eax
    # v = lookup(inouts->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "c" "F - test-function-header-with-multiple-args-and-outputs/inout:2")
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/inout:2/type:2")  # Tree-right
$test-function-header-with-multiple-args-and-outputs:out0:
    # var outputs/edx: (addr list var) = lookup(result->outputs)
    (lookup *(ecx+0x10) *(ecx+0x14))  # Function-outputs Function-outputs => eax
    89/<- %edx 0/r32/eax
    # v = lookup(outputs->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "x" "F - test-function-header-with-multiple-args-and-outputs/output:0")
    # check v->register
    (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
    (check-strings-equal %eax "ecx" "F - test-function-header-with-multiple-args-and-outputs/output:0/register")
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-function-header-with-multiple-args-and-outputs/output:0/type:2")  # Tree-right
$test-function-header-with-multiple-args-and-outputs:out1:
    # outputs = lookup(outputs->next)
    (lookup *(edx+8) *(edx+0xc))  # List-next List-next => eax
    89/<- %edx 0/r32/eax
    # v = lookup(inouts->value)
    (lookup *edx *(edx+4))  # List-value List-value => eax
    89/<- %ebx 0/r32/eax
    # check v->name
    (lookup *ebx *(ebx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "y" "F - test-function-header-with-multiple-args-and-outputs/output:1")
    # check v->register
    (lookup *(ebx+0x18) *(ebx+0x1c))  # Var-register Var-register => eax
    (check-strings-equal %eax "edx" "F - test-function-header-with-multiple-args-and-outputs/output:1/register")
    # check v->type
    (lookup *(ebx+8) *(ebx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:1")  # Tree-value
    (check-ints-equal *(eax+0c) 0 "F - test-function-header-with-multiple-args-and-outputs/output:1/type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# format for variables with types
#   x: int
#   x: int,
#   x/eax: int
#   x/eax: int,
# ignores at most one trailing comma
# WARNING: modifies name
parse-var-with-type:  # name: (addr slice), first-line: (addr stream byte), out: (addr handle var)
    # pseudocode:
    #   var s: slice
    #   if (!slice-ends-with(name, ":"))
    #     abort
    #   --name->end to skip ':'
    #   next-token-from-slice(name->start, name->end, '/', s)
    #   new-var-from-slice(s, out)
    #   ## register
    #   next-token-from-slice(s->end, name->end, '/', s)
    #   if (!slice-empty?(s))
    #     out->register = slice-to-string(s)
    #   ## type
    #   var type: (handle tree type-id) = parse-type(first-line)
    #   out->type = type
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # esi = name
    8b/-> *(ebp+8) 6/r32/esi
    # if (!slice-ends-with?(name, ":")) abort
    8b/-> *(esi+4) 1/r32/ecx  # Slice-end
    49/decrement-ecx
    8a/copy-byte *ecx 1/r32/CL
    81 4/subop/and %ecx 0xff/imm32
    81 7/subop/compare %ecx 0x3a/imm32/colon
    0f 85/jump-if-!= $parse-var-with-type:abort/disp32
    # --name->end to skip ':'
    ff 1/subop/decrement *(esi+4)
    # var s/ecx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %ecx 4/r32/esp
$parse-var-with-type:parse-name:
    (next-token-from-slice *esi *(esi+4) 0x2f %ecx)  # Slice-start, Slice-end, '/'
$parse-var-with-type:create-var:
    # new-var-from-slice(s, out)
    (new-var-from-slice Heap %ecx *(ebp+0x10))
    # save out->register
$parse-var-with-type:save-register:
    # . var out-addr/edi: (addr var) = lookup(*out)
    8b/-> *(ebp+0x10) 7/r32/edi
    (lookup *edi *(edi+4))  # => eax
    89/<- %edi 0/r32/eax
    # . s = next-token(...)
    (next-token-from-slice *(ecx+4) *(esi+4) 0x2f %ecx)  # s->end, name->end, '/'
    # . if (!slice-empty?(s)) out->register = slice-to-string(s)
    {
$parse-var-with-type:write-register:
      (slice-empty? %ecx)  # => eax
      3d/compare-eax-and 0/imm32/false
      75/jump-if-!= break/disp8
      # out->register = slice-to-string(s)
      8d/copy-address *(edi+0x18) 0/r32/eax  # Var-register
      (slice-to-string Heap %ecx %eax)
    }
$parse-var-with-type:save-type:
    8d/copy-address *(edi+8) 0/r32/eax  # Var-type
    (parse-type Heap *(ebp+0xc) %eax)
$parse-var-with-type:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$parse-var-with-type:abort:
    # error("var should have form 'name: type' in '" line "'\n")
    (write-buffered Stderr "var should have form 'name: type' in '")
    (flush Stderr)
    (rewind-stream *(ebp+0xc))
    (write-stream 2 *(ebp+0xc))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

parse-type:  # ad: (addr allocation-descriptor), in: (addr stream byte), out: (addr handle tree type-id)
    # pseudocode:
    #   var s: slice = next-mu-token(in)
    #   assert s != ""
    #   assert s != "->"
    #   assert s != "{"
    #   assert s != "}"
    #   if s == ")"
    #     return
    #   out = allocate(Tree)
    #   if s != "("
    #     HACK: if s is an int, parse and return it
    #     out->left-is-atom? = true
    #     out->value = pos-or-insert-slice(Type-id, s)
    #     return
    #   out->left = parse-type(ad, in)
    #   out->right = parse-type-tree(ad, in)
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    # clear out
    (zero-out *(ebp+0x10) *Handle-size)
    # var s/ecx: slice
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ecx 4/r32/esp
    # s = next-mu-token(in)
    (next-mu-token *(ebp+0xc) %ecx)
#?     (write-buffered Stderr "tok: ")
#?     (write-slice-buffered Stderr %ecx)
#?     (write-buffered Stderr "$\n")
#?     (flush Stderr)
    # assert s != ""
    (slice-equal? %ecx "")  # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $parse-type:abort/disp32
    # assert s != "{"
    (slice-equal? %ecx "{")  # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $parse-type:abort/disp32
    # assert s != "}"
    (slice-equal? %ecx "}")  # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $parse-type:abort/disp32
    # assert s != "->"
    (slice-equal? %ecx "->")  # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $parse-type:abort/disp32
    # if (s == ")") return
    (slice-equal? %ecx ")")  # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $parse-type:end/disp32
    # out = new tree
    (allocate *(ebp+8) *Tree-size *(ebp+0x10))
    # var out-addr/edx: (addr tree type-id) = lookup(*out)
    8b/-> *(ebp+0x10) 2/r32/edx
    (lookup *edx *(edx+4))  # => eax
    89/<- %edx 0/r32/eax
    {
      # if (s != "(") break
      (slice-equal? %ecx "(")  # => eax
      3d/compare-eax-and 0/imm32/false
      75/jump-if-!= break/disp8
      # EGREGIOUS HACK for static array sizes: if s is a number, parse it
      {
$parse-type:check-for-int:
        (is-hex-int? %ecx)  # => eax
        3d/compare-eax-and 0/imm32/false
        74/jump-if-= break/disp8
$parse-type:int:
        (parse-hex-int-from-slice %ecx)  # => eax
        89/<- *(edx+4) 0/r32/eax  # Tree-value
        e9/jump $parse-type:end/disp32
      }
$parse-type:atom:
      # out->left-is-atom? = true
      c7 0/subop/copy *edx 1/imm32/true  # Tree-is-atom
      # out->value = pos-or-insert-slice(Type-id, s)
      (pos-or-insert-slice Type-id %ecx)  # => eax
      89/<- *(edx+4) 0/r32/eax  # Tree-value
      e9/jump $parse-type:end/disp32
    }
$parse-type:non-atom:
    # otherwise s == "("
    # out->left = parse-type(ad, in)
    8d/copy-address *(edx+4) 0/r32/eax  # Tree-left
    (parse-type *(ebp+8) *(ebp+0xc) %eax)
    # out->right = parse-type-tree(ad, in)
    8d/copy-address *(edx+0xc) 0/r32/eax  # Tree-right
    (parse-type-tree *(ebp+8) *(ebp+0xc) %eax)
$parse-type:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$parse-type:abort:
    # error("unexpected token when parsing type: '" s "'\n")
    (write-buffered Stderr "unexpected token when parsing type: '")
    (write-slice-buffered Stderr %ecx)
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

parse-type-tree:  # ad: (addr allocation-descriptor), in: (addr stream byte), out: (addr handle tree type-id)
    # pseudocode:
    #   var tmp: (handle tree type-id) = parse-type(ad, in)
    #   if tmp == 0
    #     return 0
    #   out = allocate(Tree)
    #   out->left = tmp
    #   out->right = parse-type-tree(ad, in)
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    #
    (zero-out *(ebp+0x10) *Handle-size)
    # var tmp/ecx: (handle tree type-id)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ecx 4/r32/esp
    # tmp = parse-type(ad, in)
    (parse-type *(ebp+8) *(ebp+0xc) %ecx)
    # if (tmp == 0) return
    81 7/subop/compare *ecx 0/imm32
    74/jump-if-= $parse-type-tree:end/disp8
    # out = new tree
    (allocate *(ebp+8) *Tree-size *(ebp+0x10))
    # var out-addr/edx: (addr tree) = lookup(*out)
    8b/-> *(ebp+0x10) 2/r32/edx
    (lookup *edx *(edx+4))  # => eax
    89/<- %edx 0/r32/eax
    # out->left = tmp
    8b/-> *ecx 0/r32/eax
    89/<- *(edx+4) 0/r32/eax  # Tree-left
    8b/-> *(ecx+4) 0/r32/eax
    89/<- *(edx+8) 0/r32/eax  # Tree-left
    # out->right = parse-type-tree(ad, in)
    8d/copy-address *(edx+0xc) 0/r32/eax  # Tree-right
    (parse-type-tree *(ebp+8) *(ebp+0xc) %eax)
$parse-type-tree:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

next-mu-token:  # in: (addr stream byte), out: (addr slice)
    # pseudocode:
    # start:
    #   skip-chars-matching-whitespace(in)
    #   if in->read >= in->write              # end of in
    #     out = {0, 0}
    #     return
    #   out->start = &in->data[in->read]
    #   var curr-byte/eax: byte = in->data[in->read]
    #   if curr->byte == ','                  # comment token
    #     ++in->read
    #     goto start
    #   if curr-byte == '#'                   # comment
    #     goto done                             # treat as eof
    #   if curr-byte == '"'                   # string literal
    #     skip-string(in)
    #     goto done                           # no metadata
    #   if curr-byte == '('
    #     ++in->read
    #     goto done
    #   if curr-byte == ')'
    #     ++in->read
    #     goto done
    #   # read a word
    #   while true
    #     if in->read >= in->write
    #       break
    #     curr-byte = in->data[in->read]
    #     if curr-byte == ' '
    #       break
    #     if curr-byte == '\r'
    #       break
    #     if curr-byte == '\n'
    #       break
    #     if curr-byte == '('
    #       break
    #     if curr-byte == ')'
    #       break
    #     if curr-byte == ','
    #       break
    #     ++in->read
    # done:
    #   out->end = &in->data[in->read]
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    57/push-edi
    # esi = in
    8b/-> *(ebp+8) 6/r32/esi
    # edi = out
    8b/-> *(ebp+0xc) 7/r32/edi
$next-mu-token:start:
    (skip-chars-matching-whitespace %esi)
$next-mu-token:check0:
    # if (in->read >= in->write) return out = {0, 0}
    # . ecx = in->read
    8b/-> *(esi+4) 1/r32/ecx
    # . if (ecx >= in->write) return out = {0, 0}
    3b/compare<- *esi 1/r32/ecx
    c7 0/subop/copy *edi 0/imm32
    c7 0/subop/copy *(edi+4) 0/imm32
    0f 8d/jump-if->= $next-mu-token:end/disp32
    # out->start = &in->data[in->read]
    8d/copy-address *(esi+ecx+0xc) 0/r32/eax
    89/<- *edi 0/r32/eax
    # var curr-byte/eax: byte = in->data[in->read]
    31/xor-with %eax 0/r32/eax
    8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
    {
$next-mu-token:check-for-comma:
      # if (curr-byte != ',') break
      3d/compare-eax-and 0x2c/imm32/comma
      75/jump-if-!= break/disp8
      # ++in->read
      ff 0/subop/increment *(esi+4)
      # restart
      e9/jump $next-mu-token:start/disp32
    }
    {
$next-mu-token:check-for-comment:
      # if (curr-byte != '#') break
      3d/compare-eax-and 0x23/imm32/pound
      75/jump-if-!= break/disp8
      # return eof
      e9/jump $next-mu-token:done/disp32
    }
    {
$next-mu-token:check-for-string-literal:
      # if (curr-byte != '"') break
      3d/compare-eax-and 0x22/imm32/dquote
      75/jump-if-!= break/disp8
      (skip-string %esi)
      # return
      e9/jump $next-mu-token:done/disp32
    }
    {
$next-mu-token:check-for-open-paren:
      # if (curr-byte != '(') break
      3d/compare-eax-and 0x28/imm32/open-paren
      75/jump-if-!= break/disp8
      # ++in->read
      ff 0/subop/increment *(esi+4)
      # return
      e9/jump $next-mu-token:done/disp32
    }
    {
$next-mu-token:check-for-close-paren:
      # if (curr-byte != ')') break
      3d/compare-eax-and 0x29/imm32/close-paren
      75/jump-if-!= break/disp8
      # ++in->read
      ff 0/subop/increment *(esi+4)
      # return
      e9/jump $next-mu-token:done/disp32
    }
    {
$next-mu-token:regular-word-without-metadata:
      # if (in->read >= in->write) break
      # . ecx = in->read
      8b/-> *(esi+4) 1/r32/ecx
      # . if (ecx >= in->write) break
      3b/compare<- *esi 1/r32/ecx
      7d/jump-if->= break/disp8
      # var c/eax: byte = in->data[in->read]
      31/xor-with %eax 0/r32/eax
      8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
      # if (c == ' ') break
      3d/compare-eax-and 0x20/imm32/space
      74/jump-if-= break/disp8
      # if (c == '\r') break
      3d/compare-eax-and 0xd/imm32/carriage-return
      74/jump-if-= break/disp8
      # if (c == '\n') break
      3d/compare-eax-and 0xa/imm32/newline
      74/jump-if-= break/disp8
      # if (c == '(') break
      3d/compare-eax-and 0x28/imm32/open-paren
      0f 84/jump-if-= break/disp32
      # if (c == ')') break
      3d/compare-eax-and 0x29/imm32/close-paren
      0f 84/jump-if-= break/disp32
      # if (c == ',') break
      3d/compare-eax-and 0x2c/imm32/comma
      0f 84/jump-if-= break/disp32
      # ++in->read
      ff 0/subop/increment *(esi+4)
      #
      e9/jump loop/disp32
    }
$next-mu-token:done:
    # out->end = &in->data[in->read]
    8b/-> *(esi+4) 1/r32/ecx
    8d/copy-address *(esi+ecx+0xc) 0/r32/eax
    89/<- *(edi+4) 0/r32/eax
$next-mu-token:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

pos-or-insert-slice:  # arr: (addr stream (addr array byte)), s: (addr slice) -> index/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # if (pos-slice(arr, s) != -1) return it
    (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
    3d/compare-eax-and -1/imm32
    75/jump-if-!= $pos-or-insert-slice:end/disp8
$pos-or-insert-slice:insert:
    # var s2/eax: (handle array byte)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %eax 4/r32/esp
    (slice-to-string Heap *(ebp+0xc) %eax)
    # throw away alloc-id
    (lookup *eax *(eax+4))  # => eax
    (write-int *(ebp+8) %eax)
    (pos-slice *(ebp+8) *(ebp+0xc))  # => eax
$pos-or-insert-slice:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# return the index in an array of strings matching 's', -1 if not found
# index is denominated in elements, not bytes
pos-slice:  # arr: (addr stream (addr array byte)), s: (addr slice) -> index/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
#?     (write-buffered Stderr "pos-slice: ")
#?     (write-slice-buffered Stderr *(ebp+0xc))
#?     (write-buffered Stderr "\n")
#?     (flush Stderr)
    # esi = arr
    8b/-> *(ebp+8) 6/r32/esi
    # var index/ecx: int = 0
    b9/copy-to-ecx 0/imm32
    # var curr/edx: (addr (addr array byte)) = arr->data
    8d/copy-address *(esi+0xc) 2/r32/edx
    # var max/ebx: (addr (addr array byte)) = &arr->data[arr->write]
    8b/-> *esi 3/r32/ebx
    8d/copy-address *(esi+ebx+0xc) 3/r32/ebx
    {
#?       (write-buffered Stderr "  ")
#?       (print-int32-buffered Stderr %ecx)
#?       (write-buffered Stderr "\n")
#?       (flush Stderr)
      # if (curr >= max) return -1
      39/compare %edx 3/r32/ebx
      b8/copy-to-eax -1/imm32
      73/jump-if-addr>= $pos-slice:end/disp8
      # if (slice-equal?(s, *curr)) break
      (slice-equal? *(ebp+0xc) *edx)  # => eax
      3d/compare-eax-and 0/imm32/false
      75/jump-if-!= break/disp8
      # ++index
      41/increment-ecx
      # curr += 4
      81 0/subop/add %edx 4/imm32
      #
      eb/jump loop/disp8
    }
    # return index
    89/<- %eax 1/r32/ecx
$pos-slice:end:
#?     (write-buffered Stderr "=> ")
#?     (print-int32-buffered Stderr %eax)
#?     (write-buffered Stderr "\n")
    # . restore registers
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-var-with-type:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "x:"
    b8/copy-to-eax "x:"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    # _test-input-stream contains "int"
    (clear-stream _test-input-stream)
    (write _test-input-stream "int")
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    #
    (parse-var-with-type %ecx _test-input-stream %edx)
    # var v-addr/edx: (addr var) = lookup(v)
    (lookup *edx *(edx+4))  # => eax
    89/<- %edx 0/r32/eax
    # check v-addr->name
    (lookup *edx *(edx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "x" "F - test-parse-var-with-type/name")
    # check v-addr->type
    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-parse-var-with-type/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-type/type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-type/type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-var-with-type-and-register:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "x/eax:"
    b8/copy-to-eax "x/eax:"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    # _test-input-stream contains "int"
    (clear-stream _test-input-stream)
    (write _test-input-stream "int")
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    #
    (parse-var-with-type %ecx _test-input-stream %edx)
    # var v-addr/edx: (addr var) = lookup(v)
    (lookup *edx *(edx+4))  # => eax
    89/<- %edx 0/r32/eax
    # check v-addr->name
    (lookup *edx *(edx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "x" "F - test-parse-var-with-type-and-register/name")
    # check v-addr->register
    (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
    (check-strings-equal %eax "eax" "F - test-parse-var-with-type-and-register/register")
    # check v-addr->type
    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-parse-var-with-type-and-register/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-type-and-register/type:1")  # Tree-left
    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-type-and-register/type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-var-with-trailing-characters:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "x:"
    b8/copy-to-eax "x:"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    # _test-input-stream contains "int,"
    (clear-stream _test-input-stream)
    (write _test-input-stream "int,")
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    #
    (parse-var-with-type %ecx _test-input-stream %edx)
    # var v-addr/edx: (addr var) = lookup(v)
    (lookup *edx *(edx+4))  # => eax
    89/<- %edx 0/r32/eax
    # check v-addr->name
    (lookup *edx *(edx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "x" "F - test-parse-var-with-trailing-characters/name")
    # check v-addr->register
    (check-ints-equal *(edx+0x18) 0 "F - test-parse-var-with-trailing-characters/register")  # Var-register
    # check v-addr->type
    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-parse-var-with-trailing-characters/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-trailing-characters/type:1")  # Tree-left
    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-trailing-characters/type:1")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-var-with-register-and-trailing-characters:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "x/eax:"
    b8/copy-to-eax "x/eax:"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    # _test-input-stream contains "int,"
    (clear-stream _test-input-stream)
    (write _test-input-stream "int,")
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    #
    (parse-var-with-type %ecx _test-input-stream %edx)
    # var v-addr/edx: (addr var) = lookup(v)
    (lookup *edx *(edx+4))  # => eax
    89/<- %edx 0/r32/eax
    # check v-addr->name
    (lookup *edx *(edx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "x" "F - test-parse-var-with-register-and-trailing-characters/name")
    # check v-addr->register
    (lookup *(edx+0x18) *(edx+0x1c))  # Var-register Var-register => eax
    (check-strings-equal %eax "eax" "F - test-parse-var-with-register-and-trailing-characters/register")
    # check v-addr->type
    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-parse-var-with-register-and-trailing-characters/type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-register-and-trailing-characters/type:1")  # Tree-left
    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-register-and-trailing-characters/type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-var-with-compound-type:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "x:"
    b8/copy-to-eax "x:"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    # _test-input-stream contains "(addr int)"
    (clear-stream _test-input-stream)
    (write _test-input-stream "(addr int)")
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    #
    (parse-var-with-type %ecx _test-input-stream %edx)
    # var v-addr/edx: (addr var) = lookup(v)
    (lookup *edx *(edx+4))  # => eax
    89/<- %edx 0/r32/eax
    # check v-addr->name
    (lookup *edx *(edx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "x" "F - test-parse-var-with-compound-type/name")
    # check v-addr->register
    (check-ints-equal *(edx+0x18) 0 "F - test-parse-var-with-compound-type/register")  # Var-register
    # - check v-addr->type
    # var type/edx: (addr tree type-id) = var->type
    (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
    89/<- %edx 0/r32/eax
    # type is a non-atom
    (check-ints-equal *edx 0 "F - test-parse-var-with-compound-type/type:0")  # Tree-is-atom
    # type->left == atom(addr)
    (lookup *(edx+4) *(edx+8))  # Tree-left Tree-left => eax
    (check-ints-equal *eax 1 "F - test-parse-var-with-compound-type/type:1")  # Tree-is-atom
    (check-ints-equal *(eax+4) 2 "F - test-parse-var-with-compound-type/type:2")  # Tree-value
    # type->right->left == atom(int)
    (lookup *(edx+0xc) *(edx+0x10))  # Tree-right Tree-right => eax
    (lookup *(eax+4) *(eax+8))  # Tree-left Tree-left => eax
    (check-ints-equal *eax 1 "F - test-parse-var-with-compound-type/type:3")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-parse-var-with-compound-type/type:4")  # Tree-value
    # type->right->right == null
    (check-ints-equal *(eax+0xc) 0 "F - test-parse-var-with-compound-type/type:5")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# identifier starts with a letter or '$' or '_'
# no constraints at the moment on later letters
# all we really want to do so far is exclude '{', '}' and '->'
is-identifier?:  # in: (addr slice) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # if (slice-empty?(in)) return false
    (slice-empty? *(ebp+8))  # => eax
    3d/compare-eax-and 0/imm32/false
    75/jump-if-!= $is-identifier?:false/disp8
    # var c/eax: byte = *in->start
    8b/-> *(ebp+8) 0/r32/eax
    8b/-> *eax 0/r32/eax
    8a/copy-byte *eax 0/r32/AL
    81 4/subop/and %eax 0xff/imm32
    # if (c == '$') return true
    3d/compare-eax-and 0x24/imm32/$
    74/jump-if-= $is-identifier?:true/disp8
    # if (c == '_') return true
    3d/compare-eax-and 0x5f/imm32/_
    74/jump-if-= $is-identifier?:true/disp8
    # drop case
    25/and-eax-with 0x5f/imm32
    # if (c < 'A') return false
    3d/compare-eax-and 0x41/imm32/A
    7c/jump-if-< $is-identifier?:false/disp8
    # if (c > 'Z') return false
    3d/compare-eax-and 0x5a/imm32/Z
    7f/jump-if-> $is-identifier?:false/disp8
    # otherwise return true
$is-identifier?:true:
    b8/copy-to-eax 1/imm32/true
    eb/jump $is-identifier?:end/disp8
$is-identifier?:false:
    b8/copy-to-eax 0/imm32/false
$is-identifier?:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-dollar:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "$a"
    b8/copy-to-eax "$a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 1 "F - test-is-identifier-dollar")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-underscore:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "_a"
    b8/copy-to-eax "_a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 1 "F - test-is-identifier-underscore")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-a:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "a$"
    b8/copy-to-eax "a$"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 1 "F - test-is-identifier-a")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-z:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "z$"
    b8/copy-to-eax "z$"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 1 "F - test-is-identifier-z")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-A:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "A$"
    b8/copy-to-eax "A$"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 1 "F - test-is-identifier-A")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-Z:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "Z$"
    b8/copy-to-eax "Z$"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 1 "F - test-is-identifier-Z")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-at:
    # character before 'A' is invalid
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "@a"
    b8/copy-to-eax "@a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 0 "F - test-is-identifier-@")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-square-bracket:
    # character after 'Z' is invalid
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "[a"
    b8/copy-to-eax "[a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 0 "F - test-is-identifier-@")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-backtick:
    # character before 'a' is invalid
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "`a"
    b8/copy-to-eax "`a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 0 "F - test-is-identifier-backtick")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-curly-brace-open:
    # character after 'z' is invalid; also used for blocks
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "{a"
    b8/copy-to-eax "{a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-open")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-curly-brace-close:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "}a"
    b8/copy-to-eax "}a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 0 "F - test-is-identifier-curly-brace-close")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-is-identifier-hyphen:
    # disallow leading '-' since '->' has special meaning
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # (eax..ecx) = "-a"
    b8/copy-to-eax "-a"/imm32
    8b/-> *eax 1/r32/ecx
    8d/copy-address *(eax+ecx+4) 1/r32/ecx
    05/add-to-eax 4/imm32
    # var slice/ecx: slice = {eax, ecx}
    51/push-ecx
    50/push-eax
    89/<- %ecx 4/r32/esp
    #
    (is-identifier? %ecx)
    (check-ints-equal %eax 0 "F - test-is-identifier-hyphen")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

populate-mu-function-body:  # in: (addr buffered-file), out: (addr function), vars: (addr stack (handle var))
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    56/push-esi
    57/push-edi
    # esi = in
    8b/-> *(ebp+8) 6/r32/esi
    # edi = out
    8b/-> *(ebp+0xc) 7/r32/edi
    # parse-mu-block(in, vars, out, out->body)
    8d/copy-address *(edi+0x18) 0/r32/eax  # Function-body
    (parse-mu-block %esi *(ebp+0x10) %edi %eax)
$populate-mu-function-body:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# parses a block, assuming that the leading '{' has already been read by the caller
parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle block)
    # pseudocode:
    #   var line: (stream byte 512)
    #   var word-slice: slice
    #   allocate(Heap, Stmt-size, out)
    #   var out-addr: (addr block) = lookup(*out)
    #   out-addr->tag = 0/block
    #   out-addr->var = some unique name
    #   push(vars, out-addr->var)
    #   while true                                  # line loop
    #     clear-stream(line)
    #     read-line-buffered(in, line)
    #     if (line->write == 0) break               # end of file
    #     word-slice = next-mu-token(line)
    #     if slice-empty?(word-slice)               # end of line
    #       continue
    #     else if slice-starts-with?(word-slice, "#")
    #       continue
    #     else if slice-equal?(word-slice, "{")
    #       assert(no-tokens-in(line))
    #       block = parse-mu-block(in, vars, fn)
    #       append-to-block(out-addr, block)
    #     else if slice-equal?(word-slice, "}")
    #       break
    #     else if slice-ends-with?(word-slice, ":")
    #       # TODO: error-check the rest of 'line'
    #       --word-slice->end to skip ':'
    #       named-block = parse-mu-named-block(word-slice, in, vars, fn)
    #       append-to-block(out-addr, named-block)
    #     else if slice-equal?(word-slice, "var")
    #       var-def = parse-mu-var-def(line, vars)
    #       append-to-block(out-addr, var-def)
    #     else
    #       stmt = parse-mu-stmt(line, vars, fn)
    #       append-to-block(out-addr, stmt)
    #   pop(vars)
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    57/push-edi
    # var line/ecx: (stream byte 512)
    81 5/subop/subtract %esp 0x200/imm32
    68/push 0x200/imm32/size
    68/push 0/imm32/read
    68/push 0/imm32/write
    89/<- %ecx 4/r32/esp
    # var word-slice/edx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %edx 4/r32/esp
    # allocate into out
    (allocate Heap *Stmt-size *(ebp+0x14))
    # var out-addr/edi: (addr block) = lookup(*out)
    8b/-> *(ebp+0x14) 7/r32/edi
    (lookup *edi *(edi+4))  # => eax
    89/<- %edi 0/r32/eax
    # out-addr->tag is 0 (block) by default
    # set out-addr->var
    8d/copy-address *(edi+0xc) 0/r32/eax  # Block-var
    (new-block-name *(ebp+0x10) %eax)
    # push(vars, out-addr->var)
    (push *(ebp+0xc) *(edi+0xc))  # Block-var
    (push *(ebp+0xc) *(edi+0x10))  # Block-var
    {
$parse-mu-block:line-loop:
      # line = read-line-buffered(in)
      (clear-stream %ecx)
      (read-line-buffered *(ebp+8) %ecx)
#?       (write-buffered Stderr "line: ")
#?       (write-stream-data Stderr %ecx)
#?       (write-buffered Stderr Newline)
#?       (flush Stderr)
      # if (line->write == 0) break
      81 7/subop/compare *ecx 0/imm32
      0f 84/jump-if-= break/disp32
      # word-slice = next-mu-token(line)
      (next-mu-token %ecx %edx)
#?       (write-buffered Stderr "word: ")
#?       (write-slice-buffered Stderr %edx)
#?       (write-buffered Stderr Newline)
#?       (flush Stderr)
      # if slice-empty?(word-slice) continue
      (slice-empty? %edx)
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= loop/disp32
      # if (slice-starts-with?(word-slice, '#') continue
      # . eax = *word-slice->start
      8b/-> *edx 0/r32/eax
      8a/copy-byte *eax 0/r32/AL
      81 4/subop/and %eax 0xff/imm32
      # . if (eax == '#') continue
      3d/compare-eax-and 0x23/imm32/hash
      0f 84/jump-if-= loop/disp32
      # if slice-equal?(word-slice, "{")
      {
$parse-mu-block:check-for-block:
        (slice-equal? %edx "{")
        3d/compare-eax-and 0/imm32/false
        74/jump-if-= break/disp8
        (check-no-tokens-left %ecx)
        # parse new block and append
        # . var tmp/eax: (handle block)
        68/push 0/imm32
        68/push 0/imm32
        89/<- %eax 4/r32/esp
        # .
        (parse-mu-block *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax)
        (append-to-block Heap %edi  *eax *(eax+4))
        # . reclaim tmp
        81 0/subop/add %esp 8/imm32
        # .
        e9/jump $parse-mu-block:line-loop/disp32
      }
      # if slice-equal?(word-slice, "}") break
$parse-mu-block:check-for-end:
      (slice-equal? %edx "}")
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= break/disp32
      # if slice-ends-with?(word-slice, ":") parse named block and append
      {
$parse-mu-block:check-for-named-block:
        # . eax = *(word-slice->end-1)
        8b/-> *(edx+4) 0/r32/eax
        48/decrement-eax
        8a/copy-byte *eax 0/r32/AL
        81 4/subop/and %eax 0xff/imm32
        # . if (eax != ':') break
        3d/compare-eax-and 0x3a/imm32/colon
        0f 85/jump-if-!= break/disp32
        # TODO: error-check the rest of 'line'
        #
        # skip ':'
        ff 1/subop/decrement *(edx+4)  # Slice-end
        # var tmp/eax: (handle block)
        68/push 0/imm32
        68/push 0/imm32
        89/<- %eax 4/r32/esp
        #
        (parse-mu-named-block %edx *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax)
        (append-to-block Heap %edi  *eax *(eax+4))
        # reclaim tmp
        81 0/subop/add %esp 8/imm32
        #
        e9/jump $parse-mu-block:line-loop/disp32
      }
      # if slice-equal?(word-slice, "var")
      {
$parse-mu-block:check-for-var:
        (slice-equal? %edx "var")
        3d/compare-eax-and 0/imm32/false
        74/jump-if-= break/disp8
        # var tmp/eax: (handle block)
        68/push 0/imm32
        68/push 0/imm32
        89/<- %eax 4/r32/esp
        #
        (parse-mu-var-def %ecx *(ebp+0xc) %eax)
        (append-to-block Heap %edi  *eax *(eax+4))
        # reclaim tmp
        81 0/subop/add %esp 8/imm32
        #
        e9/jump $parse-mu-block:line-loop/disp32
      }
$parse-mu-block:regular-stmt:
      # otherwise
      # var tmp/eax: (handle block)
      68/push 0/imm32
      68/push 0/imm32
      89/<- %eax 4/r32/esp
      #
      (parse-mu-stmt %ecx *(ebp+0xc) *(ebp+0x10) %eax)
      (append-to-block Heap %edi  *eax *(eax+4))
      # reclaim tmp
      81 0/subop/add %esp 8/imm32
      #
      e9/jump loop/disp32
    } # end line loop
    # pop(vars)
    (pop *(ebp+0xc))  # => eax
    (pop *(ebp+0xc))  # => eax
$parse-mu-block:end:
    # . reclaim locals
    81 0/subop/add %esp 0x214/imm32
    # . restore registers
    5f/pop-to-edi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$parse-mu-block:abort:
    # error("'{' or '}' should be on its own line, but got '")
    (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
    (rewind-stream %ecx)
    (write-stream 2 %ecx)
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

new-block-name:  # fn: (addr function), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    # var n/ecx: int = len(fn->name) + 10 for an int + 2 for '$:'
    8b/-> *(ebp+8) 0/r32/eax
    8b/-> *eax 0/r32/eax  # Function-name
    8b/-> *eax 0/r32/eax  # String-size
    05/add-to-eax 0xd/imm32  # 10 + 2 for '$:'
    89/<- %ecx 0/r32/eax
    # var name/edx: (stream byte n)
    29/subtract-from %esp 1/r32/ecx
    ff 6/subop/push %ecx
    68/push 0/imm32/read
    68/push 0/imm32/write
    89/<- %edx 4/r32/esp
    (clear-stream %edx)
    # eax = fn->name
    8b/-> *(ebp+8) 0/r32/eax
    8b/-> *eax 0/r32/eax  # Function-name
    # construct result using Next-block-index (and increment it)
    (write %edx "$")
    (write %edx %eax)
    (write %edx ":")
    (print-int32 %edx *Next-block-index)
    ff 0/subop/increment *Next-block-index
    # var s/eax: slice = {name->data, name->data + name->write}  (clobbering edx)
    # . eax = name->write
    8b/-> *edx 0/r32/eax
    # . edx = name->data
    8d/copy-address *(edx+0xc) 2/r32/edx
    # . eax = name->write + name->data
    01/add-to %eax 2/r32/edx
    # . push {edx, eax}
    ff 6/subop/push %eax
    ff 6/subop/push %edx
    89/<- %eax 4/r32/esp
    # out = new literal(s)
    (new-literal Heap %eax *(ebp+0xc))
$new-block-name:end:
    # . reclaim locals
    81 0/subop/add %ecx 0xc/imm32  # name.{read/write/len}
    81 0/subop/add %ecx 8/imm32  # slice
    01/add-to %esp 1/r32/ecx
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

== data

# Global state added to each var record when parsing a function
Next-block-index:  # (addr int)
    1/imm32

== code

check-no-tokens-left:  # line: (addr stream byte)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # var s/ecx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %ecx 4/r32/esp
    #
    (next-mu-token *(ebp+8) %ecx)
    # if slice-empty?(s) return
    (slice-empty? %ecx)
    3d/compare-eax-and 0/imm32/false
    75/jump-if-!= $check-no-tokens-left:end/disp8
    # if (slice-starts-with?(s, '#') return
    # . eax = *s->start
    8b/-> *edx 0/r32/eax
    8a/copy-byte *eax 0/r32/AL
    81 4/subop/and %eax 0xff/imm32
    # . if (eax == '#') continue
    3d/compare-eax-and 0x23/imm32/hash
    74/jump-if-= $check-no-tokens-left:end/disp8
    # abort
    (write-buffered Stderr "'{' or '}' should be on its own line, but got '")
    (rewind-stream %ecx)
    (write-stream 2 %ecx)
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here
$check-no-tokens-left:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle stmt)
    # pseudocode:
    #   var v: (handle var)
    #   new-literal(name, v)
    #   push(vars, v)
    #   parse-mu-block(in, vars, fn, out)
    #   pop(vars)
    #   out->tag = block
    #   out->var = v
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    57/push-edi
    # var v/ecx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ecx 4/r32/esp
    #
    (new-literal Heap *(ebp+8) %ecx)
    # push(vars, v)
    (push *(ebp+0x10) *ecx)
    (push *(ebp+0x10) *(ecx+4))
    #
    (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18))
    # pop v off vars
    (pop *(ebp+0x10))  # => eax
    (pop *(ebp+0x10))  # => eax
    # var out-addr/edi: (addr stmt) = lookup(*out)
    8b/-> *(ebp+0x18) 7/r32/edi
    (lookup *edi *(edi+4))  # => eax
    89/<- %edi 0/r32/eax
    # out-addr->tag = named-block
    c7 0/subop/copy *edi 0/imm32/block  # Stmt-tag
    # out-addr->var = v
    8b/-> *ecx 0/r32/eax
    89/<- *(edi+0xc) 0/r32/eax  # Block-var
    8b/-> *(ecx+4) 0/r32/eax
    89/<- *(edi+0x10) 0/r32/eax  # Block-var
$parse-mu-named-block:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    5f/pop-to-edi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)), out: (addr handle stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    57/push-edi
    # edi = out
    8b/-> *(ebp+0x10) 7/r32/edi
    # var word-slice/ecx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %ecx 4/r32/esp
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    # v = parse-var-with-type(next-mu-token(line))
    (next-mu-token *(ebp+8) %ecx)
    (parse-var-with-type %ecx *(ebp+8) %edx)
    #
    (push *(ebp+0xc) *edx)
    (push *(ebp+0xc) *(edx+4))
    # either v has no register and there's no more to this line
    (lookup *edx *(edx+4))  # => eax
    8b/-> *(eax+0x18) 0/r32/eax  # Var-register
    3d/compare-eax-and 0/imm32
    {
      75/jump-if-!= break/disp8
      # TODO: ensure that there's nothing else on this line
      (new-var-def Heap  *edx *(edx+4)  %edi)
      eb/jump $parse-mu-var-def:end/disp8
    }
    # or v has a register and there's more to this line
    {
      74/jump-if-= break/disp8
      # ensure that the next word is '<-'
      (next-mu-token *(ebp+8) %ecx)
      (slice-equal? %ecx "<-")  # => eax
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= $parse-mu-var-def:abort/disp8
      #
      (new-reg-var-def Heap  *edx *(edx+4)  %edi)
      (lookup *edi *(edi+4))  # => eax
      (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
    }
$parse-mu-var-def:end:
    # . reclaim locals
    81 0/subop/add %esp 0x10/imm32
    # . restore registers
    5f/pop-to-edi
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$parse-mu-var-def:abort:
    (rewind-stream *(ebp+8))
    # error("register variable requires a valid instruction to initialize but got '" line "'\n")
    (write-buffered Stderr "register variable requires a valid instruction to initialize but got '")
    (flush Stderr)
    (write-stream 2 *(ebp+8))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

test-parse-mu-var-def:
    # 'var n: int'
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
    # var out/esi: (handle stmt)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %esi 4/r32/esp
    # var vars/ecx: (stack (addr var) 16)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/size
    68/push 0/imm32/top
    89/<- %ecx 4/r32/esp
    (clear-stack %ecx)
    # convert
    (parse-mu-var-def _test-input-stream %ecx %esi)
    # var out-addr/esi: (addr stmt)
    (lookup *esi *(esi+4))  # => eax
    89/<- %esi 0/r32/eax
    #
    (check-ints-equal *esi 2 "F - test-parse-mu-var-def/tag")  # Stmt-tag is var-def
    # var v/ecx: (addr var) = lookup(out->var)
    (lookup *(esi+4) *(esi+8))  # Vardef-var Vardef-var => eax
    89/<- %ecx 0/r32/eax
    # v->name
    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "n" "F - test-parse-mu-var-def/var-name")
    # v->register
    (check-ints-equal *(ecx+0x18) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
    # v->type == int
    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-parse-mu-var-def/var-type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-var-def/var-type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-mu-reg-var-def:
    # 'var n/eax: int <- copy 0'
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
    # var out/esi: (handle stmt)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %esi 4/r32/esp
    # var vars/ecx: (stack (addr var) 16)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/size
    68/push 0/imm32/top
    89/<- %ecx 4/r32/esp
    (clear-stack %ecx)
    # convert
    (parse-mu-var-def _test-input-stream %ecx %esi)
    # var out-addr/esi: (addr stmt)
    (lookup *esi *(esi+4))  # => eax
    89/<- %esi 0/r32/eax
    #
    (check-ints-equal *esi 3 "F - test-parse-mu-reg-var-def/tag")  # Stmt-tag is reg-var-def
    # var v/ecx: (addr var) = lookup(out->outputs->value)
    # . eax: (addr stmt-var) = lookup(out->outputs)
    (lookup *(esi+0x14) *(esi+0x18))  # Regvardef-outputs Regvardef-outputs => eax
    # .
    (check-ints-equal *(eax+8) 0 "F - test-parse-mu-reg-var-def/single-output")  # Stmt-var-next
    # . eax: (addr var) = lookup(eax->value)
    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
    # . ecx = eax
    89/<- %ecx 0/r32/eax
    # v->name
    (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
    (check-strings-equal %eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
    # v->register
    (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
    (check-strings-equal %eax "eax" "F - test-parse-mu-reg-var-def/output-register")
    # v->type == int
    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
    (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-is-atom
    (check-ints-equal *(eax+4) 1 "F - test-parse-mu-reg-var-def/output-type:1")  # Tree-value
    (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-reg-var-def/output-type:2")  # Tree-right
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle stmt)
    # pseudocode:
    #   var name: slice
    #   allocate(Heap, Stmt-size, out)
    #   var out-addr: (addr stmt) = lookup(*out)
    #   out-addr->tag = stmt
    #   if stmt-has-outputs?(line)
    #     while true
    #       name = next-mu-token(line)
    #       if (name == '<-') break
    #       assert(is-identifier?(name))
    #       var v: (handle var) = lookup-or-define-var(name, vars, fn)  # regular stmts may define vars in fn outputs
    #       out-addr->outputs = append(v, out-addr->outputs)
    #   add-operation-and-inputs-to-stmt(out-addr, line, vars)
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    57/push-edi
    # var name/ecx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %ecx 4/r32/esp
    # var is-deref?/edx: boolean = false
    ba/copy-to-edx 0/imm32/false
    # var v: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ebx 4/r32/esp
    #
    (allocate Heap *Stmt-size *(ebp+0x14))
    # var out-addr/edi: (addr stmt) = lookup(*out)
    8b/-> *(ebp+0x14) 7/r32/edi
    (lookup *edi *(edi+4))  # => eax
    89/<- %edi 0/r32/eax
    # out-addr->tag = 1/stmt
    c7 0/subop/copy *edi 1/imm32/stmt1  # Stmt-tag
    {
      (stmt-has-outputs? *(ebp+8))
      3d/compare-eax-and 0/imm32/false
      0f 84/jump-if-= break/disp32
      {
$parse-mu-stmt:read-outputs:
        # name = next-mu-token(line)
        (next-mu-token *(ebp+8) %ecx)
        # if slice-empty?(word-slice) break
        (slice-empty? %ecx)  # => eax
        3d/compare-eax-and 0/imm32/false
        0f 85/jump-if-!= break/disp32
        # if (name == "<-") break
        (slice-equal? %ecx "<-")  # => eax
        3d/compare-eax-and 0/imm32/false
        0f 85/jump-if-!= break/disp32
        # is-deref? = false
        ba/copy-to-edx 0/imm32/false
        # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
        8b/-> *ecx 0/r32/eax  # Slice-start
        8a/copy-byte *eax 0/r32/AL
        81 4/subop/and %eax 0xff/imm32
        3d/compare-eax-and 0x2a/imm32/asterisk
        {
          75/jump-if-!= break/disp8
          ff 0/subop/increment *ecx
          ba/copy-to-edx 1/imm32/true
        }
        # assert(is-identifier?(name))
        (is-identifier? %ecx)  # => eax
        3d/compare-eax-and 0/imm32/false
        0f 84/jump-if-= $parse-mu-stmt:abort/disp32
        #
        (lookup-or-define-var %ecx *(ebp+0xc) *(ebp+0x10) %ebx)
        8d/copy-address *(edi+0x14) 0/r32/eax  # Stmt1-outputs
        (append-stmt-var Heap  *ebx *(ebx+4)  *(edi+0x14) *(edi+0x18)  %edx  %eax)  # Stmt1-outputs
        #
        e9/jump loop/disp32
      }
    }
    (add-operation-and-inputs-to-stmt %edi *(ebp+8) *(ebp+0xc))
$parse-mu-stmt:end:
    # . reclaim locals
    81 0/subop/add %esp 0x10/imm32
    # . restore registers
    5f/pop-to-edi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$parse-mu-stmt:abort:
    # error("invalid identifier '" name "'\n")
    (write-buffered Stderr "invalid identifier '")
    (write-slice-buffered Stderr %ecx)
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

add-operation-and-inputs-to-stmt:  # stmt: (addr stmt), line: (addr stream byte), vars: (addr stack (handle var))
    # pseudocode:
    #   stmt->name = slice-to-string(next-mu-token(line))
    #   while true
    #     name = next-mu-token(line)
    #     v = lookup-var-or-literal(name)
    #     stmt->inouts = append(v, stmt->inouts)
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # edi = stmt
    8b/-> *(ebp+8) 7/r32/edi
    # var name/ecx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %ecx 4/r32/esp
    # var is-deref?/edx: boolean = false
    ba/copy-to-edx 0/imm32/false
    # var v/esi: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %esi 4/r32/esp
$add-operation-and-inputs-to-stmt:read-operation:
    (next-mu-token *(ebp+0xc) %ecx)
    8d/copy-address *(edi+4) 0/r32/eax  # Stmt1-operation or Regvardef-operationStmt1-operation or Regvardef-operation
    (slice-to-string Heap %ecx %eax)
    # var is-get?/ebx: boolean = (name == "get")
    (slice-equal? %ecx "get")  # => eax
    89/<- %ebx 0/r32/eax
    {
$add-operation-and-inputs-to-stmt:read-inouts:
      # name = next-mu-token(line)
      (next-mu-token *(ebp+0xc) %ecx)
      # if slice-empty?(word-slice) break
      (slice-empty? %ecx)  # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= break/disp32
      # if (name == "<-") abort
      (slice-equal? %ecx "<-")
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= $add-operation-and-inputs-to-stmt:abort/disp32
      # if (is-get? && second operand) lookup or create offset
      {
        81 7/subop/compare %ebx 0/imm32/false
        74/jump-if-= break/disp8
        81 7/subop/compare *(edi+8) 0/imm32  # Stmt1-inouts or Regvardef-inouts
        74/jump-if-= break/disp8
        (lookup-or-create-constant *(edi+8) %ecx)  # Stmt1-inouts => eax
#?         (write-buffered Stderr "creating new output var ")
#?         (print-int32-buffered Stderr %eax)
#?         (write-buffered Stderr " for field called ")
#?         (write-slice-buffered Stderr %ecx)
#?         (write-buffered Stderr Newline)
#?         (flush Stderr)
        e9/jump $add-operation-and-inputs-to-stmt:save-var/disp32
      }
      # is-deref? = false
      ba/copy-to-edx 0/imm32/false
      # if (slice-starts-with?(name, '*')) ++name->start and set is-deref?
      8b/-> *ecx 0/r32/eax  # Slice-start
      8a/copy-byte *eax 0/r32/AL
      81 4/subop/and %eax 0xff/imm32
      3d/compare-eax-and 0x2a/imm32/asterisk
      {
        75/jump-if-!= break/disp8
$add-operation-and-inputs-to-stmt:inout-is-deref:
        ff 0/subop/increment *ecx
        ba/copy-to-edx 1/imm32/true
      }
      (lookup-var-or-literal %ecx *(ebp+0x10) %esi)
$add-operation-and-inputs-to-stmt:save-var:
      8d/copy-address *(edi+0xc) 0/r32/eax
      (append-stmt-var Heap  *esi *(esi+4)  *(edi+0xc) *(edi+0x10)  %edx  %eax)  # Stmt1-inouts or Regvardef-inouts
      #
      e9/jump loop/disp32
    }
$add-operation-and-inputs-to-stmt:end:
    # . reclaim locals
    81 0/subop/add %esp 0x10/imm32
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$add-operation-and-inputs-to-stmt:abort:
    # error("invalid statement '" line "'\n")
    (rewind-stream *(ebp+8))
    (write-buffered Stderr "invalid identifier '")
    (flush Stderr)
    (write-stream 2 *(ebp+8))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

stmt-has-outputs?:  # line: (addr stream byte) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var word-slice/ecx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %ecx 4/r32/esp
    # result = false
    b8/copy-to-eax 0/imm32/false
    (rewind-stream *(ebp+8))
    {
      (next-mu-token *(ebp+8) %ecx)
      # if slice-empty?(word-slice) break
      (slice-empty? %ecx)
      3d/compare-eax-and 0/imm32/false
      b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
      0f 85/jump-if-!= break/disp32
      # if slice-starts-with?(word-slice, '#') break
      # . eax = *word-slice->start
      8b/-> *ecx 0/r32/eax
      8a/copy-byte *eax 0/r32/AL
      81 4/subop/and %eax 0xff/imm32
      # . if (eax == '#') break
      3d/compare-eax-and 0x23/imm32/hash
      b8/copy-to-eax 0/imm32/false/result  # restore result (if we're here it's still false)
      0f 84/jump-if-= break/disp32
      # if slice-equal?(word-slice, '<-') return true
      (slice-equal? %ecx "<-")
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= loop/disp8
      b8/copy-to-eax 1/imm32/true
    }
$stmt-has-outputs:end:
    (rewind-stream *(ebp+8))
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# if 'name' starts with a digit, create a new literal var for it
# otherwise return first 'name' from the top (back) of 'vars' and abort if not found
lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    # esi = name
    8b/-> *(ebp+8) 6/r32/esi
    # if slice-empty?(name) abort
    (slice-empty? %esi)  # => eax
    3d/compare-eax-and 0/imm32/false
    0f 85/jump-if-!= $lookup-var-or-literal:abort/disp32
    # var c/ecx: byte = *name->start
    8b/-> *esi 1/r32/ecx
    8a/copy-byte *ecx 1/r32/CL
    81 4/subop/and %ecx 0xff/imm32
    # if is-decimal-digit?(c) return new var(name)
    {
      (is-decimal-digit? %ecx)  # => eax
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= break/disp8
      (new-literal-integer Heap %esi *(ebp+0x10))
      eb/jump $lookup-var-or-literal:end/disp8
    }
    # else if (c == '"') return new var(name)
    {
      81 7/subop/compare %ecx 0x22/imm32/dquote
      75/jump-if-!= break/disp8
      (new-literal Heap %esi *(ebp+0x10))
      eb/jump $lookup-var-or-literal:end/disp8
    }
    # otherwise return lookup-var(name, vars)
    {
      (lookup-var %esi *(ebp+0xc))  # => eax
    }
$lookup-var-or-literal:end:
    # . restore registers
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$lookup-var-or-literal:abort:
    (write-buffered Stderr "empty variable!")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

# return first 'name' from the top (back) of 'vars' and abort if not found
lookup-var:  # name: (addr slice), vars: (addr stack (handle var)), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    #
    (lookup-var-helper *(ebp+8) *(ebp+0xc) *(ebp+0x10))
    # if (*out == 0) abort
    8b/-> *(ebp+0x10) 0/r32/eax
    81 7/subop/compare *eax 0/imm32
    74/jump-if-= $lookup-var:abort/disp8
$lookup-var:end:
    # . restore registers
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$lookup-var:abort:
    (write-buffered Stderr "unknown variable '")
    (write-slice-buffered Stderr *(ebp+8))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

# return first 'name' from the top (back) of 'vars', and 0/null if not found
lookup-var-helper:  # name: (addr slice), vars: (addr stack (handle var)), out: (addr handle var)
    # pseudocode:
    #   var curr: (addr handle var) = &vars->data[vars->top - 8]
    #   var min = vars->data
    #   while curr >= min
    #     var v: (handle var) = *curr
    #     if v->name == name
    #       return
    #     curr -= 8
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    # clear out
    (zero-out *(ebp+0x10) *Handle-size)
    # esi = vars
    8b/-> *(ebp+0xc) 6/r32/esi
    # ebx = vars->top
    8b/-> *esi 3/r32/ebx
    # if (vars->top > vars->size) abort
    3b/compare<- *(esi+4) 0/r32/eax
    0f 8f/jump-if-> $lookup-var-helper:error1/disp32
    # var min/edx: (addr handle var) = vars->data
    8d/copy-address *(esi+8) 2/r32/edx
    # var curr/ebx: (addr handle var) = &vars->data[vars->top - 8]
    8d/copy-address *(esi+ebx) 3/r32/ebx
    {
      # if (curr < min) return
      39/compare %ebx 2/r32/edx
      0f 82/jump-if-addr< break/disp32
      # var v/ecx: (addr var) = lookup(*curr)
      (lookup *ebx *(ebx+4))  # => eax
      89/<- %ecx 0/r32/eax
      # var vn/eax: (addr array byte) = lookup(v->name)
      (lookup *ecx *(ecx+4))  # Var-name Var-name => eax
      # if (vn == name) return curr
      (slice-equal? *(ebp+8) %eax)  # => eax
      3d/compare-eax-and 0/imm32/false
      {
        74/jump-if-= break/disp8
        # esi = out
        8b/-> *(ebp+0x10) 6/r32/esi
        # *out = *curr
        8b/-> *ebx 0/r32/eax
        89/<- *esi 0/r32/eax
        8b/-> *(ebx+4) 0/r32/eax
        89/<- *(esi+4) 0/r32/eax
        # return
        eb/jump $lookup-var-helper:end/disp8
      }
      # curr -= 8
      81 5/subop/subtract %ebx 8/imm32
      e9/jump loop/disp32
    }
$lookup-var-helper:end:
    # . restore registers
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$lookup-var-helper:error1:
    (write-buffered Stderr "malformed stack when looking up '")
    (write-slice-buffered Stderr *(ebp+8))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

# return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    #
    (lookup-var-helper *(ebp+8) *(ebp+0xc) *(ebp+0x14))
    {
      # if (out != 0) return
      8b/-> *(ebp+0x14) 0/r32/eax
      81 7/subop/compare *eax 0/imm32
      75/jump-if-!= break/disp8
      # if name is one of fn's outputs, return it
      {
        (find-in-function-outputs *(ebp+0x10) *(ebp+8) *(ebp+0x14))
        81 7/subop/compare *eax 0/imm32
        # otherwise abort
        0f 84/jump-if-= $lookup-var:abort/disp32
      }
    }
$lookup-or-define-var:end:
    # . restore registers
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

find-in-function-outputs:  # fn: (addr function), name: (addr slice), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # var curr/ecx: (addr list var) = lookup(fn->outputs)
    8b/-> *(ebp+8) 1/r32/ecx
    (lookup *(ecx+0x10) *(ecx+0x14))  # => eax
    89/<- %ecx 0/r32/eax
    # while curr != null
    {
      81 7/subop/compare %ecx 0/imm32
      74/jump-if-= break/disp8
      # var v/eax: (addr var) = lookup(curr->value)
      (lookup *ecx *(ecx+4))  # List-value List-value => eax
      # var s/eax: (addr array byte) = lookup(v->name)
      (lookup *eax *(eax+4))  # Var-name Var-name => eax
      # if (s == name) return curr->value
      (slice-equal? *(ebp+0xc) %eax)  # => eax
      3d/compare-eax-and 0/imm32/false
      {
        74/jump-if-= break/disp8
        # var edi = out
        57/push-edi
        89/<- *(ebp+0x10) 7/r32/edi
        # *out = curr->value
        8b/-> *ecx 0/r32/eax
        89/<- *edi 0/r32/eax
        8b/-> *(ecx+4) 0/r32/eax
        89/<- *(edi+4) 0/r32/eax
        #
        5f/pop-to-edi
        eb/jump $find-in-function-outputs:end/disp8
      }
      # curr = curr->next
      (lookup *(ecx+8) *(ecx+0xc))  # List-next List-next => eax
      89/<- %ecx 0/r32/eax
      #
      eb/jump loop/disp8
    }
    b8/copy-to-eax 0/imm32
$find-in-function-outputs:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-mu-stmt:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (write _test-input-stream "increment n\n")
    # var vars/ecx: (stack (addr var) 16)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/size
    68/push 0/imm32/top
    89/<- %ecx 4/r32/esp
    (clear-stack %ecx)
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    # var s/eax: (handle array byte)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %eax 4/r32/esp
    # v = new var("n")
    (copy-array Heap "n" %eax)
    (new-var Heap *eax *(eax+4) %edx)
    #
    (push %ecx *edx)
    (push %ecx *(edx+4))
    # var out/eax: (handle stmt)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %eax 4/r32/esp
    # convert
    (parse-mu-stmt _test-input-stream %ecx 0 %eax)
    # var out-addr/edx: (addr stmt) = lookup(*out)
    (lookup *eax *(eax+4))  # => eax
    89/<- %edx 0/r32/eax
    # out->tag
    (check-ints-equal *edx 1 "F - test-parse-mu-stmt/tag")  # Stmt-tag is Stmt1
    # out->operation
    (lookup *(edx+4) *(edx+8))  # Stmt1-operation Stmt1-operation => eax
    (check-strings-equal %eax "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
    # out->inouts->value->name
    # . eax = out->inouts
    (lookup *(edx+0xc) *(edx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
    # . eax = out->inouts->value
    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
    # . eax = out->inouts->value->name
    (lookup *eax *(eax+4))  # Var-name Var-name => eax
    # .
    (check-strings-equal %eax "n" "F - test-parse-mu-stmt/inout:0")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-parse-mu-stmt-with-comma:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-input-stream)
    (write _test-input-stream "copy-to n, 3\n")
    # var vars/ecx: (stack (addr var) 16)
    81 5/subop/subtract %esp 0x80/imm32
    68/push 0x80/imm32/size
    68/push 0/imm32/top
    89/<- %ecx 4/r32/esp
    (clear-stack %ecx)
    # var v/edx: (handle var)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edx 4/r32/esp
    # var s/eax: (handle array byte)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %eax 4/r32/esp
    # v = new var("n")
    (copy-array Heap "n" %eax)
    (new-var Heap *eax *(eax+4) %edx)
    #
    (push %ecx *edx)
    (push %ecx *(edx+4))
    # var out/eax: (handle stmt)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %eax 4/r32/esp
    # convert
    (parse-mu-stmt _test-input-stream %ecx 0 %eax)
    # var out-addr/edx: (addr stmt) = lookup(*out)
    (lookup *eax *(eax+4))  # => eax
    89/<- %edx 0/r32/eax
    # out->tag
    (check-ints-equal *edx 1 "F - test-parse-mu-stmt-with-comma/tag")  # Stmt-tag is Stmt1
    # out->operation
    (lookup *(edx+4) *(edx+8))  # Stmt1-operation Stmt1-operation => eax
    (check-strings-equal %eax "copy-to" "F - test-parse-mu-stmt-with-comma/name")  # Stmt1-operation
    # out->inouts->value->name
    # . eax = out->inouts
    (lookup *(edx+0xc) *(edx+0x10))  # Stmt1-inouts Stmt1-inouts => eax
    # . eax = out->inouts->value
    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
    # . eax = out->inouts->value->name
    (lookup *eax *(eax+4))  # Var-name Var-name => eax
    # .
    (check-strings-equal %eax "n" "F - test-parse-mu-stmt-with-comma/inout:0")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

new-var:  # ad: (addr allocation-descriptor), name: (handle array byte), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # ecx = out
    8b/-> *(ebp+0x14) 1/r32/ecx
    #
    (allocate *(ebp+8) *Var-size %ecx)
    # var out-addr/eax: (addr var)
    (lookup *ecx *(ecx+4))  # => eax
    # out-addr->name = name
    8b/-> *(ebp+0xc) 1/r32/ecx
    89/<- *eax 1/r32/ecx  # Var-name
    8b/-> *(ebp+0x10) 1/r32/ecx
    89/<- *(eax+4) 1/r32/ecx  # Var-name
$new-var:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

new-literal-integer:  # ad: (addr allocation-descriptor), name: (addr slice), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # if (!is-hex-int?(name)) abort
    (is-hex-int? *(ebp+0xc))  # => eax
    3d/compare-eax-and 0/imm32/false
    0f 84/jump-if-= $new-literal-integer:abort/disp32
    # out = new var(s)
    (new-var-from-slice *(ebp+8) *(ebp+0xc) *(ebp+0x10))
    # var out-addr/ecx: (addr var) = lookup(*out)
    8b/-> *(ebp+0x10) 0/r32/eax
    (lookup *eax *(eax+4))  # => eax
    89/<- %ecx 0/r32/eax
    # out-addr->type = new tree()
    8d/copy-address *(ecx+8) 0/r32/eax  # Var-type
    (allocate *(ebp+8) *Tree-size %eax)
    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
    c7 0/subop/copy *eax 1/imm32/true  # Tree-is-atom
    # nothing else to do; default type is 'literal'
$new-literal-integer:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$new-literal-integer:abort:
    (write-buffered Stderr "variable cannot begin with a digit '")
    (write-slice-buffered Stderr *(ebp+0xc))
    (write-buffered Stderr "'\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

new-literal:  # ad: (addr allocation-descriptor), name: (addr slice), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # var s/ecx: (handle array byte)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ecx 0/r32/eax
    # s = slice-to-string(name)
    (slice-to-string Heap *(ebp+0xc) %ecx)
    # allocate to out
    (new-var *(ebp+8) *ecx *(ecx+4) *(ebp+0x10))
    # ecx = out
    8b/-> *(ebp+0x10) 1/r32/ecx
    # out->type = new type
    8d/copy-address *(ecx+8) 0/r32/eax  # Var-type
    (allocate *(ebp+8) *Tree-size %eax)
    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
    # nothing else to do; default type is 'literal'
    c7 0/subop/copy *eax 1/imm32/true  # Tree-is-atom
$new-literal:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

new-var-from-slice:  # ad: (addr allocation-descriptor), name: (addr slice), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var tmp/ecx: (handle array byte)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ecx 4/r32/esp
    # tmp = slice-to-string(name)
    (slice-to-string Heap *(ebp+0xc) %ecx)
    # out = new-var(tmp)
    (new-var *(ebp+8) *ecx *(ecx+4) *(ebp+0x10))
$new-var-from-slice:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

new-var-def:  # ad: (addr allocation-descriptor), var: (handle var), out: (addr handle stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    #
    (allocate *(ebp+8) *Stmt-size *(ebp+0x14))
    # var out-addr/eax: (addr stmt) = lookup(*out)
    8b/-> *(ebp+0x14) 0/r32/eax
    (lookup *eax *(eax+4))  # => eax
    # out-addr->tag = stmt
    c7 0/subop/copy *eax 2/imm32/tag/var-on-stack  # Stmt-tag
    # result->var = var
    8b/-> *(ebp+0xc) 1/r32/ecx
    89/<- *(eax+4) 1/r32/ecx  # Vardef-var
    8b/-> *(ebp+0x10) 1/r32/ecx
    89/<- *(eax+8) 1/r32/ecx  # Vardef-var
$new-var-def:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

new-reg-var-def:  # ad: (addr allocation-descriptor), var: (handle var), out: (addr handle stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    # eax = out
    8b/-> *(ebp+0x14) 0/r32/eax
    #
    (allocate *(ebp+8) *Stmt-size %eax)
    # var out-addr/eax: (addr stmt) = lookup(*out)
    (lookup *eax *(eax+4))  # => eax
    # set tag
    c7 0/subop/copy *eax 3/imm32/tag/var-in-register  # Stmt-tag
    # set output
    8d/copy-address *(eax+0x14) 0/r32/eax  # Regvardef-outputs
    (append-stmt-var Heap  *(ebp+0xc) *(ebp+0x10)  0 0  0  %eax)
$new-reg-var-def:end:
    # . restore registers
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

append-list:  # ad: (addr allocation-descriptor), value: (handle _type), list: (handle list _type), out: (addr handle list _type)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    57/push-edi
    # edi = out
    8b/-> *(ebp+0x1c) 7/r32/edi
    # *out = new list
    (allocate *(ebp+8) *List-size %edi)
    # var out-addr/edi: (addr list _type) = lookup(*out)
    (lookup *edi *(edi+4))  # => eax
    89/<- %edi 0/r32/eax
    # out-addr->value = value
    8b/-> *(ebp+0xc) 0/r32/eax
    89/<- *edi 0/r32/eax  # List-value
    8b/-> *(ebp+0x10) 0/r32/eax
    89/<- *(edi+4) 0/r32/eax  # List-value
    # if (list == null) return
    81 7/subop/compare *(ebp+0x14) 0/imm32
    74/jump-if-= $append-list:end/disp8
    # otherwise append
$append-list:non-empty-list:
    # var curr/eax: (addr list _type) = lookup(list)
    (lookup *(ebp+0x14) *(ebp+0x18))  # => eax
    # while (curr->next != null) curr = curr->next
    {
      81 7/subop/compare *(eax+8) 0/imm32  # List-next
      74/jump-if-= break/disp8
      # curr = lookup(curr->next)
      (lookup *(eax+8) *(eax+0xc))  # List-next, List-next => eax
      #
      eb/jump loop/disp8
    }
    # edi = out
    8b/-> *(ebp+0x1c) 7/r32/edi
    # curr->next = out
    8b/-> *edi 1/r32/ecx
    89/<- *(eax+8) 1/r32/ecx  # List-next
    8b/-> *(edi+4) 1/r32/ecx
    89/<- *(eax+0xc) 1/r32/ecx  # List-next
    # out = list
    8b/-> *(ebp+0x14) 1/r32/ecx
    89/<- *edi 1/r32/ecx
    8b/-> *(ebp+0x18) 1/r32/ecx
    89/<- *(edi+4) 1/r32/ecx
$append-list:end:
    # . restore registers
    5f/pop-to-edi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

append-stmt-var:  # ad: (addr allocation-descriptor), v: (handle var), vars: (handle stmt-var), is-deref?: boolean, out: (addr handle stmt-var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    57/push-edi
    # edi = out
    8b/-> *(ebp+0x20) 7/r32/edi
    # out = new stmt-var
    (allocate *(ebp+8) *Stmt-var-size %edi)
    # var out-addr/ecx: (addr stmt-var) = lookup(*out)
    (lookup *edi *(edi+4))  # => eax
    89/<- %ecx 0/r32/eax
    # out-addr->value = v
    8b/-> *(ebp+0xc) 0/r32/eax
    89/<- *ecx 0/r32/eax  # Stmt-var-value
    8b/-> *(ebp+0x10) 0/r32/eax
    89/<- *(ecx+4) 0/r32/eax  # Stmt-var-value
    # out-addr->is-deref? = is-deref?
    8b/-> *(ebp+0x1c) 0/r32/eax
    89/<- *(ecx+0x10) 0/r32/eax  # Stmt-var-is-deref
    # if (vars == null) return result
    81 7/subop/compare *(ebp+0x14) 0/imm32/null
    74/jump-if-= $append-stmt-var:end/disp8
    # otherwise append
    # var curr/eax: (addr stmt-var) = lookup(vars)
    (lookup *(ebp+0x14) *(ebp+0x18))  # => eax
    # while (curr->next != null) curr = curr->next
    {
      81 7/subop/compare *(eax+8) 0/imm32  # Stmt-var-next
      74/jump-if-= break/disp8
      # curr = lookup(curr->next)
      (lookup *(eax+8) *(eax+0xc))  # Stmt-var-next, Stmt-var-next => eax
      #
      eb/jump loop/disp8
    }
    # curr->next = out
    8b/-> *edi 1/r32/ecx
    89/<- *(eax+8) 1/r32/ecx  # Stmt-var-next
    8b/-> *(edi+4) 1/r32/ecx
    89/<- *(eax+0xc) 1/r32/ecx  # Stmt-var-next
    # out = vars
    8b/-> *(ebp+0x14) 1/r32/ecx
    89/<- *edi 1/r32/ecx
    8b/-> *(ebp+0x18) 1/r32/ecx
    89/<- *(edi+4) 1/r32/ecx
$append-stmt-var:end:
    # . restore registers
    5f/pop-to-edi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

append-to-block:  # ad: (addr allocation-descriptor), block: (addr block), x: (handle stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    56/push-esi
    # esi = block
    8b/-> *(ebp+0xc) 6/r32/esi
    # block->stmts = append(x, block->stmts)
    8d/copy-address *(esi+4) 0/r32/eax  # Block-stmts
    (append-list *(ebp+8)  *(ebp+0x10) *(ebp+0x14)  *(esi+4) *(esi+8)  %eax)  # ad, x, x, Block-stmts, Block-stmts
$append-to-block:end:
    # . restore registers
    5e/pop-to-esi
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

## Parsing types
# We need to create metadata on user-defined types, and we need to use this
# metadata as we parse instructions.
# However, we also want to allow types to be used before their definitions.
# This means we can't ever assume any type data structures exist.

lookup-or-create-constant:  # container: (addr stmt-var), field-name: (addr slice), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    56/push-esi
    # var container-type/esi: type-id
    (container-type *(ebp+8))  # => eax
    89/<- %esi 0/r32/eax
    # var tmp/eax: (handle typeinfo) = find-or-create-typeinfo(container-type)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %eax 4/r32/esp
    (find-or-create-typeinfo %esi %eax)
    # var tmp-addr/eax: (addr typeinfo) = lookup(tmp)
    (lookup *eax *(eax+4))  # => eax
    # result = find-or-create-typeinfo-output-var(typeinfo, field-name)
    (find-or-create-typeinfo-output-var %eax *(ebp+0xc) *(ebp+0x10))
$lookup-or-create-constant:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    5e/pop-to-esi
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# if addr var:
#   container->var->type->right->left->value
# otherwise
#   container->var->type->value
container-type:  # container: (addr stmt-var) -> result/eax: type-id
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
    8b/-> *(ebp+8) 0/r32/eax
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    8b/-> *(eax+4) 0/r32/eax  # Var-type
    {
      81 7/subop/compare *(eax+8) 0/imm32  # Tree-right
      74/jump-if-= break/disp8
      8b/-> *(eax+8) 0/r32/eax  # Tree-right
      8b/-> *(eax+4) 0/r32/eax  # Tree-left
    }
    8b/-> *(eax+4) 0/r32/eax  # Tree-value
$container-type:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

find-or-create-typeinfo:  # t: type-id, out: (addr handle typeinfo)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    57/push-edi
    # edi = out
    8b/-> *(ebp+0xc) 7/r32/edi
    # var fields/ecx: (handle table (handle array byte) (handle typeinfo-entry))
    68/push 0/imm32
    68/push 0/imm32
    89/<- %ecx 4/r32/esp
    # find-typeinfo(t, out)
    (find-typeinfo *(ebp+8) %edi)
    {
      # if (*out != 0) break
      81 7/subop/compare *edi 0/imm32
      0f 85/jump-if-!= break/disp32
$find-or-create-typeinfo:create:
      # *out = allocate
      (allocate Heap *Typeinfo-size %edi)
      # var tmp/eax: (addr typeinfo) = lookup(*out)
      (lookup *edi *(edi+4))  # => eax
      # tmp->id = t
      8b/-> *(ebp+8) 2/r32/edx
      89/<- *eax 2/r32/edx  # Typeinfo-id
      # tmp->fields = new table
      # . fields = new table
      (new-stream Heap 0x40 *Typeinfo-fields-row-size %ecx)
      # . tmp->fields = fields
      8b/-> *ecx 2/r32/edx
      89/<- *(eax+4) 2/r32/edx  # Typeinfo-fields
      8b/-> *(ecx+4) 2/r32/edx
      89/<- *(eax+8) 2/r32/edx  # Typeinfo-fields
      # tmp->next = Program->types
      8b/-> *_Program-types 1/r32/ecx
      89/<- *(eax+0x10) 1/r32/ecx  # Typeinfo-next
      8b/-> *_Program-types->payload 1/r32/ecx
      89/<- *(eax+0x14) 1/r32/ecx  # Typeinfo-next
      # Program->types = out
      8b/-> *edi 1/r32/ecx
      89/<- *_Program-types 1/r32/ecx
      8b/-> *(edi+4) 1/r32/ecx
      89/<- *_Program-types->payload 1/r32/ecx
    }
$find-or-create-typeinfo:end:
    # . restore registers
    5f/pop-to-edi
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

find-typeinfo:  # t: type-id, out: (addr handle typeinfo)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    57/push-edi
    # ecx = t
    8b/-> *(ebp+8) 1/r32/ecx
    # edi = out
    8b/-> *(ebp+0xc) 7/r32/edi
    # *out = Program->types
    8b/-> *_Program-types 0/r32/eax
    89/<- *edi 0/r32/eax
    8b/-> *_Program-types->payload 0/r32/eax
    89/<- *(edi+4) 0/r32/eax
    {
      # if (*out == 0) break
      81 7/subop/compare *edi 0/imm32
      74/jump-if-= break/disp8
      # var tmp/eax: (addr typeinfo) = lookup(*out)
      (lookup *edi *(edi+4))  # => eax
      # if (tmp->id == t) break
      39/compare *eax 1/r32/ecx  # Typeinfo-id
      74/jump-if-= break/disp8
      # *out = tmp->next
      8b/-> *(eax+0x10) 2/r32/edx  # Typeinfo-next
      89/<- *edi 2/r32/edx
      8b/-> *(eax+0x14) 2/r32/edx  # Typeinfo-next
      89/<- *(edi+4) 2/r32/edx
      #
      eb/jump loop/disp8
    }
$find-typeinfo:end:
    # . restore registers
    5f/pop-to-edi
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

find-or-create-typeinfo-output-var:  # T: (addr typeinfo), f: (addr slice), out: (addr handle var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    52/push-edx
    57/push-edi
    # var tmp-addr/edi: (addr typeinfo-entry) = find-or-create-typeinfo-fields(T, f)
    # . var tmp/edi: (handle typeinfo-entry)
    68/push 0/imm32
    68/push 0/imm32
    89/<- %edi 4/r32/esp
    # . find-or-create-typeinfo-fields(T, f, tmp)
    (find-or-create-typeinfo-fields *(ebp+8) *(ebp+0xc) %edi)
    # . tmp-addr = lookup(tmp)
    (lookup *edi *(edi+4))  # => eax
    89/<- %edi 0/r32/eax
    # if output var doesn't exist, create it
    {
      81 7/subop/compare *(edi+8) 0/imm32  # Typeinfo-entry-output-var
      0f 85/jump-if-!= break/disp32
      # out/edx = new var(dummy name, type, -1 offset)
      # . var name/eax: (handle array byte) = "field"
      68/push 0/imm32
      68/push 0/imm32
      89/<- %eax 4/r32/esp
      (copy-array Heap "field" %eax)
      # . new var
      (new-var Heap *eax *(eax+4) *(ebp+0x10))
      # . reclaim name
      81 0/subop/add %esp 8/imm32
      # save out in typeinfo
      8b/-> *(ebp+0x10) 2/r32/edx
      8b/-> *edx 0/r32/eax
      89/<- *(edi+0xc) 0/r32/eax  # Typeinfo-entry-output-var
      8b/-> *(edx+4) 0/r32/eax
      89/<- *(edi+0x10) 0/r32/eax  # Typeinfo-entry-output-var
      # initialize out
      # . var out-addr/edx: (addr var) = lookup(*out)
      (lookup *edx *(edx+4))  # => eax
      89/<- %edx 0/r32/eax
      # . out->type = new constant type
      8d/copy-address *(edx+8) 0/r32/eax  # Var-type
      (allocate Heap *Tree-size %eax)
      (lookup *(edx+8) *(edx+0xc))  # => eax
      c7 0/subop/copy *eax 1/imm32/true  # Tree-is-atom
      c7 0/subop/copy *(eax+4) 6/imm32/constant  # Tree-value
      c7 0/subop/copy *(eax+8) 0/imm32  # Tree-left
      c7 0/subop/copy *(eax+0xc) 0/imm32  # Tree-right
      c7 0/subop/copy *(eax+0x10) 0/imm32  # Tree-right
      # . out-addr->offset isn't filled out yet
      c7 0/subop/copy *(edx+0x14) -1/imm32/uninitialized  # Var-offset
    }
$find-or-create-typeinfo-output-var:end:
    # . reclaim locals
    81 0/subop/add %esp 8/imm32
    # . restore registers
    5f/pop-to-edi
    5a/pop-to-edx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

find-or-create-typeinfo-fields:  # T: (addr typeinfo), f: (addr slice), out: (addr handle typeinfo-entry)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    56/push-esi
    57/push-edi
    # esi = T->fields
    8b/-> *(ebp+8) 6/r32/esi
    8b/-> *(esi+4) 6/r32/esi  # Typeinfo-fields
    # var src/esi: (addr handle typeinfo-entry) = get-or-insert-slice(T->fields, f)
    (get-or-insert-slice %esi *(ebp+0xc) *Typeinfo-fields-row-size Heap)  # => eax
    89/<- %esi 0/r32/eax
    # if src doesn't exist, allocate it
    {
      81 7/subop/compare *esi 0/imm32
      75/jump-if-!= break/disp8
      (allocate Heap *Typeinfo-entry-size *(ebp+0x10))
      eb/jump $find-or-create-typeinfo-fields:end/disp8
    }
    # otherwise copy it
    # . edi = out
    8b/-> *(ebp+0x10) 7/r32/edi
    8b/-> *esi 0/r32/eax
    89/<- *edi 0/r32/eax
    8b/-> *(esi+4) 0/r32/eax
    89/<- *(edi+4) 0/r32/eax
$find-or-create-typeinfo-fields:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

populate-mu-type:  # in: (addr stream byte), t: (addr typeinfo)
    # pseudocode:
    #   var line: (stream byte 512)
    #   curr-index = 0
    #   while true
    #     clear-stream(line)
    #     read-line-buffered(in, line)
    #     if line->write == 0
    #       abort
    #     word-slice = next-mu-token(line)
    #     if slice-empty?(word-slice)               # end of line
    #       continue
    #     if slice-equal?(word-slice, "}")
    #       break
    #     var v: (handle var) = parse-var-with-type(word-slice, line)
    #     var r: (handle typeinfo-fields) = find-or-create-typeinfo-fields(t, word-slice/v->name)
    #     TODO: ensure that r->first is null
    #     r->index = curr-index
    #     curr-index++
    #     r->input-var = v
    #     if r->output-var == 0
    #       r->output-var = new literal
    #     TODO: ensure nothing else in line
    # t->total-size-in-bytes = -2 (not yet initialized)
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # var curr-index: int at *(ebp-4)
    68/push 0/imm32
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # edi = t
    8b/-> *(ebp+0xc) 7/r32/edi
    # var line/ecx: (stream byte 512)
    81 5/subop/subtract %esp 0x200/imm32
    68/push 0x200/imm32/size
    68/push 0/imm32/read
    68/push 0/imm32/write
    89/<- %ecx 4/r32/esp
    # var word-slice/edx: slice
    68/push 0/imm32/end
    68/push 0/imm32/start
    89/<- %edx 4/r32/esp
    {
$populate-mu-type:line-loop:
      (clear-stream %ecx)
      (read-line-buffered *(ebp+8) %ecx)
      # if (line->write == 0) abort
      81 7/subop/compare *ecx 0/imm32
      0f 84/jump-if-= $populate-mu-type:abort/disp32
#?       # dump line {{{
#?       (write 2 "parse-mu: ^")
#?       (write-stream 2 %ecx)
#?       (write 2 "$\n")
#?       (rewind-stream %ecx)
#?       # }}}
      (next-mu-token %ecx %edx)
      # if slice-empty?(word-slice) continue
      (slice-empty? %edx)  # => eax
      3d/compare-eax-and 0/imm32
      0f 85/jump-if-!= loop/disp32
      # if slice-equal?(word-slice, "}") break
      (slice-equal? %edx "}")
      3d/compare-eax-and 0/imm32
      0f 85/jump-if-!= break/disp32
$populate-mu-type:parse-element:
      # var v/esi: (handle var) = parse-var-with-type(word-slice, first-line)
      (parse-var-with-type %edx %ecx)  # => eax
      89/<- %esi 0/r32/eax
$populate-mu-type:create-typeinfo-fields:
      # var r/ebx: (handle typeinfo-entry)
      (find-or-create-typeinfo-fields %edi %edx)  # => eax
      89/<- %ebx 0/r32/eax
#?       (write-buffered Stderr "var ")
#?       (write-buffered Stderr *esi)  # Var-name
#?       (write-buffered Stderr " is at index ")
#?       (print-int32-buffered Stderr *(ebp-4))
#?       (write-buffered Stderr Newline)
#?       (flush Stderr)
      # r->index = curr-index
      8b/-> *(ebp-4) 0/r32/eax
      89/<- *(ebx+4) 0/r32/eax  # Typeinfo-entry-index
      # ++curr-index
      ff 0/subop/increment *(ebp-4)
$populate-mu-type:set-input-type:
      # r->input-var = v
      89/<- *ebx 6/r32/esi  # Typeinfo-entry-input-var
      {
$populate-mu-type:create-output-type:
        # if (r->output-var == 0) create a new var with some placeholder data
        81 7/subop/compare *(ebx+8) 0/imm32  # Typeinfo-entry-output-var
        75/jump-if-!= break/disp8
        (new-literal Heap %edx)  # => eax
        89/<- *(ebx+8) 0/r32/eax  # Typeinfo-entry-output-var
      }
      e9/jump loop/disp32
    }
$populate-mu-type:invalidate-total-size-in-bytes:
    # Offsets and total size may not be accurate here since we may not yet
    # have encountered the element types.
    # We'll recompute them separately after parsing the entire program.
    c7 0/subop/copy *(edi+8) -2/imm32/uninitialized  # Typeinfo-total-size-in-bytes
$populate-mu-type:end:
    # . reclaim locals
    81 0/subop/add %esp 0x214/imm32
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # reclaim curr-index
    81 0/subop/add %esp 4/imm32
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$populate-mu-type:abort:
    # error("unexpected top-level command: " word-slice "\n")
    (write-buffered Stderr "incomplete type definition '")
    (type-name *edi)  # Typeinfo-id => eax
    (write-buffered Stderr %eax)
    (write-buffered Stderr "\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

type-name:  # index: int -> result/eax: (addr array byte)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
    (index Type-id *(ebp+8))
$type-name:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

index:  # arr: (addr stream (handle array byte)), index: int -> result/eax: (addr array byte)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    56/push-esi
    # TODO: bounds-check index
    # esi = arr
    8b/-> *(ebp+8) 6/r32/esi
    # eax = index
    8b/-> *(ebp+0xc) 0/r32/eax
    # eax = *(arr + 12 + index)
    8b/-> *(esi+eax+0xc) 0/r32/eax
$index:end:
    # . restore registers
    5e/pop-to-esi
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

#######################################################
# Compute type sizes
#######################################################

# Compute the sizes of all user-defined types.
# We'll need the sizes of their elements, which may be other user-defined
# types, which we will compute as needed.

# Initially, all user-defined types have their sizes set to -2 (invalid)
populate-mu-type-sizes:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
$populate-mu-type-sizes:total-sizes:
    # var curr/ecx: (handle typeinfo) = *Program->types
    8b/-> *_Program-types 1/r32/ecx
    {
      # if (curr == null) break
      81 7/subop/compare %ecx 0/imm32
      74/jump-if-= break/disp8
      (populate-mu-type-sizes-in-type %ecx)
      # curr = curr->next
      8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
      eb/jump loop/disp8
    }
$populate-mu-type-sizes:offsets:
    # var curr/ecx: (handle typeinfo) = *Program->types
    8b/-> *_Program-types 1/r32/ecx
    {
      # if (curr == null) break
      81 7/subop/compare %ecx 0/imm32
      74/jump-if-= break/disp8
      (populate-mu-type-offsets %ecx)
      # curr = curr->next
      8b/-> *(ecx+0xc) 1/r32/ecx  # Typeinfo-next
      eb/jump loop/disp8
    }
$populate-mu-type-sizes:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# compute sizes of all fields, recursing as necessary
# sum up all their sizes to arrive at total size
# fields may be out of order, but that doesn't affect the answer
populate-mu-type-sizes-in-type:  # T: (addr typeinfo)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    56/push-esi
    57/push-edi
    # esi = T
    8b/-> *(ebp+8) 6/r32/esi
    # if T is already computed, return
    81 7/subop/compare *(esi+8) 0/imm32  # Typeinfo-total-size-in-bytes
    7d/jump-if->= $populate-mu-type-sizes-in-type:end/disp8
    # if T is being computed, abort
    81 7/subop/compare *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
    74/jump-if-= $populate-mu-type-sizes-in-type:abort/disp8
    # tag T (-2 to -1) to avoid infinite recursion
    c7 0/subop/copy *(esi+8) -1/imm32/being-computed  # Typeinfo-total-size-in-bytes
    # var total-size/edi: int = 0
    bf/copy-to-edi 0/imm32
    # - for every field, if it's a user-defined type, compute its size
    # var table/ecx: (handle table string_key (handle typeinfo-entry)) = T->fields
    8b/-> *(esi+4) 1/r32/ecx  # Typeinfo-fields
    # var table-size/edx: int = table->write
    8b/-> *ecx 2/r32/edx  # stream-write
    # var curr/ecx: (addr table_row) = table->data
    8d/copy-address *(ecx+0xc) 1/r32/ecx
    # var max/edx: (addr table_row) = table->data + table->write
    8d/copy-address *(ecx+edx) 2/r32/edx
    {
$populate-mu-type-sizes-in-type:loop:
      # if (curr >= max) break
      39/compare %ecx 2/r32/edx
      73/jump-if-addr>= break/disp8
      # var t/eax: (handle typeinfo-entry) = curr->value
      8b/-> *(ecx+4) 0/r32/eax
      # compute size of t
      (compute-size-of-var *eax)  # Typeinfo-entry-input-var => eax
      # result += eax
      01/add-to %edi 0/r32/eax
      # curr += row-size
      81 0/subop/add %ecx 8/imm32
      #
      eb/jump loop/disp8
    }
    # - save result
    89/<- *(esi+8) 7/r32/edi  # Typeinfo-total-size-in-bytes
$populate-mu-type-sizes-in-type:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$populate-mu-type-sizes-in-type:abort:
    (write-buffered Stderr "cycle in type definitions\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

# Analogous to size-of, except we need to compute what size-of can just read
# off the right data structures.
compute-size-of-var:  # in: (addr var) -> result/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . push registers
    51/push-ecx
    # var t/ecx: (addr tree type-id) = lookup(v->type)
    8b/-> *(ebp+8) 1/r32/ecx
    (lookup *(ecx+8) *(ecx+0xc))  # => eax
    89/<- %ecx 0/r32/eax
    # if (t->left-is-atom == false) t = lookup(t->left)
    {
      81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
      75/jump-if-!= break/disp8
      (lookup *(ecx+4) *(ecx+8))  # Tree-left Tree-left => eax
      89/<- %ecx 0/r32/eax
    }
    # TODO: ensure t is an atom
    (compute-size-of-type-id *(ecx+4))  # Tree-value => eax
$compute-size-of-var:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

compute-size-of-type-id:  # t: type-id -> result/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
    8b/-> *(ebp+8) 0/r32/eax
    # if v is a literal, return 0
    3d/compare-eax-and 0/imm32
    74/jump-if-= $compute-size-of-type-id:end/disp8  # eax changes type from type-id to int
    # if v has a user-defined type, compute its size
    # TODO: support non-atom type
    (find-typeinfo %eax)  # => eax
    {
      3d/compare-eax-and 0/imm32
      74/jump-if-= break/disp8
$compute-size-of-type-id:user-defined:
      (populate-mu-type-sizes %eax)
      8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
      eb/jump $compute-size-of-type-id:end/disp8
    }
    # otherwise return the word size
    b8/copy-to-eax 4/imm32
$compute-size-of-type-id:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# at this point we have total sizes for all user-defined types
# compute offsets for each element
# complication: fields may be out of order
populate-mu-type-offsets:  # in: (addr typeinfo)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # var curr-offset/edi: int = 0
    bf/copy-to-edi 0/imm32
    # var table/ecx: (addr table string_key (handle typeinfo-entry)) = lookup(T->fields)
    8b/-> *(ebp+8) 1/r32/ecx
    (lookup *(ecx+4) *(ecx+8))  # Typeinfo-fields Typeinfo-fields => eax
    89/<- %ecx 0/r32/eax
    # var num-elems/edx: int = table->write / Typeinfo-fields-row-size
    8b/-> *ecx 2/r32/edx  # stream-write
    c1 5/subop/shift-right-logical  %edx 4/imm8
    # var i/ebx: int = 0
    bb/copy-to-ebx 0/imm32
    {
$populate-mu-type-offsets:loop:
      39/compare %ebx 2/r32/edx
      7d/jump-if->= break/disp8
      # var v/esi: (addr typeinfo-entry)
      (locate-typeinfo-entry-with-index %ecx %ebx)  # => eax
      89/<- %esi 0/r32/eax
      # v->output-var->offset = curr-offset
      # . eax: (addr var)
      (lookup *(esi+0xc) *(esi+0x10))  # Typeinfo-entry-output-var Typeinfo-entry-output-var => eax
      89/<- *(eax+0x14) 7/r32/edi  # Var-offset
      # curr-offset += size-of(v->input-var)
      # . eax: (addr var)
      (lookup *esi *(esi+4))  # Typeinfo-entry-input-var Typeinfo-entry-input-var => eax
      # .
      (size-of %eax)  # => eax
      01/add-to %edi 0/r32/eax
      # ++i
      43/increment-ebx
      eb/jump loop/disp8
    }
$populate-mu-type-offsets:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

locate-typeinfo-entry-with-index:  # table: (addr table (handle array byte) (handle typeinfo-entry)), idx: int -> result/eax: (addr typeinfo-entry)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # esi = table
    8b/-> *(ebp+8) 6/r32/esi
    # var curr/ecx: (addr row (handle array byte) (handle typeinfo-entry)) = table->data
    8d/copy-address *(esi+0xc) 1/r32/ecx
    # var max/edx: (addr byte) = &table->data[table->write]
    8b/-> *esi 2/r32/edx
    8d/copy-address *(ecx+edx) 2/r32/edx
    {
$locate-typeinfo-entry-with-index:loop:
      39/compare %ecx 2/r32/edx
      73/jump-if-addr>= $locate-typeinfo-entry-with-index:abort/disp8
      # var v/eax: (addr typeinfo-entry)
      (lookup *(ecx+8) *(ecx+0xc))  # => eax
      # if (v->index == idx) return v
      8b/-> *(eax+8) 3/r32/ebx  # Typeinfo-entry-index
      39/compare *(ebp+0xc) 3/r32/ebx
      74/jump-if-= $locate-typeinfo-entry-with-index:end/disp8
      # curr += Typeinfo-entry-size
      81 0/subop/add %ecx 0x10/imm32  # Typeinfo-entry-size
      #
      eb/jump loop/disp8
    }
    # return 0
    b8/copy-to-eax 0/imm32
$locate-typeinfo-entry-with-index:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$locate-typeinfo-entry-with-index:abort:
    (write-buffered Stderr "overflowing typeinfo-entry->index ")
    (print-int32-buffered Stderr %ecx)
    (write-buffered Stderr "\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

#######################################################
# Type-checking
#######################################################

check-mu-types:
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
$check-mu-types:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

size-of:  # v: (addr var) -> result/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var t/ecx: (addr tree type-id) = lookup(v->type)
    8b/-> *(ebp+8) 1/r32/ecx
    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
    89/<- %ecx 0/r32/eax
    # if is-mu-array?(t) return size-of-array(t)
    {
      (is-mu-array? %ecx)  # => eax
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= break/disp8
      (size-of-array %ecx)  # => eax
      eb/jump $size-of:end/disp8
    }
    # if (!t->is-atom?) t = lookup(t->left)
    {
      81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
      75/jump-if-!= break/disp8
      (lookup *(ecx+4) *(ecx+8))  # Tree-left Tree-left => eax
      89/<- %ecx 0/r32/eax
    }
    # TODO: assert t->is-atom?
    (size-of-type-id *(ecx+4))  # Tree-value => eax
$size-of:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

size-of-deref:  # v: (addr var) -> result/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var t/ecx: (addr tree type-id) = lookup(v->type)
    8b/-> *(ebp+8) 1/r32/ecx
    (lookup *(ecx+8) *(ecx+0xc))  # Var-type Var-type => eax
    89/<- %ecx 0/r32/eax
    # TODO: assert(t is an addr)
    # t = lookup(t->right)
    (lookup *(ecx+0xc) *(ecx+0x10))  # Tree-right Tree-right => eax
    89/<- %ecx 0/r32/eax
    # if is-mu-array?(t) return size-of-array(t)
    {
      (is-mu-array? %ecx)  # => eax
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= break/disp8
      (size-of-array %ecx)  # => eax
      eb/jump $size-of:end/disp8
    }
    # if (!t->is-atom?) t = lookup(t->left)
    {
      81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
      75/jump-if-!= break/disp8
      (lookup *(ecx+4) *(ecx+8))  # Tree-left Tree-left => eax
      89/<- %ecx 0/r32/eax
    }
    # TODO: assert t->is-atom?
    (size-of-type-id *(ecx+4))  # Tree-value => eax
$size-of-deref:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

is-mu-array?:  # t: (addr tree type-id) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # ecx = t
    8b/-> *(ebp+8) 1/r32/ecx
    # result = false
    b8/copy-to-eax 0/imm32/false
    # if t->left-is-atom, return false
    81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
    75/jump-if-!= $is-mu-array?:end/disp8
    # if !t->left->left-is-atom, return false
    8b/-> *(ecx+4) 1/r32/ecx  # Tree-left
    81 7/subop/compare *ecx 0/imm32/false  # Tree-is-atom
    74/jump-if-= $is-mu-array?:end/disp8
    # return t->left->value == array
    81 7/subop/compare *(ecx+4) 3/imm32/array-type-id  # Tree-value
    0f 94/set-if-= %al
$is-mu-array?:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

size-of-array:  # a: (addr tree type-id) -> result/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    52/push-edx
    #
    8b/-> *(ebp+8) 1/r32/ecx
    # TODO: assert that a->left is 'array'
    8b/-> *(ecx+8) 1/r32/ecx  # Tree-right
    # var elem-type/edx: type-id = a->right->value
    8b/-> *(ecx+4) 2/r32/edx  # Tree-value
    8b/-> *(edx+4) 2/r32/edx  # Tree-value
    # var array-size/ecx: int = a->right->right->left->value
    8b/-> *(ecx+8) 1/r32/ecx  # Tree-right
    8b/-> *(ecx+4) 1/r32/ecx  # Tree-left
    8b/-> *(ecx+4) 1/r32/ecx  # Tree-value
    # return array-size * size-of(elem-type)
    (size-of-type-id %edx)  # => eax
    f7 4/subop/multiply-into-eax %ecx
    05/add-to-eax 4/imm32  # for array size
$size-of-array:end:
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

size-of-type-id:  # t: type-id -> result/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
    8b/-> *(ebp+8) 0/r32/eax
    # if v is a literal, return 0
    3d/compare-eax-and 0/imm32
    74/jump-if-= $size-of-type-id:end/disp8  # eax changes type from type-id to int
    # if v has a user-defined type, return its size
    # TODO: support non-atom type
    (find-typeinfo %eax)  # => eax
    {
      3d/compare-eax-and 0/imm32
      74/jump-if-= break/disp8
$size-of-type-id:user-defined:
      8b/-> *(eax+8) 0/r32/eax  # Typeinfo-total-size-in-bytes
      eb/jump $size-of-type-id:end/disp8
    }
    # otherwise return the word size
    b8/copy-to-eax 4/imm32
$size-of-type-id:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

type-equal?:  # a: (addr tree type-id), b: (addr tree type-id) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    52/push-edx
    # ecx = a
    8b/-> *(ebp+8) 1/r32/ecx
    # edx = b
    8b/-> *(ebp+0xc) 2/r32/edx
    # if (a == b) return true
    8b/-> %ecx 0/r32/eax  # Var-type
    39/compare %edx 0/r32/eax  # Var-type
    b8/copy-to-eax 1/imm32/true
    74/jump-if-= $type-equal?:end/disp8
    # if (a < MAX_TYPE_ID) return false
    81 7/subop/compare %ecx 0x10000/imm32
    b8/copy-to-eax 0/imm32/false
    72/jump-if-addr< $type-equal?:end/disp8
    # if (b < MAX_TYPE_ID) return false
    81 7/subop/compare %edx 0x10000/imm32
    b8/copy-to-eax 0/imm32/false
    72/jump-if-addr< $type-equal?:end/disp8
    # if (!type-equal?(a->left, b->left)) return false
    (type-equal? *(ecx+4) *(edx+4))  # Tree-left, Tree-left => eax
    3d/compare-eax-and 0/imm32/false
    74/jump-if-= $type-equal?:end/disp8
    # return type-equal?(a->right, b->right)
    (type-equal? *(ecx+8) *(edx+8))  # Tree-right, Tree-right => eax
$type-equal?:end:
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

#######################################################
# Code-generation
#######################################################

== data

Curr-block-depth:  # (addr int)
    0/imm32
Curr-local-stack-offset:  # (addr int)
    0/imm32

== code

emit-subx:  # out: (addr buffered-file)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    57/push-edi
    # edi = out
    8b/-> *(ebp+8) 7/r32/edi
    # var curr/ecx: (addr function) = *Program->functions
    (lookup *_Program-functions *_Program-functions->payload)  # => eax
    89/<- %ecx 0/r32/eax
    {
      # if (curr == null) break
      81 7/subop/compare %ecx 0/imm32
      0f 84/jump-if-= break/disp32
      (emit-subx-function %edi %ecx)
      # curr = curr->next
      8b/-> *(ecx+0x14) 1/r32/ecx  # Function-next
      e9/jump loop/disp32
    }
$emit-subx:end:
    # . restore registers
    5f/pop-to-edi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-function:  # out: (addr buffered-file), f: (addr function)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # some preprocessing
    (populate-mu-type-offsets-in-inouts *(ebp+0xc))
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    57/push-edi
    # edi = out
    8b/-> *(ebp+8) 7/r32/edi
    # ecx = f
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var vars/edx: (stack (addr var) 256)
    81 5/subop/subtract %esp 0x800/imm32
    68/push 0x800/imm32/size
    68/push 0/imm32/top
    89/<- %edx 4/r32/esp
    #
    (write-buffered %edi *ecx)
    (write-buffered %edi ":\n")
    # initialize some global state
    c7 0/subop/copy *Curr-block-depth 1/imm32
    c7 0/subop/copy *Curr-local-stack-offset 0/imm32
    #
    (emit-subx-prologue %edi)
    (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
    (emit-subx-epilogue %edi)
    # TODO: validate that *Curr-block-depth and *Curr-local-stack-offset have
    # been cleaned up
$emit-subx-function:end:
    # . reclaim locals
    81 0/subop/add %esp 408/imm32
    # . restore registers
    5f/pop-to-edi
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

populate-mu-type-offsets-in-inouts:  # f: (addr function)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    57/push-edi
    # var next-offset/edx: int = 8
    ba/copy-to-edx 8/imm32
    # var curr/ecx: (handle list var) = f->inouts
    8b/-> *(ebp+8) 1/r32/ecx
    8b/-> *(ecx+8) 1/r32/ecx  # Function-inouts
    {
$populate-mu-type-offsets-in-inouts:loop:
      81 7/subop/compare %ecx 0/imm32
      74/jump-if-= break/disp8
      # var v/ebx: (handle var) = curr->value
      8b/-> *ecx 3/r32/ebx  # List-value
      # v->offset = next-offset
      89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
      # next-offset += size-of(v)
      (size-of %ebx)  # => eax
      01/add-to %edx 0/r32/eax
      # curr = curr->next
      8b/-> *(ecx+4) 1/r32/ecx  # List-next
      eb/jump loop/disp8
    }
$populate-mu-type-offsets-in-inouts:end:
    # . restore registers
    5f/pop-to-edi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (addr list stmt), vars: (addr stack (handle var))
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    # esi = stmts
    8b/-> *(ebp+0xc) 6/r32/esi
    # var var-seen?/edx: boolean <- copy false
    ba/copy-to-edx 0/imm32/false
    #
    {
$emit-subx-stmt-list:loop:
      81 7/subop/compare %esi 0/imm32
      0f 84/jump-if-= break/disp32
      # var curr-stmt/ecx = stmts->value
      8b/-> *esi 1/r32/ecx  # List-value
      {
$emit-subx-stmt-list:check-for-block:
        81 7/subop/compare *ecx 0/imm32/block  # Stmt-tag
        75/jump-if-!= break/disp8
$emit-subx-stmt-list:block:
        (emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
      }
      {
$emit-subx-stmt-list:check-for-stmt:
        81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
        0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:stmt1:
        {
          (is-mu-branch? %ecx)  # => eax
          3d/compare-eax-and 0/imm32/false
          0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:branch-stmt:
          # if !var-seen? break
          81 7/subop/compare %edx 0/imm32/false
          0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:branch-stmt-and-var-seen:
          # unconditional loops {{{
          {
            # if (!string-equal?(var->operation, "loop")) break
            (string-equal? *(ecx+4) "loop")  # Stmt1-operation => eax
            3d/compare-eax-and 0/imm32/false
            0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:unconditional-loop:
            81 7/subop/compare *(ecx+8) 0/imm32  # Stmt1-inouts
            # simple unconditional loops without a target
            {
              0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:zero-arg-unconditional-loop:
              (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
              (emit-indent *(ebp+8) *Curr-block-depth)
              (write-buffered *(ebp+8) "e9/jump loop/disp32")
              (write-buffered *(ebp+8) Newline)
              e9/jump $emit-subx-stmt-list:cleanup/disp32  # skip remaining statements; they're dead code
            }
            # unconditional loops with a target
            {
              0f 84/jump-if-= break/disp32
              (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
              e9/jump $emit-subx-stmt-list:cleanup/disp32
            }
          }
          # }}}
          # unconditional breaks {{{
          {
            # if (!string-equal?(curr-stmt->operation, "break")) break
            (string-equal? *(ecx+4) "break")  # Stmt1-operation => eax
            3d/compare-eax-and 0/imm32/false
            0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:unconditional-break:
            81 7/subop/compare *(ecx+8) 0/imm32  # Stmt1-inouts
            # simple unconditional breaks without a target
            0f 84/jump-if-= $emit-subx-stmt-list:emit-cleanup/disp32  # easy: just skip remaining statements
            # unconditional breaks with a target
            (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
            e9/jump $emit-subx-stmt-list:cleanup/disp32
          }
          # }}}
          # simple conditional branches without a target {{{
          81 7/subop/compare *(ecx+8) 0/imm32  # Stmt1-inouts
          {
            0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:zero-arg-conditional-branch:
            # var old-block-depth/eax: int = Curr-block-depth - 1
            8b/-> *Curr-block-depth 3/r32/ebx
            # cleanup prologue
            (emit-indent *(ebp+8) *Curr-block-depth)
            (write-buffered *(ebp+8) "{\n")
            ff 0/subop/increment *Curr-block-depth
            #
            (emit-reverse-break *(ebp+8) %ecx)
            # clean up until old block depth
            (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) %ebx)
            # var target-block-depth/ebx: int = Curr-block-depth - 1
            4b/decrement-ebx
            # emit jump to target block
            (string-starts-with? *(ecx+4) "break")
            3d/compare-eax-and 0/imm32/false
            {
              74/jump-if-= break/disp8
              (emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "break")
            }
            3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
            {
              75/jump-if-!= break/disp8
              (emit-unconditional-jump-to-depth *(ebp+8) *(ebp+0x10) %ebx "loop")
            }
            # cleanup epilogue
            ff 1/subop/decrement *Curr-block-depth
            (emit-indent *(ebp+8) *Curr-block-depth)
            (write-buffered *(ebp+8) "}\n")
            # continue
            e9/jump $emit-subx-stmt-list:continue/disp32
          }
          # }}}
          # conditional branches with an explicit target {{{
          {
            0f 84/jump-if-= break/disp32
$emit-subx-stmt-list:conditional-branch-with-target:
            # cleanup prologue
            (emit-indent *(ebp+8) *Curr-block-depth)
            (write-buffered *(ebp+8) "{\n")
            ff 0/subop/increment *Curr-block-depth
            #
            (emit-reverse-break *(ebp+8) %ecx)
            (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10))
            # cleanup epilogue
            ff 1/subop/decrement *Curr-block-depth
            (emit-indent *(ebp+8) *Curr-block-depth)
            (write-buffered *(ebp+8) "}\n")
            # continue
            e9/jump $emit-subx-stmt-list:continue/disp32
          }
          # }}}
        }
$emit-subx-stmt-list:1-to-1:
        (emit-subx-stmt *(ebp+8) %ecx Primitives)
        e9/jump $emit-subx-stmt-list:continue/disp32
      }
      {
$emit-subx-stmt-list:check-for-var-def:
        81 7/subop/compare *ecx 2/imm32/var-def  # Stmt-tag
        75/jump-if-!= break/disp8
$emit-subx-stmt-list:var-def:
        (emit-subx-var-def *(ebp+8) %ecx)
        (push *(ebp+0x10) *(ecx+4))  # Vardef-var
        # var-seen? = true
        ba/copy-to-edx 1/imm32/true
        eb/jump $emit-subx-stmt-list:continue/disp8
      }
      {
$emit-subx-stmt-list:check-for-reg-var-def:
        81 7/subop/compare *ecx 3/imm32/reg-var-def  # Stmt-tag
        0f 85/jump-if-!= break/disp32
$emit-subx-stmt-list:reg-var-def:
        # TODO: ensure that there's exactly one output
        (push-output-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10))
        # emit the instruction as usual
        (emit-subx-stmt *(ebp+8) %ecx Primitives)
        # var-seen? = true
        ba/copy-to-edx 1/imm32/true
        eb/jump $emit-subx-stmt-list:continue/disp8
      }
$emit-subx-stmt-list:continue:
      # TODO: raise an error on unrecognized Stmt-tag
      8b/-> *(esi+4) 6/r32/esi  # List-next
      e9/jump loop/disp32
    }
$emit-subx-stmt-list:emit-cleanup:
    (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth)
$emit-subx-stmt-list:cleanup:
    (clean-up-blocks *(ebp+0x10) *Curr-block-depth)
$emit-subx-stmt-list:end:
    # . restore registers
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

push-output-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (addr reg-var-def), vars: (addr stack (handle var))
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var sv/eax: (addr stmt-var) = lookup(curr-stmt->outputs)
    (lookup *(ecx+0x14) *(ecx+0x18))  # Regvardef-outputs Regvardef-outputs => eax
    # push(vars, sv->value)
    (push *(ebp+0x10) *eax)  # Stmt-var-value
    (push *(ebp+0x10) *(eax+4))  # Stmt-var-value
    # var v/ecx: (addr var) = lookup(sv->value)
    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
    89/<- %ecx 0/r32/eax
    # v->block-depth = *Curr-block-depth
    8b/-> *Curr-block-depth 0/r32/eax
    89/<- *(ecx+0x10) 0/r32/eax  # Var-block-depth
    # ensure that v is in a register
    81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
    0f 84/jump-if-= $push-output-and-maybe-emit-spill:abort/disp32
    # if already-spilled-this-block?(reg, vars) return
    (already-spilled-this-block? %ecx *(ebp+0x10))  # => eax
    3d/compare-eax-and 0/imm32/false
    75/jump-if-!= $push-output-and-maybe-emit-spill:end/disp8
    # TODO: assert(size-of(output) == 4)
    # *Curr-local-stack-offset -= 4
    81 5/subop/subtract *Curr-local-stack-offset 4/imm32
    # emit spill
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "ff 6/subop/push %")
    (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
    (write-buffered *(ebp+8) %eax)
    (write-buffered *(ebp+8) Newline)
$push-output-and-maybe-emit-spill:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$push-output-and-maybe-emit-spill:abort:
    # error("var '" var->name "' initialized from an instruction must live in a register\n")
    (write-buffered Stderr "var '")
    (write-buffered Stderr *eax)  # Var-name
    (write-buffered Stderr "' initialized from an instruction must live in a register\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var target/edx: (addr array byte) = curr-stmt->inouts->value->name
    8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
    8b/-> *edx 2/r32/edx  # Stmt-var-value
    8b/-> *edx 2/r32/edx  # Var-name
    # clean up until target block
    (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %edx)
    # emit jump to target block
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "e9/jump ")
    (write-buffered *(ebp+8) %edx)
    (string-starts-with? *(ecx+4) "break")
    3d/compare-eax-and 0/imm32/false
    {
      74/jump-if-= break/disp8
      (write-buffered *(ebp+8) ":break/disp32\n")
    }
    3d/compare-eax-and 0/imm32/false  # just in case the function call modified flags
    {
      75/jump-if-!= break/disp8
      (write-buffered *(ebp+8) ":loop/disp32\n")
    }
$emit-subx-cleanup-and-unconditional-nonlocal-branch:end:
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

is-mu-branch?:  # stmt: (addr stmt1) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # ecx = stmt
    8b/-> *(ebp+8) 1/r32/ecx
    # if (stmt->operation starts with "loop") return true
    (string-starts-with? *(ecx+4) "loop")  # Stmt1-operation => eax
    3d/compare-eax-and 0/imm32/false
    75/jump-if-not-equal $is-mu-branch?:end/disp8
    # otherwise return (stmt->operation starts with "break")
    (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
$is-mu-branch?:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-reverse-break:  # out: (addr buffered-file), stmt: (addr stmt1)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    # eax = stmt
    8b/-> *(ebp+0xc) 0/r32/eax
    #
    (get Reverse-branch *(eax+4) 8 "reverse-branch: ")  # Stmt1-operation => eax: (addr addr array byte)
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) *eax)
    (write-buffered *(ebp+8) " break/disp32\n")
$emit-reverse-break:end:
    # . restore registers
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

== data

Reverse-branch:  # (table string string)
  # a table is a stream
  0xa0/imm32/write
  0/imm32/read
  0xa0/imm32/size
  # data
  "break-if-="/imm32      "0f 85/jump-if-!="/imm32
  "loop-if-="/imm32       "0f 85/jump-if-!="/imm32
  "break-if-!="/imm32     "0f 84/jump-if-="/imm32
  "loop-if-!="/imm32      "0f 84/jump-if-="/imm32
  "break-if-<"/imm32      "0f 8d/jump-if->="/imm32
  "loop-if-<"/imm32       "0f 8d/jump-if->="/imm32
  "break-if->"/imm32      "0f 8e/jump-if-<="/imm32
  "loop-if->"/imm32       "0f 8e/jump-if-<="/imm32
  "break-if-<="/imm32     "0f 87/jump-if->"/imm32
  "loop-if-<="/imm32      "0f 87/jump-if->"/imm32
  "break-if->="/imm32     "0f 8c/jump-if-<"/imm32
  "loop-if->="/imm32      "0f 8c/jump-if-<"/imm32
  "break-if-addr<"/imm32  "0f 83/jump-if-addr>="/imm32
  "loop-if-addr<"/imm32   "0f 83/jump-if-addr>="/imm32
  "break-if-addr>"/imm32  "0f 86/jump-if-addr<="/imm32
  "loop-if-addr>"/imm32   "0f 86/jump-if-addr<="/imm32
  "break-if-addr<="/imm32 "0f 87/jump-if-addr>"/imm32
  "loop-if-addr<="/imm32  "0f 87/jump-if-addr>"/imm32
  "break-if-addr>="/imm32 "0f 82/jump-if-addr<"/imm32
  "loop-if-addr>="/imm32  "0f 82/jump-if-addr<"/imm32

== code

emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    # ecx = vars
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var eax: int = vars->top
    8b/-> *ecx 0/r32/eax
    # var min/ecx: (addr handle var) = vars->data
    81 0/subop/add %ecx 8/imm32
    # var curr/eax: (addr handle var) = &vars->data[vars->top - 4]
    81 5/subop/subtract %eax 4/imm32
    8d/copy-address *(ecx+eax) 0/r32/eax
    # edx = depth
    8b/-> *(ebp+0x10) 2/r32/edx
    {
$emit-unconditional-jump-to-depth:loop:
      # if (curr < min) break
      39/compare %eax 1/r32/ecx
      0f 82/jump-if-addr< break/disp32
      # var v/ebx: (handle var) = *curr
      8b/-> *eax 3/r32/ebx
      # if (v->block-depth < until-block-depth) break
      39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
      0f 8c/jump-if-< break/disp32
      {
$emit-unconditional-jump-to-depth:check:
        # if v->block-depth != until-block-depth, continue
        39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
        0f 85/jump-if-!= break/disp32
$emit-unconditional-jump-to-depth:depth-found:
        # if v is not a literal, continue
        # . var eax: int = size-of(v)
        50/push-eax
        (size-of %ebx)  # => eax
        # . if (eax != 0) continue
        3d/compare-eax-and 0/imm32
        58/pop-to-eax
        #
        0f 85/jump-if-!= break/disp32
$emit-unconditional-jump-to-depth:label-found:
        # emit unconditional jump, then return
        (emit-indent *(ebp+8) *Curr-block-depth)
        (write-buffered *(ebp+8) "e9/jump ")
        (write-buffered *(ebp+8) *ebx)  # Var-name
        (write-buffered *(ebp+8) ":")
        (write-buffered *(ebp+8) *(ebp+0x14))
        (write-buffered *(ebp+8) "/disp32\n")
        eb/jump $emit-unconditional-jump-to-depth:end/disp8
      }
      # curr -= 4
      2d/subtract-from-eax 4/imm32
      e9/jump loop/disp32
    }
    # TODO: error if no label at 'depth' was found
$emit-unconditional-jump-to-depth:end:
    # . restore registers
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# emit clean-up code for 'vars' until some block depth
# doesn't actually modify 'vars' so we need traverse manually inside the stack
emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    # ecx = vars
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var eax: int = vars->top
    8b/-> *ecx 0/r32/eax
    # var min/ecx: (addr handle var) = vars->data
    81 0/subop/add %ecx 8/imm32
    # var curr/eax: (addr handle var) = &vars->data[vars->top - 4]
    81 5/subop/subtract %eax 4/imm32
    8d/copy-address *(ecx+eax) 0/r32/eax
    # edx = until-block-depth
    8b/-> *(ebp+0x10) 2/r32/edx
    {
$emit-cleanup-code-until-depth:loop:
      # if (curr < min) break
      39/compare %eax 1/r32/ecx
      0f 82/jump-if-addr< break/disp32
      # var v/ebx: (handle var) = *curr
      8b/-> *eax 3/r32/ebx
      # if (v->block-depth < until-block-depth) break
      39/compare *(ebx+8) 2/r32/edx  # Var-block-depth
      0f 8c/jump-if-< break/disp32
      # if v is in a register
      81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
      {
        0f 84/jump-if-= break/disp32
        50/push-eax
        {
$emit-cleanup-code-until-depth:check-for-previous-spill:
          (same-register-spilled-before? %ebx *(ebp+0xc) %eax)  # => eax
          3d/compare-eax-and 0/imm32/false
          0f 85/jump-if-!= break/disp32
$emit-cleanup-code-until-depth:reclaim-var-in-register:
          (emit-indent *(ebp+8) *Curr-block-depth)
          (write-buffered *(ebp+8) "8f 0/subop/pop %")
          (write-buffered *(ebp+8) *(ebx+0x10))
          (write-buffered *(ebp+8) Newline)
        }
        58/pop-to-eax
        eb/jump $emit-cleanup-code-until-depth:continue/disp8
      }
      # otherwise v is on the stack
      {
        75/jump-if-!= break/disp8
$emit-cleanup-code-until-depth:reclaim-var-on-stack:
        50/push-eax
        (size-of %ebx)  # => eax
        # don't emit code for labels
        3d/compare-eax-and 0/imm32
        74/jump-if-= break/disp8
        #
        (emit-indent *(ebp+8) *Curr-block-depth)
        (write-buffered *(ebp+8) "81 0/subop/add %esp ")
        (print-int32-buffered *(ebp+8) %eax)
        (write-buffered *(ebp+8) "/imm32\n")
        58/pop-to-eax
      }
$emit-cleanup-code-until-depth:continue:
      # curr -= 4
      2d/subtract-from-eax 4/imm32
      e9/jump loop/disp32
    }
$emit-cleanup-code-until-depth:end:
    # . restore registers
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# emit clean-up code for 'vars' until a given label is encountered
# doesn't actually modify 'vars' so we need traverse manually inside the stack
emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    # ecx = vars
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var eax: int = vars->top
    8b/-> *ecx 0/r32/eax
    # var min/ecx: (addr handle var) = vars->data
    81 0/subop/add %ecx 8/imm32
    # var curr/edx: (addr handle var) = &vars->data[vars->top - 4]
    81 5/subop/subtract %eax 4/imm32
    8d/copy-address *(ecx+eax) 2/r32/edx
    {
$emit-cleanup-code-until-target:loop:
      # if (curr < min) break
      39/compare %edx 1/r32/ecx
      0f 82/jump-if-addr< break/disp32
      # var v/ebx: (handle var) = *curr
      8b/-> *edx 3/r32/ebx
      # if (v->name == until-block-label) break
      (string-equal? *ebx *(ebp+0x10))  # => eax
      3d/compare-eax-and 0/imm32/false
      0f 85/jump-if-!= break/disp32
      # if v is in a register
      81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
      {
        74/jump-if-= break/disp8
        50/push-eax
        {
$emit-cleanup-code-until-target:check-for-previous-spill:
          (same-register-spilled-before? %ebx *(ebp+0xc) %edx)  # => eax
          3d/compare-eax-and 0/imm32/false
          75/jump-if-!= break/disp8
$emit-cleanup-code-until-target:reclaim-var-in-register:
          (emit-indent *(ebp+8) *Curr-block-depth)
          (write-buffered *(ebp+8) "8f 0/subop/pop %")
          (write-buffered *(ebp+8) *(ebx+0x10))
          (write-buffered *(ebp+8) Newline)
        }
        58/pop-to-eax
        eb/jump $emit-cleanup-code-until-target:continue/disp8
      }
      # otherwise v is on the stack
      {
        75/jump-if-!= break/disp8
$emit-cleanup-code-until-target:reclaim-var-on-stack:
        (size-of %ebx)  # => eax
        # don't emit code for labels
        3d/compare-eax-and 0/imm32
        74/jump-if-= break/disp8
        #
        (emit-indent *(ebp+8) *Curr-block-depth)
        (write-buffered *(ebp+8) "81 0/subop/add %esp ")
        (print-int32-buffered *(ebp+8) %eax)
        (write-buffered *(ebp+8) "/imm32\n")
      }
$emit-cleanup-code-until-target:continue:
      # curr -= 4
      81 5/subop/subtract %edx 4/imm32
      e9/jump loop/disp32
    }
$emit-cleanup-code-until-target:end:
    # . restore registers
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# is there already a var with the same block-depth and register as 'v' on the 'vars' stack?
# v is guaranteed not to be within vars
already-spilled-this-block?:  # v: (addr var), vars: (addr stack (handle var)) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # ecx = vars
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var eax: int = vars->top
    8b/-> *ecx 0/r32/eax
    # var min/ecx: (addr handle var) = vars->data
    81 0/subop/add %ecx 8/imm32
    # var curr/edx: (addr handle var) = &vars->data[vars->top - 4]
    81 5/subop/subtract %eax 4/imm32
    8d/copy-address *(ecx+eax) 2/r32/edx
    # var depth/ebx: int = v->block-depth
    8b/-> *(ebp+8) 3/r32/ebx
    8b/-> *(ebx+8) 3/r32/ebx  # Var-block-depth
    # var needle/esi: (handle array byte) = v->register
    8b/-> *(ebp+8) 6/r32/esi
    8b/-> *(esi+0x10) 6/r32/esi  # Var-register
    {
$already-spilled-this-block?:loop:
      # if (curr < min) break
      39/compare %edx 1/r32/ecx
      0f 82/jump-if-addr< break/disp32
      # var cand/edi: (handle var) = *curr
      8b/-> *edx 7/r32/edi
      # if (cand->block-depth < depth) break
      39/compare *(edi+8) 3/r32/ebx  # Var-block-depth
      0f 8c/jump-if-< break/disp32
      # var cand-reg/edi: (handle array byte) = cand->reg
      8b/-> *(edi+0x10) 7/r32/edi
      # if (cand-reg == null) continue
      {
$already-spilled-this-block?:check-reg:
        81 7/subop/compare %edi 0/imm32
        74/jump-if-= break/disp8
        # if (cand-reg == needle) return true
        (string-equal? %esi %edi)  # => eax
        3d/compare-eax-and 0/imm32/false
        74/jump-if-= break/disp8
        b8/copy-to-eax 1/imm32/true
        eb/jump $already-spilled-this-block?:end/disp8
      }
$already-spilled-this-block?:continue:
      # curr -= 4
      81 5/subop/subtract %edx 4/imm32
      e9/jump loop/disp32
    }
    # return false
    b8/copy-to-eax 0/imm32/false
$already-spilled-this-block?:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# is there a var before 'v' with the same block-depth and register on the 'vars' stack?
# v is guaranteed to be within vars
# 'start' is provided as an optimization, a pointer within vars
# *start == v
same-register-spilled-before?:  # v: (addr var), vars: (addr stack (handle var)), start: (addr handle var) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # ecx = v
    8b/-> *(ebp+8) 1/r32/ecx
    # var reg/edx: (handle array byte) = v->register
    8b/-> *(ecx+0x10) 2/r32/edx  # Var-register
    # var depth/ebx: int = v->block-depth
    8b/-> *(ecx+8) 3/r32/ebx  # Var-block-depth
    # var min/ecx: (addr handle var) = vars->data
    8b/-> *(ebp+0xc) 1/r32/ecx
    81 0/subop/add %ecx 8/imm32
    # TODO: check that start >= min and start < &vars->data[top]
    # TODO: check that *start == v
    # var curr/esi: (addr handle var) = start
    8b/-> *(ebp+0x10) 6/r32/esi
    # curr -= 4
    81 5/subop/subtract %esi 4/imm32
    {
$same-register-spilled-before?:loop:
      # if (curr < min) break
      39/compare %esi 1/r32/ecx
      0f 82/jump-if-addr< break/disp32
      # var x/eax: (handle var) = *curr
      8b/-> *esi 0/r32/eax
      # if (x->block-depth < depth) break
      39/compare *(eax+8) 3/r32/ebx  # Var-block-depth
      0f 8c/jump-if-< break/disp32
      # if (x->register == 0) continue
      81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
      74/jump-if-= $same-register-spilled-before?:continue/disp8
      # if (x->register == reg) return true
      (string-equal? *(eax+0x10) %edx)  # Var-register => eax
      3d/compare-eax-and 0/imm32/false
      75/jump-if-!= $same-register-spilled-before?:end/disp8
$same-register-spilled-before?:continue:
      # curr -= 4
      81 5/subop/subtract %esi 4/imm32
      e9/jump loop/disp32
    }
$same-register-spilled-before?:false:
    b8/copy-to-eax 0/imm32/false
$same-register-spilled-before?:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# clean up global state for 'vars' until some block depth
clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    # esi = vars
    8b/-> *(ebp+8) 6/r32/esi
    # ecx = until-block-depth
    8b/-> *(ebp+0xc) 1/r32/ecx
    {
$clean-up-blocks:reclaim-loop:
      # if (vars->top <= 0) break
      81 7/subop/compare *esi 0/imm32  # Stack-top
      7e/jump-if-<= break/disp8
      # var v/eax: (handle var) = top(vars)
      (top %esi)  # => eax
      # if (v->block-depth < until-block-depth) break
      39/compare *(eax+8) 1/r32/ecx  # Var-block-depth
      7c/jump-if-< break/disp8
      # if v is on the stack, update Curr-local-stack-offset
      81 7/subop/compare *(eax+0x10) 0/imm32  # Var-register
      {
        75/jump-if-!= break/disp8
$clean-up-blocks:reclaim-var-on-stack:
        (size-of %eax)  # => eax
        01/add-to *Curr-local-stack-offset 0/r32/eax
      }
      (pop %esi)
      e9/jump loop/disp32
    }
$clean-up-blocks:end:
    # . restore registers
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-var-def:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    # eax = stmt
    8b/-> *(ebp+0xc) 0/r32/eax
    # var v/ecx: (handle var)
    8b/-> *(eax+4) 1/r32/ecx  # Vardef-var
    # v->block-depth = *Curr-block-depth
    8b/-> *Curr-block-depth 0/r32/eax
    89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
    # var n/edx: int = size-of(stmt->var)
    (size-of %ecx)  # => eax
    89/<- %edx 0/r32/eax
    # *Curr-local-stack-offset -= n
    29/subtract-from *Curr-local-stack-offset 2/r32/edx
    # v->offset = *Curr-local-stack-offset
    8b/-> *Curr-local-stack-offset 0/r32/eax
    89/<- *(ecx+0xc) 0/r32/eax  # Var-offset
    # if v is an array, do something special
    {
      (is-mu-array? *(ecx+4))  # Var-type => eax
      3d/compare-eax-and 0/imm32/false
      0f 84/jump-if-= break/disp32
      # var array-size-without-size/edx: int = n-4
      81 5/subop/subtract %edx 4/imm32
      (emit-indent *(ebp+8) *Curr-block-depth)
      (write-buffered *(ebp+8) "(push-n-zero-bytes ")
      (print-int32-buffered *(ebp+8) %edx)
      (write-buffered *(ebp+8) ")\n")
      (emit-indent *(ebp+8) *Curr-block-depth)
      (write-buffered *(ebp+8) "68/push ")
      (print-int32-buffered *(ebp+8) %edx)
      (write-buffered *(ebp+8) "/imm32\n")
      eb/jump $emit-subx-var-def:end/disp8
    }
    # while n > 0
    {
      81 7/subop/compare %edx 0/imm32
      7e/jump-if-<= break/disp8
      (emit-indent *(ebp+8) *Curr-block-depth)
      (write-buffered *(ebp+8) "68/push 0/imm32\n")
      # n -= 4
      81 5/subop/subtract %edx 4/imm32
      #
      eb/jump loop/disp8
    }
$emit-subx-var-def:end:
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-stmt:  # out: (addr buffered-file), stmt: (addr stmt), primitives: (addr primitive)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # - some special-case primitives that don't actually use the 'primitives' data structure
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    # array size
    {
      # if (!string-equal?(stmt->operation, "length")) break
      (string-equal? *(ecx+4) "length")  # Stmt1-operation => eax
      3d/compare-eax-and 0/imm32
      0f 84/jump-if-= break/disp32
      (translate-mu-length-stmt *(ebp+8) *(ebp+0xc))
      e9/jump $emit-subx-stmt:end/disp32
    }
    # index into array
    {
      # if (!string-equal?(var->operation, "index")) break
      (string-equal? *(ecx+4) "index")  # Stmt1-operation => eax
      3d/compare-eax-and 0/imm32
      0f 84/jump-if-= break/disp32
      (translate-mu-index-stmt *(ebp+8) *(ebp+0xc))
      e9/jump $emit-subx-stmt:end/disp32
    }
    # compute-offset for index into array
    {
      # if (!string-equal?(var->operation, "compute-offset")) break
      (string-equal? *(ecx+4) "compute-offset")  # Stmt1-operation => eax
      3d/compare-eax-and 0/imm32
      0f 84/jump-if-= break/disp32
      (translate-mu-compute-index-stmt *(ebp+8) *(ebp+0xc))
      e9/jump $emit-subx-stmt:end/disp32
    }
    # get field from record
    {
      # if (!string-equal?(var->operation, "get")) break
      (string-equal? *(ecx+4) "get")  # Stmt1-operation => eax
      3d/compare-eax-and 0/imm32
      0f 84/jump-if-= break/disp32
      (translate-mu-get-stmt *(ebp+8) *(ebp+0xc))
      e9/jump $emit-subx-stmt:end/disp32
    }
    # - if stmt matches a primitive, emit it
    {
$emit-subx-stmt:check-for-primitive:
      (find-matching-primitive *(ebp+0x10) *(ebp+0xc))  # primitives, stmt => curr/eax
      3d/compare-eax-and 0/imm32
      74/jump-if-= break/disp8
$emit-subx-stmt:primitive:
      (emit-subx-primitive *(ebp+8) *(ebp+0xc) %eax)  # out, stmt, curr
      e9/jump $emit-subx-stmt:end/disp32
    }
    # - otherwise emit a call
    # TODO: type-checking
$emit-subx-stmt:call:
    (emit-call *(ebp+8) *(ebp+0xc))
$emit-subx-stmt:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# TODO: actually return the length in array elements, rather than the size in bytes
translate-mu-length-stmt:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    #
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "8b/-> *")
    # var base/eax: (handle var) = inouts[0]
    8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    # if base is an (addr array ...) in a register
    {
      81 7/subop/compare *(eax+0x10)) 0/imm32  # Var-register
      74/jump-if-= break/disp8
      (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
      eb/jump $translate-mu-length-stmt:emit-output/disp8
    }
    # otherwise if base is an (array ...) on the stack
    {
      81 7/subop/compare *(eax+0xc)) 0/imm32  # Var-offset
      74/jump-if-= break/disp8
      (write-buffered *(ebp+8) "(ebp+")
      (print-int32-buffered *(ebp+8) *(eax+0xc))  # Var-offset
      (write-buffered *(ebp+8) ")")
    }
$translate-mu-length-stmt:emit-output:
    (write-buffered *(ebp+8) " ")
    # outputs[0] "/r32"
    8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (get Registers *(eax+0x10) 0xc "Registers")  # Var-register => eax
    (print-int32-buffered *(ebp+8) *eax)
    (write-buffered *(ebp+8) "/r32\n")
$translate-mu-length-stmt:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

translate-mu-index-stmt:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var base/ecx: (handle var) = stmt->inouts[0]
    8b/-> *(ebp+0xc) 1/r32/ecx
    8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
    8b/-> *ecx 1/r32/ecx  # Stmt-var-value
    # if (var->register) do one thing
    {
      81 7/subop/compare *(ecx+0x10) 0/imm32  # Var-register
      74/jump-if-= break/disp8
      # TODO: ensure there's no dereference
      (translate-mu-index-stmt-with-array-in-register *(ebp+8) *(ebp+0xc))
      eb/jump $translate-mu-index-stmt:end/disp8
    }
    # if (var->offset) do a different thing
    {
      81 7/subop/compare *(ecx+0xc) 0/imm32  # Var-offset
      74/jump-if-= break/disp8
      # TODO: ensure there's no dereference
      (translate-mu-index-stmt-with-array-on-stack *(ebp+8) *(ebp+0xc))
      eb/jump $translate-mu-index-stmt:end/disp8
    }
$translate-mu-index-stmt:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$translate-mu-index-stmt-with-array:error1:
    (write-buffered Stderr "couldn't translate an index instruction. second (index) input must either lie in a register or be a literal\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

$translate-mu-index-stmt-with-array:error2:
    (write-buffered Stderr "couldn't translate an index instruction. second (index) input when in a register must be an int or offset\n")
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

translate-mu-index-stmt-with-array-in-register:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    #
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "8d/copy-address *(")
    # TODO: ensure inouts[0] is in a register and not dereferenced
$translate-mu-index-stmt-with-array-in-register:emit-base:
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var base/ebx: (handle var) = inouts[0]
    8b/-> *(ecx+8) 3/r32/ebx  # Stmt1-inouts
    8b/-> *ebx 3/r32/ebx  # Stmt-var-value
    # print base->register " + "
    (write-buffered *(ebp+8) *(ebx+0x10))  # Var-register
    #
    (write-buffered *(ebp+8) " + ")
    # var index/edx: (handle var) = inouts[1]
    8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
    8b/-> *(edx+4) 2/r32/edx  # Stmt-var-next
    8b/-> *edx 2/r32/edx  # Stmt-var-value
    # if index->register
    81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
    {
      0f 84/jump-if-= break/disp32
$translate-mu-index-stmt-with-array-in-register:emit-register-index:
      # if index is an int
      (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
      3d/compare-eax-and 0/imm32/false
      {
        0f 84/jump-if-= break/disp32
$translate-mu-index-stmt-with-array-in-register:emit-int-register-index:
        # print index->register "<<" log2(size-of(element(base->type))) " + 4) "
        # . index->register "<<"
        (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
        (write-buffered *(ebp+8) "<<")
        # . log2(size-of(element(base->type)))
        # TODO: ensure size is a power of 2
        (array-element-type-id %ebx)  # => eax
        (size-of-type-id %eax)  # => eax
        (num-shift-rights %eax)  # => eax
        (print-int32-buffered *(ebp+8) %eax)
        e9/jump $translate-mu-index-stmt-with-array-in-register:emit-register-index-done/disp32
      }
      # if index->type is any other atom, abort
      (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
      81 7/subop/compare *eax 0/imm32/false  # Tree-is-atom
      0f 85/jump-if-!= $translate-mu-index-stmt-with-array:error2/disp32
      # if index has type (offset ...)
      (is-simple-mu-type? *(eax+4) 7)  # Tree-left, offset => eax
      3d/compare-eax-and 0/imm32/false
      {
        0f 84/jump-if-= break/disp32
        # print index->register " + 4) "
$translate-mu-index-stmt-with-array-in-register:emit-offset-register-index:
        (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
      }
$translate-mu-index-stmt-with-array-in-register:emit-register-index-done:
      (write-buffered *(ebp+8) " + 4) ")
      e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
    }
    # otherwise if index is a literal
    (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
    3d/compare-eax-and 0/imm32/false
    {
      0f 84/jump-if-= break/disp32
$translate-mu-index-stmt-with-array-in-register:emit-literal-index:
      # var index-value/edx: int = parse-hex-int(index->name)
      (parse-hex-int *edx)  # Var-name => eax
      89/<- %edx 0/r32/eax
      # offset = idx-value * size-of(element(base->type))
      (array-element-type-id %ebx)  # => eax
      (size-of-type-id %eax)  # => eax
      f7 4/subop/multiply-into-eax %edx  # clobbers edx
      # offset += 4 for array size
      05/add-to-eax 4/imm32
      # TODO: check edx for overflow
      # print offset
      (print-int32-buffered *(ebp+8) %eax)
      (write-buffered *(ebp+8) ") ")
      e9/jump $translate-mu-index-stmt-with-array-in-register:emit-output/disp32
    }
    # otherwise abort
    e9/jump $translate-mu-index-stmt-with-array:error1/disp32
$translate-mu-index-stmt-with-array-in-register:emit-output:
    # outputs[0] "/r32"
    8b/-> *(ebp+0xc) 1/r32/ecx
    8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (get Registers *(eax+0x10) 0xc "Registers")  # Var-register => eax
    (print-int32-buffered *(ebp+8) *eax)
    (write-buffered *(ebp+8) "/r32\n")
$translate-mu-index-stmt-with-array-in-register:end:
    # . restore registers
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

translate-mu-index-stmt-with-array-on-stack:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    #
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "8d/copy-address *(ebp + ")
    # var curr/eax = stmt->inouts
    8b/-> *(ebp+0xc) 0/r32/eax
    # var base/ecx: (handle var) = stmt->inouts[0]
    8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
    8b/-> *eax 1/r32/ecx  # Stmt-var-value
    # curr = curr->next
    8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
    # var index/edx: (handle var) = stmt->inouts[1]
    8b/-> *eax 2/r32/edx  # Stmt-var-value
    # if index->register
    81 7/subop/compare *(edx+0x10) 0/imm32  # Var-register
    {
      0f 84/jump-if-= break/disp32
$translate-mu-index-stmt-with-array-on-stack:emit-register-index:
      # if index is an int
      (is-simple-mu-type? *(edx+4) 1)  # Var-type, int => eax
      3d/compare-eax-and 0/imm32/false
      {
        0f 84/jump-if-= break/disp32
$translate-mu-index-stmt-with-array-on-stack:emit-int-register-index:
        # print index->register "<<" log2(size-of(element-type(base))) " + " base->offset+4
        # . inouts[1]->register "<<"
        (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
        (write-buffered *(ebp+8) "<<")
        # . log2(size-of(element(base)))
        # TODO: ensure size is a power of 2
        (array-element-type-id %ecx)  # => eax
        (size-of-type-id %eax)  # => eax
        (num-shift-rights %eax)  # => eax
        (print-int32-buffered *(ebp+8) %eax)
        #
        (write-buffered *(ebp+8) " + ")
        #
        8b/-> *(ecx+0xc) 0/r32/eax  # Var-offset
        05/add-to-eax 4/imm32
        (print-int32-buffered *(ebp+8) %eax)
        e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-register-index-done/disp32
      }
      # if index->type is any other atom, abort
      (lookup *(edx+8) *(edx+0xc))  # Var-type Var-type => eax
      81 7/subop/compare *eax 0/imm32/false  # Tree-is-atom
      0f 85/jump-if-!= $translate-mu-index-stmt-with-array:error2/disp32
      # if index has type (offset ...)
      (is-simple-mu-type? *(eax+4) 7)  # Tree-left, offset => eax
      3d/compare-eax-and 0/imm32/false
      {
        0f 84/jump-if-= break/disp32
        # print index->register
$translate-mu-index-stmt-with-array-on-stack:emit-offset-register-index:
        (write-buffered *(ebp+8) *(edx+0x10))  # Var-register
      }
$translate-mu-index-stmt-with-array-on-stack:emit-register-index-done:
      (write-buffered *(ebp+8) ") ")
      e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
    }
    # otherwise if index is a literal
    (is-simple-mu-type? *(edx+4) 0)  # Var-type => eax
    3d/compare-eax-and 0/imm32/false
    {
      0f 84/jump-if-= break/disp32
$translate-mu-index-stmt-with-array-on-stack:emit-literal-index:
      # var idx-value/edx: int = parse-hex-int(index->name)
      (parse-hex-int *edx)  # Var-name => eax
      89/<- %ebx 0/r32/eax
      # offset = idx-value * size-of(element-type(base->type))
      (array-element-type-id %ecx)  # => eax
      (size-of-type-id %eax)  # => eax
      f7 4/subop/multiply-into-eax %ebx  # clobbers edx
      # offset += base->offset
      03/add *(ecx+0xc) 0/r32/eax  # Var-offset
      # offset += 4 for array size
      05/add-to-eax 4/imm32
      # TODO: check edx for overflow
      # print offset
      (print-int32-buffered *(ebp+8) %eax)
      (write-buffered *(ebp+8) ") ")
      e9/jump $translate-mu-index-stmt-with-array-on-stack:emit-output/disp32
    }
    # otherwise abort
    e9/jump $translate-mu-index-stmt-with-array:error1/disp32
$translate-mu-index-stmt-with-array-on-stack:emit-output:
    # outputs[0] "/r32"
    8b/-> *(ebp+0xc) 0/r32/eax
    8b/-> *(eax+0xc) 0/r32/eax  # Stmt1-outputs
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (get Registers *(eax+0x10) 0xc "Registers")  # Var-register => eax
    (print-int32-buffered *(ebp+8) *eax)
    (write-buffered *(ebp+8) "/r32\n")
$translate-mu-index-stmt-with-array-on-stack:end:
    # . restore registers
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

translate-mu-compute-index-stmt:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    #
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "69/multiply")
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var first-inout/edx: (handle stmt-var) = stmt->inouts[0]
    8b/-> *(ecx+8) 2/r32/edx  # Stmt1-inouts
$translate-mu-compute-index-stmt:emit-index:
    (emit-subx-var-as-rm32 *(ebp+8) *(edx+4))  # Stmt-var-next
    (write-buffered *(ebp+8) Space)
$translate-mu-compute-index-stmt:emit-elem-size:
    # var base/ebx: (handle var)
    8b/-> *edx 3/r32/ebx  # Stmt-var-value
    # print size-of(element(base->type))
    (array-element-type-id %ebx)  # => eax
    (size-of-type-id %eax)  # => eax
    (print-int32-buffered *(ebp+8) %eax)
    (write-buffered *(ebp+8) "/imm32 ")
$translate-mu-compute-index-stmt:emit-output:
    # outputs[0] "/r32"
    8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (get Registers *(eax+0x10) 0xc "Registers")  # Var-register => eax
    (print-int32-buffered *(ebp+8) *eax)
    (write-buffered *(ebp+8) "/r32\n")
$translate-mu-compute-index-stmt:end:
    # . restore registers
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

translate-mu-get-stmt:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    #
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "8d/copy-address ")
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var offset/edx: int = get offset of stmt
    (mu-get-offset %ecx)  # => eax
    89/<- %edx 0/r32/eax
    # var base/eax: (handle var) = stmt->inouts[0]
    8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    # if base is in a register
    81 7/subop/compare *(eax+0x10) 0/imm32
    {
      0f 84/jump-if-= break/disp32
$translate-mu-get-stmt:emit-register-input:
      # "*(" inouts[0]->register " + " offset ")"
      (write-buffered *(ebp+8) "*(")
      (write-buffered *(ebp+8) *(eax+0x10))  # Var-register
      (write-buffered *(ebp+8) " + ")
      (print-int32-buffered *(ebp+8) %edx)
      (write-buffered *(ebp+8) ") ")
      e9/jump $translate-mu-get-stmt:emit-output/disp32
    }
    # otherwise base is on the stack
    {
$translate-mu-get-stmt:emit-stack-input:
      # "*(ebp + " inouts[0]->offset + offset ")"
      (write-buffered *(ebp+8) "*(ebp+")
      03/add *(eax+0xc) 2/r32/edx  # Var-offset
      (print-int32-buffered *(ebp+8) %edx)
      (write-buffered *(ebp+8) ") ")
      eb/jump $translate-mu-get-stmt:emit-output/disp8
    }
$translate-mu-get-stmt:emit-output:
    # outputs[0] "/r32"
    8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (get Registers *(eax+0x10) 0xc "Registers")  # Var-register => eax
    (print-int32-buffered *(ebp+8) *eax)
    (write-buffered *(ebp+8) "/r32\n")
$translate-mu-get-stmt:end:
    # . restore registers
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

array-element-type-id:  # v: (addr var) -> result/eax: type-id
    # precondition: n is positive
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
    8b/-> *(ebp+8) 0/r32/eax
    8b/-> *(eax+4) 0/r32/eax  # Var-type
    # TODO: ensure type->left is 'addr'
    8b/-> *(eax+8) 0/r32/eax  # Tree-right
    # TODO: ensure that type->right is non-null
    # TODO: ensure that type->right->left is 'array'
    8b/-> *(eax+8) 0/r32/eax  # Tree-right
    # TODO: ensure that type->right->right is non-null
    8b/-> *(eax+4) 0/r32/eax  # Tree-left
    8b/-> *(eax+4) 0/r32/eax  # Tree-value
$array-element-type-id:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

num-shift-rights:  # n: int -> result/eax: int
    # precondition: n is a positive power of 2
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var curr/ecx: int = n
    8b/-> *(ebp+8) 1/r32/ecx
    # result = 0
    b8/copy-to-eax 0/imm32
    {
      # if (curr <= 1) break
      81 7/subop/compare %ecx 1/imm32
      7e/jump-if-<= break/disp8
      40/increment-eax
      c1/shift 5/subop/arithmetic-right %ecx 1/imm8
      eb/jump loop/disp8
    }
$num-shift-rights:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

mu-get-offset:  # stmt: (addr stmt) -> result/eax: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # var second-inout/eax: (handle stmt-var) = stmt->inouts->next
    8b/-> *(ebp+8) 0/r32/eax
    8b/-> *(eax+8) 0/r32/eax  # Stmt1-inouts
    8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
    # var output-var/eax: (handle var) = second-inout->value
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    # return output-var->offset
    8b/-> *(eax+0xc) 0/r32/eax  # Var-offset
$emit-get-offset:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-block:  # out: (addr buffered-file), block: (addr block), vars: (addr stack (handle var))
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    # esi = block
    8b/-> *(ebp+0xc) 6/r32/esi
    # block->var->block-depth = *Curr-block-depth
    8b/-> *(esi+8) 0/r32/eax  # Block-var
    8b/-> *Curr-block-depth 1/r32/ecx
    89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
    # var stmts/eax: (handle list stmt) = block->statements
    8b/-> *(esi+4) 0/r32/eax  # Block-stmts
    #
    {
$emit-subx-block:check-empty:
      3d/compare-eax-and 0/imm32
      0f 84/jump-if-= break/disp32
      (emit-indent *(ebp+8) *Curr-block-depth)
      (write-buffered *(ebp+8) "{\n")
      # var v/ecx: (handle var)
      8b/-> *(esi+8) 1/r32/ecx  # Block-var
      #
      (write-buffered *(ebp+8) *ecx)  # Var-name
      (write-buffered *(ebp+8) ":loop:\n")
      ff 0/subop/increment *Curr-block-depth
      (push *(ebp+0x10) %ecx)
      (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
      (pop *(ebp+0x10))  # => eax
      ff 1/subop/decrement *Curr-block-depth
      (emit-indent *(ebp+8) *Curr-block-depth)
      (write-buffered *(ebp+8) "}\n")
      (write-buffered *(ebp+8) *ecx)  # Var-name
      (write-buffered *(ebp+8) ":break:\n")
    }
$emit-subx-block:end:
    # . restore registers
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

# Primitives supported
# For each operation, put variants with hard-coded registers before flexible ones.
== data
Primitives:
# - increment/decrement
_Primitive-inc-eax:
    # var/eax <- increment => 40/increment-eax
    "increment"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-eax/imm32/outputs
    "40/increment-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-inc-ecx/imm32/next
_Primitive-inc-ecx:
    # var/ecx <- increment => 41/increment-ecx
    "increment"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-ecx/imm32/outputs
    "41/increment-ecx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-inc-edx/imm32/next
_Primitive-inc-edx:
    # var/edx <- increment => 42/increment-edx
    "increment"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-edx/imm32/outputs
    "42/increment-edx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-inc-ebx/imm32/next
_Primitive-inc-ebx:
    # var/ebx <- increment => 43/increment-ebx
    "increment"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-ebx/imm32/outputs
    "43/increment-ebx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-inc-esi/imm32/next
_Primitive-inc-esi:
    # var/esi <- increment => 46/increment-esi
    "increment"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-esi/imm32/outputs
    "46/increment-esi"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-inc-edi/imm32/next
_Primitive-inc-edi:
    # var/edi <- increment => 47/increment-edi
    "increment"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-edi/imm32/outputs
    "47/increment-edi"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-eax/imm32/next
_Primitive-dec-eax:
    # var/eax <- decrement => 48/decrement-eax
    "decrement"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-eax/imm32/outputs
    "48/decrement-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-ecx/imm32/next
_Primitive-dec-ecx:
    # var/ecx <- decrement => 49/decrement-ecx
    "decrement"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-ecx/imm32/outputs
    "49/decrement-ecx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-edx/imm32/next
_Primitive-dec-edx:
    # var/edx <- decrement => 4a/decrement-edx
    "decrement"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-edx/imm32/outputs
    "4a/decrement-edx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-ebx/imm32/next
_Primitive-dec-ebx:
    # var/ebx <- decrement => 4b/decrement-ebx
    "decrement"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-ebx/imm32/outputs
    "4b/decrement-ebx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-esi/imm32/next
_Primitive-dec-esi:
    # var/esi <- decrement => 4e/decrement-esi
    "decrement"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-esi/imm32/outputs
    "4e/decrement-esi"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-edi/imm32/next
_Primitive-dec-edi:
    # var/edi <- decrement => 4f/decrement-edi
    "decrement"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-edi/imm32/outputs
    "4f/decrement-edi"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-inc-mem/imm32/next
_Primitive-inc-mem:
    # increment var => ff 0/subop/increment *(ebp+__)
    "increment"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    0/imm32/no-outputs
    "ff 0/subop/increment"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-inc-reg/imm32/next
_Primitive-inc-reg:
    # var/reg <- increment => ff 0/subop/increment %__
    "increment"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-some-register/imm32/outputs
    "ff 0/subop/increment"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-mem/imm32/next
_Primitive-dec-mem:
    # decrement var => ff 1/subop/decrement *(ebp+__)
    "decrement"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    0/imm32/no-outputs
    "ff 1/subop/decrement"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-dec-reg/imm32/next
_Primitive-dec-reg:
    # var/reg <- decrement => ff 1/subop/decrement %__
    "decrement"/imm32/name
    0/imm32/no-inouts
    Single-int-var-in-some-register/imm32/outputs
    "ff 1/subop/decrement"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-add-to-eax/imm32/next
# - add
_Primitive-add-to-eax:
    # var/eax <- add lit => 05/add-to-eax lit/imm32
    "add"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-eax/imm32/outputs
    "05/add-to-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-add-reg-to-reg/imm32/next
_Primitive-add-reg-to-reg:
    # var1/reg <- add var2/reg => 01/add-to var1/rm32 var2/r32
    "add"/imm32/name
    Single-int-var-in-some-register/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "01/add-to"/imm32/subx-name
    3/imm32/rm32-is-first-output
    1/imm32/r32-is-first-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-add-reg-to-mem/imm32/next
_Primitive-add-reg-to-mem:
    # add-to var1 var2/reg => 01/add-to var1 var2/r32
    "add-to"/imm32/name
    Two-args-int-stack-int-reg/imm32/inouts
    0/imm32/outputs
    "01/add-to"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    2/imm32/r32-is-second-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-add-mem-to-reg/imm32/next
_Primitive-add-mem-to-reg:
    # var1/reg <- add var2 => 03/add var2/rm32 var1/r32
    "add"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "03/add"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-add-lit-to-reg/imm32/next
_Primitive-add-lit-to-reg:
    # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32
    "add"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "81 0/subop/add"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-add-lit-to-mem/imm32/next
_Primitive-add-lit-to-mem:
    # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32
    "add-to"/imm32/name
    Int-var-and-literal/imm32/inouts
    0/imm32/outputs
    "81 0/subop/add"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-second-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-subtract-from-eax/imm32/next
# - subtract
_Primitive-subtract-from-eax:
    # var/eax <- subtract lit => 2d/subtract-from-eax lit/imm32
    "subtract"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-eax/imm32/outputs
    "2d/subtract-from-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-subtract-reg-from-reg/imm32/next
_Primitive-subtract-reg-from-reg:
    # var1/reg <- subtract var2/reg => 29/subtract-from var1/rm32 var2/r32
    "subtract"/imm32/name
    Single-int-var-in-some-register/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "29/subtract-from"/imm32/subx-name
    3/imm32/rm32-is-first-output
    1/imm32/r32-is-first-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-subtract-reg-from-mem/imm32/next
_Primitive-subtract-reg-from-mem:
    # subtract-from var1 var2/reg => 29/subtract-from var1 var2/r32
    "subtract-from"/imm32/name
    Two-args-int-stack-int-reg/imm32/inouts
    0/imm32/outputs
    "29/subtract-from"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    2/imm32/r32-is-second-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-subtract-mem-from-reg/imm32/next
_Primitive-subtract-mem-from-reg:
    # var1/reg <- subtract var2 => 2b/subtract var2/rm32 var1/r32
    "subtract"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "2b/subtract"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-subtract-lit-from-reg/imm32/next
_Primitive-subtract-lit-from-reg:
    # var1/reg <- subtract lit => 81 5/subop/subtract var1/rm32 lit/imm32
    "subtract"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "81 5/subop/subtract"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-subtract-lit-from-mem/imm32/next
_Primitive-subtract-lit-from-mem:
    # subtract-from var1, lit => 81 5/subop/subtract var1/rm32 lit/imm32
    "subtract-from"/imm32/name
    Int-var-and-literal/imm32/inouts
    0/imm32/outputs
    "81 5/subop/subtract"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-and-with-eax/imm32/next
# - and
_Primitive-and-with-eax:
    # var/eax <- and lit => 25/and-with-eax lit/imm32
    "and"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-eax/imm32/outputs
    "25/and-with-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-and-reg-with-reg/imm32/next
_Primitive-and-reg-with-reg:
    # var1/reg <- and var2/reg => 21/and-with var1/rm32 var2/r32
    "and"/imm32/name
    Single-int-var-in-some-register/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "21/and-with"/imm32/subx-name
    3/imm32/rm32-is-first-output
    1/imm32/r32-is-first-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-and-reg-with-mem/imm32/next
_Primitive-and-reg-with-mem:
    # and-with var1 var2/reg => 21/and-with var1 var2/r32
    "and-with"/imm32/name
    Two-args-int-stack-int-reg/imm32/inouts
    0/imm32/outputs
    "21/and-with"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    2/imm32/r32-is-second-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-and-mem-with-reg/imm32/next
_Primitive-and-mem-with-reg:
    # var1/reg <- and var2 => 23/and var2/rm32 var1/r32
    "and"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "23/and"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-and-lit-with-reg/imm32/next
_Primitive-and-lit-with-reg:
    # var1/reg <- and lit => 81 4/subop/and var1/rm32 lit/imm32
    "and"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "81 4/subop/and"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-and-lit-with-mem/imm32/next
_Primitive-and-lit-with-mem:
    # and-with var1, lit => 81 4/subop/and var1/rm32 lit/imm32
    "and-with"/imm32/name
    Int-var-and-literal/imm32/inouts
    0/imm32/outputs
    "81 4/subop/and"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-or-with-eax/imm32/next
# - or
_Primitive-or-with-eax:
    # var/eax <- or lit => 0d/or-with-eax lit/imm32
    "or"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-eax/imm32/outputs
    "0d/or-with-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-or-reg-with-reg/imm32/next
_Primitive-or-reg-with-reg:
    # var1/reg <- or var2/reg => 09/or-with var1/rm32 var2/r32
    "or"/imm32/name
    Single-int-var-in-some-register/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "09/or-with"/imm32/subx-name
    3/imm32/rm32-is-first-output
    1/imm32/r32-is-first-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-or-reg-with-mem/imm32/next
_Primitive-or-reg-with-mem:
    # or-with var1 var2/reg => 09/or-with var1 var2/r32
    "or-with"/imm32/name
    Two-args-int-stack-int-reg/imm32/inouts
    0/imm32/outputs
    "09/or-with"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    2/imm32/r32-is-second-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-or-mem-with-reg/imm32/next
_Primitive-or-mem-with-reg:
    # var1/reg <- or var2 => 0b/or var2/rm32 var1/r32
    "or"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "0b/or"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-or-lit-with-reg/imm32/next
_Primitive-or-lit-with-reg:
    # var1/reg <- or lit => 81 1/subop/or var1/rm32 lit/imm32
    "or"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "81 1/subop/or"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-or-lit-with-mem/imm32/next
_Primitive-or-lit-with-mem:
    # or-with var1, lit => 81 1/subop/or var1/rm32 lit/imm32
    "or-with"/imm32/name
    Int-var-and-literal/imm32/inouts
    0/imm32/outputs
    "81 1/subop/or"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-second-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-xor-with-eax/imm32/next
# - xor
_Primitive-xor-with-eax:
    # var/eax <- xor lit => 35/xor-with-eax lit/imm32
    "xor"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-eax/imm32/outputs
    "35/xor-with-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-xor-reg-with-reg/imm32/next
_Primitive-xor-reg-with-reg:
    # var1/reg <- xor var2/reg => 31/xor-with var1/rm32 var2/r32
    "xor"/imm32/name
    Single-int-var-in-some-register/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "31/xor-with"/imm32/subx-name
    3/imm32/rm32-is-first-output
    1/imm32/r32-is-first-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-xor-reg-with-mem/imm32/next
_Primitive-xor-reg-with-mem:
    # xor-with var1 var2/reg => 31/xor-with var1 var2/r32
    "xor-with"/imm32/name
    Two-args-int-stack-int-reg/imm32/inouts
    0/imm32/outputs
    "31/xor-with"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    2/imm32/r32-is-second-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-xor-mem-with-reg/imm32/next
_Primitive-xor-mem-with-reg:
    # var1/reg <- xor var2 => 33/xor var2/rm32 var1/r32
    "xor"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "33/xor"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-xor-lit-with-reg/imm32/next
_Primitive-xor-lit-with-reg:
    # var1/reg <- xor lit => 81 6/subop/xor var1/rm32 lit/imm32
    "xor"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "81 6/subop/xor"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-xor-lit-with-mem/imm32/next
_Primitive-xor-lit-with-mem:
    # xor-with var1, lit => 81 6/subop/xor var1/rm32 lit/imm32
    "xor-with"/imm32/name
    Int-var-and-literal/imm32/inouts
    0/imm32/outputs
    "81 6/subop/xor"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-copy-to-eax/imm32/next
# - copy
_Primitive-copy-to-eax:
    # var/eax <- copy lit => b8/copy-to-eax lit/imm32
    "copy"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-eax/imm32/outputs
    "b8/copy-to-eax"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-to-ecx/imm32/next
_Primitive-copy-to-ecx:
    # var/ecx <- copy lit => b9/copy-to-ecx lit/imm32
    "copy"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-ecx/imm32/outputs
    "b9/copy-to-ecx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-to-edx/imm32/next
_Primitive-copy-to-edx:
    # var/edx <- copy lit => ba/copy-to-edx lit/imm32
    "copy"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-edx/imm32/outputs
    "ba/copy-to-edx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-to-ebx/imm32/next
_Primitive-copy-to-ebx:
    # var/ebx <- copy lit => bb/copy-to-ebx lit/imm32
    "copy"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-ebx/imm32/outputs
    "bb/copy-to-ebx"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-to-esi/imm32/next
_Primitive-copy-to-esi:
    # var/esi <- copy lit => be/copy-to-esi lit/imm32
    "copy"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-esi/imm32/outputs
    "be/copy-to-esi"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-to-edi/imm32/next
_Primitive-copy-to-edi:
    # var/edi <- copy lit => bf/copy-to-edi lit/imm32
    "copy"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-edi/imm32/outputs
    "bf/copy-to-edi"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-reg-to-reg/imm32/next
_Primitive-copy-reg-to-reg:
    # var1/reg <- copy var2/reg => 89/<- var1/rm32 var2/r32
    "copy"/imm32/name
    Single-int-var-in-some-register/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "89/<-"/imm32/subx-name
    3/imm32/rm32-is-first-output
    1/imm32/r32-is-first-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-reg-to-mem/imm32/next
_Primitive-copy-reg-to-mem:
    # copy-to var1 var2/reg => 89/<- var1 var2/r32
    "copy-to"/imm32/name
    Two-args-int-stack-int-reg/imm32/inouts
    0/imm32/outputs
    "89/<-"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    2/imm32/r32-is-second-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-mem-to-reg/imm32/next
_Primitive-copy-mem-to-reg:
    # var1/reg <- copy var2 => 8b/-> var2/rm32 var1/r32
    "copy"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "8b/->"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-lit-to-reg/imm32/next
_Primitive-copy-lit-to-reg:
    # var1/reg <- copy lit => c7 0/subop/copy var1/rm32 lit/imm32
    "copy"/imm32/name
    Single-lit-var/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "c7 0/subop/copy"/imm32/subx-name
    3/imm32/rm32-is-first-output
    0/imm32/no-r32
    1/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-copy-lit-to-mem/imm32/next
_Primitive-copy-lit-to-mem:
    # copy-to var1, lit => c7 0/subop/copy var1/rm32 lit/imm32
    "copy-to"/imm32/name
    Int-var-and-literal/imm32/inouts
    0/imm32/outputs
    "c7 0/subop/copy"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-first-inout
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-address/imm32/next
# - address
_Primitive-address:
    # var1/reg <- address var2 => 8d/copy-address var2/rm32 var1/r32
    "address"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-addr-var-in-some-register/imm32/outputs
    "8d/copy-address"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    1/imm32/output-is-write-only
    _Primitive-compare-mem-with-reg/imm32/next
# - compare
_Primitive-compare-mem-with-reg:
    # compare var1 var2/reg => 39/compare var1/rm32 var2/r32
    "compare"/imm32/name
    Two-args-int-stack-int-reg/imm32/inouts
    0/imm32/outputs
    "39/compare->"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    2/imm32/r32-is-second-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-compare-reg-with-mem/imm32/next
_Primitive-compare-reg-with-mem:
    # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
    "compare"/imm32/name
    Two-args-int-reg-int-stack/imm32/inouts
    0/imm32/outputs
    "3b/compare<-"/imm32/subx-name
    2/imm32/rm32-is-second-inout
    1/imm32/r32-is-first-inout
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-compare-eax-with-literal/imm32/next
_Primitive-compare-eax-with-literal:
    # compare var1/eax n => 3d/compare-eax-with n/imm32
    "compare"/imm32/name
    Two-args-int-eax-int-literal/imm32/inouts
    0/imm32/outputs
    "3d/compare-eax-with"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    2/imm32/imm32-is-second-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-compare-reg-with-literal/imm32/next
_Primitive-compare-reg-with-literal:
    # compare var1/reg n => 81 7/subop/compare %reg n/imm32
    "compare"/imm32/name
    Int-var-in-register-and-literal/imm32/inouts
    0/imm32/outputs
    "81 7/subop/compare"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-second-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-compare-mem-with-literal/imm32/next
_Primitive-compare-mem-with-literal:
    # compare var1 n => 81 7/subop/compare *(ebp+___) n/imm32
    "compare"/imm32/name
    Int-var-and-literal/imm32/inouts
    0/imm32/outputs
    "81 7/subop/compare"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    0/imm32/no-r32
    2/imm32/imm32-is-second-inout
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-multiply-reg-by-mem/imm32/next
# - multiply
_Primitive-multiply-reg-by-mem:
    # var1/reg <- multiply var2 => 0f af/multiply var2/rm32 var1/r32
    "multiply"/imm32/name
    Single-int-var-in-mem/imm32/inouts
    Single-int-var-in-some-register/imm32/outputs
    "0f af/multiply"/imm32/subx-name
    1/imm32/rm32-is-first-inout
    3/imm32/r32-is-first-output
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/output-is-write-only
    _Primitive-break-if-addr</imm32/next
# - branches
_Primitive-break-if-addr<:
    "break-if-addr<"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 82/jump-if-addr< break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-addr>=/imm32/next
_Primitive-break-if-addr>=:
    "break-if-addr>="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 83/jump-if-addr>= break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-=/imm32/next
_Primitive-break-if-=:
    "break-if-="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 84/jump-if-= break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-!=/imm32/next
_Primitive-break-if-!=:
    "break-if-!="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 85/jump-if-!= break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-addr<=/imm32/next
_Primitive-break-if-addr<=:
    "break-if-addr<="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 86/jump-if-addr<= break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-addr>/imm32/next
_Primitive-break-if-addr>:
    "break-if-addr>"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 87/jump-if-addr> break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-</imm32/next
_Primitive-break-if-<:
    "break-if-<"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8c/jump-if-< break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if->=/imm32/next
_Primitive-break-if->=:
    "break-if->="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8d/jump-if->= break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-<=/imm32/next
_Primitive-break-if-<=:
    "break-if-<="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8e/jump-if-<= break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if->/imm32/next
_Primitive-break-if->:
    "break-if->"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8f/jump-if-> break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break/imm32/next
_Primitive-break:
    "break"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "e9/jump break/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-addr</imm32/next
_Primitive-loop-if-addr<:
    "loop-if-addr<"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 82/jump-if-addr< loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-addr>=/imm32/next
_Primitive-loop-if-addr>=:
    "loop-if-addr>="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 83/jump-if-addr>= loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-=/imm32/next
_Primitive-loop-if-=:
    "loop-if-="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 84/jump-if-= loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-!=/imm32/next
_Primitive-loop-if-!=:
    "loop-if-!="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 85/jump-if-!= loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-addr<=/imm32/next
_Primitive-loop-if-addr<=:
    "loop-if-addr<="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 86/jump-if-addr<= loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-addr>/imm32/next
_Primitive-loop-if-addr>:
    "loop-if-addr>"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 87/jump-if-addr> loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-</imm32/next
_Primitive-loop-if-<:
    "loop-if-<"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8c/jump-if-< loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if->=/imm32/next
_Primitive-loop-if->=:
    "loop-if->="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8d/jump-if->= loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if-<=/imm32/next
_Primitive-loop-if-<=:
    "loop-if-<="/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8e/jump-if-<= loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop-if->/imm32/next
_Primitive-loop-if->:
    "loop-if->"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "0f 8f/jump-if-> loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-loop/imm32/next  # we probably don't need an unconditional break
_Primitive-loop:
    "loop"/imm32/name
    0/imm32/inouts
    0/imm32/outputs
    "e9/jump loop/disp32"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    0/imm32/no-disp32
    0/imm32/no-output
    _Primitive-break-if-addr<-named/imm32/next
# - branches to named blocks
_Primitive-break-if-addr<-named:
    "break-if-addr<"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 82/jump-if-addr<"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if-addr>=-named/imm32/next
_Primitive-break-if-addr>=-named:
    "break-if-addr>="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 83/jump-if-addr>="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if-=-named/imm32/next
_Primitive-break-if-=-named:
    "break-if-="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 84/jump-if-="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if-!=-named/imm32/next
_Primitive-break-if-!=-named:
    "break-if-!="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 85/jump-if-!="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if-addr<=-named/imm32/next
_Primitive-break-if-addr<=-named:
    "break-if-addr<="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 86/jump-if-addr<="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if-addr>-named/imm32/next
_Primitive-break-if-addr>-named:
    "break-if-addr>"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 87/jump-if-addr>"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if-<-named/imm32/next
_Primitive-break-if-<-named:
    "break-if-<"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8c/jump-if-<"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if->=-named/imm32/next
_Primitive-break-if->=-named:
    "break-if->="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8d/jump-if->="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if-<=-named/imm32/next
_Primitive-break-if-<=-named:
    "break-if-<="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8e/jump-if-<="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-if->-named/imm32/next
_Primitive-break-if->-named:
    "break-if->"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8f/jump-if->"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-break-named/imm32/next
_Primitive-break-named:
    "break"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "e9/jump"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-addr<-named/imm32/next
_Primitive-loop-if-addr<-named:
    "loop-if-addr<"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 82/jump-if-addr<"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-addr>=-named/imm32/next
_Primitive-loop-if-addr>=-named:
    "loop-if-addr>="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 83/jump-if-addr>="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-=-named/imm32/next
_Primitive-loop-if-=-named:
    "loop-if-="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 84/jump-if-="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-!=-named/imm32/next
_Primitive-loop-if-!=-named:
    "loop-if-!="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 85/jump-if-!="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-addr<=-named/imm32/next
_Primitive-loop-if-addr<=-named:
    "loop-if-addr<="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 86/jump-if-addr<="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-addr>-named/imm32/next
_Primitive-loop-if-addr>-named:
    "loop-if-addr>"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 87/jump-if-addr>"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-<-named/imm32/next
_Primitive-loop-if-<-named:
    "loop-if-<"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8c/jump-if-<"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if->=-named/imm32/next
_Primitive-loop-if->=-named:
    "loop-if->="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8d/jump-if->="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if-<=-named/imm32/next
_Primitive-loop-if-<=-named:
    "loop-if-<="/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8e/jump-if-<="/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-if->-named/imm32/next
_Primitive-loop-if->-named:
    "loop-if->"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "0f 8f/jump-if->"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    _Primitive-loop-named/imm32/next  # we probably don't need an unconditional break
_Primitive-loop-named:
    "loop"/imm32/name
    Single-lit-var/imm32/inouts
    0/imm32/outputs
    "e9/jump"/imm32/subx-name
    0/imm32/no-rm32
    0/imm32/no-r32
    0/imm32/no-imm32
    1/imm32/disp32-is-first-inout
    0/imm32/no-output
    0/imm32/next

Single-int-var-in-mem:
    Int-var-in-mem/imm32
    0/imm32/next

Int-var-in-mem:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    1/imm32/some-stack-offset
    0/imm32/no-register

Two-args-int-stack-int-reg:
    Int-var-in-mem/imm32
    Single-int-var-in-some-register/imm32/next

Two-args-int-reg-int-stack:
    Int-var-in-some-register/imm32
    Single-int-var-in-mem/imm32/next

Two-args-int-eax-int-literal:
    Int-var-in-eax/imm32
    Single-lit-var/imm32/next

Int-var-and-literal:
    Int-var-in-mem/imm32
    Single-lit-var/imm32/next

Int-var-in-register-and-literal:
    Int-var-in-some-register/imm32
    Single-lit-var/imm32/next

Single-int-var-in-some-register:
    Int-var-in-some-register/imm32
    0/imm32/next

Single-addr-var-in-some-register:
    Addr-var-in-some-register/imm32
    0/imm32/next

Int-var-in-some-register:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    Any-register/imm32

Addr-var-in-some-register:
    "arg1"/imm32/name
    Type-addr/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    Any-register/imm32

Single-int-var-in-eax:
    Int-var-in-eax/imm32
    0/imm32/next

Int-var-in-eax:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    "eax"/imm32/register

Single-int-var-in-ecx:
    Int-var-in-ecx/imm32
    0/imm32/next

Int-var-in-ecx:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    "ecx"/imm32/register

Single-int-var-in-edx:
    Int-var-in-edx/imm32
    0/imm32/next

Int-var-in-edx:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    "edx"/imm32/register

Single-int-var-in-ebx:
    Int-var-in-ebx/imm32
    0/imm32/next

Int-var-in-ebx:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    "ebx"/imm32/register

Single-int-var-in-esi:
    Int-var-in-esi/imm32
    0/imm32/next

Int-var-in-esi:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    "esi"/imm32/register

Single-int-var-in-edi:
    Int-var-in-edi/imm32
    0/imm32/next

Int-var-in-edi:
    "arg1"/imm32/name
    Type-int/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    "edi"/imm32/register

Single-lit-var:
    Lit-var/imm32
    0/imm32/next

Lit-var:
    "literal"/imm32/name
    Type-literal/imm32
    1/imm32/some-block-depth
    0/imm32/no-stack-offset
    0/imm32/no-register

Type-int:
    1/imm32/left-is-atom
    1/imm32/left/int
    0/imm32/right/null

Type-literal:
    1/imm32/left-is-atom
    0/imm32/left/literal
    0/imm32/right/null

Type-addr:
    1/imm32/left-is-atom
    2/imm32/left/addr
    0/imm32/right/null

== code
emit-subx-primitive:  # out: (addr buffered-file), stmt: (addr stmt), primitive: (addr function)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # ecx = primitive
    8b/-> *(ebp+0x10) 1/r32/ecx
    # emit primitive name
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) *(ecx+0xc))  # Primitive-subx-name
    # emit rm32 if necessary
    (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc))  # out, Primitive-subx-rm32, stmt
    # emit r32 if necessary
    (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc))  # out, Primitive-subx-r32, stmt
    # emit imm32 if necessary
    (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc))  # out, Primitive-subx-imm32, stmt
    # emit disp32 if necessary
    (emit-subx-disp32 *(ebp+8) *(ecx+0x1c) *(ebp+0xc))  # out, Primitive-subx-disp32, stmt
    (write-buffered *(ebp+8) Newline)
$emit-subx-primitive:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-rm32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    # if (l == 0) return
    81 7/subop/compare *(ebp+0xc) 0/imm32
    74/jump-if-= $emit-subx-rm32:end/disp8
    # var v/eax: (handle var)
    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
    (emit-subx-var-as-rm32 *(ebp+8) %eax)
$emit-subx-rm32:end:
    # . restore registers
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

get-stmt-operand-from-arg-location:  # stmt: (addr stmt), l: arg-location -> var/eax: (addr stmt-var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # eax = l
    8b/-> *(ebp+0xc) 0/r32/eax
    # ecx = stmt
    8b/-> *(ebp+8) 1/r32/ecx
    # if (l == 1) return stmt->inouts
    {
      3d/compare-eax-and 1/imm32
      75/jump-if-!= break/disp8
$get-stmt-operand-from-arg-location:1:
      8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
      eb/jump $get-stmt-operand-from-arg-location:end/disp8
    }
    # if (l == 2) return stmt->inouts->next
    {
      3d/compare-eax-and 2/imm32
      75/jump-if-!= break/disp8
$get-stmt-operand-from-arg-location:2:
      8b/-> *(ecx+8) 0/r32/eax  # Stmt1-inouts or Regvardef-inouts
      8b/-> *(eax+4) 0/r32/eax  # Stmt-var-next
      eb/jump $get-stmt-operand-from-arg-location:end/disp8
    }
    # if (l == 3) return stmt->outputs
    {
      3d/compare-eax-and 3/imm32
      75/jump-if-!= break/disp8
$get-stmt-operand-from-arg-location:3:
      8b/-> *(ecx+0xc) 0/r32/eax  # Stmt1-outputs
      eb/jump $get-stmt-operand-from-arg-location:end/disp8
    }
    # abort
    e9/jump $get-stmt-operand-from-arg-location:abort/disp32
$get-stmt-operand-from-arg-location:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

$get-stmt-operand-from-arg-location:abort:
    # error("invalid arg-location " eax)
    (write-buffered Stderr "invalid arg-location ")
    (print-int32-buffered Stderr %eax)
    (write-buffered Stderr Newline)
    (flush Stderr)
    # . syscall(exit, 1)
    bb/copy-to-ebx  1/imm32
    b8/copy-to-eax  1/imm32/exit
    cd/syscall  0x80/imm8
    # never gets here

emit-subx-r32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # if (location == 0) return
    81 7/subop/compare *(ebp+0xc) 0/imm32
    0f 84/jump-if-= $emit-subx-r32:end/disp32
    # var v/eax: (handle var)
    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (maybe-get Registers *(eax+0x10) 0xc)  # Var-register => eax: (addr register-index)
    (write-buffered *(ebp+8) Space)
    (print-int32-buffered *(ebp+8) *eax)
    (write-buffered *(ebp+8) "/r32")
$emit-subx-r32:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-imm32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # if (location == 0) return
    81 7/subop/compare *(ebp+0xc) 0/imm32
    74/jump-if-= $emit-subx-imm32:end/disp8
    # var v/eax: (handle var)
    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (write-buffered *(ebp+8) Space)
    (write-buffered *(ebp+8) *eax)  # Var-name
    (write-buffered *(ebp+8) "/imm32")
$emit-subx-imm32:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-disp32:  # out: (addr buffered-file), l: arg-location, stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # if (location == 0) return
    81 7/subop/compare *(ebp+0xc) 0/imm32
    0f 84/jump-if-= $emit-subx-disp32:end/disp32
    # var v/eax: (handle var)
    (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc))  # => eax
    8b/-> *eax 0/r32/eax  # Stmt-var-value
    (write-buffered *(ebp+8) Space)
    (write-buffered *(ebp+8) *eax)  # Var-name
    # hack: if instruction operation starts with "break", emit ":break"
    # var name/ecx: (addr array byte) = stmt->operation
    8b/-> *(ebp+0x10) 0/r32/eax
    8b/-> *(eax+4) 1/r32/ecx
    {
      (string-starts-with? %ecx "break")  # => eax
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= break/disp8
      (write-buffered *(ebp+8) ":break")
    }
    # hack: if instruction operation starts with "loop", emit ":loop"
    {
      (string-starts-with? %ecx "loop")  # => eax
      3d/compare-eax-and 0/imm32/false
      74/jump-if-= break/disp8
      (write-buffered *(ebp+8) ":loop")
    }
    (write-buffered *(ebp+8) "/disp32")
$emit-subx-disp32:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-call:  # out: (addr buffered-file), stmt: (addr stmt)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    #
    (emit-indent *(ebp+8) *Curr-block-depth)
    (write-buffered *(ebp+8) "(")
    # ecx = stmt
    8b/-> *(ebp+0xc) 1/r32/ecx
    # - emit function name
    (write-buffered *(ebp+8) *(ecx+4))  # Stmt1-operation
    # - emit arguments
    # var curr/ecx: (handle stmt-var) = stmt->inouts
    8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
    {
      # if (curr == null) break
      81 7/subop/compare %ecx 0/imm32
      74/jump-if-= break/disp8
      #
      (emit-subx-call-operand *(ebp+8) %ecx)
      # curr = curr->next
      8b/-> *(ecx+4) 1/r32/ecx  # Stmt-var-next
      eb/jump loop/disp8
    }
    #
    (write-buffered *(ebp+8) ")\n")
$emit-call:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-call-operand:  # out: (addr buffered-file), s: (addr stmt-var)
    # shares code with emit-subx-var-as-rm32
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    # ecx = s
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var operand/esi: (handle var) = s->value
    8b/-> *ecx 6/r32/esi  # Stmt-var-value
    # if (operand->register && !s->is-deref?) emit "%__"
    {
$emit-subx-call-operand:check-for-register-direct:
      81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
      74/jump-if-= break/disp8
      81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
      75/jump-if-!= break/disp8
$emit-subx-call-operand:register-direct:
      (write-buffered *(ebp+8) " %")
      (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
      e9/jump $emit-subx-call-operand:end/disp32
    }
    # else if (operand->register && s->is-deref?) emit "*__"
    {
$emit-subx-call-operand:check-for-register-indirect:
      81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
      74/jump-if-= break/disp8
      81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
      74/jump-if-= break/disp8
$emit-subx-call-operand:register-indirect:
      (emit-subx-call-operand-register-indirect *(ebp+8) %esi)
      e9/jump $emit-subx-call-operand:end/disp32
    }
    # else if (operand->stack-offset) emit "*(ebp+__)"
    {
      81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
      74/jump-if-= break/disp8
$emit-subx-call-operand:stack:
      (emit-subx-call-operand-stack *(ebp+8) %esi)
      e9/jump $emit-subx-call-operand:end/disp32
    }
    # else if (operand->type == literal) emit "__"
    {
      8b/-> *(esi+4) 0/r32/eax  # Var-type
      81 7/subop/compare *(eax+4) 0/imm32  # Tree-left
      75/jump-if-!= break/disp8
$emit-subx-call-operand:literal:
      (write-buffered *(ebp+8) Space)
      (write-buffered *(ebp+8) *esi)
    }
$emit-subx-call-operand:end:
    # . restore registers
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-call-operand-register-indirect:  # out: (addr buffered-file), v: (addr var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    # esi = v
    8b/-> *(ebp+0xc) 6/r32/esi
    # var size/ecx: int = size-of-deref(v)
    (size-of-deref %esi)  # => eax
    89/<- %ecx 0/r32/eax
    # TODO: assert size is a multiple of 4
    # var i/eax: int = 0
    b8/copy-to-eax 0/imm32
    {
$emit-subx-call-operand-register-indirect:loop:
      # if (i >= size) break
      39/compare %eax 1/r32/ecx
      7d/jump-if->= break/disp8
      # emit " *(" v->register "+" i ")"
      (write-buffered *(ebp+8) " *(")
      (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
      (write-buffered *(ebp+8) "+")
      (print-int32-buffered *(ebp+8) %eax)
      (write-buffered *(ebp+8) ")")
      # i += 4
      05/add-to-eax 4/imm32
      #
      eb/jump loop/disp8
    }
$emit-subx-call-operand-register-indirect:end:
    # . restore registers
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-call-operand-stack:  # out: (addr buffered-file), v: (addr var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    # esi = v
    8b/-> *(ebp+0xc) 6/r32/esi
    # var curr/ecx: int = v->offset
    8b/-> *(esi+0xc) 1/r32/ecx  # Var-offset
    # var max/eax: int = v->offset + size-of(v)
    (size-of %esi)  # => eax
    # TODO: assert size is a multiple of 4
    01/add-to %eax 1/r32/ecx
    {
$emit-subx-call-operand-stack:loop:
      # if (curr >= max) break
      39/compare %ecx 0/r32/eax
      7d/jump-if->= break/disp8
      # emit " *(ebp+" curr ")"
      (write-buffered *(ebp+8) " *(ebp+")
      (print-int32-buffered *(ebp+8) %ecx)
      (write-buffered *(ebp+8) ")")
      # i += 4
      81 0/subop/add %ecx 4/imm32
      #
      eb/jump loop/disp8
    }
$emit-subx-call-operand-stack:end:
    # . restore registers
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-var-as-rm32:  # out: (addr buffered-file), s: (addr stmt-var)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    # ecx = s
    8b/-> *(ebp+0xc) 1/r32/ecx
    # var operand/esi: (handle var) = s->value
    8b/-> *ecx 6/r32/esi  # Stmt-var-value
    # if (operand->register && s->is-deref?) emit "*__"
    {
$emit-subx-var-as-rm32:check-for-register-indirect:
      81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
      74/jump-if-= break/disp8
      81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
      74/jump-if-= break/disp8
$emit-subx-var-as-rm32:register-indirect:
      (write-buffered *(ebp+8) " *")
      (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
    }
    # if (operand->register && !s->is-deref?) emit "%__"
    {
$emit-subx-var-as-rm32:check-for-register-direct:
      81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
      74/jump-if-= break/disp8
      81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
      75/jump-if-!= break/disp8
$emit-subx-var-as-rm32:register-direct:
      (write-buffered *(ebp+8) " %")
      (write-buffered *(ebp+8) *(esi+0x10))  # Var-register
    }
    # else if (operand->stack-offset) emit "*(ebp+__)"
    {
      81 7/subop/compare *(esi+0xc) 0/imm32  # Var-offset
      74/jump-if-= break/disp8
$emit-subx-var-as-rm32:stack:
      (write-buffered *(ebp+8) Space)
      (write-buffered *(ebp+8) "*(ebp+")
      (print-int32-buffered *(ebp+8) *(esi+0xc))  # Var-offset
      (write-buffered *(ebp+8) ")")
    }
$emit-subx-var-as-rm32:end:
    # . restore registers
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

find-matching-primitive:  # primitives: (addr primitive), stmt: (addr stmt) -> result/eax: (addr primitive)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    51/push-ecx
    # var curr/ecx: (addr primitive) = primitives
    8b/-> *(ebp+8) 1/r32/ecx
    {
$find-matching-primitive:loop:
      # if (curr == null) break
      81 7/subop/compare %ecx 0/imm32
      0f 84/jump-if-= break/disp32
      # if match(curr, stmt) return curr
      {
        (mu-stmt-matches-primitive? *(ebp+0xc) %ecx)  # => eax
        3d/compare-eax-and 0/imm32/false
        74/jump-if-= break/disp8
        89/<- %eax 1/r32/ecx
        eb/jump $find-matching-primitive:end/disp8
      }
$find-matching-primitive:next-primitive:
      # curr = curr->next
      (lookup *(ecx+0x34) *(ecx+0x38))  # Primitive-next Primitive-next => eax
      89/<- %ecx 0/r32/eax
      #
      e9/jump loop/disp32
    }
    # return null
    b8/copy-to-eax 0/imm32
$find-matching-primitive:end:
    # . restore registers
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

mu-stmt-matches-primitive?:  # stmt: (addr stmt), primitive: (addr primitive) -> result/eax: boolean
    # A mu stmt matches a primitive if the name matches, all the inout vars
    # match, and all the output vars match.
    # Vars match if types match and registers match.
    # In addition, a stmt output matches a primitive's output if types match
    # and the primitive has a wildcard register.
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # ecx = stmt
    8b/-> *(ebp+8) 1/r32/ecx
    # edx = primitive
    8b/-> *(ebp+0xc) 2/r32/edx
    {
$mu-stmt-matches-primitive?:check-name:
      # if (primitive->name != stmt->operation) return false
      (string-equal? *(ecx+4) *edx)  # Stmt1-operation, Primitive-name => eax
      3d/compare-eax-and 0/imm32/false
      75/jump-if-!= break/disp8
      b8/copy-to-eax 0/imm32
      e9/jump $mu-stmt-matches-primitive?:end/disp32
    }
$mu-stmt-matches-primitive?:check-inouts:
    # for (curr/esi in stmt->inouts, curr2/edi in primitive->inouts)
    8b/-> *(ecx+8) 6/r32/esi  # Stmt1-inouts or Regvardef-inouts
    8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
    {
      # if (curr == 0 && curr2 == 0) move on to check outputs
      {
        81 7/subop/compare %esi 0/imm32
        75/jump-if-!= break/disp8
$mu-stmt-matches-primitive?:stmt-inout-is-null:
        {
          81 7/subop/compare %edi 0/imm32
          75/jump-if-!= break/disp8
          #
          e9/jump $mu-stmt-matches-primitive?:check-outputs/disp32
        }
        # return false
        b8/copy-to-eax 0/imm32/false
        e9/jump $mu-stmt-matches-primitive?:end/disp32
      }
      # if (curr2 == 0) return false
      {
        81 7/subop/compare %edi 0/imm32
        75/jump-if-!= break/disp8
$mu-stmt-matches-primitive?:prim-inout-is-null:
        b8/copy-to-eax 0/imm32/false
        e9/jump $mu-stmt-matches-primitive?:end/disp32
      }
      # if (curr != curr2) return false
      {
        (operand-matches-primitive? %esi *edi)  # List-value => eax
        3d/compare-eax-and 0/imm32/false
        75/jump-if-!= break/disp8
        b8/copy-to-eax 0/imm32/false
        e9/jump $mu-stmt-matches-primitive?:end/disp32
      }
      # curr=curr->next
      8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
      # curr2=curr2->next
      8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
      eb/jump loop/disp8
    }
$mu-stmt-matches-primitive?:check-outputs:
    # for (curr/esi in stmt->outputs, curr2/edi in primitive->outputs)
    8b/-> *(ecx+0xc) 6/r32/esi  # Stmt1-outputs
    8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
    {
      # if (curr == 0) return (curr2 == 0)
      {
$mu-stmt-matches-primitive?:check-output:
        81 7/subop/compare %esi 0/imm32
        75/jump-if-!= break/disp8
        {
          81 7/subop/compare %edi 0/imm32
          75/jump-if-!= break/disp8
          # return true
          b8/copy-to-eax 1/imm32
          e9/jump $mu-stmt-matches-primitive?:end/disp32
        }
        # return false
        b8/copy-to-eax 0/imm32
        e9/jump $mu-stmt-matches-primitive?:end/disp32
      }
      # if (curr2 == 0) return false
      {
        81 7/subop/compare %edi 0/imm32
        75/jump-if-!= break/disp8
        b8/copy-to-eax 0/imm32
        e9/jump $mu-stmt-matches-primitive?:end/disp32
      }
      # if (curr != curr2) return false
      {
        (operand-matches-primitive? %esi *edi)  # List-value => eax
        3d/compare-eax-and 0/imm32/false
        75/jump-if-!= break/disp8
        b8/copy-to-eax 0/imm32
        e9/jump $mu-stmt-matches-primitive?:end/disp32
      }
      # curr=curr->next
      8b/-> *(esi+4) 6/r32/esi  # Stmt-var-next
      # curr2=curr2->next
      8b/-> *(edi+4) 7/r32/edi  # Stmt-var-next
      eb/jump loop/disp8
    }
$mu-stmt-matches-primitive?:return-true:
    b8/copy-to-eax 1/imm32
$mu-stmt-matches-primitive?:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

operand-matches-primitive?:  # s: (addr stmt-var), prim-var: (addr var) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    56/push-esi
    57/push-edi
    # ecx = s
    8b/-> *(ebp+8) 1/r32/ecx
    # var var/esi : (handle var) = s->value
    8b/-> *ecx 6/r32/esi  # Stmt-var-value
    # edi = prim-var
    8b/-> *(ebp+0xc) 7/r32/edi
$operand-matches-primitive?:check-type:
    # if (var->type != prim-var->type) return false
    (subx-type-equal? *(esi+4) *(edi+4))  # Var-type, Var-type => eax
    3d/compare-eax-and 0/imm32/false
    b8/copy-to-eax 0/imm32/false
    0f 84/jump-if-= $operand-matches-primitive?:end/disp32
    {
$operand-matches-primitive?:check-register:
      # if prim-var is in memory and var is in register but dereference, match
      {
        81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
        0f 85/jump-if-!= break/disp32
        81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
        74/jump-if-= break/disp8
        81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
        74/jump-if-= break/disp8
        e9/jump $operand-matches-primitive?:return-true/disp32
      }
      # if prim-var is in register and var is in register but dereference, no match
      {
        81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
        0f 84/jump-if-= break/disp32
        81 7/subop/compare *(esi+0x10) 0/imm32  # Var-register
        0f 84/jump-if-= break/disp32
        81 7/subop/compare *(ecx+8) 0/imm32/false  # Stmt-var-is-deref
        74/jump-if-= break/disp8
        e9/jump $operand-matches-primitive?:return-false/disp32
      }
      # return false if var->register doesn't match prim-var->register
      {
        # if register addresses are equal, it's a match
        8b/-> *(esi+0x10) 0/r32/eax  # Var-register
        39/compare *(edi+0x10) 0/r32/eax  # Var-register
        74/jump-if-= break/disp8
        # if either address is 0, return false
        3d/compare-eax-and 0/imm32
        74/jump-if-=  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
        81 7/subop/compare *(edi+0x10) 0/imm32  # Var-register
        74/jump-if-=  $operand-matches-primitive?:return-false/disp8
        # if prim-var->register is wildcard, it's a match
        (string-equal? *(edi+0x10) Any-register)  # Var-register => eax
        3d/compare-eax-and 0/imm32/false
        75/jump-if-!= break/disp8
        # if string contents aren't equal, return false
        (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register => eax
        3d/compare-eax-and 0/imm32/false
        74/jump-if-= $operand-matches-primitive?:return-false/disp8
      }
    }
$operand-matches-primitive?:return-true:
    b8/copy-to-eax 1/imm32/true
    eb/jump $operand-matches-primitive?:end/disp8
$operand-matches-primitive?:return-false:
    b8/copy-to-eax 0/imm32/false
$operand-matches-primitive?:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

subx-type-equal?:  # a: (addr tree type-id), b: (addr tree type-id) -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # var alit/ecx: boolean = is-literal-type?(a)
    (is-simple-mu-type? *(ebp+8) 0)  # => eax
    89/<- %ecx 0/r32/eax
    # var blit/eax: boolean = is-literal-type?(b)
    (is-simple-mu-type? *(ebp+0xc) 0)  # => eax
    # return alit == blit
    39/compare %eax 1/r32/ecx
    0f 94/set-byte-if-= %al
    81 4/subop/and %eax 0xff/imm32
$subx-type-equal?:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

is-simple-mu-type?:  # a: (addr tree type-id), n: type-id -> result/eax: boolean
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    51/push-ecx
    # ecx = n
    8b/-> *(ebp+0xc) 1/r32/ecx
    # return (a->value == n)
    8b/-> *(ebp+8) 0/r32/eax
    39/compare *(eax+4) 1/r32/ecx  # Tree-value
    0f 94/set-byte-if-= %al
    81 4/subop/and %eax 0xff/imm32
$is-simple-mu-type?:end:
    # . restore registers
    59/pop-to-ecx
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-emit-subx-stmt-primitive:
    # Primitive operation on a variable on the stack.
    #   increment foo
    # =>
    #   ff 0/subop/increment *(ebp-8)
    #
    # There's a variable on the var stack as follows:
    #   name: 'foo'
    #   type: int
    #   stack-offset: -8
    #
    # There's a primitive with this info:
    #   name: 'increment'
    #   inouts: int/mem
    #   value: 'ff 0/subop/increment'
    #
    # There's nothing in functions.
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var
    68/push 0/imm32/no-register
    68/push -8/imm32/stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "foo"/imm32
    89/<- %ecx 4/r32/esp
    # var operand/ebx: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %ebx 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    53/push-ebx/operands
    68/push "increment"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # var primitives/ebx: primitive
    68/push 0/imm32/next
    68/push 0/imm32/output-is-write-only
    68/push 0/imm32/no-disp32
    68/push 0/imm32/no-imm32
    68/push 0/imm32/no-r32
    68/push 1/imm32/rm32-is-first-inout
    68/push "ff 0/subop/increment"/imm32/subx-name
    68/push 0/imm32/outputs
    53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
    68/push "increment"/imm32/name
    89/<- %ebx 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi %ebx)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-stmt-primitive")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-emit-subx-stmt-primitive-register:
    # Primitive operation on a variable in a register.
    #   foo <- increment
    # =>
    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
    #
    # There's a variable on the var stack as follows:
    #   name: 'foo'
    #   type: int
    #   register: 'eax'
    #
    # There's a primitive with this info:
    #   name: 'increment'
    #   out: int/reg
    #   value: 'ff 0/subop/increment'
    #
    # There's nothing in functions.
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "foo"/imm32
    89/<- %ecx 4/r32/esp
    # var operand/ebx: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %ebx 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    53/push-ebx/outputs
    68/push 0/imm32/inouts
    68/push "increment"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # var formal-var/ebx: var in any register
    68/push Any-register/imm32
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "dummy"/imm32
    89/<- %ebx 4/r32/esp
    # var operand/ebx: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    53/push-ebx/formal-var
    89/<- %ebx 4/r32/esp
    # var primitives/ebx: primitive
    68/push 0/imm32/next
    68/push 0/imm32/output-is-write-only
    68/push 0/imm32/no-disp32
    68/push 0/imm32/no-imm32
    68/push 0/imm32/no-r32
    68/push 3/imm32/rm32-in-first-output
    68/push "ff 0/subop/increment"/imm32/subx-name
    53/push-ebx/outputs
    68/push 0/imm32/inouts
    68/push "increment"/imm32/name
    89/<- %ebx 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi %ebx)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-primitive-register")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-emit-subx-stmt-select-primitive:
    # Select the right primitive between overloads.
    #   foo <- increment
    # =>
    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
    #
    # There's a variable on the var stack as follows:
    #   name: 'foo'
    #   type: int
    #   register: 'eax'
    #
    # There's two primitives, as follows:
    #   - name: 'increment'
    #     out: int/reg
    #     value: 'ff 0/subop/increment'
    #   - name: 'increment'
    #     inout: int/mem
    #     value: 'ff 0/subop/increment'
    #
    # There's nothing in functions.
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "foo"/imm32
    89/<- %ecx 4/r32/esp
    # var real-outputs/edi: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    57/push-edi/outputs
    68/push 0/imm32/inouts
    68/push "increment"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # var formal-var/ebx: var in any register
    68/push Any-register/imm32
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "dummy"/imm32
    89/<- %ebx 4/r32/esp
    # var formal-outputs/ebx: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    53/push-ebx/formal-var
    89/<- %ebx 4/r32/esp
    # var primitive1/ebx: primitive
    68/push 0/imm32/next
    68/push 0/imm32/output-is-write-only
    68/push 0/imm32/no-disp32
    68/push 0/imm32/no-imm32
    68/push 0/imm32/no-r32
    68/push 3/imm32/rm32-in-first-output
    68/push "ff 0/subop/increment"/imm32/subx-name
    53/push-ebx/outputs/formal-outputs
    68/push 0/imm32/inouts
    68/push "increment"/imm32/name
    89/<- %ebx 4/r32/esp
    # var primitives/ebx: primitive
    53/push-ebx/next
    68/push 0/imm32/output-is-write-only
    68/push 0/imm32/no-disp32
    68/push 0/imm32/no-imm32
    68/push 0/imm32/no-r32
    68/push 1/imm32/rm32-is-first-inout
    68/push "ff 0/subop/increment"/imm32/subx-name
    68/push 0/imm32/outputs
    57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
    68/push "increment"/imm32/name
    89/<- %ebx 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi %ebx)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-emit-subx-stmt-select-primitive-2:
    # Select the right primitive between overloads.
    #   foo <- increment
    # =>
    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
    #
    # There's a variable on the var stack as follows:
    #   name: 'foo'
    #   type: int
    #   register: 'eax'
    #
    # There's two primitives, as follows:
    #   - name: 'increment'
    #     out: int/reg
    #     value: 'ff 0/subop/increment'
    #   - name: 'increment'
    #     inout: int/mem
    #     value: 'ff 0/subop/increment'
    #
    # There's nothing in functions.
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "foo"/imm32
    89/<- %ecx 4/r32/esp
    # var inouts/edi: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    57/push-edi/inouts
    68/push "increment"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # var formal-var/ebx: var in any register
    68/push Any-register/imm32
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "dummy"/imm32
    89/<- %ebx 4/r32/esp
    # var operand/ebx: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    53/push-ebx/formal-var
    89/<- %ebx 4/r32/esp
    # var primitive1/ebx: primitive
    68/push 0/imm32/next
    68/push 0/imm32/output-is-write-only
    68/push 0/imm32/no-disp32
    68/push 0/imm32/no-imm32
    68/push 0/imm32/no-r32
    68/push 3/imm32/rm32-in-first-output
    68/push "ff 0/subop/increment"/imm32/subx-name
    53/push-ebx/outputs/formal-outputs
    68/push 0/imm32/inouts
    68/push "increment"/imm32/name
    89/<- %ebx 4/r32/esp
    # var primitives/ebx: primitive
    53/push-ebx/next
    68/push 0/imm32/output-is-write-only
    68/push 0/imm32/no-disp32
    68/push 0/imm32/no-imm32
    68/push 0/imm32/no-r32
    68/push 1/imm32/rm32-is-first-inout
    68/push "ff 0/subop/increment"/imm32/subx-name
    68/push 0/imm32/outputs
    57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
    68/push "increment"/imm32/name
    89/<- %ebx 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi %ebx)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-stmt-select-primitive-2")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-increment-register:
    # Select the right register between overloads.
    #   foo <- increment
    # =>
    #   50/increment-eax
    #
    # There's a variable on the var stack as follows:
    #   name: 'foo'
    #   type: int
    #   register: 'eax'
    #
    # Primitives are the global definitions.
    #
    # There are no functions defined.
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "foo"/imm32
    89/<- %ecx 4/r32/esp
    # var real-outputs/edi: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    57/push-edi/outputs
    68/push 0/imm32/inouts
    68/push "increment"/imm32/operation
    68/push 1/imm32/regular-stmt
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-register")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-increment-var:
    # Select the right primitive between overloads.
    #   foo <- increment
    # =>
    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
    #
    # There's a variable on the var stack as follows:
    #   name: 'foo'
    #   type: int
    #   register: 'eax'
    #
    # Primitives are the global definitions.
    #
    # There are no functions defined.
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "foo"/imm32
    89/<- %ecx 4/r32/esp
    # var inouts/edi: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    57/push-edi/outputs
    68/push 0/imm32/inouts
    68/push "increment"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "40/increment-eax" "F - test-increment-var")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-add-reg-to-reg:
    #   var1/reg <- add var2/reg
    # =>
    #   01/add-to %var1 var2
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var var-var2/edx: var in ecx
    68/push "ecx"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "var2"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # var outputs/edi: (handle stmt-var) = [var1, var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-var1
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    57/push-edi/outputs
    56/push-esi/inouts
    68/push "add"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "01/add-to %eax 0x00000001/r32" "F - test-add-reg-to-reg")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-add-reg-to-mem:
    #   add-to var1 var2/reg
    # =>
    #   01/add-to *(ebp+__) var2
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var
    68/push 0/imm32/no-register
    68/push 8/imm32/stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var var-var2/edx: var in ecx
    68/push "ecx"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "var2"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # var inouts = (handle stmt-var) = [var1, var2]
    56/push-esi/next
    51/push-ecx/var-var1
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "add-to"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "01/add-to *(ebp+0x00000008) 0x00000001/r32" "F - test-add-reg-to-mem")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-add-mem-to-reg:
    #   var1/reg <- add var2
    # =>
    #   03/add *(ebp+__) var1
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var var-var2/edx: var
    68/push 0/imm32/no-register
    68/push 8/imm32/stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "var2"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # var outputs/edi = (handle stmt-var) = [var1]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-var1
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    57/push-edi/outputs
    56/push-esi/inouts
    68/push "add"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "03/add *(ebp+0x00000008) 0x00000000/r32" "F - test-add-mem-to-reg")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-add-literal-to-eax:
    #   var1/eax <- add 0x34
    # =>
    #   05/add-to-eax 0x34/imm32
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var type/edx: (handle tree type-id) = literal
    68/push 0/imm32/right/null
    68/push 0/imm32/left/literal
    89/<- %edx 4/r32/esp
    # var var-var2/edx: var literal
    68/push 0/imm32/no-register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    52/push-edx
    68/push "0x34"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # var outputs/edi: (handle stmt-var) = [var1]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-var1
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    57/push-edi/outputs
    56/push-esi/inouts
    68/push "add"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "05/add-to-eax 0x34/imm32" "F - test-add-literal-to-eax")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-add-literal-to-reg:
    #   var1/ecx <- add 0x34
    # =>
    #   81 0/subop/add %ecx 0x34/imm32
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var in ecx
    68/push "ecx"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var type/edx: (handle tree type-id) = literal
    68/push 0/imm32/right/null
    68/push 0/imm32/left/literal
    89/<- %edx 4/r32/esp
    # var var-var2/edx: var literal
    68/push 0/imm32/no-register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    52/push-edx
    68/push "0x34"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # var outputs/edi: (handle stmt-var) = [var1]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-var1
    89/<- %edi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    57/push-edi/outputs
    56/push-esi/inouts
    68/push "add"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "81 0/subop/add %ecx 0x34/imm32" "F - test-add-literal-to-reg")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-add-literal-to-mem:
    #   add-to var1, 0x34
    # =>
    #   81 0/subop/add %eax 0x34/imm32
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var
    68/push 0/imm32/no-register
    68/push 8/imm32/stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var type/edx: (handle tree type-id) = literal
    68/push 0/imm32/right/null
    68/push 0/imm32/left/literal
    89/<- %edx 4/r32/esp
    # var var-var2/edx: var literal
    68/push 0/imm32/no-register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    52/push-edx
    68/push "0x34"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # var inouts = (handle stmt-var) = [var1, var2]
    68/push 0/imm32/is-deref:false
    56/push-esi/next
    51/push-ecx/var-var1
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "add-to"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "81 0/subop/add *(ebp+0x00000008) 0x34/imm32" "F - test-add-literal-to-mem")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-compare-mem-with-reg:
    #   compare var1, var2/eax
    # =>
    #   39/compare *(ebp+___) 0/r32/eax
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var2/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var2"/imm32
    89/<- %ecx 4/r32/esp
    # var var-var1/edx: var
    68/push 0/imm32/no-register
    68/push 8/imm32/stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "var1"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-var2
    89/<- %esi 4/r32/esp
    # inouts = [var1, var2]
    68/push 0/imm32/is-deref:false
    56/push-esi
    52/push-edx/var-var1
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "compare"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-compare-reg-with-mem:
    #   compare var1/eax, var2
    # =>
    #   3b/compare<- *(ebp+___) 0/r32/eax
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var var-var2/edx: var
    68/push 0/imm32/no-register
    68/push 8/imm32/stack-offset
    68/push 1/imm32/block-depth
    ff 6/subop/push *(ecx+4)  # Var-type
    68/push "var2"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # inouts = [var1, var2]
    68/push 0/imm32/is-deref:false
    56/push-esi
    51/push-ecx/var-var1
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "compare"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-compare-mem-with-literal:
    #   compare var1, 0x34
    # =>
    #   81 7/subop/compare *(ebp+___) 0x34/imm32
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var
    68/push 0/imm32/no-register
    68/push 8/imm32/stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var type/edx: (handle tree type-id) = literal
    68/push 0/imm32/right/null
    68/push 0/imm32/left/literal
    89/<- %edx 4/r32/esp
    # var var-var2/edx: var literal
    68/push 0/imm32/no-register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    52/push-edx
    68/push "0x34"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # inouts = [var1, var2]
    68/push 0/imm32/is-deref:false
    56/push-esi/next
    51/push-ecx/var-var1
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "compare"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "81 7/subop/compare *(ebp+0x00000008) 0x34/imm32" "F - test-compare-mem-with-literal")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-compare-eax-with-literal:
    #   compare var1/eax 0x34
    # =>
    #   3d/compare-eax-with 0x34/imm32
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var in eax
    68/push "eax"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var type/edx: (handle tree type-id) = literal
    68/push 0/imm32/right/null
    68/push 0/imm32/left/literal
    89/<- %edx 4/r32/esp
    # var var-var2/edx: var literal
    68/push 0/imm32/no-register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    52/push-edx
    68/push "0x34"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # inouts = [var1, var2]
    68/push 0/imm32/is-deref:false
    56/push-esi/next
    51/push-ecx/var-var1
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "compare"/imm32/operation
    68/push 1/imm32/regular-stmt
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "3d/compare-eax-with 0x34/imm32" "F - test-compare-eax-with-literal")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-compare-reg-with-literal:
    #   compare var1/ecx 0x34
    # =>
    #   81 7/subop/compare %ecx 0x34/imm32
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-var1/ecx: var in ecx
    68/push "ecx"/imm32/register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    51/push-ecx
    68/push "var1"/imm32
    89/<- %ecx 4/r32/esp
    # var type/edx: (handle tree type-id) = literal
    68/push 0/imm32/right/null
    68/push 0/imm32/left/literal
    89/<- %edx 4/r32/esp
    # var var-var2/edx: var literal
    68/push 0/imm32/no-register
    68/push 0/imm32/no-stack-offset
    68/push 1/imm32/block-depth
    52/push-edx
    68/push "0x34"/imm32
    89/<- %edx 4/r32/esp
    # var inouts/esi: (handle stmt-var) = [var2]
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    52/push-edx/var-var2
    89/<- %esi 4/r32/esp
    # inouts = [var1, var2]
    68/push 0/imm32/is-deref:false
    56/push-esi/next
    51/push-ecx/var-var1
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "compare"/imm32/operation
    68/push 1/imm32/regular-stmt
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi Primitives)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "81 7/subop/compare %ecx 0x34/imm32" "F - test-compare-reg-with-literal")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-emit-subx-stmt-function-call:
    # Call a function on a variable on the stack.
    #   f foo
    # =>
    #   (f *(ebp-8))
    # (Changing the function name supports overloading in general, but here it
    # just serves to help disambiguate things.)
    #
    # There's a variable on the var stack as follows:
    #   name: 'foo'
    #   type: int
    #   stack-offset: -8
    #
    # There's nothing in primitives.
    #
    # We don't perform any checking here on the type of 'f'.
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = int
    68/push 0/imm32/right/null
    68/push 1/imm32/left/int
    68/push 1/imm32/is-atom
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var
    68/push 0/imm32/no-register
    68/push -8/imm32/stack-offset
    68/push 0/imm32/block-depth
    51/push-ecx
    68/push "foo"/imm32
    89/<- %ecx 4/r32/esp
    # var inouts/esi: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "f"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi 0)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "(f *(ebp+0xfffffff8))" "F - test-emit-subx-stmt-function-call")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

test-emit-subx-stmt-function-call-with-literal-arg:
    # Call a function on a literal.
    #   f 34
    # =>
    #   (f2 34)
    #
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # setup
    (clear-stream _test-output-stream)
    (clear-stream $_test-output-buffered-file->buffer)
    # var type/ecx: (handle tree type-id) = literal
    68/push 0/imm32/right/null
    68/push 0/imm32/left/literal
    89/<- %ecx 4/r32/esp
    # var var-foo/ecx: var literal
    68/push 0/imm32/no-register
    68/push 0/imm32/no-stack-offset
    68/push 0/imm32/block-depth
    51/push-ecx
    68/push "34"/imm32
    89/<- %ecx 4/r32/esp
    # var inouts/esi: (handle stmt-var)
    68/push 0/imm32/is-deref:false
    68/push 0/imm32/next
    51/push-ecx/var-foo
    89/<- %esi 4/r32/esp
    # var stmt/esi: statement
    68/push 0/imm32/next
    68/push 0/imm32/outputs
    56/push-esi/inouts
    68/push "f"/imm32/operation
    68/push 1/imm32
    89/<- %esi 4/r32/esp
    # convert
    c7 0/subop/copy *Curr-block-depth 0/imm32
    (emit-subx-stmt _test-output-buffered-file %esi 0 %ebx)
    (flush _test-output-buffered-file)
#?     # dump _test-output-stream {{{
#?     (write 2 "^")
#?     (write-stream 2 _test-output-stream)
#?     (write 2 "$\n")
#?     (rewind-stream _test-output-stream)
#?     # }}}
    # check output
    (check-next-stream-line-equal _test-output-stream "(f 34)" "F - test-emit-subx-stmt-function-call-with-literal-arg")
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-indent:  # out: (addr buffered-file), n: int
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    # . save registers
    50/push-eax
    # var i/eax: int = n
    8b/-> *(ebp+0xc) 0/r32/eax
    {
      # if (i <= 0) break
      3d/compare-eax-with 0/imm32
      7e/jump-if-<= break/disp8
      (write-buffered *(ebp+8) "  ")
      48/decrement-eax
      eb/jump loop/disp8
    }
$emit-indent:end:
    # . restore registers
    58/pop-to-eax
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-prologue:  # out: (addr buffered-file)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
    (write-buffered *(ebp+8) "  # . prologue\n")
    (write-buffered *(ebp+8) "  55/push-ebp\n")
    (write-buffered *(ebp+8) "  89/<- %ebp 4/r32/esp\n")
$emit-subx-prologue:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return

emit-subx-epilogue:  # out: (addr buffered-file)
    # . prologue
    55/push-ebp
    89/<- %ebp 4/r32/esp
    #
    (write-buffered *(ebp+8) "  # . epilogue\n")
    (write-buffered *(ebp+8) "  89/<- %esp 5/r32/ebp\n")
    (write-buffered *(ebp+8) "  5d/pop-to-ebp\n")
    (write-buffered *(ebp+8) "  c3/return\n")
$emit-subx-epilogue:end:
    # . epilogue
    89/<- %esp 5/r32/ebp
    5d/pop-to-ebp
    c3/return