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