aboutsummaryrefslogblamecommitdiff
path: root/ssa.c
blob: af1311f42aff00dba903bf66b4b24dca10e18451 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                   
                   
                   

                
                 


                                                             
                         
                                


                                                                
 
                 



                                                                         
                         
                  
 
                          
                           
                     
                     
                      
                    
                 

                   


               
                            



                              
                   


                
                                 
                                
                                 






                            
                      

                          









                                                 





                                                    





                                                





                                     





                                      
                                       

                                           





                                            
                          

                                  
                                                 

 
                                

                               


               
                      

                                   


                                        
         
                         
                    
         



                                         
                    


                            

 
                        
          

                                                      


                                          
                    
         

 
                                               
                                     
                  
                                                    
      
                                            
               





                              

 
                                                 
                  
                                                     
      






                             
                                      


                      
                                                                     
                        
                                                        
                        
                                                  
                        
                                                             
                        
                                                   
                        


     
                                         

                     
                  

                                      
                  
                  


                                      
                  

                               
                                      
                               
                                      

                                      
                  
                 

                                      

                                      

                                      
                  
                  

                                      
                  
                 





                                      
                  
                 


                                      

                  




                                      
                  
                  

                                      

                  


                                      

                  

                           

                                          
             
                                      
                  
                  


                                      
                  














                                               



                                              


                                                

                              




                                          
             
     
                     

 









                                                               
                                 
                                                 
     










                                                
                                             
                                   
         





                                 
                       
                              
               

                   

                                   




                                     

                           


                       
               
     




                                                     
     

 

                            
                           

                                       

                                        

                                        
                                  
       

                                                         



                            

                                                                          

                               
                                  



                              

                                             
                                      

                                                      
                              
                                                                 
                                          

                                                    
                                         
 
                                           
                                       

                                                     
                                                               
                                       
                                   



                         
                                           
                                       

                                                     
                                                               
                                           

                                                     
                                   



                         
                                           
                                       

                                                     
                                                               
                                           

                                                     
                                   

                  


                                            

                                  

                                                               
                                           
                                       

                                                     

                                            
                                                

                                                              

                                 
                 

                                          
                 


                                           

                  

                
                                              

                                                                    
                                           

                                         




                                                               
                               


                    

                                                   
                                           

                                                         



                                             
                                                
                                            

                                                          







                                               






                                
                                  
                   

                          
                              


                      
































                                                          



                                               
                                                 
                                         
                                                                              
               
                                  



                          
                                

                                       
                                    














                                                     




                                 
                  
                 
                                
                             
                                                          
                  


                                
                                    

                                                 


                

                                           
 





                                                                   
                 

                                                                         
                                         
                     


                                                            







                                                    
                                                      


                                              

                        
                                                      
                                                  


                                                                      
                                                   
                                               

                                                             
                                                  

                                          
                 
                                                    
                 
                             
                     




                                                                       
                                                  












                                                                       
                                           

                                                  
                     
                 


                                                                        

                                                                        

                                                               

                                         
                             









































                                                                               
 
                                                                     

                                                                                         
 




                                                                           

                                                                                          





                                                                             

                                                                                       


                                             




                                                              
                                            

                                      


                                             
 



                                                   



                                    


                                                                        

                                                                        

                                                               

                                         
                             









































                                                                               
 
                                                                     

                                                                                        
 




                                                                          

                                                                                         





                                                                             

                                                                                      


                                             




                                                              
                                             

                                      


                                            
 



                                                   



                                    












                                                                         

                                                               







                                                          

                                                         

                                             

                                               



















                                                                         

                                                             





                                                   

                                                             









                                                             

                                                                  







                                                             

                                                             


                                                

                                              

                                      






                                                      





                                                
                                               
                                           

                                                         
                                              

                                      

                    
                                  
                                            











                                                            

                                                                       
                     
                                                 
                     
                                                      



                                                                                        
                                                       

                                                     


                                                                                 


                                                    
                                                      




                                              

                                                           
                                                   

                                                                 



                                                                           
                                                        
                                                    

                                                                  


                                                       







                                               





                                                                          

                                     
                                                               


                                                                          

                                                                           
                                             
                                                                              




                                             











                                                                    


                                                         


                                                         
















                                                               
                                                                     

                                                                   

                                                
                                                        
                                                           
                                                                     

                                                                   
                                                






                                                        
                                                               
                                                           

                                                                         
                                 
                                                          


                                                 
                 


               
        
 
                                                           
                                     
                                              






                                      
                                   


                   
                                
                             
                                              
                                                                 
     
                                           
                             
                   



               
                                                   
                        

                                                       
                                         


                                                                        
                    
 

                                                   

                      
                                 
                             

                                                 

                               



                                   
 

                           

                    
        
 
                                                 


                             

                                                        
                                         


                                                                        
                    
 

                                                    
 

                              

                      
                                 
                             

                                                 

                               



                                   
 

                           

                    
        
 
                                                                    

                                
                                        
                              
                                            









                                                       
                        
     
                         





                                                   
                                
                                          





                                                  
                                        
                          
                                     
                                 
 
                                    
                                                        






                                                      
                                        
                                     
                                           

         
                                                       




                                       






                                   
                                        
                                     
                                           

                                    
                                                        


                          
        
 
                                          
                                  
                   
                            
                                              




                                                      
                                  

                      
                               
                           
                                                  






                                                     
                                  


                                                   
                               
                           
                                                  





                                 




                                                               
                                     









                                              
                       
                                            
                        
                                             
                      
                                   




                                                               
































                                                      





                                              
                                                                         
                         
                                         



                             
                   
                    
                                             



                             
                                  

                                     










                                  
                                   




                                
                                            

























                                                           
                                  




















                                                         

                                                        

 



                                
                                





                                      









                                               







                                                                  
                                                         


                                           
                                              
                                          
                                              
                                                

                                                                           
                                                       







                                       











                                              





                                 
                             




                                                
                               
                                                       




                             

                        



                                            
                                                        
              


                                
         



                                                                     
                                             

                                                                
         
                 
         


                                                     
             
                                                           



                                                 
                                        




                                         

                                    

                 









                                                    




                                                             


                                                 
                           
     
                                         


                                       

                        


     





                                        

                                          
                                                                  
                          



                                       

                                        


                        
                       





                                   




                                                    



                                        
                                                        


                                               
                                
             


                                    





                           









                                                                        



                                   










                                       



                                   





















                                                 

                                                   
              
                                                


                           

                                
 
                            

                              
                                    








                                      
                                   
                                   








                                                                                 
                                                                                      




                                              
                                                          

                 
                                  

                                                                     
















                                                    
                                                          


                                                                      

                                  
                                                             

                    


                                          
                                  

                                       



                                            
                                                                                      

                                                  
                                      

                                            
                                                              
                                                 



                     


                                                    
                                                         
         



                                               
                                                              
                                                                                
         
     










                                                  

 



                                                                       


                                        
                  
                                
                          
                         
                          
                          
      


                                                
                     

 
                     
               
                 
                    






                                                

                                   

                                     


                                                    

                                

                                 











                                                     









                                                

                                                                   

                                                

 











                                            
                                                     


















                                                               
                                       
                                         

                                          

                                                                      
                           


                                                             

                               
                      
               




                                               
               




                                                





                                              













                                                      
     

                                  


                                                   
     
                                                    
































































                                                                      
                                                            































                                                                                           




                                                                     






                                                              


                                      
                                            
                                     
                                              








                                 






                                      
                                 
                                                
                                                         



                                                                           
                    

 
                              
                                                                 



                                   

                                           


                         

                                 
                                                               




                                        
                                                               




                                        




                             




























                                                             




















                                                               



                                        
                                                          





                                            



         










                                   




                                             
                                          






                                                    







                                                      
                             









                                                       
          
                     









                                           
                             
                         





                                                                   








                                                         
















                                                         



                                                  



                               






























                                                                                      







                                                                
                                                               











                                                                                  
                                                               












                                                                                   
                                                               









































                                                                                     

                                 





                          





























                                                     


                                        

                                            








                                           
















                                              

                                              

                                                   






                                                 
                       
















                                                          
                         







                                     


                                 


                       
 
 
                                                     
                                 
             
                                       
     
                  
                     
                          
         




                                          
                               














                                    
             



                                                                           
                                                                     

                          


             
                                               




                                          




                                     
                         

 


























                                                                                  
                             
                        
          
                                                               

                                       
                             
                            
                       


               

                    
                                                







                                       

                             
                                                      
                         
                         
                    
                
                  
                                               
      


                                           
                                                   
 
                                   

                                   



                                                  

                                                    
                                                                          
                                  
                                                                          
                                  
                              
                                  
                                                     
             

                                             

                                  


                                     



                        
                       
                      

                             
                     

                             
                        
                         
                        
                         
                           
                    

                      
                     
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "ast.h"
#include "ssa.h"
#include "mips.h"
#define NEW(type) ((type *)malloc(sizeof(type)))
#define DBLINK(from, to) ((from)->next = (to))->prev = (from)

static CGraph cfg, dtree;
static CBlock_t blks[MAX_BLOCK];
static COList_t raw_defs;   /* defintion of all vars and tmps */
static int bcnt;            /* block counter */
static int tcnt;            /* temporary counter */

static int quiet;
static int gbbase;
static CBlock_t entry;
static COList_t defs;       /* all defintions that have actual effects */

/* for code generation */
CFuncIR_t func_ir;

COpr_t copr_create(void) {
    COpr_t opr = NEW(COpr);
    opr->type = NULL;
    opr->cval = NULL;
    opr->range = NULL;
    opr->same = opr;
    opr->dep = 0;
    opr->mod = 0;
    opr->par = opr;
    return opr;
}

CInst_t cinst_create(void) {
    CInst_t inst = NEW(CInst);
    inst->dest = NULL;
    inst->src1 = NULL;
    inst->src2 = NULL;
    inst->sysp = 0;
    return inst;
}

CBlock_t cblock_create(int inc) {
    CBlock_t cblk = NEW(CBlock);
    CInst_t dum = cinst_create();
    CPhi_t pdum = NEW(CPhi);
    dum->prev = dum;
    dum->next = dum;
    pdum->prev = pdum;
    pdum->next = pdum;
    cblk->insts = dum;
    cblk->phis = pdum;
    cblk->next = NULL;
    if (inc)
        cblk->id = bcnt++;
    cblk->ref = 0;
    return cblk;
}

void cblock_append(CBlock_t cblk, CInst_t inst) {
    CInst_t head = cblk->insts;
    (inst->prev = head->prev)->next = inst;
    (inst->next = head)->prev = inst;
}

void cblock_pushfront(CBlock_t cblk, CInst_t inst) {
    CInst_t head = cblk->insts;
    (inst->next = head->next)->prev = inst;
    (inst->prev = head)->next = inst;
}

void cblock_pappend(CBlock_t cblk, CPhi_t phi) {
    CPhi_t head = cblk->phis;
    (phi->prev = head->prev)->next = phi;
    (phi->next = head)->prev = phi;
}

void cblock_popback(CBlock_t cblk) {
    CInst_t last = cblk->insts->prev;
    last->next->prev = last->prev;
    last->prev->next = last->next;
}

void cblock_popfront(CBlock_t cblk) {
    CInst_t first = cblk->insts->next;
    first->next->prev = first->prev;
    first->prev->next = first->next;
}

CInst_t cblock_getback(CBlock_t cblk) {
    CInst_t res = cblk->insts->prev;
    return res != cblk->insts ? res : NULL;
}

int cblock_isempty(CBlock_t cblk) {
    return cblk->insts->prev == cblk->insts;
}

