You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在GCC IPA阶段实现结构体拆分Pass时,如何定位并修改目标GIMPLE代码

在GCC IPA阶段实现结构体拆分Pass时,如何定位并修改目标GIMPLE代码

看起来你已经找对了方向,但卡在了定位目标GIMPLE序列和替换逻辑上。我来帮你梳理下具体的思路和实现细节:

首先,我们得先拆解清楚你要处理的GIMPLE模式:
原来的r->node[i].state在GIMPLE里是一连串的赋值:

_1 = r->node;          // 取reg里的node指针
_2 = (long unsigned int) i;
_3 = _2 * 16;          // 16是原struct node的大小
_4 = _1 + _3;          // 计算第i个node的地址
_5 = _4->state;        // 取该node的state成员

而你要把它改成:

_1 = r->node;
_2 = _1->state;        // 取node里的state指针
_3 = (long unsigned int) i;
_4 = _3 * 8;           // 8是unsigned long long的大小
_5 = _2 + _4;          // 计算第i个state的地址
_6 = *_5;              // 取值

一、精准定位目标语句

你当前只找到了r->node的赋值,但真正的突破口是访问struct nodestate成员的COMPONENT_REF语句(也就是上面的_5 = _4->state;)。具体步骤:

  1. 先拿到struct nodestate成员的tree节点:
tree state_field = lookup_field(desire_type, "state");
if (!state_field) return 0;
  1. 在遍历GIMPLE语句时,筛选出访问该成员的赋值语句:
// 遍历stmt的循环内
if (gimple_code(stmt) == GIMPLE_ASSIGN) {
  if (gimple_assign_rhs_code(stmt) == COMPONENT_REF) {
    tree rhs_comp = gimple_assign_rhs1(stmt);
    tree comp_field = TREE_OPERAND(rhs_comp, 1);
    // 验证是不是我们要处理的state成员,且父类型是原struct node
    if (comp_field == state_field && TREE_TYPE(TREE_OPERAND(rhs_comp, 0)) == desire_type) {
      // 找到了目标语句!接下来追踪它的来源链
      gimple *target_stmt = stmt;
      tree ptr_to_node = TREE_OPERAND(rhs_comp, 0); // 对应上面的_4
      this->process_array_access(target_stmt, ptr_to_node, func);
    }
  }
}

二、回溯数组访问的计算链

找到_4->state后,需要确认_4是数组索引计算的结果(_1 + i*sizeof(node))。可以写个辅助函数验证:

bool pass_struct_split::is_array_index(tree var, tree *base_ptr, tree *index_var, tree struct_type) {
  gimple *def_stmt = SSA_NAME_DEF_STMT(var);
  if (!def_stmt || gimple_code(def_stmt) != GIMPLE_ASSIGN) return false;

  // 检查是不是指针加法:base + offset
  if (gimple_assign_rhs_code(def_stmt) != POINTER_PLUS_EXPR) return false;
  tree base = gimple_assign_rhs1(def_stmt);
  tree offset = gimple_assign_rhs2(def_stmt);

  // 检查offset是不是索引乘以原结构体大小
  gimple *offset_def = SSA_NAME_DEF_STMT(offset);
  if (!offset_def || gimple_assign_rhs_code(offset_def) != MULT_EXPR) return false;
  tree idx = gimple_assign_rhs1(offset_def);
  tree size_cst = gimple_assign_rhs2(offset_def);

  // 验证大小是否匹配原struct node的字节数
  if (TREE_INT_CST_LOW(size_cst) != int_size_in_bytes(struct_type)) return false;
  // 验证base是不是原struct node的指针
  if (TREE_TYPE(base) != build_pointer_type(struct_type)) return false;

  *base_ptr = base;
  *index_var = idx;
  return true;
}

调用这个函数后,就能拿到base_ptr(对应_1,即r->node)和index_var(对应i),确认这是我们要处理的数组访问场景。

三、替换GIMPLE代码序列

确认目标后,就可以删除旧的语句,插入新的逻辑:

void pass_struct_split::process_array_access(gimple *target_stmt, tree ptr_to_node, function *func) {
  tree base_ptr, index_var;
  if (!is_array_index(ptr_to_node, &base_ptr, &index_var, desire_type)) return;

  gimple_stmt_iterator gsi = gsi_for_stmt(target_stmt);
  tree lhs = gimple_assign_lhs(target_stmt);

  // 回溯并删除旧的计算语句:cast i -> mult -> ptr add -> target stmt
  gimple *ptr_add_stmt = SSA_NAME_DEF_STMT(ptr_to_node);
  tree offset_var = gimple_assign_rhs2(ptr_add_stmt);
  gimple *mult_stmt = SSA_NAME_DEF_STMT(offset_var);
  tree cast_var = gimple_assign_rhs1(mult_stmt);
  gimple *cast_stmt = SSA_NAME_DEF_STMT(cast_var);

  // 从后往前删,避免迭代器失效
  gsi_remove(&gsi, true);
  gsi = gsi_for_stmt(ptr_add_stmt); gsi_remove(&gsi, true);
  gsi = gsi_for_stmt(mult_stmt); gsi_remove(&gsi, true);
  gsi = gsi_for_stmt(cast_stmt); gsi_remove(&gsi, true);

  // 插入新的语句序列
  tree state_ptr_type = build_pointer_type(TREE_TYPE(state_field));
  // 1. 取node里的state指针
  tree new_state_ptr = make_ssa_name(state_ptr_type, NULL);
  gimple *comp_stmt = gimple_build_assign(new_state_ptr, COMPONENT_REF, base_ptr, state_field);
  gsi_insert_after(&gsi, comp_stmt, GSI_NEW_STMT);
  gsi_next(&gsi);

  // 2. 转换索引类型
  tree cast_type = unsigned_long_type_node;
  tree new_cast = make_ssa_name(cast_type, NULL);
  gimple *new_cast_stmt = gimple_build_assign(new_cast, NOP_EXPR, index_var, cast_type);
  gsi_insert_after(&gsi, new_cast_stmt, GSI_NEW_STMT);
  gsi_next(&gsi);

  // 3. 计算索引偏移
  tree elem_size = build_int_cst(cast_type, int_size_in_bytes(TREE_TYPE(state_field)));
  tree new_mult = make_ssa_name(cast_type, NULL);
  gimple *new_mult_stmt = gimple_build_assign(new_mult, MULT_EXPR, new_cast, elem_size);
  gsi_insert_after(&gsi, new_mult_stmt, GSI_NEW_STMT);
  gsi_next(&gsi);

  // 4. 指针加法
  tree new_ptr_add = make_ssa_name(build_pointer_type(TREE_TYPE(state_field)), NULL);
  gimple *new_ptr_stmt = gimple_build_assign(new_ptr_add, POINTER_PLUS_EXPR, new_state_ptr, new_mult);
  gsi_insert_after(&gsi, new_ptr_stmt, GSI_NEW_STMT);
  gsi_next(&gsi);

  // 5. 取值并赋值给原来的lhs
  gimple *deref_stmt = gimple_build_assign(lhs, INDIRECT_REF, new_ptr_add);
  gsi_insert_after(&gsi, deref_stmt, GSI_NEW_STMT);
}

四、额外注意事项

  • SSA名字管理:所有新变量要用make_ssa_name创建,确保类型匹配,避免SSA形式出错。
  • IPA阶段的跨函数处理:如果你的结构体在多个函数中被使用,要在IPA阶段同步处理所有引用,比如其他函数里的malloc(sizeof(struct node)),需要改成适配拆分后的结构体的分配逻辑。
  • 边界场景测试:比如结构体的直接声明、传递、返回等情况,都要验证你的Pass是否能正确处理。
  • 利用GCC内置工具:GCC提供了大量tree/gimple辅助函数,比如int_size_in_bytesSSA_NAME_DEF_STMT,要充分利用这些工具简化逻辑。

备注:内容来源于stack exchange,提问作者Dbettkk

火山引擎 最新活动