Only now we get a testcase using __builtin_va_arg_pack() and friends two levels deep which doesn't work since we (reliably) perform inlining bottom-up.
The following fixes the bug by simply not replacing the builtins with garbage in case the caller wasn't passing args but an argument pack itself. Bootstrapped and tested on x86_64-unknown-linux-gnu, applied to trunk. Richard. 2017-03-21 Richard Biener <rguent...@suse.de> PR tree-optimization/80122 * tree-inline.c (copy_bb): Do not expans va-arg packs or va_arg_pack_len when the inlined call stmt requires pack expansion itself. * tree-inline.h (struct copy_body_data): Make call_stmt a gcall *. * gcc.dg/torture/pr80122.c: New testcase. Index: gcc/tree-inline.c =================================================================== *** gcc/tree-inline.c (revision 246277) --- gcc/tree-inline.c (working copy) *************** copy_bb (copy_body_data *id, basic_block *** 1860,1866 **** call_stmt = dyn_cast <gcall *> (stmt); if (call_stmt && gimple_call_va_arg_pack_p (call_stmt) ! && id->call_stmt) { /* __builtin_va_arg_pack () should be replaced by all arguments corresponding to ... in the caller. */ --- 1860,1867 ---- call_stmt = dyn_cast <gcall *> (stmt); if (call_stmt && gimple_call_va_arg_pack_p (call_stmt) ! && id->call_stmt ! && ! gimple_call_va_arg_pack_p (id->call_stmt)) { /* __builtin_va_arg_pack () should be replaced by all arguments corresponding to ... in the caller. */ *************** copy_bb (copy_body_data *id, basic_block *** 1940,1946 **** && id->call_stmt && (decl = gimple_call_fndecl (stmt)) && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL ! && DECL_FUNCTION_CODE (decl) == BUILT_IN_VA_ARG_PACK_LEN) { /* __builtin_va_arg_pack_len () should be replaced by the number of anonymous arguments. */ --- 1941,1948 ---- && id->call_stmt && (decl = gimple_call_fndecl (stmt)) && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL ! && DECL_FUNCTION_CODE (decl) == BUILT_IN_VA_ARG_PACK_LEN ! && ! gimple_call_va_arg_pack_p (id->call_stmt)) { /* __builtin_va_arg_pack_len () should be replaced by the number of anonymous arguments. */ *************** expand_call_inline (basic_block bb, gimp *** 4584,4590 **** /* Record the function we are about to inline. */ id->src_fn = fn; id->src_cfun = DECL_STRUCT_FUNCTION (fn); ! id->call_stmt = stmt; /* If the src function contains an IFN_VA_ARG, then so will the dst function after inlining. Likewise for IFN_GOMP_USE_SIMT. */ --- 4586,4592 ---- /* Record the function we are about to inline. */ id->src_fn = fn; id->src_cfun = DECL_STRUCT_FUNCTION (fn); ! id->call_stmt = call_stmt; /* If the src function contains an IFN_VA_ARG, then so will the dst function after inlining. Likewise for IFN_GOMP_USE_SIMT. */ Index: gcc/tree-inline.h =================================================================== *** gcc/tree-inline.h (revision 246277) --- gcc/tree-inline.h (working copy) *************** struct copy_body_data *** 81,87 **** /* GIMPLE_CALL if va arg parameter packs should be expanded or NULL is not. */ ! gimple *call_stmt; /* Exception landing pad the inlined call lies in. */ int eh_lp_nr; --- 81,87 ---- /* GIMPLE_CALL if va arg parameter packs should be expanded or NULL is not. */ ! gcall *call_stmt; /* Exception landing pad the inlined call lies in. */ int eh_lp_nr; Index: gcc/testsuite/gcc.dg/torture/pr80122.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr80122.c (nonexistent) --- gcc/testsuite/gcc.dg/torture/pr80122.c (working copy) *************** *** 0 **** --- 1,52 ---- + /* { dg-do run } */ + + #define __GNU_ALWAYS_INLINE inline __attribute__(( __always_inline__)) + + #define DEVT_ALL 0 + + #define CMD_ABI_DEVICES 100 + + static __GNU_ALWAYS_INLINE int + send_msg_to_gm_w_dev_t(int msg_type, unsigned int dev_msg_type, + int devt, ...) + { + char s[256]; + int nArgs = __builtin_va_arg_pack_len(); + if (nArgs != 2) + __builtin_abort (); + __builtin_sprintf (s, "%d", __builtin_va_arg_pack ()); + if (__builtin_strcmp (s, "99") != 0) + __builtin_abort (); + /* do something with nArgs and ... */ + return 0; + } + + static __GNU_ALWAYS_INLINE int + send_msg_to_gm(int msg_type, unsigned int dev_msg_type, + ...) + { + int nArgs = __builtin_va_arg_pack_len(); + if (nArgs != 2) + __builtin_abort (); + return send_msg_to_gm_w_dev_t(msg_type, dev_msg_type, + DEVT_ALL, __builtin_va_arg_pack()); + } + + static __GNU_ALWAYS_INLINE int + send_enable(unsigned int dev_msg_type, ...) + { + int nArgs = __builtin_va_arg_pack_len(); + if (nArgs != 2) + __builtin_abort (); + return send_msg_to_gm(CMD_ABI_DEVICES, dev_msg_type, __builtin_va_arg_pack()); + } + + int + main(void) + { + int mode = 99; + + send_enable(1, mode, sizeof(mode)); + + return 0; + }