CVar_t ctmp_create(void) {
    static char buff[MAX_NAMELEN];
    sprintf(buff, "t%d", tcnt++);
    return cvar_create(strdup(buff), NULL, NULL);
}

void ctmp_destroy(CVar_t type) {
    /* allocated dynamically */
    free(type->name);
    free(type);
}

void cfg_clear(void) {
    int i;
    for (i = 0; i < MAX_BLOCK; i++)
    {
        CEdge *p, *np;
        for (p = cfg.head[i]; p; p = np)
        {
            np = p->next;
            free(p);
        }
        cfg.head[i] = NULL;
        for (p = cfg.rhead[i]; p; p = np)
        {
            np = p->next;
            free(p);
        }
        cfg.rhead[i] = NULL;
    }
}

void dtree_clear(void) {
    int i;
    CEdge *p, *np;
    for (i = 0; i < MAX_BLOCK; dtree.head[i++] = NULL)
        for (p = dtree.head[i]; p; p = np)
        {
            np = p->next;
            free(p);
        }
}

void cfg_add_edge(CBlock_t from, CBlock_t to) {
    int fid = from->id, tid = to->id;
#ifdef CIBIC_DEBUG
    fprintf(stderr, "%d -> %d\n", from->id, to->id);
#endif
    CEdge *e = NEW(CEdge), *re = NEW(CEdge);
    e->to = to;
    e->next = cfg.head[fid];
    cfg.head[fid] = e;

    re->to = from;
    re->next = cfg.rhead[tid];
    cfg.rhead[tid] = re;
}

void dtree_add_edge(CBlock_t from, CBlock_t to) {
#ifdef CIBIC_DEBUG
    fprintf(stderr, "%d d-> %d\n", from->id, to->id);
#endif
    int id = from->id;
    CEdge *e = NEW(CEdge);
    e->to = to;
    e->next = dtree.head[id];
    dtree.head[id] = e;
}

void copr_print(FILE *f, COpr_t opr) {
    switch (opr->kind)
    {
        case VAR: 
                  fprintf(f, "%s_%d", opr->info.var->name, opr->sub);
                  break;
        case TMP: fprintf(f, "%s", opr->info.var->name);
                  break;
        case IMM: fprintf(f, "%d", opr->info.imm);
                  break;
        case IMMS: fprintf(f, "\"%s\"", opr->info.cstr->str);
                  break;
        case IMMF: fprintf(f, "%s", opr->info.str);
                  break;
    }
}

void cinst_print(FILE *f, CInst_t inst) {
    switch (inst->op)
    {
        case LOAD:
            fprintf(f, "load ");
            copr_print(f, inst->dest);
            break;
        case MOVE:
            copr_print(f, inst->dest);
            fprintf(f, " = ");
            copr_print(f, inst->src1);
            break;
        case BEQ:
            fprintf(f, "if (");
            copr_print(f, inst->src1);
            fprintf(f, " == ");
            copr_print(f, inst->src2);
            fprintf(f, ") goto _L");
            copr_print(f, inst->dest);
            break;
        case BNE:
            fprintf(f, "if (");
            copr_print(f, inst->src1);
            fprintf(f, " != ");
            copr_print(f, inst->src2);
            fprintf(f, ") goto _L");
            copr_print(f, inst->dest);
            break;
        case GOTO:
            fprintf(f, "goto _L");
            copr_print(f, inst->dest);
            break;
        case ARR:
            copr_print(f, inst->dest);
            fprintf(f, " = ");
            copr_print(f, inst->src1);
            fprintf(f, "[");
            copr_print(f, inst->src2);
            fprintf(f, "]");
            break;
        case NEG:
            copr_print(f, inst->dest);
            fprintf(f, " = -");
            copr_print(f, inst->src1);
            break;
        case WARR:
            copr_print(f, inst->dest);
            fprintf(f, "[");
            copr_print(f, inst->src2);
            fprintf(f, "] = ");
            copr_print(f, inst->src1);
            break;
        case PUSH:
            fprintf(f, "push ");
            copr_print(f, inst->src1);
            break;
        case CALL:
            copr_print(f, inst->dest);
            fprintf(f, " = call ");
            copr_print(f, inst->src1);
            break;
        case RET:
            if (inst->src1)
            {
                fprintf(f, "return ");
                copr_print(f, inst->src1);
            }
            else fprintf(f, "return");
            break;
        case ADDR:
            copr_print(f, inst->dest);
            fprintf(f, " = addr ");
            copr_print(f, inst->src1);
            break;
        default:
            {
                const char *op;
                switch (inst->op)
                {
                    case MUL: op = "*"; break;
                    case DIV: op = "/"; break;
                    case MOD: op = "%"; break;
                    case ADD: op = "+"; break;
                    case SUB: op = "-"; break;
                    case SHL: op = "<<"; break;
                    case SHR: op = ">>"; break;
                    case AND: op = "&"; break;
                    case XOR: op = "^"; break;
                    case OR: op = "|"; break;
                    case LT: op = "<"; break;
                    case GT: op = ">"; break;
                    case LE: op = "<="; break;
                    case GE: op = ">="; break;
                    case EQ: op = "=="; break;
                    case NE: op = "!="; break;
                    case NOR: op = "nor"; break;
                    default: ;
                }
                copr_print(f, inst->dest);
                fprintf(f, " = ");
                copr_print(f, inst->src1);
                fprintf(f, " %s ", op);
                copr_print(f, inst->src2);
            }
    }
    fprintf(f, "\n");
}

void cphi_print(CPhi_t phi, CBlock_t blk) {
    int i;
    fprintf(stderr, "%s_%d = phi", phi->dest->info.var->name,
                                    phi->dest->sub);
    for (i = 0; i < blk->pred; i++)
        fprintf(stderr, " %s_%d", phi->oprs[i]->info.var->name,
                                    phi->oprs[i]->sub);
    fprintf(stderr, "\n");
}

void cblock_print(CBlock_t blk) {
    fprintf(stderr, "_L%d:\n", blk->id + gbbase);
    {
        CPhi_t p, sp = blk->phis;
        for (p = sp->next; p != sp; p = p->next)
        {
            fprintf(stderr, "\t");
            cphi_print(p, blk);
        }
    }
    {
        CInst_t p, sp = blk->insts;
        for (p = sp->next; p != sp; p = p->next)
        {
            fprintf(stderr, "%02d\t", p->id);
            cinst_print(stderr, p);
        }
    }
}
void ssa_func_print(CBlock_t p) {
    for (; p; p = p->next)
        cblock_print(p);
}
void ssa_func(CType_t);
void ssa_generate(int quiet) {
    CTList_t f;
    CFuncIR_t cf;
    func_ir = NULL;
    for (f = funcs; f; f = f->next)
    {
        cf = NEW(CFuncIR);
        ssa_func(cf->func = f->type);
        cf->gbbase = gbbase;
        cf->defs = defs;
        cf->entry = entry;
        cf->next = func_ir;
        func_ir = cf;
        gbbase += bcnt;
        bcnt = 0;
    }
    if (!quiet)
    {
        for (cf = func_ir; cf; cf = cf->next)
        {
            fprintf(stderr, "%s:\n", cf->func->name);
            ssa_func_print(cf->entry);
        }
    }
}

#define POINTER_CONV(inst) \
do { \
    if (rt->type == CARR) \
    { \
        /* convert to pointer type */ \
        CType_t a; \
        a = ctype_create("", CPTR, p); \
        a->rec.ref = rt->rec.arr.elem; \
        (inst)->op = ADD; \
        (inst)->dest->type = a;  \
    } \
    else if (rt->type == CSTRUCT || rt->type == CUNION) \
        (inst)->op = ADD; \
    else (inst)->op = ARR; \
} while (0)


COpr_t ssa_exp_(CNode *p, CBlock_t *, CInst_t, CBlock_t);
COpr_t ssa_postfix(CNode *p, CBlock_t *cur, CInst_t lval, CBlock_t succ) {
    CNode *post = p->chd->next;
    CType_t rt = p->ext.type;
    CInst_t base = cinst_create();
    switch (post->rec.subtype)
    {
        case POSTFIX_ARR:
            {
                CInst_t off = cinst_create();
                off->dest = copr_create();
                off->dest->kind = TMP;
                off->dest->info.var = ctmp_create();
                off->dest->type = post->chd->ext.type;
                off->op = MUL;
                off->src1 = ssa_exp_(post->chd, cur, NULL, succ);
                off->src2 = copr_create();
                off->src2->kind = IMM;
                off->src2->info.imm = calc_size(rt);
                cblock_append(*cur, off);

                base->dest = copr_create();
                base->dest->kind = TMP;
                base->dest->info.var = ctmp_create();
                base->dest->type = rt;
                base->src1 = ssa_exp_(p->chd, cur, NULL, succ);
                base->src2 = off->dest;
                POINTER_CONV(base);
            }
            break;
        case POSTFIX_DOT:
            {
                base->dest = copr_create();
                base->dest->kind = TMP;
                base->dest->info.var = ctmp_create();
                base->dest->type = rt;
                base->src1 = ssa_exp_(p->chd, cur, NULL, succ);
                base->src2 = copr_create();
                base->src2->kind = IMM;
                base->src2->info.imm = p->ext.offset;
                POINTER_CONV(base);
            }
            break;
        case POSTFIX_PTR:
            {
                base->dest = copr_create();
                base->dest->kind = TMP;
                base->dest->info.var = ctmp_create();
                base->dest->type = rt;
                base->src1 = ssa_exp_(p->chd, cur, NULL, succ);
                base->src2 = copr_create();
                base->src2->kind = IMM;
                base->src2->info.imm = p->ext.offset;
                POINTER_CONV(base);
            }
            break;
        case POSTFIX_CALL:
            {
                CNode *arg = post->chd->chd;
                CInst h;
                CInst_t t = &h, n;
                base->op = CALL;
                base->src1 = ssa_exp_(p->chd, cur, lval, succ);
                base->dest = copr_create();
                base->dest->kind = TMP;
                base->dest->info.var = ctmp_create();
                base->dest->type = rt;
                for (; arg; arg = arg->next)
                {
                    CInst_t pi = cinst_create();
                    pi->op = PUSH;
                    pi->src1 = ssa_exp_(arg, cur, lval, succ);
                    t->next = pi;
                    t = pi;
                }
                t->next = NULL;
                for (t = h.next; t; t = n)
                {
                    n = t->next;
                    cblock_append(*cur, t);
                }
            }
            break;
        default:
            {
                CInst_t tins = cinst_create();
                ssa_exp_(p->chd, cur, tins, succ);
                base->op = post->rec.subtype == OPT_INC ? ADD : SUB;
                base->src2 = copr_create();
                base->src2->kind = IMM;
                base->src2->info.imm = 1;
                base->src1 = ssa_exp_(p->chd, cur, NULL, succ);
                if (tins->op == MOVE)
                {
                    base->dest = tins->dest;
                    cblock_append(succ, base);
                    free(tins);
                }
                else
                {
                    CInst_t tins2 = cinst_create();
                    base->dest = copr_create();
                    base->dest->kind = TMP;
                    base->dest->info.var = ctmp_create();
                    base->dest->type = rt;
                    tins->src1 = base->dest;
                    tins2->op = ARR;
                    tins2->src1 = tins->dest;
                    tins2->src2 = tins->src2;
                    tins2->dest = copr_create();
                    tins2->dest->kind = TMP;
                    tins2->dest->info.var = ctmp_create();
                    tins2->dest->type = rt;

                    cblock_append(succ, base);
                    cblock_append(succ, tins);
                    cblock_append(succ, tins2);
                }
                return base->src1;
            }
            break;
    }

    if (lval)
    {
        lval->op = WARR;
        lval->dest = base->src1;
        lval->src2 = base->src2;
        lval->wtype = p->ext.type;
        free(base);
        return lval->dest;
    }
    cblock_append(*cur, base);
    return base->dest;
}

