GCC Return Slot

Table of Contents

1. GCC Return Slot

pass_return_slot (name 是 retslot) 与 pass_nrv 都实现在 tree-nrv.c 中, 它与 pass_nrv 有关.

在 pass_nrv 中, caller 会将返回地址通过一个隐式的参数传入 callee, retslot 解决的问题是: 这个需要传入的返回地址是否需要使用临时变量, 例如:

正常没有 retslot 优化时执行的代码类似于:

int main(int argc, char *argv[]) {
    struct X x = {};
    struct X temp = foo();
    x = temp;
    return 0;
}

经过 retslot 后类似于:

int main(int argc, char *argv[]) {
    struct X x = {};
    x = foo();
    return 0;
}

嗯? 所谓的 retslot 难道不是理所当然的么?

实际上, 由于 nrv 的存在, retslot 优化并不是理所当然.

例如, 考虑下面的代码:

struct X foo(struct X* p) {
    struct X ret;
    ret.x = 2;
    p->x = 1;
    return ret;
}

int main(int argc, char *argv[]) {
    struct X x = {};
    x = foo(&x);
    printf("%d\n", x.x);
    return 0;
}

若 nrv 和 retslot 同时有效, 则代码会输出 `1`, 但实际上正确的结果是 `2`.

1.1. impls

unsigned int pass_return_slot::execute(function *fun) {
    basic_block bb;

    FOR_EACH_BB_FN(bb, fun) {
        gimple_stmt_iterator gsi;
        for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
            gcall *stmt;
            bool slot_opt_p;

            stmt = dyn_cast<gcall *>(gsi_stmt(gsi));
            if (stmt && gimple_call_lhs(stmt) &&
                !gimple_call_return_slot_opt_p(stmt) &&
                !gimple_call_internal_p(stmt) &&
                aggregate_value_p(
                    TREE_TYPE(gimple_call_lhs(stmt)),
                    gimple_call_fndecl(stmt))) {
                /* NOTE: dest_safe_for_nrv_p 需要判断 call 的 lhs 是否被 call
                 * 本身使用了, 类似于例子中 x = foo(struct X * x) 的情况 */
                slot_opt_p = dest_safe_for_nrv_p(stmt);
                gimple_call_set_return_slot_opt(stmt, slot_opt_p);
            }
        }
    }
    return 0;
}

static bool dest_safe_for_nrv_p(gcall *call) {
    tree dest = gimple_call_lhs(call);

    dest = get_base_address(dest);
    if (!dest) return false;

    /* NOTE: call 是否会修改 ref */
    if (call_may_clobber_ref_p(call, dest, false) ||
        /* NOTE: call 是否使用了 ref */
        ref_maybe_used_by_stmt_p(call, dest, false))
        return false;

    return true;
}

call_may_clobber_ref_p():
  /* callee 是否是 const */
  /* callee 使用的 ref 的引用是否是 const */
  /* ... */

ref_maybe_used_by_stmt_p():
  /* ... */
  for (i = 0; i < gimple_call_num_args (call); ++i) {
      tree op = gimple_call_arg (call, i);
      int flags = gimple_call_arg_flags (call, i);

      if (flags & EAF_UNUSED) continue;

      if (TREE_CODE (op) != SSA_NAME
          && !is_gimple_min_invariant (op)) {
      ao_ref r;
      ao_ref_init (&r, op);
      if (refs_may_alias_p_1 (&r, ref, tbaa_p))
          return true;
      }
  }

Backlinks

GCC Pass (GCC Pass > tree pass > pass_return_slot): pass_return_slot

Author: [email protected]
Date: 2022-04-20 Wed 13:49
Last updated: 2022-04-20 Wed 20:05

知识共享许可协议