版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lidan113lidan/article/details/119986661

更多内容可关注微信公众号 

一、gimple高端化

前面提到整个编译单元所有外部声明的AST树节点生成完毕后会遍历符号表中的所有符号节点进行分析,对于函数节点最终会执行到 cgraph_node::analyze对其进行gimple高端化和低端化分析,其中 gimple高端化是通过函数gimplify_function_tree(decl)来完成的,此函数用来分析一个具体的函数声明节点,其代码大体如下:
/*此函数负责整个gimple高端化,其创建了一个gbind节点,其中:.body记录当前函数形参和函数体gimplie高端化生成的语句序列.vars记录gimplify过程中编译器自动生成的和函数体内显示声明的所有变量声明节点.block记录函数体的tree_block最终此gbind节点被记录到fndecl->function->gimple_body中,且同时清空其AST树节点指针(fndecl->saved_tree),代表从此此函数只能继续通过gimple指令序列来分析,而不能通过AST树节点来分析了.
*/
void gimplify_function_tree (tree fndecl)
{gimple_seq seq;gbind *bind;bind = gimplify_body (fndecl, true);          /* gimplify函数参数链表和函数体,最终返回一个gbind结果记录此函数所有语义 */seq = NULL;gimple_seq_add_stmt (&seq, bind);             /* 在seq指令序列中加入此gbind节点,实际上最终seq = bind */gimple_set_body (fndecl, seq);                /* gimplify_body返回的gbind节点记录了当前函数AST树节点gimple高端化的结果,此结果被记录到fndecl->function->gimple_body 中 */if (flag_instrument_function_entry_exit && ... ) { ... }    /* 若命令行指定了-finstrument-functions,则会为此函数进行gimple级别的插桩 */DECL_SAVED_TREE (fndecl) = NULL_TREE;         /* gimplify_body生成的gbind节点代表了此函数AST树节点的所有语义,后续AST树节点不会再被使用,这里清空 */dump_function (TDI_gimple, fndecl);           /* 将函数信息dump到 TDI_gimple对应的文件中去(通常是*.c.005t.gimple) */
}

在源码解析->AST树节点后,一个AST树节点即可代表源码中此外部声明的所有语义,不论是函数中代码的顺序执行、分支、循环, 在树节点中都可以表示出来.

源码本质上是顺序的,其转化为的树节点默认是需要一种遍历顺序才可以与源码对应的, gimple高端化的过程实际上就是通过深度优先算法(自上而下,从左到右)遍历AST树节点,将树节点中的参数链表,语句链表解析为一个顺序的gimple指令序列. 解析后的gimple指令序列是一个一维的序列,但更严谨的说应该是一个趋于一维的指令序列,因为此时 gimple指令序列中还存在类似gbind的节点,一般的gimple节点仅仅代表一个语句,而一个gbind节点又包括一个子语句序列,故存在gbind等节点时不能认为其完全是一维的。


二、gimplify_body

gimplify_body实现了函数参数列表和函数体的gimple高端化,其代码如下:
gbind * gimplify_body (tree fndecl, bool do_parms)
{gimple_seq parm_stmts, parm_cleanup = NULL, seq;gimple *outer_stmt;   gbind *outer_bind;init_tree_ssa (cfun);            /* 此函数主要初始化 function->gimple_df结构体,gimplify过程中生成的SSA_NAME节点都保存在 gimple_df.ssanames中 */push_gimplify_context (true);     /* 初始化一个 gimplify上下文,保存在 全局变量gimplify_ctxp中, 在gimplify过程中新生成的变量节点等都会临时记录在这里 */parm_stmts = do_parms ? gimplify_parameters (&parm_cleanup) : NULL;    /* 若需要,则这里gimplify 函数的参数链表, 此过程中若产生了gimple指令序列则记录到parm_stmts中 */seq = NULL;/* 此函数负责gimplify 函数的函数体节点,函数体节点通常是一个 BIND_EXPR,代表函数{}内的定义,但对于main函数来说这里是一个statement_list,不论是什么都属于statement,故通过 gimplify_stmt来解析.函数体的解析结果最终形成了一个gimple指令序列,此指令序列通过seq返回(通常是gbind节点),而解析过程中编译器新创建的变量则记录在全局变量gimplify_ctxp->temps中.*/gimplify_stmt (&DECL_SAVED_TREE (fndecl), &seq);    outer_stmt = gimple_seq_first_stmt (seq);                      /* 获取seq指令序列中第一条指令节点(是一个gimple的指针) */if (gimple_code (outer_stmt) == GIMPLE_BIND                    /* 若当前outer_stmt本身已经是 gbind节点了,则outer_bind直接使用outer_stmt */&& gimple_seq_first (seq) == gimple_seq_last (seq))outer_bind = as_a <gbind *> (outer_stmt);elseouter_bind = gimple_build_bind (NULL_TREE, seq, NULL);        /* 否则新创建一个gbind节点包裹原有的指令序列(如main函数) *//* 清空 fndecl.saved_tree 也就是生成gbind节点后清除了原有AST树节点的指针,后续针对此函数的分析都应该基于此gbind节点, 这也是cgraph_node::analyze时是否执行gimple高端化的判断依据 */DECL_SAVED_TREE (fndecl) = NULL_TREE;                            /* 若解析函数参数链表时产生了gimple语句,则此语句添加到outer_bind这个gbind节点的.body的最前面, gimplify之后 gbind.body记录了此block中生成的所有gimple的语句序列 */if (!gimple_seq_empty_p (parm_stmts)) { ... }/* pop 整个 gimplify上下文,主要将gimplify过程中生成的所有临时变量都保存到gbind->vars中(此时outer_bind必然是一个gbind节点) */pop_gimplify_context (outer_bind);return outer_bind;    /* 返回最终的 gbind节点 */
}

gimplify_body可以总结为3步:

  1. gimplify参数链表
  2. gimplify函数体
  3. 将此过程中生成的所有gimple指令序列和临时变量记录到一个gbind节点中并返回

三、gimplify_stmt

gimplify_stmt负责一个语句的gimplify,此函数定义如下:
bool gimplify_stmt (tree *stmt_p, gimple_seq *seq_p)
{gimple_seq_node last;last = gimple_seq_last (*seq_p);    /* 获取gimple指令序列 seq_p中最后一条gimple指令地址 */gimplify_expr (stmt_p, seq_p, NULL, is_gimple_stmt, fb_none);    /* gimplify stmt_p代表的语句,其返回结果顺序写入seq_p指令序列的末尾 */return last != gimple_seq_last (*seq_p);    /* 若seq_p指令序列中新增了语句则返回true,未新增则返回false */
}

gimplify_stmt的逻辑很简单,但需要注意的是在AST中实际上并没有一个statement节点类型,而判断一个AST节点是否是statement(语句)的标准则是is_gimple_stmt函数,此函数就是个swith case, 此函数中判定为语句(statement)的节点包括:

  1. 非空的NOP_EXPR节点
  2. 非VOID类型的BIND_EXPR/COND_EXPR节点
  3. SWITCH_EXPR/GOTO_EXPR/RETURN_EXPR/LABEL_EXPR/.../ASM_EXPR/STATEMENT_LIST/CALL_EXPR/MODIFY_EXPR /PREDICT_EXPR节点