CInst_t compress_branch(COpr_t r, CBlock_t blk, int rev) {
    int flag = -1;
    CInst_t b;
    if (r->kind == TMP)
    {
        b = cblock_getback(blk);
        if (b)
        {
            assert(r == b->dest);
            if (b->op == EQ)
                flag = 0;
            else if (b->op == NE)
                flag = 1;
        }
    }
    if (flag != -1)
        b->op = flag ? BNE : BEQ;
    else
    {
        b = cinst_create();
        b->op = BNE;
        b->src1 = r;
        b->src2 = copr_create();
        b->src2->kind = IMM;
        b->src2->info.imm = 0;
        cblock_append(blk, b);
    }
    b->op ^= rev;
    b->dest = copr_create();
    b->dest->kind = IMM;
    return b;
}

static CNode *opt_if = NULL;
static CBlock_t opt_if_loop_exit = NULL;

CBlock_t ssa_stmt(CNode *, CBlock_t, CBlock_t);
#define IS_PTR(tt) ((tt) == CPTR || (tt) == CARR)
COpr_t ssa_exp(CNode *, CBlock_t *, int);
COpr_t ssa_exp_(CNode *p, CBlock_t *cur, CInst_t lval, CBlock_t succ) {/*{{{*/
    COpr_t res;
    CInst_t inst = cinst_create();
    switch (p->type)
    {
        case NOP: ; break;
        case ID:
            res = copr_create();
            res->kind = VAR;
            res->info.var = p->ext.var;
            res->type = p->ext.type;
            {
                CVar_t var = res->info.var;
                CType_t type = var->type;
                if (type->type == CPTR &&
                        type->rec.ref->type == CFUNC)
                {
                    char *name = type->rec.ref->name;
                    if (*name != '\0')
                    {
                        res->kind = IMMF;
                        res->info.str = name;
                    }
                }
            }

            if (lval)
            {
                lval->op = MOVE;
                lval->dest = res;
            }
            break;
        case STR:
            res = copr_create();
            res->kind = IMMS;
            res->info.cstr = (CSList_t)(p->ext.const_val);
            break;
        default:
            if (p->ext.is_const)
            {
                res = copr_create();
                res->kind = IMM;
                res->info.imm = p->ext.const_val;
            }
            else
            {
                int op = p->rec.subtype;
                int rec = 1, auto_dest = 1;

                if (op == ',')
                {
                    ssa_exp(p->chd, cur, 1);
                    return ssa_exp_(p->chd->next, cur, NULL, succ);
                }
                else if (op == '=')
                {
                    inst->src1 = ssa_exp_(p->chd->next, cur, NULL, succ);
                    ssa_exp_(p->chd, cur, inst, succ);
                    if (inst->op == MOVE)
                    {
                        CInst_t last = cblock_getback(*cur);
                        if (last && last->dest->kind == TMP
                                && last->dest == inst->src1)
                        {
                            free(last->dest);
                            last->dest = inst->dest;
                            free(inst);
                            return last->dest;
                        }
                        else 
                        {
                            cblock_append(*cur, inst);
                            return inst->dest;
                        }
                    }
                    else
                    {
                        CInst_t tins = cinst_create();
                        cblock_append(*cur, inst);
                        tins->op = ARR;
                        tins->src1 = inst->dest;    /* base */
                        tins->src2 = inst->src2;    /* displacement */
                        tins->dest = copr_create();
                        tins->dest->kind = TMP;
                        tins->dest->info.var = ctmp_create();
                        tins->dest->type = p->ext.type;
                        cblock_append(*cur, tins);
                        return tins->dest;
                    }
                }
                else if (op == '*' && !p->chd->next)
                {
                    if (lval)
                    {
                        lval->op = WARR;
                        lval->dest = ssa_exp_(p->chd, cur, NULL, succ);
                        lval->src2 = copr_create();
                        lval->src2->kind = IMM;
                        lval->src2->info.imm = 0;
                        lval->wtype = p->ext.type;
                        return lval->dest;
                    }
                    else
                    {
                        CType_t rt = p->ext.type;
                        inst->src1 = ssa_exp_(p->chd, cur, NULL, succ);
                        inst->src2 = copr_create();
                        inst->src2->kind = IMM;
                        inst->src2->info.imm = 0;
                        inst->dest = copr_create();
                        inst->dest->kind = TMP;
                        inst->dest->info.var = ctmp_create();
                        inst->dest->type = rt;
                        POINTER_CONV(inst);
                        cblock_append(*cur, inst);
                        return inst->dest;
                    }
                }
                else if (op == OPT_AND)
                {
                    CBlock_t else_h = cblock_create(1), else_t = else_h,
                             one_h = cblock_create(1), one_t = one_h,
                             zero_h = cblock_create(1), zero_t = zero_h,
                             next_blk = cblock_create(1), sblk;
                    COpr_t r0, r1, ri;
                    CInst_t b, a0, a1;
                    CPhi_t m = NEW(CPhi);
                    CNode *t;
                    if (opt_if)
                    {
                        CNode *body1 = opt_if->chd->next, *body2 = body1->next;
                        one_t = ssa_stmt(body1, one_h, opt_if_loop_exit);
                        if (body2->type != NOP)
                            zero_t = ssa_stmt(body2, zero_h, opt_if_loop_exit);
                        opt_if = NULL;
                    }
                    else
                    {
                        /* constant opt */
                        a0 = cinst_create();
                        a0->op = MOVE;
                        a0->dest = copr_create();
                        a0->dest->kind = TMP;
                        a0->dest->info.var = ctmp_create();
                        a0->dest->type = p->ext.type; /* int */
                        a0->src1 = copr_create();
                        a0->src1->kind = IMM;
                        a0->src1->info.imm = 0;
                        cblock_append(zero_h, a0);

                        a1 = cinst_create();
                        a1->op = MOVE;
                        a1->dest = copr_create();
                        a1->dest->kind = TMP;
                        a1->dest->info.var = ctmp_create();
                        a1->dest->type = p->ext.type;
                        a1->src1 = copr_create();
                        a1->src1->kind = IMM;
                        a1->src1->info.imm = 1;
                        cblock_append(one_h, a1);

                        m->dest = copr_create();
                        m->dest->kind = TMP;
                        m->dest->info.var = ctmp_create();
                        m->dest->type = p->ext.type;
                        m->oprs = (COpr_t *)malloc(sizeof(COpr_t) * 2);
                        m->oprs[0] = a0->dest;
                        m->oprs[1] = a1->dest;
                        cblock_pappend(next_blk, m);
                    }

                    r1 = ssa_exp_(p->chd->next, &else_t, NULL, succ);
                    compress_branch(r1, else_t, 1)->dest->info.imm = zero_h->id + gbbase;
                    zero_h->ref = 1;

                    sblk = else_h;
                    for (t = p->chd; t->rec.subtype == OPT_AND; t = t->chd)
                    {
                        CBlock_t c_h = cblock_create(1), c_t = c_h;
                        ri = ssa_exp_(t->chd->next, &c_t, NULL, succ);
                        compress_branch(ri, c_t, 1)->dest->info.imm = zero_h->id + gbbase;
                        cfg_add_edge(c_t, zero_h);  /* tail */
                        DBLINK(c_t, sblk);
                        cfg_add_edge(c_t, sblk);      /* connect to header */
                        sblk = c_h;
                    }

                    r0 = ssa_exp_(t, cur, NULL, succ);
                    compress_branch(r0, *cur, 1)->dest->info.imm = zero_h->id + gbbase;
                    cfg_add_edge(*cur, zero_h);
                    DBLINK(*cur, sblk);
                    cfg_add_edge(*cur, sblk);

                    b = cinst_create();
                    b->op = GOTO;
                    b->dest = copr_create();
                    b->dest->kind = IMM;
                    b->dest->info.imm = next_blk->id + gbbase;
                    cblock_append(one_t, b);
                    next_blk->ref = 1;

                    DBLINK(else_t, one_h);
                    DBLINK(one_t, zero_h);
                    DBLINK(zero_t, next_blk);

                    cfg_add_edge(else_t, one_h);
                    cfg_add_edge(else_t, zero_h);
                    cfg_add_edge(one_t, next_blk);
                    cfg_add_edge(zero_t, next_blk);

                    *cur = next_blk;
                    return m->dest;
                }
                else if (op == OPT_OR)
                {
                    CBlock_t else_h = cblock_create(1), else_t = else_h,
                             one_h = cblock_create(1), one_t = one_h,
                             zero_h = cblock_create(1), zero_t = zero_h,
                             next_blk = cblock_create(1), sblk;
                    COpr_t r0, r1, ri;
                    CInst_t b, a0, a1;
                    CPhi_t m = NEW(CPhi);
                    CNode *t;
                    if (opt_if)
                    {
                        CNode *body1 = opt_if->chd->next, *body2 = body1->next;
                        one_t = ssa_stmt(body1, one_h, opt_if_loop_exit);
                        if (body2->type != NOP)
                            zero_t = ssa_stmt(body2, zero_h, opt_if_loop_exit);
                        opt_if = NULL;
                    }
                    else
                    {
                        /* constant opt */
                        a0 = cinst_create();
                        a0->op = MOVE;
                        a0->dest = copr_create();
                        a0->dest->kind = TMP;
                        a0->dest->info.var = ctmp_create();
                        a0->dest->type = p->ext.type; /* int */
                        a0->src1 = copr_create();
                        a0->src1->kind = IMM;
                        a0->src1->info.imm = 0;
                        cblock_append(zero_h, a0);

                        a1 = cinst_create();
                        a1->op = MOVE;
                        a1->dest = copr_create();
                        a1->dest->kind = TMP;
                        a1->dest->info.var = ctmp_create();
                        a1->dest->type = p->ext.type;
                        a1->src1 = copr_create();
                        a1->src1->kind = IMM;
                        a1->src1->info.imm = 1;
                        cblock_append(one_h, a1);

                        m->dest = copr_create();
                        m->dest->kind = TMP;
                        m->dest->info.var = ctmp_create();
                        m->dest->type = p->ext.type;
                        m->oprs = (COpr_t *)malloc(sizeof(COpr_t) * 2);
                        m->oprs[0] = a1->dest;
                        m->oprs[1] = a0->dest;
                        cblock_pappend(next_blk, m);
                    }

                    r1 = ssa_exp_(p->chd->next, &else_t, NULL, succ);
                    compress_branch(r1, else_t, 0)->dest->info.imm = one_h->id + gbbase;
                    one_h->ref = 1;

                    sblk = else_h;
                    for (t = p->chd; t->rec.subtype == OPT_OR; t = t->chd)
                    {
                        CBlock_t c_h = cblock_create(1), c_t = c_h;
                        ri = ssa_exp_(t->chd->next, &c_t, NULL, succ);
                        compress_branch(ri, c_t, 0)->dest->info.imm = one_h->id + gbbase;
                        cfg_add_edge(c_t, one_h);  /* tail */
                        DBLINK(c_t, sblk);
                        cfg_add_edge(c_t, sblk);      /* connect to header */
                        sblk = c_h;
                    }

                    r0 = ssa_exp_(t, cur, NULL, succ);
                    compress_branch(r0, *cur, 0)->dest->info.imm = one_h->id + gbbase;
                    cfg_add_edge(*cur, one_h);
                    DBLINK(*cur, sblk);
                    cfg_add_edge(*cur, sblk);

                    b = cinst_create();
                    b->op = GOTO;
                    b->dest = copr_create();
                    b->dest->kind = IMM;
                    b->dest->info.imm = next_blk->id + gbbase;
                    cblock_append(zero_t, b);
                    next_blk->ref = 1;

                    DBLINK(else_t, zero_h);
                    DBLINK(zero_t, one_h);
                    DBLINK(one_t, next_blk);

                    cfg_add_edge(else_t, zero_h);
                    cfg_add_edge(else_t, one_h);
                    cfg_add_edge(zero_t, next_blk);
                    cfg_add_edge(one_t, next_blk);

                    *cur = next_blk;
                    return m->dest;
                }
                else if (op == '+' && IS_PTR(p->ext.type->type))
                {
                    COpr_t lhs = ssa_exp_(p->chd, cur, lval, succ),
                           rhs = ssa_exp_(p->chd->next, cur, lval, succ);
                    CInst_t index = cinst_create();
                    CType_t et = p->chd->ext.type;
                    if (et->type == CPTR)
                        et = et->rec.ref;
                    else
                        et = et->rec.arr.elem;
                    index->op = MUL;
                    index->dest = copr_create();
                    index->dest->kind = TMP;
                    index->dest->info.var = ctmp_create();
                    index->dest->type = p->chd->next->ext.type;
                    index->src1 = rhs;
                    index->src2 = copr_create();
                    index->src2->kind = IMM;
                    index->src2->info.imm = calc_size(et);

                    inst->op = ADD;
                    inst->dest = copr_create();
                    inst->dest->kind = TMP;
                    inst->dest->info.var = ctmp_create();
                    inst->dest->type = p->ext.type;
                    inst->src1 = lhs;
                    inst->src2 = index->dest;
                    cblock_append(*cur, index);
                    cblock_append(*cur, inst);
                    return inst->dest;
                }
                else if (op == '-' && IS_PTR(p->chd->ext.type->type))
                {
                    CType_t nt = p->chd->next->ext.type;
                    CType_t et = p->chd->ext.type;
                    COpr_t lhs = ssa_exp_(p->chd, cur, lval, succ),
                           rhs = ssa_exp_(p->chd->next, cur, lval, succ);
                    CInst_t diff = cinst_create();

                    if (et->type == CPTR)
                        et = et->rec.ref;
                    else
                        et = et->rec.arr.elem;

                    if (IS_PTR(nt->type))
                    {
                        diff->op = SUB;
                        diff->dest = copr_create();
                        diff->dest->kind = TMP;
                        diff->dest->info.var = ctmp_create();
                        diff->dest->type = p->ext.type;
                        diff->src1 = lhs;
                        diff->src2 = rhs;

                        inst->op = DIV;
                        inst->dest = copr_create();
                        inst->dest->kind = TMP;
                        inst->dest->info.var = ctmp_create();
                        inst->dest->type = p->ext.type;
                        inst->src1 = diff->dest;
                        inst->src2 = copr_create();
                        inst->src2->kind = IMM;
                        inst->src2->info.imm = calc_size(et);
                    }
                    else
                    {
                        diff->op = MUL;
                        diff->dest = copr_create();
                        diff->dest->kind = TMP;
                        diff->dest->info.var = ctmp_create();
                        diff->dest->type = p->chd->next->ext.type;
                        diff->src1 = rhs;
                        diff->src2 = copr_create();
                        diff->src2->kind = IMM;
                        diff->src2->info.imm = calc_size(et);

                        inst->op = SUB;
                        inst->dest = copr_create();
                        inst->dest->kind = TMP;
                        inst->dest->info.var = ctmp_create();
                        inst->dest->type = p->ext.type;
                        inst->src1 = lhs;
                        inst->src2 = diff->dest;
                    }
                    cblock_append(*cur, diff);
                    cblock_append(*cur, inst);
                    return inst->dest;
                }
                else if (op == '&' && !p->chd->next)
                {
                    ssa_exp_(p->chd, cur, inst, succ);
                    if (inst->op == MOVE)
                    {
                        inst->op = ADDR;
                        inst->src1 = inst->dest;
                    }
                    else
                    {
                        inst->op = ADD;
                        inst->src1 = inst->dest;
                    }
                    inst->dest = copr_create();
                    inst->dest->kind = TMP;
                    inst->dest->info.var = ctmp_create();
                    inst->dest->type = p->ext.type;
                    cblock_append(*cur, inst);
                    return inst->dest;
                }
                else
                {
                    int unary = 0;
                    inst->op = (unsigned)-1;
                    switch (op)
                    {
                        case ASS_MUL: inst->op = MUL; break;
                        case ASS_DIV: inst->op = DIV; break;
                        case ASS_MOD: inst->op = MOD; break;
                        case ASS_ADD: inst->op = ADD; break;
                        case ASS_SUB: inst->op = SUB; break;
                        case ASS_SHL: inst->op = SHL; break;
                        case ASS_SHR: inst->op = SHR; break;
                        case ASS_AND: inst->op = AND; break;
                        case ASS_XOR: inst->op = XOR; break;
                        case ASS_OR:  inst->op = OR; break;
                        case OPT_INC: inst->op = ADD; unary = 1; break;
                        case OPT_DEC: inst->op = SUB; unary = 1; break;
                    }
                    if (inst->op != (unsigned)-1)
                    {
                        CInst_t tins = cinst_create();
                        ssa_exp_(p->chd, cur, tins, succ);                 /* as lval */
                        inst->src1 = ssa_exp_(p->chd, cur, NULL, succ);    /* as rval */
                        if (unary)
                        {
                            inst->src2 = copr_create();
                            inst->src2->kind = IMM;
                            inst->src2->info.imm = 1;
                        }
                        else
                            inst->src2 = ssa_exp_(p->chd->next, cur, NULL, succ);
                        if (tins->op == MOVE)
                        {
                            inst->dest = tins->dest;
                            cblock_append(*cur, inst);
                            free(tins);
                            return inst->dest;
                        }
                        else
                        {
                            CInst_t tins2 = cinst_create();
                            inst->dest = copr_create();
                            inst->dest->kind = TMP;
                            inst->dest->info.var = ctmp_create();
                            inst->dest->type = p->ext.type;
                            tins->src1 = inst->dest;
                            tins2->op = ARR;
                            tins2->src1 = tins->dest;    /* base */
                            tins2->src2 = tins->src2;    /* displacement */
                            tins2->dest = copr_create();
                            tins2->dest->kind = TMP;
                            tins2->dest->info.var = ctmp_create();
                            tins2->dest->type = p->ext.type;
                            cblock_append(*cur, inst);
                            cblock_append(*cur, tins);
                            cblock_append(*cur, tins2);
                            return tins2->dest;
                        }
                    }
                }

                switch (op)
                {
                    case EXP_CAST:
                        {
                            res = ssa_exp_(p->chd->next, cur, lval, succ);
                            res->type = p->ext.type;
                            free(inst);
                            return res;
                        }
                    case EXP_POSTFIX:
                        free(inst);
                        return ssa_postfix(p, cur, lval, succ);
                    /* KW_SIZEOF is eliminated during semantic checking */
                    default:
                        {
                            COpr_t lhs = ssa_exp_(p->chd, cur, lval, succ),
                                   rhs = NULL;
                            if (p->chd->next)
                                rhs = ssa_exp_(p->chd->next, cur, lval, succ);

                            inst->src1 = lhs;
                            inst->src2 = rhs;
                            switch (op)
                            {
                                case OPT_SHL: inst->op = SHL; break;
                                case OPT_SHR: inst->op = SHR; break;
                                case '|': inst->op = OR; break;
                                case '^': inst->op = XOR; break;
                                case OPT_EQ: inst->op = EQ; break;
                                case OPT_NE: inst->op = NE; break;
                                case '<': inst->op = LT; break;
                                case '>': inst->op = GT; break;
                                case OPT_LE: inst->op = LE; break;
                                case OPT_GE: inst->op = GE; break;
                                case '/': inst->op = DIV; break;
                                case '%': inst->op = MOD; break;
                                case '*':
                                          inst->op = MUL;
                                          break;
                                case '&':
                                          inst->op = AND;
                                          break;
                                case '+':
                                          if (p->chd->next)
                                              inst->op = ADD;
                                          else res = lhs;
                                          break;
                                case '-':
                                          if (p->chd->next)
                                              inst->op = SUB;
                                          else
                                          {
                                              inst->op = NEG;
                                              inst->src1 = lhs;
                                          }
                                          break;
                                case '~':
                                          inst->op = NOR;
                                          inst->src1 = lhs;
                                          inst->src2 = copr_create();
                                          inst->src2->kind = IMM;
                                          inst->src2->info.imm = 0;
                                          break;
                                case '!':
                                          inst->op = EQ;
                                          inst->src1 = lhs;
                                          inst->src2 = copr_create();
                                          inst->src2->kind = IMM;
                                          inst->src2->info.imm = 0;
                                          break;
                                default:
                                          auto_dest = 0;
                            }
                            if (rec)
                            {
                                if (auto_dest)
                                {
                                    inst->dest = copr_create();
                                    inst->dest->kind = TMP;
                                    inst->dest->info.var = ctmp_create();
                                    inst->dest->type = p->ext.type;
                                }
                                cblock_append(*cur, inst);
                                res = inst->dest;
                            }
                        }
                }
            }
    }
    return res;
}/*}}}*/

