Hi! The PR119190 patch I've posted regresses the PR119120 testcase (not adding to testsuite, as it is fairly hard to scan for that problem). The issue is that for the partial setting of _Complex floating vars through __real__ on it first and __imag__ later (or vice versa) and since we forced all complex vars into SSA form we often have undefined (D) arguments of those COMPLEX_EXPRs. When we don't DCE them (for -O0 debug info reasons), their expansion will copy both the real and imag parts using the floating mode and on some targets like 387 that copying alone can unfortunately trigger exceptions on sNaNs or other problematic bit patterns and for uninitialized memory it can be triggered randomly based on whatever is on the stack before.
The following patch deals just with the unused COMPLEX_EXPRs at -O0, if it is used, either complex lowering should have replaced its parts or it represents a user store somewhere, or passing to function argument etc., all that should be really avoided for partially uninitialized complex. And the patch attempts to find the uninitialized arguments (unfortunately at -O0 we don't forward propagate the (D) SSA_NAMEs) and only copy the initialized parts. Another option would be to arrange for the copying to be done in integral mode rather than floating, kind of VCE and back. Though, not sure how to handle then XFmode or on 32-bit targets TFmode when there is no corresponding intergral mode or it isn't usable. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? Or shall I go the VCE route instead? 2025-03-11 Jakub Jelinek <ja...@redhat.com> PR target/119120 * cfgexpand.cc (expand_gimple_stmt_1): At -O0 expand a COMPLEX_EXPR with zero uses of lhs if it has complex floating point mode as stores of just the parts which aren't known to be uninitialized. --- gcc/cfgexpand.cc.jj 2025-01-07 20:11:04.632662813 +0100 +++ gcc/cfgexpand.cc 2025-03-10 21:28:01.071078448 +0100 @@ -4294,6 +4294,70 @@ expand_gimple_stmt_1 (gimple *stmt) if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target)) promoted = true; + /* At -O0 an unused COMPLEX_EXPR might be kept in the IL by + cplxlower0 pass to ensure correct debug info. If one or both + arguments of COMPLEX_EXPR is unitialized and it is a complex + floating-point mode, don't actually copy the uninitialized + part(s) using floating-point mode, as that could cause extra + exceptions. */ + if (!optimize + && gimple_assign_rhs_code (assign_stmt) == COMPLEX_EXPR + && TREE_CODE (lhs) == SSA_NAME + && has_zero_uses (lhs) + && MEM_P (target) + && SCALAR_FLOAT_MODE_P (GET_MODE_INNER (GET_MODE (target)))) + { + op0 = gimple_assign_rhs1 (assign_stmt); + tree op1 = gimple_assign_rhs2 (assign_stmt); + gimple *g; + bool ignore_op0 = (TREE_CODE (op0) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (op0) + && (!SSA_NAME_VAR (op0) + || VAR_P (SSA_NAME_VAR (op0)))); + bool ignore_op1 = (TREE_CODE (op1) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (op1) + && (!SSA_NAME_VAR (op1) + || VAR_P (SSA_NAME_VAR (op1)))); + tree tem = op0; + while (!ignore_op0 + && TREE_CODE (tem) == SSA_NAME + && (g = SSA_NAME_DEF_STMT (tem)) + && is_gimple_assign (g) + && gimple_assign_rhs_code (g) == SSA_NAME) + { + tem = gimple_assign_rhs1 (g); + if (SSA_NAME_IS_DEFAULT_DEF (tem) + && (!SSA_NAME_VAR (tem) || VAR_P (SSA_NAME_VAR (tem)))) + ignore_op0 = true; + } + tem = op1; + while (!ignore_op1 + && TREE_CODE (tem) == SSA_NAME + && (g = SSA_NAME_DEF_STMT (tem)) + && is_gimple_assign (g) + && gimple_assign_rhs_code (g) == SSA_NAME) + { + tem = gimple_assign_rhs1 (g); + if (SSA_NAME_IS_DEFAULT_DEF (tem) + && (!SSA_NAME_VAR (tem) || VAR_P (SSA_NAME_VAR (tem)))) + ignore_op1 = true; + } + if (ignore_op0 || ignore_op1) + { + if (!ignore_op0) + { + rtx rop0 = expand_normal (op0); + write_complex_part (target, rop0, 0, true); + } + else if (!ignore_op1) + { + rtx rop1 = expand_normal (op1); + write_complex_part (target, rop1, 1, true); + } + break; + } + } + /* If we store into a promoted register, don't directly expand to target. */ temp = promoted ? NULL_RTX : target; Jakub