四、gimplify_expr

所有节点都被认为是表达式,故gimplify_expr也是gimple高端化中最常用的一个函数,此函数逻辑比较复杂,其定义如下:

/*此函数用来分析expr_p代表的一个表达式树(包括递归分析其所有的子树),分析完毕后expr_p代表的表达式生成的所有gimple指令序列都会被添加到pre_p和post_p中,如果expr_p代表的表达式自身是有返回值,那么返回值最终会写回到expr_p中(见后)* 如果当前分析的是一条语句(statement),或者调用gimplify_expr时显示指定不需要返回值(fb_none,目前看到的只有statment会传入fb_none),那么expr_p被设置为NULL_TREE返回即可,当前解析不返回返回值.* 如果当前gimplify_expr需要返回值,且当前解析的非语句(statment):- 若解析完毕后的表达式结果 expr_p满足gimple_test_f这个条件,则直接返回*expr_p作为返回值即可.- 若解析完毕后的表达式结果 expr_p不满足gimple_test_f条件:-- 若当前需要返回一个左值(fb_lvalue),且 expr_p是可获取地址的,则构建一个对expr_p的MEM_REF引用节点并返回.-- 若当前需要返回一个右值(fb_rvalue),且 expr_p是可作为右值的节点,则构建一个临时变量(或SSA_NAME)来记录当前的右值并返回.-- 其他情况均报错
*/
enum gimplify_status gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tree), fallback_t fallback)
{tree tmp;gimple_seq internal_pre = NULL;gimple_seq internal_post = NULL;tree save_expr;bool is_statement;location_t saved_location;enum gimplify_status ret;gimple_stmt_iterator pre_last_gsi, post_last_gsi;tree label;save_expr = *expr_p;        /* 若expr_p传入一个空的AST树节点,则直接返回 GS_ALL_DONE,代表当前expr_p表达式树节点gimplify完毕 */if (save_expr == NULL_TREE) return GS_ALL_DONE;    is_statement = gimple_test_f == is_gimple_stmt;    /* 记录当前分析的是否是一个语句表达式 */if (pre_p == NULL) pre_p = &internal_pre;if (post_p == NULL) post_p = &internal_post;do{save_expr = *expr_p;    /* 再次记录当前正在处理的表达式的AST树节点 */switch (TREE_CODE (*expr_p))    /* 根据不同的TREE_CODE,执行不同的 gimplify转换 */{case POSTINCREMENT_EXPR:    /* ++, -- 表达式的转换 */......ret = gimplify_self_mod_expr (expr_p, pre_p, post_p, fallback != fb_none, TREE_TYPE (*expr_p));break;......case CALL_EXPR:    /* 函数调用的gimplify转换, 如函数体中解析printf的调用会走到这里 */ret = gimplify_call_expr (expr_p, pre_p, fallback != fb_none);/* CALL_EXPR的执行结果肯定是一个右值,而如果此时需要返回一个左值节点,那么肯定是要构建一个临时变量记录CALL_EXPR的执行结果的. */if (fallback == fb_lvalue){*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p, false);mark_addressable (*expr_p);ret = GS_OK;}break;case COMPOUND_EXPR:    /* 复合表达式的转换 */ret = gimplify_compound_expr (expr_p, pre_p, fallback != fb_none);break;case MODIFY_EXPR:    /* 赋值表达式的转换, 如x=1;就是个MODIFY_EXPR; int x=1;就是个INIT_EXPR,此外临时变量的初始化也会新增INIT_EXPR */case INIT_EXPR:ret = gimplify_modify_expr (expr_p, pre_p, post_p,fallback != fb_none);break;case ADDR_EXPR:         /* 地址表达式的处理(一个 CALL_EXPR 节点的op[1]记录其调用的函数,而这个函数记录通常会在一个ADDR_EXPR中) */ret = gimplify_addr_expr (expr_p, pre_p, post_p);break;case NOP_EXPR:    /* 代表类型转换的表达式 */case CONVERT_EXPR:......ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p, is_gimple_val, fb_rvalue);recalculate_side_effects (*expr_p);break;case INDIRECT_REF:      /* 间接引用的处理 */......case MEM_REF:           /* 内存引用的处理 */......case INTEGER_CST:       /* 常量表达式的处理 */case REAL_CST:case FIXED_CST:case STRING_CST:case COMPLEX_CST:case VECTOR_CST:       if (TREE_OVERFLOW_P (*expr_p))  *expr_p = drop_tree_overflow (*expr_p);ret = GS_ALL_DONE;break;case CONST_DECL:        /* 常量声明的处理 */if (fallback & fb_lvalue) ret = GS_ALL_DONE;else{*expr_p = DECL_INITIAL (*expr_p); ret = GS_OK;}break;case DECL_EXPR:         /* 声明表达式的处理, block中的声明会以声明表达式的形式出现在语句列表中,如int x;*/ret = gimplify_decl_expr (expr_p, pre_p);break;case BIND_EXPR:        /* BIND_EXPR的转换(也就是block的转换) */ret = gimplify_bind_expr (expr_p, pre_p);break;case LOOP_EXPR:         /* 循环表达式的处理 */ret = gimplify_loop_expr (expr_p, pre_p);break;case SWITCH_EXPR:       /* switch的处理 */ret = gimplify_switch_expr (expr_p, pre_p);break;case EXIT_EXPR:ret = gimplify_exit_expr (expr_p);break;case GOTO_EXPR:         /* goto表达式的处理 */......gimplify_seq_add_stmt (pre_p, gimple_build_goto (GOTO_DESTINATION (*expr_p)));ret = GS_ALL_DONE;break;case PREDICT_EXPR:      /* 分支预测 */gimplify_seq_add_stmt (pre_p, gimple_build_predict (PREDICT_EXPR_PREDICTOR (*expr_p), PREDICT_EXPR_OUTCOME (*expr_p)));ret = GS_ALL_DONE;break;case LABEL_EXPR:        /* label表达式处理函数 */ret = gimplify_label_expr (expr_p, pre_p);label = LABEL_EXPR_LABEL (*expr_p);......break;case CASE_LABEL_EXPR:   /* case表达式处理函数 */ret = gimplify_case_label_expr (expr_p, pre_p);if (gimplify_ctxp->live_switch_vars) asan_poison_variables (gimplify_ctxp->live_switch_vars, false, pre_p);break;case RETURN_EXPR:       /* 返回表达式的处理, 如一个return 0; */ret = gimplify_return_expr (*expr_p, pre_p);break;case CONSTRUCTOR:       /* 构造函数的处理 */............case ASM_EXPR:                  /* ASM表达式的处理 */ret = gimplify_asm_expr (expr_p, pre_p, post_p);break;......case STATEMENT_LIST:            /* 语句链表的处理,如处理到一个{}后,里面会出现一个语句链表 */ret = gimplify_statement_list (expr_p, pre_p);break;case VAR_DECL:case PARM_DECL:         /* 变量或参数声明节点表达式的处理 */ret = gimplify_var_or_parm_decl (expr_p);    /* 这里基本直接返回 GS_ALL_DONE即可,不需要做什么处理 */break;case RESULT_DECL:       /* 返回值声明节点的处理 */if (gimplify_omp_ctxp) omp_notice_variable (gimplify_omp_ctxp, *expr_p, true);ret = GS_ALL_DONE;break;case SSA_NAME:          /* SSA_NAME */ret = GS_ALL_DONE;break;......default:                /* 其他的case都在这里处理 */switch (TREE_CODE_CLASS (TREE_CODE (*expr_p))){case tcc_comparison:    /* 比较类表达式的处理 */......case tcc_unary:        /* 单目运算的处理(实际上就是gimplify单目运算对应的树节点,然后返回一个右值) */ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p, is_gimple_val, fb_rvalue);break;case tcc_binary:       /* 双目运算的处理 */......break;case tcc_declaration:  /* 其他声明和常量节点均不处理 */case tcc_constant:ret = GS_ALL_DONE;goto dont_recalculate;default:                /* 其他case报错 */gcc_unreachable ();}recalculate_side_effects (*expr_p);dont_recalculate:break;}} while (ret == GS_OK);  /* 如果处理结果返回的是GS_OK,那么需要循环再次处理; 如果返回GS_ALL_DONE则代表处理完毕 *//* 如果当前解析过程不需要返回值,而解析结果(重写入expr_p中了) 还是个有返回值的非语句节点,那么递归解析此节点后expr_p返回NULL_TREE */if (fallback == fb_none && *expr_p && !is_gimple_stmt (*expr_p)){......*expr_p = NULL;}if (fallback == fb_none || is_statement)    /* 若当前正在处理一条语句,或当前调用没有要求返回值 */{*expr_p = NULL_TREE;     /* 直接置空 expr_p */goto out;}/* 到这里说明当前解析需要返回值,且当前解析的不是一个statement语句, 此时若当前解析的返回值(expr_p)满足gimple_test_f测试条件则直接返回(前提是没有post_p)*/if (gimple_seq_empty_p (internal_post) && (*gimple_test_f) (*expr_p))goto out;/*如果表达式要求返回一个左值节点,且当前表达式又是可以被获取地址的(如VAR_DECL)这个case编译了部分kernel都没看到过..先pass这里也说明能为其生成左值节点的表达式必须满足 is_gimple_addressable*//* 如当前需要返回一个左值节点,且返回的结果表达式(expr_p)是可获取地址的(is_gimple_addressable),则为其构建一个内存引用节点(MEM_REF)作为左值并返回 */if ((fallback & fb_lvalue) && gimple_seq_empty_p (internal_post) && is_gimple_addressable (*expr_p)){ ......*expr_p = build2 (MEM_REF, ref_type, tmp, build_zero_cst (ref_alias_type));}/* 若当前需要返回一个右值节点,且返回的结果表达式(expr_p)可以作为右值(is_gimple_reg_rhs_or_call),则将expr_p的当前值赋值给一个临时变量记录expr_p的当前值,并返回此临时变量节点到expr_P作为新的右值 */else if ((fallback & fb_rvalue) && is_gimple_reg_rhs_or_call (*expr_p)){......*expr_p = get_formal_tmp_var (*expr_p, pre_p);}else {     /* 其他情况均错误  */ret = GS_ERROR;goto out;} gcc_assert ((*gimple_test_f) (*expr_p));            /* 确保返回值节点是满足 gimple_test_f的 */if (!gimple_seq_empty_p (internal_post))        /* 将最后的post加到 pre_p指令序列中,并为这些指令添加源码位置 */{annotate_all_with_location (internal_post, input_location);gimplify_seq_add_seq (pre_p, internal_post);}out:return ret;
}