COpr_t ssa_exp(CNode *p, CBlock_t *cur, int discard_last) {
    CBlock_t succ = cblock_create(0);
    COpr_t res = ssa_exp_(p, cur, NULL, succ);
    CInst_t last; 
    {
        CInst_t head = succ->insts, t;
        while (head->next != head)
        {
            t = head->next;
            cblock_popfront(succ);
            cblock_append(*cur, t);
        }
        free(succ);
    }
    last = cblock_getback(*cur);
    if (discard_last && last 
                    && last->dest->kind == TMP
                    && last->op != CALL) /* temporary not used */
    {
        ctmp_destroy(last->dest->info.var);
        cblock_popback(*cur);
        free(last);
    }
    return res;
}

CBlock_t ssa_while(CNode *p, CBlock_t cur) {/*{{{*/
    CNode *exp = p->chd;
    CBlock_t loop_h = cblock_create(1), loop_t,
             cond_h= cblock_create(1), cond_t = cond_h,
             next_blk = cblock_create(1);
    CInst_t j_inst = cinst_create();
    COpr_t e = ssa_exp(exp, &cond_t, 0);
    compress_branch(e, cond_t, 0)->dest->info.imm = loop_h->id + gbbase;
    loop_h->ref = 1;

    DBLINK(cond_t, next_blk);
    loop_t = ssa_stmt(exp->next, loop_h, next_blk);

    j_inst->op = GOTO;
    j_inst->dest = copr_create();
    j_inst->dest->kind = IMM;
    j_inst->dest->info.imm = cond_h->id + gbbase;
    cond_h->ref = 1;
    cblock_append(cur, j_inst);

    cfg_add_edge(cur, cond_h);
    cfg_add_edge(cond_t, loop_h);
    cfg_add_edge(loop_t, cond_h);
    cfg_add_edge(cond_t, next_blk);

    DBLINK(cur, loop_h);
    DBLINK(loop_t, cond_h);

    return next_blk;
}/*}}}*/

