GCC Named Return Value
Table of Contents
1. GCC Named Return Value
named return value (nrv) 是指返回 struct 的函数使用隐式传入的 retval 指令直接修改返回值, 而不是先修改局部变量然后在函数返回时再复制给 retval.
例如:
// 2022-04-19 11:57 struct X { int x; int y; int z; int a; int b; }; struct X foo() { struct X ret; ret.x = 0xff; return ret; }
不使用 nrv:
$> /opt/riscv/bin/riscv64-unknown-linux-gnu-gcc $PWD/test.c -O0 -c -fdump-tree-optimized $> /opt/riscv/bin/riscv64-unknown-linux-gnu-objdump -d ./test.o 0000000000000000 <foo>: 0: 7139 addi sp,sp,-64 2: fc22 sd s0,56(sp) 4: 0080 addi s0,sp,64 # a0 是返回址的指针 6: fca43423 sd a0,-56(s0) # ret 的数据在栈上 a: 0ff00793 li a5,255 e: fcf42c23 sw a5,-40(s0) 12: fc843783 ld a5,-56(s0) 16: fd842703 lw a4,-40(s0) 1a: 853a mv a0,a4 1c: fdc42703 lw a4,-36(s0) 20: 85ba mv a1,a4 22: fe042703 lw a4,-32(s0) 26: 863a mv a2,a4 28: fe442703 lw a4,-28(s0) 2c: 86ba mv a3,a4 2e: fe842703 lw a4,-24(s0) # 复制栈上的 ret 对 a0 指向的 retval 32: c388 sw a0,0(a5) 34: c3cc sw a1,4(a5) 36: c790 sw a2,8(a5) 38: c7d4 sw a3,12(a5) 3a: cb98 sw a4,16(a5) 3c: fc843503 ld a0,-56(s0) 40: 7462 ld s0,56(sp) 42: 6121 addi sp,sp,64 44: 8082 ret $> cat test.c.244t.optimized struct X foo () { struct X ret; <bb 2> : ret.x = 255; <retval> = ret; ret ={v} {CLOBBER}; <bb 3> : <L1>: return <retval>; }
使用 nrv:
$> /opt/riscv/bin/riscv64-unknown-linux-gnu-gcc $PWD/test.c -O0 -c -fenable-tree-nrv -fdump-tree-nrv $> /opt/riscv/bin/riscv64-unknown-linux-gnu-objdump -d ./test.o 0000000000000000 <foo>: 0: 1101 addi sp,sp,-32 2: ec22 sd s0,24(sp) 4: 1000 addi s0,sp,32 6: fea43423 sd a0,-24(s0) a: fe843783 ld a5,-24(s0) e: 0ff00713 li a4,255 # 把 0xff 直接写到了 a0 指向的 reval 12: c398 sw a4,0(a5) 14: 0001 nop 16: fe843503 ld a0,-24(s0) 1a: 6462 ld s0,24(sp) 1c: 6105 addi sp,sp,32 $> cat test.c.242t.nrv struct X foo () { struct X ret [value-expr: <retval>]; <bb 2> : <retval>.x = 255; <retval> ={v} {CLOBBER}; <bb 3> : <L1>: return <retval>; }
1.1. impls
unsigned int pass_nrv::execute(function *fun) { tree result = DECL_RESULT(current_function_decl); tree result_type = TREE_TYPE(result); tree found = NULL; basic_block bb; gimple_stmt_iterator gsi; struct nrv_data_t data; /* NOTE: 返回值需要是一个结构体 */ if (!aggregate_value_p(result, current_function_decl)) return 0; if (is_gimple_reg_type(result_type)) return 0; if (TREE_ADDRESSABLE(result)) return 0; FOR_EACH_BB_FN(bb, fun) { for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { gimple *stmt = gsi_stmt(gsi); if (gimple_has_lhs(stmt) && gimple_get_lhs(stmt) == result) { /* NOTE: <retval>=xxx 形式 */ tree rhs; rhs = gimple_assign_rhs1(stmt); if (found != NULL) { /* NOTE: 所有 `return xxx` 需要返回同一个变量 */ if (found != rhs) return 0; } else found = rhs; /* NOTE: return 的变量不能是 volatile 或 addressable */ if (!VAR_P(found) || TREE_THIS_VOLATILE(found) || !auto_var_in_fn_p(found, current_function_decl) || TREE_ADDRESSABLE(found) || !useless_type_conversion_p(result_type, TREE_TYPE(found))) return 0; } else if (gimple_has_lhs(stmt)) { /* ... */ } } } if (!found) return 0; /* Now walk through the function changing all references to VAR to be RESULT. */ data.var = found; data.result = result; FOR_EACH_BB_FN(bb, fun) { for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi);) { gimple *stmt = gsi_stmt(gsi); /* NOTE: <retval>=ret 形式的直接删除 */ if (gimple_assign_copy_p(stmt) && gimple_assign_lhs(stmt) == result && gimple_assign_rhs1(stmt) == found) { unlink_stmt_vdef(stmt); gsi_remove(&gsi, true); release_defs(stmt); } else { /* NOTE: ret.x=xxx 形式的修改为 <retval>.x=xxx */ struct walk_stmt_info wi; memset(&wi, 0, sizeof(wi)); wi.info = &data; data.modified = 0; walk_gimple_op(stmt, finalize_nrv_r, &wi); if (data.modified) update_stmt(stmt); gsi_next(&gsi); } } } return 0; } bool auto_var_in_fn_p(const_tree var, const_tree fn) { return ( DECL_P(var) && DECL_CONTEXT(var) == fn && (auto_var_p(var) || TREE_CODE(var) == LABEL_DECL)); } bool auto_var_p(const_tree var) { return ( (((VAR_P(var) && !DECL_EXTERNAL(var)) || TREE_CODE(var) == PARM_DECL) && !TREE_STATIC(var)) || TREE_CODE(var) == RESULT_DECL); }
1.2. 无法应用 nvr 的场景
返回不同的变量
struct X foo(int x) { if (x > 0) { struct X ret; ret.x = 0xff; return ret; } else { struct X ret; return ret; } }
volatile
struct X foo() { /* volatile 导致 nrv 无效 */ volatile struct X ret; ret.x = 0xff; return ret; }
addressable
struct X foo() { struct X ret; ret.x = 0xff; /* px 会导致 nrv 无效 */ struct X* px = &ret; return ret; }
ret 不是 auto_var
ret 不是 auto_var, 所以对 ret 的修改会有`副作用`, 因此不能省略
/* ret 是 external */ struct X ret; struct X foo() { ret.x = 0xff; return ret; } /* ret 是 static */ struct X foo() { static struct X ret; ret.x = 0xff; return ret; }
Backlinks
GCC Pass (GCC Pass > tree pass > pass_nrv): pass_nrv
GCC Return Slot (GCC Return Slot): pass_return_slot (name 是 retslot) 与 pass_nrv 都实现在 tree-nrv.c 中, 它与 pass_nrv 有关.
GCC Target Hook (GCC Target Hook > misc > TARGET_STRUCT_VALUE_RTX): 用来设置 GCC Named Return Value 如何把 nrv 传递给 callee