gimplify_expr做为表达式gimple高端化的case分配函数,其中包含了各种表达式case的处理方式,而此函数的整体逻辑就是gimplify  expr_p指向的表达式,此过程中生成的指令序列全部记录到pre_p gimple语句序列中; 若表达式最终有返回值则重新返回到expr_p中,解析状态以函数返回值的形式返回. 这里需要注意的是关于expr_p这个返回值节点:

  • 若当前gimplify_expr分析的是一条语句(statement),或者调用时没有要求返回值(fb_none)(目前看到二者是同时出现的,没有单独出现的case), 那么最终即使解析完毕后*expr_p非空,则也要将其置空后返回.
  • 若当前gimplify_expr分析的不是语句且要求了返回值:
    • 若解析后的expr_p本身就满足 gimple_test_f,那么直接返回expr_p即可.
    • 若解析后的expr_p不满足gimple_test_f:
      • 若当前要求返回一个左值(fb_lvalue),且expr_p节点可以被获取到地址,则构建一个对expr_p的MEM_REF引用并返回.
      • 若当前要求返回一个右值(fb_rvalue),且expr_p可作为一个右值节点,则构建一个临时变量(或SSA_NAME)记录expr_p的当前值作为右值返回.
      • 其他情况均报错.

后续的代码则依次分析几个gimplify_expr中主要表达式的处理流程


五、gimplify_bind_expr

在gimplify_expr中,若解析到BIND_EXPR,则会调用gimplify_bind_expr函数,其定义大致如下:

gimplify_status gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tr`ee), fallback_t fallback)
{......case ADDR_EXPR:         ret = gimplify_addr_expr (expr_p, pre_p, post_p);break;......
}struct gbind : public gimple    /* gbind继承自gimple类型,此外只额外多了三个元素 */
{tree vars;                /* vars 正常继承自 block.vars, 而如果当前block是一个函数的函数体(而不是嵌套subblock)时, 函数gimplify完毕后会将当前block和subblock中所有动态生成的变量都加入到这里面(gimplify_ctxp->temps),这里会加入subblock的原因是因为栈分配时在函数入口出口完成的,故subblock的分配也算在这里 */tree block;               /* 当前gbind 所对应的那个{}对应的block结构体 */gimple_seq body;          /* 当前gbind对应的{}内部语句链表解析出的gimple 语句序列 */
};enum gimplify_status gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
{tree bind_expr = *expr_p;tree t;gbind *bind_stmt;/* 遍历BIND_EXPR所代表的的当前block的变量声明链表(block.vars) */for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t)) {if (VAR_P (t)) {DECL_SEEN_IN_BIND_EXPR_P (t) = 1;     /* 标记已经看到了当前变量,此值主要是为了区分后续动态创建的变量的  *//* 若局部变量指定了特殊的寄存器则会标记此位, 如 register int x asm("x18"); */if (DECL_HARD_REGISTER (t) && !is_global_var (t) && cfun)     cfun->has_local_explicit_reg_vars = true;}}/* 创建一个GIMPLE_BIND节点(gbind),代表gimplify后的BIND_EXPR表达式, 其变量链表和对应block已经随创建初始化了 */bind_stmt = gimple_build_bind (BIND_EXPR_VARS (bind_expr), NULL, BIND_EXPR_BLOCK (bind_expr));body = NULL;  /* gimplify BIND_EXPR中的body节点,通常 BIND_EXPR代表一个{}(block)内的代码,而BIND_EXPR的body则记录了其中所有的语句列表,通常应该是一个tree_statement_list,而此函数最终则将是解析后生成的gimple指令序列返回到 body变量中.*/gimplify_stmt (&BIND_EXPR_BODY (bind_expr), &body);/* body中记录 BIND_EXPR.body节点gimplify后生成的指令序列,将其记录到gbind.body中 */gimple_bind_set_body (bind_stmt, body);//到这里这个gbind节点是已经生成好了 ....../* 如果当前函数使用了可变长数组,但没调用__buildin_alloca函数,则为其增加前后的桩代码 */if (gimplify_ctxp->save_stack && !gimplify_ctxp->keep_stack){.......}gimplify_seq_add_stmt (pre_p, bind_stmt);    /* 将 BIND_EXPR解析出的语句序列 插在 pre_p gimple的后面 */*expr_p = NULL_TREE;    /* BIND_EXPR是不需要返回值的,故解析玩不后无条件将expr_p设置为空,并返回全局解析完成(GS_ALL_DONE) */return GS_ALL_DONE;
}

此函数主要为BIND_EXPR(expr_p)创建了一个gbind语句并添加到pre_p指令序列中,此过程中会将:

  • BIND_EXPR中显式声明的变量(vars)记录到gbind.vars上
  • BIND_EXPR对应的block记录到gbind.block上
  • BIND_EXPR整个树节点(包括子树节点)gimplify后的结果记录到gbind.body中.

BIND_EXPR的gimple高端化举例如下:

int func()
{int x;x = 1;{int y;y = 2;}
}//gimplie高端化后的函数在 *.005t.gimple中可见(设置 -fdump-tree-all-raw参数)
func ()
gimple_bind <            //每一个{} 对应一个gbind节点int x;gimple_assign <integer_cst, x, 1, NULL, NULL>gimple_bind <          //内层的gbind节点int y;gimple_assign <integer_cst, y, 2, NULL, NULL>>
>

六、gimplify_statement_list

通常来说BIND_EXPR的子节点是一个STATEMENT_LIST,代表一个语句链表,在gimplify_expr中则通过函数gimplify_statement_list处理:

gimplify_status gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tr`ee), fallback_t fallback)
{......case STATEMENT_LIST:ret = gimplify_statement_list (expr_p, pre_p);break;......
}enum gimplify_status gimplify_statement_list (tree *expr_p, gimple_seq *pre_p)
{......tree_stmt_iterator i = tsi_start (*expr_p);while (!tsi_end_p (i))    /* 判断是否遍历到了tree_statement_list的最后一条语句 */{/* 此函数处理一条 AST语句,并将处理后生成的指令序列记录到pre_p中, 语句不需要返回值,故这里也不用关心返回值节点 */gimplify_stmt (tsi_stmt_ptr (i), pre_p); tsi_delink (&i);    /* 从statment list中将此tree节点下链 */}return GS_ALL_DONE;
}/* stmt(语句)并非某个类型的节点,而是多种节点的集合,其是表达式的一部分,故这里使用gimplify_expr通过传入 is_gimple_stmt + fb_none 可以用来解析一条语句 */
bool gimplify_stmt (tree *stmt_p, gimple_seq *seq_p)
{gimple_seq_node last;last = gimple_seq_last (*seq_p);     /* 获取已有gimple指令序列 seq_p中最后一条gimple指令地址 *//* 此函数负责解析一条语句(stmt), 此函数返回后stmt_p应该为空, seq_p指令序列的末尾会附加当前stmt的解析结果,期间生成的:* 所有临时变量都被添加到全局变量gimplify_ctxp->temps中* 所有ssa_name都被添加到当前函数的fn->gimplf_df->ssa_names中 */gimplify_expr (stmt_p, seq_p, NULL, is_gimple_stmt, fb_none);return last != gimple_seq_last (*seq_p);    /* 如果新增了指令序列则返回true,未新增则返回false */
}

此函数主要负责对一个语句链表(STATEMENT_LIST)的gimplify,由于语句链表是不需要返回值的,故最终只需要将所有gimplify的解析结果记录到 seq_p这个gimple指令序列中返回即可.


七、gimplify_decl_expr

gimplify_decl_expr函数用来gimplify一个声明表达式,声明表达式(DECL_EXPR)实际上代表的是非file_scope(函数体内部以及其subblock内部)的一个声明,如:

int func()
{int x;
}// 见 *.004t.orginal
// bind_expr的body就是一个 decl_expr(因为就一条语句所以没有statementlist), 代表一个声明表达式.
@1      bind_expr        type: @2       vars: @3       body: @4
@2      void_type        name: @5       algn: 8
@3      var_decl         name: @6       type: @7       scpe: @8    srcp: 1.c:3                   size: @9    algn: 32       used: 0
@4      decl_expr        type: @2        //这里没显示,实际上这里DECL_EXPR_DECL (stmt)记录的是一个VAR_DECL节点,也就是x的声明节点.

和外部声明中的变量声明不同(外部声明中的变量声明会直接导致全局变量节点varpool_node的创建), block中的变量声明除了会使变量被添加到block.vars中,还会在语句链表中添加一条DECL_EXPR,因为与全局变量不同,局部变量在每次函数执行时都要赋初值或动态计算空间大小,故需要在语句链表中主线一个DECL_EXPR表达式 ,以确保在gimplify阶段生成对应的初始化语句,其代码如下:

/* 此函数用来处理函数内部的声明表达式,主要工作包括将变量的初值转化为对变量的赋值gimple语句,以及处理变长数组等 */
static enum gimplify_status gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
{tree stmt = *stmt_p;tree decl = DECL_EXPR_DECL (stmt);    /* DECL_EXPR中只在.operands[0]中记录一个 XXX_DECL声明节点,这边是其全部内容 */*stmt_p = NULL_TREE;                  /* 清空指针,声明表达式不需要返回表达式节点 *//* !external的VAR_DECL代表局部变量,此函数只对局部变量有处理,非局部变量不处理直接返回 GS_ALL_DONE */if (VAR_P (decl) && !DECL_EXTERNAL (decl)){tree init = DECL_INITIAL (decl);bool is_vla = false;if (TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST ....    /*  可变长数组的gimplify */{gimplify_vla_decl (decl, seq_p);is_vla = true;}......if (init && init != error_mark_node)    /* 若变量有初值节点 */{if (!TREE_STATIC (decl))      /* 若非静态局部变量 */{DECL_INITIAL (decl) = NULL_TREE;    /* 将声明节点的初值节点清空 */init = build2 (INIT_EXPR, void_type_node, decl, init);    /* 构建一个INIT_EXPR代表此局部变量的初始化 */gimplify_and_add (init, seq_p);    /* gimplify 此INIT_EXPR表达式,并将结果添加到seq_p队列中 */ggc_free (init);    /* 转化为gassign语句后, free 掉 INIT_EXPR */}......}}return GS_ALL_DONE;
}

声明表达式处理过程中一个主要的操作是将声明节点(若有初值)转化为一条gimple赋值语句(gassign),如:

x = 1;
//最终会被转化为
//gimple_assign <integer_cst, x, 1, NULL, NULL>

八、gimplify_var_or_parm_decl

gimplify_var_or_parm_decl负责对VAR_DECL/PARM_DECL节点的处理,VAR_DECL和PARM_DECL通常都是末端节点,gimplify_var_or_parm_decl函数基本可以认为是空函数,当递归gimplify到VAR_DECL/PARM_DECL时通常也就代表着当前表达式的gimplify结束了(前面的DECL_EXPR在没有初值的时候也是作为末端节点存在的)


九、gimplify_call_expr

gimplify_call_expr负责对CALL_EXPR节点的gimplify,一个CALL_EXPR代表源码中一个函数调用(不论是直接的还是间接的函数调用),其代码如下:

gimplify_status gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tr`ee), fallback_t fallback)
{......case CALL_EXPR:ret = gimplify_call_expr (expr_p, pre_p, fallback != fb_none);if (fallback == fb_lvalue) {    /* 函数表达式解析后要返回左值的case暂未遇到过 */*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p, false);mark_addressable (*expr_p);ret = GS_OK;}break;......
}/*expr_p:需要解析的CALL_EXPR表达式pre_p: 存储结果的gimple指令序列want_value:代表此函数调用是否需要返回值此函数负责gimplify一条CALL_EXPR指令,其首先会gimplify此CALL_EXPR的被调用函数节点FN,然后依次gimplify当前调用的所有实参,之后则根据是否需要返回值来决定如何处理:* 若此CALL_EXPR不需要返回值,则直接将此CALL_EXPR gimplify为gcall节点并添加到pre_p队列中, expr_p返回NULL_TREE(代表不需要返回值,且解析完毕)* 若此CALL_EXPR需要返回值,则这里只是将为FN包裹一个NOP_EXPR,不生成gcall指令,然后expr_p原样返回此CALL_EXPR,真正的gcall指令则是在上一层gimplify中完成(通常是MODIFY_EXPR,主要原因是需要返回值时CALL_EXPR写入的返回值节点当前不确定,需要在上层确定)
*/
static enum gimplify_status gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
{tree fndecl, parms, p, fnptrtype;enum gimplify_status ret;int i, nargs;gcall *call;bool builtin_va_start_p = false;location_t loc = EXPR_LOCATION (*expr_p);gcc_assert (TREE_CODE (*expr_p) == CALL_EXPR);if (CALL_EXPR_FN (*expr_p) == NULL_TREE)    /* CALL_EXPR_FN为空,则函数通过编号ID的形式记录在CALL_EXPR_IFN中, 这里则是对gcc内置函数的调用 */{if (want_value)                /* 若内置函数需要返回值,则到其父语句中处理(如MODIFY_EXPR) */return GS_ALL_DONE;/* 如果不需要返回值,则直接将CALL_EXPR转换为gcall指令并返回 */nargs = call_expr_nargs (*expr_p);     /* 根据CALL_EXPR结构体大小获取此函数调用的参数个数 */   enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);    /* 获取内置函数编号 */auto_vec<tree> vargs (nargs);for (i = 0; i < nargs; i++) {        /* 遍历所有参数并对所有参数节点进行gimplify */gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, EXPR_LOCATION (*expr_p));vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));        /* push参数到vargs数组中 */}gcall *call = gimple_build_call_internal_vec (ifn, vargs);    /* 根据被调用函数ifn, 实参数组vargs,为此函数调用构建gcall指令 */......gimplify_seq_add_stmt (pre_p, call);        /* 将gcall指令添加到当前pre_p指令序列中 */return GS_ALL_DONE;            /* 返回处理完毕,此时的expr_p还是只想当前CALL_EXPR */}/* 尝试获取当前CALL_EXPR调用的目标函数(若获取到,则此处的代码可以被展为直接调用),若目标函数未决或非函数(如某个内存地址)则这里通常会返回空. */fndecl = get_callee_fndecl (*expr_p);if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))     /* 若被调用函数是一个gcc builtin的函数(builtin和内置函数是两个东西) *//* 则根据不同的 builtin类型来做不同的处理,这里实际上只处理了va_start 和__builtin_alloca函数的调用(因为需要处理标记后续如何使用栈相关flag) */switch (DECL_FUNCTION_CODE (fndecl)){CASE_BUILT_IN_ALLOCA: .......case BUILT_IN_VA_START: ......default:  ;}}/*不论根据*expr_p是否找到了被调用函数, 此 CALL_EXPR中的FN节点的类型节点,一定记录着被调用函数的类型,如对于:* FN为 ADDR_EXPR ,则ADDR_EXPR就记录着函数指针的类型* FN为 VAR_DECL, 则此VAR_DECL也一定是个函数指针,其类型也是函数指针的类型.*/fnptrtype = TREE_TYPE (CALL_EXPR_FN (*expr_p));/* 递归处理CALL_EXPR中代表被调用函数的表达式节点,此函数完成后&CALL_EXPR_FN (*expr_p)才可在gcall表达式中作为被调用函数出现,若在计算被调用函数的过程中需要生成新的gimple指令,则会添加到pre_p中.若&CALL_EXPR_FN (*expr_p)的返回值满足is_gimple_call_addr,则无需处理,否则要新建一个右值节点来作为被调用函数. */ret = gimplify_expr (&CALL_EXPR_FN (*expr_p), pre_p, NULL,is_gimple_call_addr, fb_rvalue);nargs = call_expr_nargs (*expr_p);    /* 根据此CALL_EXPR节点大小计算出此call表达式的【实参】个数 */fndecl = get_callee_fndecl (*expr_p);    /* 这里再次获取函数的声明节点(前面执行过gimplify_expr,可能导致被调用函数被算出来了) */parms = NULL_TREE;if (fndecl)    /* 如果被调用函数是确定的,则直接去函数声明的类型节点去找参数类型链表 */parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl));else /* 如果被调用函数是不确定的,则去指针的类型节点中去找参数类型链表 */parms = TYPE_ARG_TYPES (TREE_TYPE (fnptrtype));/* 最终的p(形参链表)实际上是先尝试从函数声明中找(这里面可能会有默认值) 没有再从函数或函数指针(fnptrtype)的类型声明中找,若二者都没有则为空 */if (fndecl && DECL_ARGUMENTS (fndecl))p = DECL_ARGUMENTS (fndecl);else if (parms)p = parms;else p = NULL_TREE;for (i = 0; i < nargs && p; i++, p = TREE_CHAIN (p)) ;   /* 这里是获取实参和形参中参数个数的最小值 */if (nargs > 0)    /* 对此次函数调用的所有实参做gimplify */{for (i = (PUSH_ARGS_REVERSED ? nargs - 1 : 0);  PUSH_ARGS_REVERSED ? i >= 0 : i < nargs;    PUSH_ARGS_REVERSED ? i-- : i++)    /* 遍历所有参数 */{enum gimplify_status t;if ((i != 1) || !builtin_va_start_p){/* gimplify 此函数的所有实参表达式,每个实参节点gimplify后的返回值保存在CALL_EXPR的参数链表中(而不是函数声明中) */t = gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, EXPR_LOCATION (*expr_p), ! returns_twice);}}}/* 如当前CALL_EXPR并不需要返回值,那么此CALL_EXPR到这里就算解析完毕了,直接生成gcall指令,其对应的返回值节点(gcall.op[0])为空如当前CALL_EXPR需要返回值,那么这里就先不展开,而是到其上层的表达式中展开,因为如 m =func(); 实际上最终被展开为一条gcall指令,且返回值节点设置为m即可,不必为其单独创建一个返回值节点,并多一步赋值操作.*/if (!want_value){gimple_stmt_iterator gsi;call = gimple_build_call_from_tree (*expr_p, fnptrtype);     /* 为CALL_EXPR创建一个 gcall节点 */notice_special_calls (call);gimplify_seq_add_stmt (pre_p, call);        /* 将此gcall节点添加到 gimple语句链表 */gsi = gsi_last (*pre_p);maybe_fold_stmt (&gsi);*expr_p = NULL_TREE;                        /* 生成成功则expr_p设置为空 */}else/* 如果需要返回值则此处不生成gcall指令,而是将这条CALL_EXPR的FN包裹一层NOP_EXPR并还是返回输出的CALL_EXPR */CALL_EXPR_FN (*expr_p) = build1 (NOP_EXPR, fnptrtype, CALL_EXPR_FN (*expr_p));return ret;
}

此函数负责gimplify一条CALL_EXPR指令,其首先会gimplify此CALL_EXPR的被调用函数节点FN,然后依次gimplify当前调用的所有实参,之后则根据是否需要返回值来决定如何处理:

  • 若此CALL_EXPR不需要返回值,则直接将此CALL_EXPR gimplify为gcall节点并添加到pre_p队列中, expr_p返回NULL_TREE(代表不需要返回值,且解析完毕)
  • 若此CALL_EXPR需要返回值,则这里只是将为FN包裹一个NOP_EXPR,不生成gcall指令,然后expr_p原样返回此CALL_EXPR,真正的gcall指令则是在上一层gimplify中完成(通常是MODIFY_EXPR,主要原因是需要返回值时CALL_EXPR写入的返回值节点当前不确定,需要在上层确定)

十、gimplify_modify_expr

gimplify_modify_expr 用来解析一个MODIFY_EXPR/INIT_EXPR表达式,二者实际上都是赋值表达式,其代码如下:

gimplify_status gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tr`ee), fallback_t fallback)
{......case MODIFY_EXPR:case INIT_EXPR:ret = gimplify_modify_expr (expr_p, pre_p, post_p, fallback != fb_none);break;......
}/*此函数负责gimplify一个MODIFY_EXPR/INIT_EXPR表达式节点,其首先分别gimplify赋值表达式的左侧节点(to_p)和右侧节点(from_p),然后根据右侧节点的类型决定如何产生指令:* 如果右侧节点是一个CALL_EXPR,则创建一个 gcall指令,并将gcall的返回值直接设置为to_p* 如果右侧节点非CALL_EXPR,则创建一个gassign指令,并将赋值指令的左操作数设置为to_p最终:* 若want_value为true,则此表达式需要返回返回值到expr_p,若to_p没有副作用则返回fork的to_p,若有副作用则返回from_p.* 若want_value为false,则此表达式不需要返回返回值,expr_p设置为NULL_TREE.
*/
static enum gimplify_status gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool want_value)
{tree *from_p = &TREE_OPERAND (*expr_p, 1);    /* INIT_EXPR / MODIFY_EXPR节点的右操作数节点作为from_p,其含义是这个值来自哪里 */tree *to_p = &TREE_OPERAND (*expr_p, 0);      /* INIT_EXPR / MODIFY_EXPR节点的左操作数作为to_p,代表这个值要赋值给哪里 */gimple *assign;/* gimplify MODIFY_EXPR/INIT_EXPR的右值节点, 如右值是一个CALL_EXPR,则这里就返回此CALL_EXPR节点 */ret = gimplify_expr (from_p, pre_p, post_p, initial_pred, fb_rvalue);/* gimplify MODIFY_EXPR/INIT_EXPR的左值节点, 最终返回的to_p要能作为一个左值*/ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);/* 若右侧节点为一个CALL_EXPR, 则要将整个MODIFY_EXPR/INIT_EXP gimplify为 gcall指令 */if (TREE_CODE (*from_p) == CALL_EXPR){gcall *call_stmt;if (CALL_EXPR_FN (*from_p) == NULL_TREE)  {   /* 如果是gcc内置函数则走这里处理 */......}else{tree fnptrtype = TREE_TYPE (CALL_EXPR_FN (*from_p));CALL_EXPR_FN (*from_p) = TREE_OPERAND (CALL_EXPR_FN (*from_p), 0);  /* 这里通常会获取到一个NOP_EXPR */STRIP_USELESS_TYPE_CONVERSION (CALL_EXPR_FN (*from_p));            /* 去除外层NOP_EXPR*/tree fndecl = get_callee_fndecl (*from_p);      /* 尝试获取被调用函数,对于间接调用,这里可能获取为空 */......call_stmt = gimple_build_call_from_tree (*from_p, fnptrtype);    /* 正常走这里,为CALL_EXPR构建 gcall节点并返回到 call_stmt */}if (!gimple_call_noreturn_p (call_stmt) || !should_remove_lhs_p (*to_p))gimple_call_set_lhs (call_stmt, *to_p);             /* 设置 to_p 为接受当前函数返回值的左值节点 */else if (TREE_CODE (*to_p) == SSA_NAME)SSA_NAME_DEF_STMT (*to_p) = gimple_build_nop ();assign = call_stmt;                    /* gcall指令最终记录到 assign 中 */}else{assign = gimple_build_assign (*to_p, *from_p);        /* 对于右值非CALL_EXPR的赋值指令则直接构建gassign节点 */.......}gimplify_seq_add_stmt (pre_p, assign);    /* 将assign 这个 gimple节点插入到 pre_p的最后 */if (want_value){*expr_p = TREE_THIS_VOLATILE (*to_p) ? *from_p : unshare_expr (*to_p);    /* 若目标值有副作用,则返回原始值,否则返回目标值 的树节点*/return GS_OK;     /* 这里返回 GS_OK, 代表返回的expr_P也要递归走一遍 gimplify_expr的处理 */}else*expr_p = NULL;    /* 不需要返回值则直接清空expr_p */return GS_ALL_DONE;     /* 并返回ALL_DONE */
}