CBlock_t ssa_for(CNode *p, CBlock_t cur) {/*{{{*/
    CNode *exp1 = p->chd,
          *exp2 = exp1->next,
          *exp3 = exp2->next;
    CBlock_t loop_h = cblock_create(1), loop_t,
             cond_h = cblock_create(1), cond_t = cond_h,
             next_blk = cblock_create(1);
    CInst_t j_inst = cinst_create();
    COpr_t e = ssa_exp(exp2, &cond_t, 0);
    compress_branch(e, cond_t, 0)->dest->info.imm = loop_h->id + gbbase;
    loop_h->ref = 1;

    DBLINK(cond_t, next_blk);
    loop_t = ssa_stmt(exp3->next, loop_h, next_blk);

    ssa_exp(exp1, &cur, 1);
    ssa_exp(exp3, &loop_t, 1);

    j_inst->op = GOTO;
    j_inst->dest = copr_create();
    j_inst->dest->kind = IMM;
    j_inst->dest->info.imm = cond_h->id + gbbase;
    cond_h->ref = 1;
    cblock_append(cur, j_inst);

    cfg_add_edge(cur, cond_h);
    cfg_add_edge(cond_t, loop_h);
    cfg_add_edge(loop_t, cond_h);
    cfg_add_edge(cond_t, next_blk);

    DBLINK(cur, loop_h);
    DBLINK(loop_t, cond_h);

    return next_blk;
}/*}}}*/

CBlock_t ssa_if(CNode *p, CBlock_t cur, CBlock_t loop_exit) {/*{{{*/
    CNode *body1 = p->chd->next,
          *body2 = body1->next;
    CBlock_t then_blk, then_t, next_blk,
             else_blk, else_t;
    CInst_t if_inst; /* = cinst_create(); */
    COpr_t rt;
    opt_if = p;
    opt_if_loop_exit = loop_exit;
    rt = ssa_exp(p->chd, &cur, 0); /* calculate cond */
    if (!opt_if) return cur;
    else
    {
        opt_if = NULL;
        opt_if_loop_exit = NULL;
    }
    if (rt->kind == IMM)
    {
        if (rt->info.imm)
            return ssa_stmt(body1, cur, loop_exit);
        else if (body2->type != NOP)
            return ssa_stmt(body2, cur, loop_exit);
        else
            return cur;
    }
    then_blk = cblock_create(1);
    if_inst = compress_branch(rt, cur, 1);

    cfg_add_edge(cur, then_blk);
    DBLINK(cur, then_blk);
    then_t = ssa_stmt(body1, then_blk, loop_exit);
    if (body2->type != NOP)
    {
        CInst_t j_inst = cinst_create();
        j_inst->op = GOTO;
        j_inst->dest = copr_create();
        j_inst->dest->kind = IMM;

        else_blk = cblock_create(1);
        if_inst->dest->info.imm = else_blk->id + gbbase;
        else_blk->ref = 1;
        DBLINK(then_t, else_blk);
        else_t = ssa_stmt(body2, else_blk, loop_exit);
        if (cblock_isempty(else_t))
            next_blk = else_t;
        else
        {
            next_blk = cblock_create(1);
            DBLINK(else_t, next_blk);
            cfg_add_edge(else_t, next_blk);
        }

        j_inst->dest->info.imm = next_blk->id + gbbase;
        next_blk->ref = 1;
        cblock_append(then_t, j_inst);

        cfg_add_edge(cur, else_blk);
        cfg_add_edge(then_t, next_blk);
    }
    else
    {
        if (cblock_isempty(then_t))
            next_blk = then_t;
        else
        {
            next_blk = cblock_create(1);
            DBLINK(then_t, next_blk);
            cfg_add_edge(then_t, next_blk);
        }
        cfg_add_edge(cur, next_blk);
        if_inst->dest->info.imm = next_blk->id + gbbase;
        next_blk->ref = 1;
    }
    return next_blk;
}/*}}}*/

CBlock_t ssa_ret(CNode *p, CBlock_t cur) {
    CInst_t inst = cinst_create();
    inst->op = RET;
    if (p->chd->type != NOP)
        inst->src1 = ssa_exp(p->chd, &cur, 0);
    cblock_append(cur, inst);
    return cur;
}

CBlock_t ssa_break(CBlock_t cur, CBlock_t loop_exit) {
    CInst_t inst = cinst_create();
    assert(loop_exit);
    inst->op = GOTO;
    inst->dest = copr_create();
    inst->dest->kind = IMM;
    inst->dest->info.imm = loop_exit->id + gbbase;
    loop_exit->ref = 1;
    cblock_append(cur, inst);
    cfg_add_edge(cur, loop_exit);
    return cur;
}

CBlock_t ssa_cont(CBlock_t cur, CBlock_t loop_exit) {
    CInst_t inst = cinst_create();
    assert(loop_exit);
    loop_exit = loop_exit->prev;    /* loop cond */
    inst->op = GOTO;
    inst->dest = copr_create();
    inst->dest->kind = IMM;
    inst->dest->info.imm = loop_exit->id + gbbase;
    loop_exit->ref = 1;
    cblock_append(cur, inst);
    cfg_add_edge(cur, loop_exit);
    return cur;
}

CBlock_t ssa_comp(CNode *, CBlock_t, CBlock_t loop_exit);
CBlock_t ssa_stmt(CNode *p, CBlock_t cur, CBlock_t loop_exit) {
    switch (p->rec.subtype)
    {
        case STMT_EXP:
            ssa_exp(p->chd, &cur, 1);
            break;
        case STMT_COMP:
            cur = ssa_comp(p, cur, loop_exit);
            break;
        case STMT_IF:
            return ssa_if(p, cur, loop_exit);
        case STMT_FOR:
            return ssa_for(p, cur);
        case STMT_WHILE:
            return ssa_while(p, cur);
        case STMT_CONT:
            return ssa_cont(cur, loop_exit);
        case STMT_BREAK:
            return ssa_break(cur, loop_exit);
        case STMT_RET:
            return ssa_ret(p, cur);
    }
    return cur;
}

CBlock_t ssa_comp(CNode *p, CBlock_t cur, CBlock_t loop_exit) {
    CNode *decls = p->chd,
          *stmts = p->chd->next, *i;
    if (decls->chd->type != NOP)
    {
        CVList_t a;
        for (a = p->ext.autos; a; a = a->next)
        {
            CNode *initr = a->var->initr;
            CInst_t last, inst;
            if (!initr) continue;
            assert(initr->rec.subtype == INITR_NORM);
            inst = cinst_create();
            inst->src1 = ssa_exp(initr->chd, &cur, 0);
            last = cblock_getback(cur);
            if (last && last->dest->kind == TMP)
            {
                last->dest->kind = VAR;
                free(last->dest->info.var);
                free(inst);
                last->dest->info.var = a->var;
                last->dest->type = a->var->type;
            }
            else
            {
                inst->op = MOVE;
                inst->dest = copr_create();
                inst->dest->kind = VAR;
                inst->dest->info.var = a->var;
                inst->dest->type = a->var->type;
                cblock_append(cur, inst);
            }
        }
    }
    if (stmts->chd->type != NOP)
        for (i = stmts->chd; i; i = i->next)
            cur = ssa_stmt(i, cur, loop_exit);
    return cur;
}

int dom[MAX_BLOCK], ord[MAX_BLOCK], vis[MAX_BLOCK], par[MAX_BLOCK], ocnt;
int loop_tail[MAX_BLOCK];
CPSet_t dfset[MAX_BLOCK], phi[MAX_BLOCK];
CBList_t df[MAX_BLOCK];

void dfs(CBlock_t u, int v) {
    CEdge *e;
    par[u->id] = v;
    vis[u->id] = -2;
    for (e = cfg.head[u->id]; e; e = e->next)
    {
        CBlock_t v = e->to;
        if (vis[v->id] == -1)
            dfs(v, u->id);
        else if (vis[v->id] == -2)
            loop_tail[v->id] = u->id;
    }
    vis[u->id] = ocnt;
    ord[ocnt++] = u->id;
}

int intersect(int b1, int b2) {
    while (b1 != b2)
        if (b1 < b2) b1 = dom[b1];
        else b2 = dom[b2];
    return b1;
}

void calc_dominant_frontier(void) {
    int i;
    int ch = 1;
    ocnt = 0;
    memset(vis, -1, sizeof vis);
    memset(dom, -1, sizeof dom);
    memset(loop_tail, -1, sizeof loop_tail);
    dfs(entry, -1);
    dom[vis[entry->id]] = vis[entry->id];
    while (ch)
    {
        int i;
        ch = 0;
        for (i = bcnt - 2; i >= 0; i--)
        {
            int id = ord[i];
            CEdge *e = cfg.rhead[id];
            int new_idom = vis[par[id]];
            for (; e; e = e->next)
            {
                int p = e->to->id;
                if (vis[p] == new_idom) continue;
                if (dom[vis[p]] != -1)
                    new_idom = intersect(vis[p], new_idom);
            }
            if (dom[i] != new_idom)
            {
                ch = 1;
                dom[i] = new_idom;
            }
        }
    }
    for (i = 0; i < bcnt; i++)
        dfset[i] = cpset_create();
    for (i = 0; i < bcnt; i++)
        if (cfg.rhead[i] && cfg.rhead[i]->next)
        {
            CEdge *p = cfg.rhead[i];
            for (; p; p = p->next)
            {
                int runner = p->to->id;
                while (vis[runner] != dom[vis[i]])
                {
                    if (!cpset_belongs(dfset[runner], i))
                    {
                        CBList_t np = NEW(CBList);
                        np->cblk = blks[i];
                        np->next = df[runner];
                        cpset_insert(dfset[runner], i);
                        df[runner] = np;
                    }
                    runner = ord[dom[vis[runner]]];
                }
            }
        }
    for (i = 1; i < bcnt; i++)
        dtree_add_edge(blks[ord[dom[vis[i]]]], blks[i]);
}

void insert_phi(CVList_t vars) {
    CVList_t vp;
    int i;
    for (i = 0; i < bcnt; i++)
        phi[i] = cpset_create();
    for (vp = vars; vp; vp = vp->next)
    { /* for each variable */
        CVar_t var = vp->var;
        CBList_t t = var->defsite;
        CBList_t def = NULL;
        static int indef[MAX_BLOCK];
        for (t = var->defsite; t; t = t->next)
            if (++indef[t->cblk->id] == 1)
            {
                CBList_t p = NEW(CBList);
                p->cblk = t->cblk;
                p->next = def;
                def = p;
            }
        for (t = var->defsite; t; t = t->next)
            indef[t->cblk->id] = 0; /* clear */
        while (def) /* while def not empty */
        {
            CBList_t n = def, i; /* remove some node n from def */
            def = def->next;
            for (i = df[n->cblk->id]; i; i = i->next)
            {
                CBlock_t y = i->cblk;
                CPSet_t phiy = phi[y->id];
                if (!cpset_belongs(phiy, (uintptr_t)var))
                {
                    CPhi_t phi = NEW(CPhi);
                    CBList_t ndef;
                    phi->dest = copr_create();
                    phi->dest->kind = VAR;
                    phi->dest->info.var = var;
                    phi->dest->type = var->type;
                    phi->oprs = (COpr_t *)malloc(sizeof(COpr_t) * y->pred);
                    cblock_pappend(y, phi); 
                    cpset_insert(phiy, (uintptr_t)var);
                    ndef = NEW(CBList);
                    ndef->cblk = y;
                    ndef->next = def;
                    def = ndef;
                }
            }
        }
    }
    for (i = 0; i < bcnt; i++)
    {
        CBList_t p, np;
        for (p = df[i]; p; p = np)
        {
            np = p->next;
            free(p);
        }
        df[i] = NULL;
        if (phi[i]) cpset_destroy(phi[i]);
        if (dfset[i]) cpset_destroy(dfset[i]);
    }
}

void renaming_dfs(CBlock_t blk) {
    CInst_t ih = blk->insts, i;
    CPhi_t ph = blk->phis, pi;
    CEdge *e = cfg.head[blk->id];
    COList_t defl = NULL, dn;
    for (pi = ph->next; pi != ph; pi = pi->next)
    {
        COpr_t dest = pi->dest;
        CVar_t var = dest->info.var;
        COList_t n = NEW(COList), n2;
        dest->sub = var->cnt++;
/*        dest->def = ih->next; *//* the first inst */ 
        n->opr = dest;
        n->next = var->stack;
        var->stack = n;
        n2 = NEW(COList);
        n2->opr = dest;
        n2->next = defl;
        defl = n2;
    }
    for (i = ih->next; i != ih; i = i->next)
    {
        COpr_t dest = i->dest;
        COpr_t *opr[3] = {NULL, &(i->src1), &(i->src2)};
        int t;
        if (i->op == WARR)
            opr[0] = &(i->dest);
        for (t = 0; t < 3; t++)
        {
            COpr_t p;
            if (!opr[t]) continue;
            p = *(opr[t]);
            if (!(p && (p->kind == VAR || p->kind == TMP))) continue;
            /* free(p); */  /* memory leak */
            (*opr[t] = p->info.var->stack->opr)->type = p->type;
            (*opr[t])->info.var->weight++;
        }
        if (dest)
        {
            if (i->op == WARR)
                i->dest = dest->info.var->stack->opr;
            else
            {
                if (dest->kind == VAR || dest->kind == TMP)
                {
                    CVar_t var = dest->info.var;
                    COList_t n = NEW(COList), n2;
                    dest->sub = var->cnt++;
                    /* dest->def = i; */
                    n->opr = dest;
                    n->next = var->stack;
                    var->stack = n;
                    n2 = NEW(COList);
                    n2->opr = dest;
                    n2->next = defl;
                    defl = n2;
                }
            }
        }
    }
    for (; e; e = e->next) /* for each successor */
    {
        CBlock_t y = e->to;
        int j = 0;
        CEdge *pre = cfg.rhead[y->id];
        for (; pre->to != blk; pre = pre->next) j++;
        ph = y->phis;
        for (pi = ph->next; pi != ph; pi = pi->next)
        {
            if (pi->dest->kind == VAR)
                pi->oprs[j] = pi->dest->info.var->stack->opr;
            pi->oprs[j]->dep = 1;
        }
    }
    for (e = dtree.head[blk->id]; e; e = e->next)
        renaming_dfs(e->to);
    for (; defl; defl = dn)
    {
        CVar_t var = defl->opr->info.var;
        COList_t nf = var->stack->next;
        free(var->stack);
        var->stack = nf;
        dn = defl->next;
        free(defl);
    }
}

void renaming_vars(COList_t oprs) {
    COList_t p;
    for (p = oprs; p; p = p->next)
        if (p->opr->kind == VAR)
        {
            CInst_t ld = cinst_create();
            CVar_t var = p->opr->info.var;
            var->cnt = 0;
            var->reload = var->loc > 0 && var->type->type != CARR;
            ld->op = LOAD;
            ld->dest = copr_create();
            ld->dest->kind = VAR;
            ld->dest->info.var = var;
            ld->dest->type = var->type;
            cblock_pushfront(entry, ld);
        }
    renaming_dfs(entry);
}

void mark_insts(void) {
    int i, icnt = 0;
    for (i = bcnt - 1; i >= 0; i--)
    {
        CBlock_t b = blks[ord[i]];
        CInst_t ih = b->insts, ii;
        CPhi_t ph = b->phis, pi;
        for (pi = ph->next; pi != ph; pi = pi->next)
        {
            pi->dest->def = ih->next;
            icnt++;
        }
        if (cblock_isempty(b))
            b->first = b->last = icnt++;
        else
        {
            for (ii = ih->next; ii != ih; ii = ii->next)
            {
                if (ii->op != WARR && ii->dest)
                    ii->dest->def = ii;
                ii->id = icnt++;
            }
            b->first = ih->next->id;
            b->last = ih->prev->id;
        }
    }
}

CPSet_t liveset[MAX_BLOCK];
COList_t live[MAX_BLOCK];

CRange_t crange_merge(CRange_t a, CRange_t b) {
    CRange res;
    CRange_t tail = &res;
    res.next = NULL;
    res.r = -1;
    for (; a || b;)
    {
        if (a && (!b || (a->l < b->l || (a->l == b->l && a->r < b->r))))
        {
            if (tail->r >= a->l)
            {
                if (a->r > tail->r)
                    tail->r = a->r;
            }
            else
            {
                assert(tail->r < a->l);
                tail->next = a;
                tail = a;
            }
            a = a->next;
        }
        else
        {
            if (tail->r >= b->l)
            {
                if (b->r > tail->r)
                    tail->r = b->r;
            }
            else
            {
                assert(tail->r < b->l);
                tail->next = b;
                tail = b;
            }
            b = b->next;
        }
    }
    tail->next = NULL;
    return res.next;
}

void add_range_(COpr_t opr, int begin, int end) {
    CRange_t range;
    range = NEW(CRange);
    range->l = begin;
    range->r = end;
    range->next = NULL;
    opr->range = crange_merge(opr->range, range);
}

void add_range(COpr_t opr, CBlock_t blk, int end) {
    int dfid = opr->def->id;
    int begin;
    if (blk->first <= dfid && dfid <= blk->last)
        begin = dfid;
    else
        begin = blk->first;
    add_range_(opr, begin, end);
}

void build_intervals(void) {
    int i;
    for (i = 0; i < bcnt; i++)
        liveset[i] = cpset_create();
    for (i = 0; i < bcnt; i++)
    {
        int id = ord[i];
        CBlock_t b = blks[id];
        CEdge *e = cfg.head[id]; 
        CPSet_t curlive = liveset[id];
        for (; e; e = e->next)
        {
            int sid = e->to->id;
            CBlock_t s = blks[sid];
            COList_t p = live[sid];
            CPhi_t ph = s->phis, i;
            for (i = ph->prev; i != ph; i = i->prev)
            {
                CEdge *pe;
                int t;
                for (t = 0, pe = cfg.rhead[sid]; pe->to != b; pe = pe->next) t++;
                COpr_t opr = i->oprs[t];
                if (opr && 
                        (opr->kind == VAR ||
                         opr->kind == TMP) && !cpset_belongs(curlive, (uintptr_t)opr))
                {
                    COList_t np = NEW(COList);
                    np->opr = opr;
                    np->next = live[id];
                    live[id] = np;
                    cpset_insert(curlive, (uintptr_t)opr);
                }
            }
            for (; p; p = p->next)
                if (cpset_belongs(liveset[sid], (uintptr_t)p->opr) &&
                        cpset_insert(curlive, (uintptr_t)p->opr))
                {
                    COList_t np = NEW(COList);
                    np->opr = p->opr;
                    np->next = live[id];
                    live[id] = np;
                }
        }
        {
            COList_t p;
            for (p = live[id]; p; p = p->next) 
                add_range(p->opr, b, b->last + 1);
        }
        {
            CInst_t ih = b->insts, i;
            for (i = ih->prev; i != ih; i = i->prev)
            {
                int t;
                COpr_t oprs[3] = {NULL, i->src1, i->src2};
                if (i->dest &&
                    (i->dest->kind == VAR ||
                     i->dest->kind == TMP) && i->op != WARR) /* def */
                {
                    i->is_def = 1;
                    cpset_erase(curlive, (uintptr_t)i->dest);
                }
                else
                {
                    if (i->op == WARR)
                        oprs[0] = i->dest;
                    i->is_def = 0;
                }
                for (t = 0; t < 3; t++)
                {
                    COpr_t opr = oprs[t];
                    if (opr && 
                        (opr->kind == VAR ||
                         opr->kind == TMP) && !cpset_belongs(curlive, (uintptr_t)opr))
                    {
                        COList_t np = NEW(COList);
                        np->opr = opr;
                        np->next = live[id];
                        live[id] = np;
                        cpset_insert(curlive, (uintptr_t)opr);
                        add_range(opr, b, i->id);
                    }
                }
            }
        }
        {
            CPhi_t ph = b->phis, i;
            for (i = ph->prev; i != ph; i = i->prev)
                cpset_erase(curlive, (uintptr_t)i->dest);
        }
        if (loop_tail[id] != -1)
        {
            COList_t p;
            for (p = live[id]; p; p = p->next) 
                if (cpset_belongs(curlive, (uintptr_t)p->opr))
                    add_range_(p->opr, b->first, blks[loop_tail[id]]->last + 1);
        }
    }
    for (i = 0; i < bcnt; i++)
    {
        COList_t p = live[i], np;
        for (; p; p = np)
        {
            np = p->next;
            free(p);
        }
        live[i] = NULL;
        if (liveset[i]) cpset_destroy(liveset[i]);
    }
}

COpr_t cinterv_repr(COpr_t opr) {
    return opr->par == opr ? opr : (opr->par = cinterv_repr(opr->par));
}

void cinterv_union(COpr_t a, COpr_t b) {
    a = cinterv_repr(a);
    b = cinterv_repr(b);
#ifdef CIBIC_DEBUG
    fprintf(stderr, "merging ");
    copr_print(stderr, a);
    fprintf(stderr, " ");
    copr_print(stderr, b);
    fprintf(stderr, "\n");
#endif
    if (a == b) return;
    b->range = crange_merge(b->range, a->range);
    a->par = b;
    b->mod |= a->mod;
}

void init_def(void) {
    CBlock_t p;
    COList_t def;
    raw_defs = NULL;
    for (p = entry; p; p = p->next)
    {
        CInst_t i, ih = p->insts;
        CPhi_t pi, ph = p->phis;
        for (i = ih->next; i != ih; i = i->next)
            if (i->is_def)
            {
                def = NEW(COList);
                def->opr = i->dest;
                def->next = raw_defs;
                raw_defs = def;
            }
        for (pi = ph->next; pi != ph; pi = pi->next)
        {
            def = NEW(COList);
            def->opr = pi->dest;
            def->next = raw_defs;
            raw_defs = def;
        }
    }
    for (p = entry; p; p = p->next)
    {
        CPhi_t pi, ph = p->phis;
        for (pi = ph->next; pi != ph; pi = pi->next)
        {
            int i;
            for (i = 0; i < p->pred; i++)
                cinterv_union(pi->dest, pi->oprs[i]);
        }
    }
    for (def = raw_defs; def; def = def->next)
    {
        COpr_t opr = def->opr;
        opr->spill = cinterv_repr(opr);
    }
    /* coalescing */
    for (p = entry; p; p = p->next)
    {
        CInst_t i, ih = p->insts;
        for (i = ih->next; i != ih; i = i->next)
            if (i->op == MOVE && i->dest->kind == TMP &&
                    (i->src1->kind == TMP || i->src1->kind == VAR))
                cinterv_union(i->dest, i->src1);
    }
}

void colist_remove(COList_t node) {
    node->prev->next = node->next;
    node->next->prev = node->prev;
}

void colist_add(COList_t head, COList_t p) {
    (p->next = head->next)->prev = p;
    (p->prev = head)->next = p;
}

int overlap_with_beg(COpr_t i, int beg) {
    CRange_t r;
    for (r = i->range; r && r->l <= beg; r = r->next)
        if (r->r > beg) return 1;
    return 0;
}

int overlap_with_interv(COpr_t i, COpr_t cur) {
    CRange_t pi, pc;
    for (pi = i->range, pc = cur->range; pi && pc;)
    {
        if (pi->r <= pc->l) pi = pi->next;
        else if (pc->r <= pi->l) pc = pc->next;
        else return 1;
    }
    return 0;
}

int copr_comp(const void *a, const void *b) {
    return (*(COpr_t *)a)->range->l - (*(COpr_t *)b)->range->l;
}