此函数负责gimplify一个MODIFY/INIT_EXPR表达式节点,其首先分别gimplify赋值表达式的左侧节点(to_p)和右侧节点(from_p),然后根据右侧节点的类型决定如何产生指令:

  • 如果右侧节点是一个CALL_EXPR,则创建一个 gcall指令,并将gcall的返回值直接设置为to_p
  • 如果右侧节点非CALL_EXPR,则创建一个gassign指令,并将赋值指令的左操作数设置为to_p

最终:

  • 若want_value为true,则此表达式需要返回返回值到expr_p,若to_p没有副作用则返回fork的to_p,若有副作用则返回from_p.
  • 若want_value为false,则此表达式不需要返回返回值,expr_p设置为NULL_TREE.

MODIFY_EXPR/INIT_EXPR/CALL_EXPR的调用举例如下:

int func()
{int x;x = 1;x = func1();func1();
}func ()
gimple_bind <int x;gimple_assign <integer_cst, x, 1, NULL, NULL>gimple_call <func1, x>gimple_call <func1, NULL>
>


十一、gimplify_return_expr

此函数负责gimplify一个RETURN_EXPR,在源码中的每一条return语句都会对应到一个RETURN_EXPR,如:

int func()
{return func1();return 1;return;
}func ()
gimple_bind <int D.3411;        /* 此变量实际上并没有名字,而是fdump时对于此类变量自动设置为D.变量的全局id */gimple_call <func1, D.3411>gimple_return <D.3411>gimple_assign <integer_cst, D.3411, 1, NULL, NULL>gimple_return <D.3411>gimple_return <NULL>
>

此函数的源码如下:

gimplify_status gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tr`ee), fallback_t fallback)
{......case RETURN_EXPR: ret = gimplify_return_expr (*expr_p, pre_p);break;......
}/*此函数负责对返回表达式(RETURN_EXPR)进行gimplify, 其先gimplify 作为返回值的整个右侧值节点,然后生成一个临时的RESULT_DECL记录返回值,最终构建一个greturn语句,返回此result_decl节点.
*/
static enum gimplify_status gimplify_return_expr (tree stmt, gimple_seq *pre_p)
{greturn *ret;/* 在gimplify之前, RETURN_EXPR的op[0]代表返回值的表达式节点,其可以是一个RESULT_DECL/MODIFY_EXPR/INIT_EXPR或NULL_TREE(函数无返回值). */tree ret_expr = TREE_OPERAND (stmt, 0);tree result_decl, result;/* 对于 return; 这样的语句,其ret_expr为空,此时直接向序列中添加greturn语句即可 */if (!ret_expr || TREE_CODE (ret_expr) == RESULT_DECL)    {greturn *ret = gimple_build_return (ret_expr);gimplify_seq_add_stmt (pre_p, ret);return GS_ALL_DONE;}/* 如果函数的返回值类型是 void,则不管返回语句,直接设置result_decl为 NULL_TREE */if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))result_decl = NULL_TREE;else{/* 否则return语句的值节点实际上是一个 MODIFY_EXPR/INIT_EXPR表达式这里获取赋值表达式的to_p(右侧值)节点作为result_decl */result_decl = TREE_OPERAND (ret_expr, 0);......}if (!result_decl)    /* 如果没有result_decl节点,则代表无需返回值 */result = NULL_TREE;else if (aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))    /* 如果返回的是一个结构体 */{......result = result_decl;}else if (gimplify_ctxp->return_temp)result = gimplify_ctxp->return_temp;    /* 如果当前gimplify过程中已经创建了返回值节点,则直接使用 */else{result = create_tmp_reg (TREE_TYPE (result_decl));  /* 否则创建一个无名的临时变量作为函数的返回值 */gimplify_ctxp->return_temp = result;    /* 返回值的声明节点被记录到 全局gimplify上下文中 */}if (result != result_decl)TREE_OPERAND (ret_expr, 0) = result;    /* 重新设置此 RESULT_DECL为 MODIFY_EXPR/INIT_EXPR的返回值节点 */gimplify_and_add (TREE_OPERAND (stmt, 0), pre_p);    /* 对return语句的MODIFY_EXPR/INIT_EXPR表达式做gimpify */.......ret = gimple_build_return (result);    /* 最终生成一条greturn语句,其只有一个操作数result代表返回值的RESULT_DECL节点 */gimplify_seq_add_stmt (pre_p, ret);    /* 将GIMPLE_RETURN 语句,加到 语句链表中 */return GS_ALL_DONE;
}

十二、gimplify_cond_expr

gimplify_cond_expr负责条件表达式的gimplify,在源码中的每一个条件表达式(如if语句)都会通过此函数解析, COND_EXPR最终会被转化为一条GIMPLE_COND语句,以及then,else分支的代码,GIMPLE_COND定义如下:

    GIMPLE_COND <COND_CODE, OP1, OP2, TRUE_LABEL, FALSE_LABEL>

此GIMPLE指令共有5个操作数,其中:

  • COND_CODE是比较操作的条件码, 如 EQ_EXPR/NE_EXPR(实际上是if(...)中...最终转化为的tree_code)
  • OP1/OP2是此条件表达式最终比较的左侧值和右侧值节点
  • TRUE_LABEL/FALSE_LABEL是两个标签声明(LABEL_DECL)节点, GIMPLE_COND在true或false分支触发时,并没有指定跳转到指令序列中的某条语句,而是指定了一个标签声明, 而标签声明在语句序列的什么位置则需要一条glabel语句来设置,在gimple指令序列中,一条glabel语句即是用来设置某个标签实际应该跳转到的位置的.

注: 条件表达式中,true分支通常又叫做then分支(有的语言中是if..then的形式),false分支通常称为else分支.

gimplify_cond_expr的源码如下:

gimplify_status gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tr`ee), fallback_t fallback)
{......case COND_EXPR:ret = gimplify_cond_expr (expr_p, pre_p, fallback);if (fallback == fb_lvalue){*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p, false);mark_addressable (*expr_p);ret = GS_OK;}break;......
}enum gimplify_status gimplify_cond_expr (tree *expr_p, gimple_seq *pre_p, fallback_t fallback)
{tree expr = *expr_p;tree tmp, arm1, arm2;tree label_true, label_false, label_cont;bool have_then_clause_p, have_else_clause_p;gcond *cond_stmt;gimple_seq seq = NULL;......./* 先gimplify 条件表达式的内部条件, 如 if(...) 中的... */ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL, is_gimple_condexpr, fb_rvalue);have_then_clause_p = have_else_clause_p = false;/* 如果 if(...) 成立后执行的语句(除去调试指令)是一条goto label语句,那么if then后不需要单独创建一个新的标签,直接跳转到goto 后面的标签即可,相当于可以省略掉一条goto 语句,利用 gcond直接跳转到goto label对应的标签 */label_true = find_goto_label (TREE_OPERAND (expr, 1));if (label_true && .........else/* 正常情况下 then分支中的代码不是goto labelX,那么就会创建一个LABLE_DECL节点, 在后面的GIMPLE_COND语句中若then分支成立则会跳转到此标签所在的位置LABEL_DECL只是记录了此标签的树节点,而一个标签的位置则是由glabel语句决定的 */label_true = create_artificial_label (UNKNOWN_LOCATION);label_false = find_goto_label (TREE_OPERAND (expr, 2));    /* else分支同理 */if (label_false && ......elselabel_false = create_artificial_label (UNKNOWN_LOCATION);/* 获取此条件表达式的比较代码, 比较的左侧值(arm1)/右侧值(arm2) */gimple_cond_get_ops_from_tree (COND_EXPR_COND (expr), &pred_code, &arm1, &arm2);/* 构建 GIMPLE_COND语句,并插入到指令序列中, then分支会跳转到label_true标签, else分支会跳转到label_false标签, 但需要注意的是此时二标签的位置还是不确定的 */cond_stmt = gimple_build_cond (pred_code, arm1, arm2, label_true, label_false);gimplify_seq_add_stmt (&seq, cond_stmt);gimple_stmt_iterator gsi = gsi_last (seq);label_cont = NULL_TREE;if (!have_then_clause_p)    /* 若then分支 中没找到goto label则走这里为gimple指令序列中添加 glabel语句 */{....../* 前面构建GIMPLE_COND语句时已经确定了then分支,这里实际上就是在为then分支要跳转到的标签(label_true),构建glabel语句,此语句则代表label_true标签在gimple指令序列要跳转到的位置 */gimplify_seq_add_stmt (&seq, gimple_build_label (label_true));/* 将 then分支执行的代码 gimplify为指令序列并添加到seq中 */have_then_clause_p = gimplify_stmt (&TREE_OPERAND (expr, 1), &seq);/* then表达式分析完毕后,若当前COND_EXPR有else分支,则需要在then分支的结束增加一个goto label_cont的语句, 而后续else分支gimplify之后的语句序列的末尾,即为label_cont要跳转的位置,需要添加一个glabel来标注 */if (!have_else_clause_p && ......{gimple *g;label_cont = create_artificial_label (UNKNOWN_LOCATION); g = gimple_build_goto (label_cont);gimplify_seq_add_stmt (&seq, g);}}if (!have_else_clause_p)    /* 若else 分支没找到label则继续为gimple指令序列添加glabel语句,这里是为label_false标记位置 */{/* then与执行完毕后,为else分支跳转的label_decl添加其对应的glabel指令, 后续此glabel之后则继续添加 else中语句解析成的gimple指令序列 */gimplify_seq_add_stmt (&seq, gimple_build_label (label_false));have_else_clause_p = gimplify_stmt (&TREE_OPERAND (expr, 2), &seq);    /* gimplify else分支的代码 */}if (label_cont)     /* 当有else 时, else完事了还要再加上一个label,因为then语句最终要跳过else到此label上 */gimplify_seq_add_stmt (&seq, gimple_build_label (label_cont));gimple_seq_add_seq (pre_p, seq);    /* 将整个 then ...; goto label_cont; else ...; label_cont; gimplie指令序列添加到pre_p中 */......*expr_p = NULL;return ret;
}

GCC源码分析(十) — 函数节点的gimple高端化相关推荐

  1. GCC源码分析(十一) — 函数节点的gimple低端化

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

  2. GCC源码分析(十六) — gimple转RTL(pass_expand)(下)

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

  3. GCC源码分析(十四) — rtx结构体,指令与栈分配

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

  4. GCC源码分析(十七) — rtl expand之后

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

  5. 3 v4 中心节点固定_死磕以太坊源码分析之p2p节点发现

    死磕以太坊源码分析之p2p节点发现 在阅读节点发现源码之前必须要理解kadmilia算法,可以参考:KAD算法详解. 节点发现概述 节点发现,使本地节点得知其他节点的信息,进而加入到p2p网络中. 以 ...

  6. Cowboy 源码分析(十八)

    在上一篇中,我们整理了下cowboy_http_protocol:header/3函数,在文章的末尾留下2个没有讲到的函数,今天,我们先看下cowboy_http_protocol:error_ter ...

  7. jQuery源码分析-each函数

    本文部分截取自且行且思 jQuery.each方法用于遍历一个数组或对象,并对当前遍历的元素进行处理,在jQuery使用的频率非常大,下面就这个函数做了详细讲解: 复制代码代码 /*! * jQuer ...

  8. SequoiaDB 系列之六 :源码分析之coord节点

    好久不见. 在上一篇SequoiaDB 系列之五   :源码分析之main函数,有讲述进程开始运行时,会根据自身的角色,来初始化不同的CB(控制块,control block). 在之前的一篇Sequ ...

  9. x265源码分析 main函数 x265.cpp

    图片转载于x265源码流程分析_Dillon2015的博客-CSDN博客_x265编码流程 cliopt.prase main ()函数--解析函数参数并进行编码准备工作:x265.cpp (1)Ge ...

最新文章

  1. 敏捷方法适合什么样的团队?
  2. 【Linux网络编程】广播
  3. 四级计算机网络选择题,2015全国计算机等级考试四级计算机网络模拟选择题
  4. web前端学习之ruby标记和rt/rp标记
  5. 第十四章 七段数码管绘制时间
  6. 307.区域和检索-数组可修改
  7. 利用Python对Excel数据进行处理
  8. 城市智慧灯杆解决方案
  9. 自整理---Redis笔记
  10. 搭建 WordPress 博客教程(超详细)
  11. 万字长文 | 数据分析师的机遇与挑战
  12. 思科网络设备常用命令
  13. 【总结】办公编程学习你可能需要这些小利器!
  14. 万邦淘宝/天猫获得淘宝商品评论 API 返回值说明
  15. 安卓音频输出采样率_android downsample降低音频采样频率代码
  16. 名帖295 张瑞图 行书《行书帖选》
  17. VASP计算弹性常数
  18. mysql on delete_MySQL ON DELETE CASCADE
  19. 使用御剑工具,扫描网站http://43.138.211.45的后台地址
  20. win10微软输入法有些情况不显示选字栏

热门文章

  1. 【拨号】iPhone拨号功能隐藏代码,值得收藏。
  2. Android UIL图片加载缓存源码分析-内存缓存
  3. 乔布斯的“神”与“魂”
  4. 都2019了,这些曾经的经典游戏还能在mac玩到吗?(第一弹仙剑客栈mac版)
  5. 灾备机房深信服超融合IP网段调整记录2021-1-28
  6. 微信小程序获取二维码接口整理,.Net Core后台获取小程序二维码
  7. 【Mac 教程系列】如何在 Mac 上破解带有密码的 ZIP 压缩文件 ?
  8. 近代物理实验 液晶电光效应 (含数据和参考题)
  9. 2019如何成为一个优秀的程序员
  10. Weblogic清缓存