const int avail_regs[] = {8, 9, 10, 11,
                          12, 13, 14, 15,
                          16, 17, 24, 25};

const int MAX_AVAIL_REGS = sizeof(avail_regs) / sizeof(avail_regs[0]);

void register_alloc(void) {
    /* Algorithm from the paper:
     * Linear Scan Register Allocation
     * in the Context of SSA Form and Register Constraints */
    static int freg[32], f[32];
    int dn = 0, i;
    COpr_t *unhandled;
    COList_t p;
    COList_t active = NEW(COList),
             inactive = NEW(COList);
    active->next = active->prev = active;
    inactive->next = inactive->prev = inactive;
    memset(freg, -1, sizeof freg);
    init_def();
    for (i = 0; i < MAX_AVAIL_REGS; i++)
        freg[avail_regs[i]] = 1; /* available */
    for (p = raw_defs; p; p = p->next)
    {
        COpr_t opr = p->opr;
        /*
        if (opr->info.var->loc < 0)
        {
            opr->reg = 3 - opr->info.var->loc;
            continue;
        } */ /* arguments */
        opr->reg = -2;
        if (opr->par != opr) continue;
        if (cinterv_repr(opr)->range)
        {
            opr->reg = -1;
            dn++;
        }
    }
    unhandled = (COpr_t *)malloc(dn * sizeof(COpr_t));
    i = 0;
    for (p = raw_defs; p; p = p->next)
        if (p->opr->reg == -1)
            unhandled[i++] = p->opr;
    for (i = 0; i < dn; i++)
    {
        COpr_t opr = unhandled[i];
        CRange_t r;
        for (r = opr->range; r->next; r = r->next);
        opr->begin = opr->range->l;
        opr->end = r->r;
    }
    qsort(unhandled, dn, sizeof(COpr_t), copr_comp);
    /* preparation done */
    for (i = 0; i < dn; i++)
    {
        COList_t c = NEW(COList);
        COpr_t cur;
        COList_t np;
        int reg, t;
        cur = c->opr = unhandled[i];
        /* for each interval in active */
        for (p = active->next; p != active; p = np)
        {
            COpr_t i = p->opr;
            np = p->next;
            if (i->end <= cur->begin) /* if i ends before cur.beg */
            {
                colist_remove(p);
                free(p);            /* move i to handled */
                freg[i->reg] = 1;   /* add i.reg to free */
            }
            else if (!overlap_with_beg(i, cur->begin))
            {
                colist_remove(p); 
                colist_add(inactive, p); /* move i to inactive */
                freg[i->reg] = 1;        /* add i.reg to free */
            }
        }
        /* for each interval i in inactive */
        for (p = inactive->next; p != inactive; p = np)
        {
            COpr_t i = p->opr;
            np = p->next;
            if (i->end <= cur->begin)   /* if i ends before cur.beg */
            {
                colist_remove(p);
                free(p);                /* move i to handled */
            }
            else if (overlap_with_beg(i, cur->begin))
            {
                colist_remove(p);
                colist_add(active, p);  /* move i to active */
                freg[i->reg] = 0;       /* remove i.reg from free */
            }
        }
        memmove(f, freg, sizeof f);
        /* for each interval i in inactive that overlaps cur do
         * f <- f - {i.reg} */
        for (p = inactive->next; p != inactive; p = p->next)
            if (overlap_with_interv(p->opr, cur))
                f[p->opr->reg] = 0;
        reg = -1;
        for (t = 0; t < 32; t++)
            if (f[t] > 0) { reg = t; break; }
        if (reg == -1) /* if f = {} */
        { /* assign mem loc */
            static int w[32];
            int min = 0x7fffffff;
            memset(w, 0, sizeof w);
            for (p = active->next; p != active; p = p->next)
                if (overlap_with_interv(p->opr, cur))
                    w[p->opr->reg] += p->opr->info.var->weight;
            for (p = inactive->next; p != inactive; p = p->next)
                if (overlap_with_interv(p->opr, cur))
                    w[p->opr->reg] += p->opr->info.var->weight;
            for (t = 0; t < 32; t++)
                if (f[t] != -1 && w[t] < min) min = t, reg = t;
            if (reg == -1 || cur->info.var->weight < w[reg])
            {
                cur->reg = -1;      /* assign a memory location to cur */
                free(c);            /* and move cur to handled */
            }
            else
            {
                /* move all active or inactive intervals to which r was assigned to handled
                 * assign memory locations to them */
                for (p = active->next; p != active; p = np)
                {
                    np = p->next;
                    if (p->opr->reg == reg)
                    {
                        p->opr->reg = -1;
                        colist_remove(p);
                        free(p);
                    }
                }
                for (p = inactive->next; p != inactive; p = np)
                {
                    np = p->next;
                    if (p->opr->reg == reg)
                    {
                        p->opr->reg = -1;
                        colist_remove(p);
                        free(p);
                    }
                }
                cur->reg = reg;
                colist_add(active, c); /* move cur to active */
            }
        }
        else if (cur->mod)      /* may be referenced by a pointer */
        {
            cur->reg = -1;      /* assign a memory location to cur */
            free(c);            /* and move cur to handled */
        }
        else
        {
            cur->reg = reg; /* cur.reg <- any register in f */
            freg[reg] = 0;  /* free <- free - {cur.reg} */
            colist_add(active, c); /* move cur to active */
        }
    }
    for (p = raw_defs; p; p = p->next)
    {
        COpr_t opr = p->opr;
/*        opr->spill = cinterv_repr(opr); */
        if (cinterv_repr(opr)->range)
            opr->reg = cinterv_repr(opr)->reg;
    }
    defs = NULL;
    for (i = 0; i < dn; i++)
    {
        COList_t p = NEW(COList);
        p->opr = unhandled[i];
        p->next = defs;
        defs = p;
    }
    if (!quiet)
    {
        for (i = 0; i < dn; i++)
        {
            COpr_t opr = unhandled[i];
            CRange_t p;
            copr_print(stderr, opr);
            fprintf(stderr, ":");
            for (p = opr->range; p; p = p->next)
                fprintf(stderr, " [%d, %d)", p->l, p->r);
            fprintf(stderr, " (begin: %d, end: %d, weight: %d, reg: %d)\n",
                    opr->begin, opr->end, opr->info.var->weight, opr->reg);
        }
    }
    free(unhandled);
}

void const_propagation(void) {
#define IS_STATIC(opr) (!((opr)->mod || (opr)->info.var->reload))
    int i;
    for (i = bcnt - 1; i >= 0; i--)
    {
        CBlock_t b = blks[vis[i]];
        CInst_t i, ni, ih = b->insts;
        for (i = ih->next; i != ih; i = ni)
        {
            int flag = 0;
            COpr_t t;
            if (i->op == ADDR)
                i->src1->mod = 1;
            if (i->src1 && i->src1->cval && IS_STATIC(i->src1))
            {
                t = i->src1->cval;
                i->src1 = copr_create();
                *(i->src1) = *t;
            }
            if (i->src2 && i->src2->cval && IS_STATIC(i->src2))
            {
                t = i->src2->cval;
                i->src2 = copr_create();
                *(i->src2) = *t;
            }
            if (!i->dest) 
            {
                ni = i->next;
                continue;
            }
            if (i->src1 && i->src1->kind == IMM)
            {
                if (i->op == MOVE)
                    flag = 1;
                else if (i->op == NEG)
                {
                    i->op = MOVE;
                    i->src1->info.imm *= -1;
                    flag = 1;
                }
                else if (i->src2 && i->src2->kind == IMM)
                {
                    COpr_t c;
                    int immd;
                    int imm1 = i->src1->info.imm;
                    int imm2 = i->src2->info.imm;
                    flag = 1;
                    switch (i->op)
                    {
                        case MUL: immd = imm1 * imm2; break;
                        case DIV: immd = imm1 / imm2; break;
                        case MOD: immd = imm1 % imm2; break;
                        case ADD: immd = imm1 + imm2; break;
                        case SUB: immd = imm1 - imm2; break;
                        case SHL: immd = imm1 << imm2; break;
                        case SHR: immd = imm1 >> imm2; break;
                        case AND: immd = imm1 & imm2; break;
                        case XOR: immd = imm1 ^ imm2; break;
                        case OR: immd = imm1 | imm2; break;
                        case NOR: immd = ~(imm1 | imm2); break;
                        case EQ: immd = imm1 == imm2; break;
                        case NE: immd = imm1 != imm2; break;
                        case LT: immd = imm1 < imm2; break;
                        case GT: immd = imm1 > imm2; break;
                        case LE: immd = imm1 <= imm2; break;
                        case GE: immd = imm1 >= imm2; break;
                        default: flag = 0;
                    }
                    if (flag)
                    {
                        c = copr_create();
                        c->kind = IMM;
                        free(i->src1);
                        free(i->src2);
                        i->op = MOVE;
                        i->src1 = c;
                        c->info.imm = immd;
                    }
                }
            }
            ni = i->next;
            if (flag)
            {
                i->dest->cval = i->src1;
                if (i->dest->kind == TMP && !i->dest->dep)
                {
                    i->next->prev = i->prev;
                    i->prev->next = i->next;
                    free(i);
                }
            }
        }
    }
}

void strength_reduction(void) {
#define SWAP_IMM \
    do { \
        if (i->src1->kind == IMM) \
        { \
            COpr_t t = i->src1; \
            i->src1 = i->src2; \
            i->src2 = t; \
        } \
    } while (0)

#define PRINT_BARE_STRING \
    do { \
        if (bp != buff) \
        { \
            CInst_t print = cinst_create(); \
            CSList_t cstr = NEW(CSList); \
            print->op = CALL; \
            print->dest = copr_create(); \
            print->dest->kind = TMP; \
            print->dest->info.var = ctmp_create(); \
            print->dest->type = i->dest->type; \
            print->src1 = copr_create(); \
            print->src1->kind = IMMF; \
            *bp = '\0'; \
            print->src1->kind = IMMF; \
            cstr->str = strdup(buff);  \
            (cstr->prev = cstrs->prev)->next = cstr; \
            (cstr->next = cstrs)->prev = cstr; \
            cstr->id = cstr->prev->id + 1; \
            inst = cinst_create(); \
            inst->op = PUSH; \
            inst->sysp = 1; \
            inst->src1 = copr_create(); \
            inst->src1->kind = IMMS; \
            inst->src1->info.cstr = cstr; \
            cblock_append(ibuff, inst); \
            print->src1->info.str = "__print_string"; \
            cblock_append(ibuff, print); \
            bp = buff; \
        } \
    } while (0)

    int i;
    int call_cnt = 0;
    for (i = bcnt - 1; i >= 0; i--)
    {
        CBlock_t b = blks[vis[i]];
        CInst_t i, ni, ih = b->insts;
        for (i = ih->next; i != ih; i = ni)
        {
            ni = i->next;
            switch (i->op)
            {
                case ADD:
                    SWAP_IMM;
                case SUB:
                    if (i->src2->kind == IMM && !i->src2->info.imm)
                    {
                        i->op = MOVE;
                        i->src2 = NULL;
                    }
                    break;
                case MUL:
                    SWAP_IMM;
                    if (i->src2->kind == IMM)
                    {
                        int p = 0, n = i->src2->info.imm;
                        while (!(n & 1)) n >>= 1, p++;
                        if (n == 1)
                        {
                            i->op = SHL;
                            i->src2 = copr_create();
                            i->src2->kind = IMM;
                            i->src2->info.imm = p;
                        }
                    } 
                    break;
                case DIV:
                    SWAP_IMM;
                    if (i->src2->kind == IMM)
                    {
                        int p = 0, n = i->src2->info.imm;
                        while (!(n & 1)) n >>= 1, p++;
                        if (n == 1)
                        {
                            i->op = SHR;
                            i->src2 = copr_create();
                            i->src2->kind = IMM;
                            i->src2->info.imm = p;
                        }
                    } 
                    break;
                case PUSH:
                    call_cnt++;
                    break;
                case CALL:
                    if (i->src1->kind == IMMF &&
                        !strcmp(i->src1->info.str, "printf"))
                    {
                        static char buff[1024];
                        char *bp = buff, *sp;
                        CInst_t push = i, inst,  phead, np;
                        CBlock_t ibuff;
                        CSList_t fmt;
                        /* move to the first push */
                        while (call_cnt--) push = push->prev;
                        if (push->src1->kind != IMMS) break; /* not a const pattern */
                        ibuff = cblock_create(0);
                        fmt = push->src1->info.cstr;
                        push->prev->next = i->next;
                        i->next->prev = push->prev;
                        phead = push->prev;
                        np = push->next;
                        free(push);
                        push = np;
                        for (sp = fmt->str; *sp != '\0'; sp++)
                        {
                            if (*sp == '%')
                            {
                                CInst_t print = cinst_create();
                                print->op = CALL;
                                print->dest = copr_create();
                                print->dest->kind = TMP;
                                print->dest->info.var = ctmp_create();
                                print->dest->type = i->dest->type;
                                print->src1 = copr_create();
                                print->src1->kind = IMMF;
                                PRINT_BARE_STRING;
                                switch (*(++sp))
                                {
                                    case 'd': 
                                        {
                                            if (push != i)
                                            {
                                                np = push->next;
                                                push->sysp = 1;
                                                cblock_append(ibuff, push);
                                                push = np;
                                            }
                                            print->src1->info.str = "__print_int";
                                            cblock_append(ibuff, print);
                                        }
                                        break;
                                    case 'c':
                                        {
                                            if (push != i)
                                            {
                                                np = push->next;
                                                push->sysp = 1;
                                                cblock_append(ibuff, push);
                                                push = np;
                                            }
                                            print->src1->info.str = "__print_char";
                                            cblock_append(ibuff, print);
                                        }
                                        break;
                                    case 's':
                                        {

                                            if (push != i)
                                            {
                                                np = push->next;
                                                push->sysp = 1;
                                                cblock_append(ibuff, push);
                                                push = np;
                                            }
                                            print->src1->info.str = "__print_string";
                                            cblock_append(ibuff, print);
                                        }
                                        break;
                                    default:
                                        {
                                            CSList_t cstr = NEW(CSList);
                                            cstr->str = strdup(sp - 1); 
                                            (cstr->prev = cstrs->prev)->next = cstr;
                                            (cstr->next = cstrs)->prev = cstr;
                                            cstr->id = cstr->prev->id + 1;
                                            inst = cinst_create();
                                            inst->op = PUSH;
                                            inst->src1 = copr_create();
                                            inst->src1->kind = IMMS;
                                            inst->src1->info.cstr = cstr;
                                            cblock_append(ibuff, inst);
                                            for (; push != i; push = np)
                                            {
                                                np = push->next;
                                                cblock_append(ibuff, push);
                                            }
                                            print->src1->info.str = "printf";
                                            cblock_append(ibuff, print);
                                            sp = fmt->str + strlen(fmt->str) - 1;
                                        }
                                        break;
                                }
                            }
                            else *bp++ = *sp;
                        }
                        PRINT_BARE_STRING;
                        fmt->prev->next = fmt->next;
                        fmt->next->prev = fmt->prev;
                        free(fmt);
                        (i->next->prev = ibuff->insts->prev)->next = i->next;
                        (phead->next = ibuff->insts->next)->prev = phead;
                        free(ibuff);
                    }
                    call_cnt = 0;
                    break;
                default: ;
            }
        }
    }
}

unsigned int copr_hash(COpr_t opr) {
    if (!opr) return 0;
    switch (opr->kind)
    {
        case VAR:
        case TMP:
            return (unsigned long)opr;
        default:
            return (unsigned int)opr->info.imm;
    }
}

int copr_eq(COpr_t a, COpr_t b) {
    if (a->kind != b->kind) return 0;
    switch (a->kind)
    {
        case VAR:
        case TMP:
            return a == b;
        case IMM:
            return a->info.imm == b->info.imm;
        case IMMS:
            return a->info.cstr == b->info.cstr;
        case IMMF:
            return !strcmp(a->info.str, b->info.str);
        default:
            return 0;
    }
}

unsigned int cexpmap_hash(CInst_t exp) {
    unsigned int res = 0;
    res = ((unsigned int)exp->op) << 10;
    res ^= copr_hash(exp->src1);
    res = (res << 1) ^ copr_hash(exp->src2);
    return res;
}

CExpMap_t cexpmap_create(void) {
    CExpMap_t res = NEW(CExpMap);
    memset(res->head, 0, sizeof res->head);
    return res;
}

CExpMap_t cexpmap_dup(CExpMap_t cem) {
    int i;
    CExpMap_t res = cexpmap_create();
    for (i = 0; i < MAX_TABLE_SIZE; i++)
    {
        CENode *p, *t;
        for (p = cem->head[i]; p; p = p->next)
        {
            t = NEW(CENode);
            t->exp = p->exp;
            t->next = res->head[i];
            res->head[i] = t;
        }
    }
    return res;
}

int cexpmap_comp(CInst_t exp1, CInst_t exp2) {
    if (exp1->op != exp2->op) return 0;
    if (!copr_eq(exp1->src1, exp2->src1)) return 0;
    return copr_eq(exp1->src2, exp2->src2);
}

void cexpmap_insert(CExpMap_t cem, CInst_t exp) {
    int hv = cexpmap_hash(exp) % MAX_TABLE_SIZE;
    CENode *np = NEW(CENode);
    np->exp = exp;
    np->next = cem->head[hv];
    cem->head[hv] = np;
}

CInst_t cexpmap_lookup(CExpMap_t cem, CInst_t exp) {
    int hv = cexpmap_hash(exp) % MAX_TABLE_SIZE;
    CENode *p;
    for (p = cem->head[hv]; p; p = p->next)
        if (cexpmap_comp(p->exp, exp))
            return p->exp;
    return NULL;
}

void cexpmap_clear(CExpMap_t cem) {
    int i;
    CENode *p, *np;
    for (i = 0; i < MAX_TABLE_SIZE; cem->head[i++] = NULL)
        for (p = cem->head[i]; p; p = np)
        {
            np = p->next;
            free(p);
        }
}

void cexpmap_destroy(CExpMap_t cem) {
    cexpmap_clear(cem);
    free(cem);
}
void copr_shortcut(COpr_t *opr) {
    COpr_t t = *opr;
    if (!t) return;
    t = t->same;
    if (t->kind == TMP)
        *opr = t->same;
}

void subexp_elimination_(CBlock_t b, CExpMap_t cem) {
    CInst_t i, ih = b->insts, ni;
    CEdge *e;
    for (i = ih->next; i != ih; i = ni)
    {
        CInst_t t;
        ni = i->next;
        if (i->op == MOVE)
        {
            i->dest->same = i->src1->same;
            continue;
        }
        else if (i->op == CALL)
        {
            cexpmap_clear(cem);
            continue;
        }
        copr_shortcut(&i->src1);
        copr_shortcut(&i->src2);
        t = cexpmap_lookup(cem, i);
        if (t)
        {
            i->op = MOVE;
            i->src1 = t->dest;
            i->src2 = NULL;
            i->dest->same = i->src1;
        }
        else
        {
            switch (i->op)
            {
                case MUL: case DIV: case MOD: case ADD: case SUB:
                case SHL: case SHR: case AND: case XOR: case OR: case NOR: 
                case EQ: case NE: case LT: case GT: case LE: case GE:
                case NEG:
                    if (i->dest->kind == TMP) cexpmap_insert(cem, i);
                    break;
                default: ;
            }
        }
    }
    for (e = dtree.head[b->id]; e; e = e->next)
    {
        CExpMap_t scem = cexpmap_dup(cem);
        subexp_elimination_(e->to, scem);
        cexpmap_destroy(scem);
    }
}

void subexp_elimination(void) {
    CExpMap_t cem = cexpmap_create();
    subexp_elimination_(entry, cem);
    cexpmap_destroy(cem);
}

void deadcode_elimination() {
    int i;
    for (i = bcnt - 1; i >= 0; i--)
    {
        CBlock_t b = blks[vis[i]];
        CInst_t i, ih = b->insts;
        for (i = ih->next; i != ih; i = i->next)
        {
            if (i->src1) i->src1->dep = 1;
            if (i->src2) i->src2->dep = 1;
            if (i->op == WARR && i->dest) i->dest->dep = 1;
        }
    }
    for (i = bcnt - 1; i >= 0; i--)
    {
        CBlock_t b = blks[vis[i]];
        CInst_t i, ih = b->insts;
        for (i = ih->next; i != ih; i = i->next)
            if (i->op != CALL && i->dest && i->dest->kind == TMP && !i->dest->dep)
            {
                i->next->prev = i->prev;
                i->prev->next = i->next;
                free(i);
            }
    }
}

void ssa_func(CType_t func) {
#define OPRS_ADD(_opr) \
    do { \
        if (cpset_insert(avs, (uintptr_t)((_opr)->info.var))) \
        { \
            COList_t n = NEW(COList); \
            n->next = oprs; \
            n->opr = _opr; \
            oprs = n; \
        } \
    } while (0)

#define VS_ADD(_d) \
    do { \
        if (cpset_insert(vs, (uintptr_t)(_d))) \
        { \
            CVList_t n = NEW(CVList); \
            n->next = vars; \
            n->var = _d; \
            vars = n; \
        } \
    } while (0)

    CBlock_t p;
    entry = cblock_create(1);
    CPSet_t vs = cpset_create(), avs = cpset_create();
    CVList_t vars = NULL;
    COList_t oprs = NULL;
    /* CVar_t pr; */
    cfg_clear();
    dtree_clear();
    ssa_comp(func->rec.func.body, entry, NULL);
    /*
    for (i = 0, pr = func->rec.func.params;
        i < 4 && pr;
        i++, pr = pr->next)
        pr->loc = -(i + 1); */ /* mark arguments */

    for (p = entry; p; p = p->next)
    {
        CInst_t head = p->insts, i;
        CEdge *e;
        p->pred = 0;
        for (e = cfg.rhead[p->id]; e; e = e->next)
            p->pred++;
        for (i = head->next; i != head; i = i->next)
        {
            if (i->src1 && (i->src1->kind == VAR || i->src1->kind == TMP))
                OPRS_ADD(i->src1);
            if (i->src2 && (i->src2->kind == VAR || i->src2->kind == TMP))
                OPRS_ADD(i->src2);
            if (i->op == WARR)
                OPRS_ADD(i->dest);
            else if (i->dest && i->dest->kind == VAR)
            {
                CVar_t d = i->dest->info.var;
                CBList_t b = NEW(CBList);
                VS_ADD(d);
                OPRS_ADD(i->dest);
                b->next = d->defsite;
                b->cblk = p;
                d->defsite = b;
            }
        }
        blks[p->id] = p;
    }
    cpset_destroy(avs);
    cpset_destroy(vs);
    calc_dominant_frontier();
    /* build SSA */
    insert_phi(vars);
    renaming_vars(oprs);
    /* optimization on SSA */
    const_propagation();
    subexp_elimination();
    const_propagation();
    strength_reduction();
    deadcode_elimination();
    /* out of SSA */
    mark_insts();
    build_intervals();
    register_alloc();
}