Bug 91974 - function not sequenced before function argument
Summary: function not sequenced before function argument
Status: ASSIGNED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: ---
Assignee: Jakub Jelinek
URL:
Keywords:
Depends on:
Blocks: 91987
  Show dependency treegraph
 
Reported: 2019-10-02 16:16 UTC by Barry Revzin
Modified: 2019-11-21 17:13 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work: 10.0
Known to fail:
Last reconfirmed: 2019-10-02 00:00:00


Attachments
gcc10-pr91974.patch (886 bytes, patch)
2019-10-03 09:01 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Barry Revzin 2019-10-02 16:16:37 UTC
From StackOverflow (https://stackoverflow.com/q/58204296/2069064), reduced:

extern void call(int);

void a(int) {
    call(0);
}

void b(int) {
    call(1);
}

int main() {
    using T = void(*)(int);

    T f = a;
    f((f=b,0));
}

This is a well-defined (if weird) program that should a - that is call(0). With gcc, it invokes call(1) (that is, the assignment that happens in the initialization of the function parameter happens before the load of the function itself). On -O2, the codegen is:

main:
        sub     rsp, 8
        mov     edi, 1
        call    call(int)
        xor     eax, eax
        add     rsp, 8
        ret
Comment 1 Andrew Pinski 2019-10-02 17:45:15 UTC
I dont think this is well defined.  A call and its arguments are not sequence points.  Yes there is a sequence point between the assignment and 0 but nothing else.  Note c++17 does change the rules and I have not read them yet.
Comment 2 Barry Revzin 2019-10-02 18:22:29 UTC
C++17 does change this rule. expr.call/8:

The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.
Comment 3 Andrew Pinski 2019-10-02 19:36:03 UTC
Just to make sure, you are using -std=c++17 or -std=gnu++17 (or -fstrong-eval-order)?  Because it is not obvious from this report.
Comment 4 Barry Revzin 2019-10-02 20:38:23 UTC
Yes, sorry if that wasn't clear, this is with -std=c++17.
Comment 5 Jakub Jelinek 2019-10-02 20:44:16 UTC
I think we need something like:
--- gcc/cp/cp-gimplify.c.jj	2019-09-27 22:13:18.365903348 +0200
+++ gcc/cp/cp-gimplify.c	2019-10-02 22:42:41.878588998 +0200
@@ -818,6 +818,20 @@ cp_gimplify_expr (tree *expr_p, gimple_s
 
     case CALL_EXPR:
       ret = GS_OK;
+      if (flag_strong_eval_order == 2
+	  && CALL_EXPR_FN (*expr_p)
+	  && cp_get_callee_fndecl_nofold (*expr_p) == NULL_TREE)
+	{
+	  enum gimplify_status t
+	    = gimplify_expr (&CALL_EXPR_FN (*expr_p), pre_p, NULL,
+			     is_gimple_call_addr, fb_rvalue);
+	  if (t == GS_ERROR)
+	    ret = GS_ERROR;
+	  else if (TREE_CODE (CALL_EXPR_FN (*expr_p)) != SSA_NAME)
+	    CALL_EXPR_FN (*expr_p)
+	      = get_initialized_tmp_var (CALL_EXPR_FN (*expr_p), pre_p,
+					 NULL);
+	}
       if (!CALL_EXPR_FN (*expr_p))
 	/* Internal function call.  */;
       else if (CALL_EXPR_REVERSE_ARGS (*expr_p))
Comment 6 Jakub Jelinek 2019-10-03 09:01:10 UTC
Created attachment 46992 [details]
gcc10-pr91974.patch

The above patch broke a couple of devirtualization etc. testcases, this version should fix that.  Untested so far except for the tests that FAILed with the previous patch.
Comment 7 Jakub Jelinek 2019-10-04 06:54:36 UTC
Author: jakub
Date: Fri Oct  4 06:54:05 2019
New Revision: 276562

URL: https://gcc.gnu.org/viewcvs?rev=276562&root=gcc&view=rev
Log:
	PR c++/91974
	* cp-gimplify.c (cp_gimplify_expr) <case CALL_EXPR>: For
	-fstrong-eval-order ensure CALL_EXPR_FN side-effects are evaluated
	before any arguments.  Additionally, ensure CALL_EXPR_FN that isn't
	invariant nor OBJ_TYPE_REF nor SSA_NAME is forced into a temporary.

	* g++.dg/cpp1z/eval-order5.C: New test.

Added:
    trunk/gcc/testsuite/g++.dg/cpp1z/eval-order5.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/cp-gimplify.c
    trunk/gcc/testsuite/ChangeLog
Comment 8 Richard Biener 2019-10-04 07:08:09 UTC
Fixed on trunk (sofar?)
Comment 9 Jakub Jelinek 2019-10-21 11:47:40 UTC
Author: jakub
Date: Mon Oct 21 11:47:09 2019
New Revision: 277256

URL: https://gcc.gnu.org/viewcvs?rev=277256&root=gcc&view=rev
Log:
	Backported from mainline
	2019-10-04  Jakub Jelinek  <jakub@redhat.com>

	PR c++/91974
	* cp-gimplify.c (cp_gimplify_expr) <case CALL_EXPR>: For
	-fstrong-eval-order ensure CALL_EXPR_FN side-effects are evaluated
	before any arguments.  Additionally, ensure CALL_EXPR_FN that isn't
	invariant nor OBJ_TYPE_REF nor SSA_NAME is forced into a temporary.

	* g++.dg/cpp1z/eval-order5.C: New test.

Added:
    branches/gcc-9-branch/gcc/testsuite/g++.dg/cpp1z/eval-order5.C
Modified:
    branches/gcc-9-branch/gcc/cp/ChangeLog
    branches/gcc-9-branch/gcc/cp/cp-gimplify.c
    branches/gcc-9-branch/gcc/testsuite/ChangeLog
Comment 10 Jakub Jelinek 2019-10-21 12:07:45 UTC
The originally reported issue fixed for 9.3+ too.  Not really sure if it is a good idea to backport the shift and array ref changes, those are quite invasive.
Comment 11 Jakub Jelinek 2019-11-21 17:13:28 UTC
Author: jakub
Date: Thu Nov 21 17:12:57 2019
New Revision: 278577

URL: https://gcc.gnu.org/viewcvs?rev=278577&root=gcc&view=rev
Log:
	Backported from mainline
	2019-10-04  Jakub Jelinek  <jakub@redhat.com>

	PR c++/91974
	* cp-gimplify.c (cp_gimplify_expr) <case CALL_EXPR>: For
	-fstrong-eval-order ensure CALL_EXPR_FN side-effects are evaluated
	before any arguments.  Additionally, ensure CALL_EXPR_FN that isn't
	invariant nor OBJ_TYPE_REF nor SSA_NAME is forced into a temporary.

	* g++.dg/cpp1z/eval-order5.C: New test.

Added:
    branches/gcc-8-branch/gcc/testsuite/g++.dg/cpp1z/eval-order5.C
Modified:
    branches/gcc-8-branch/gcc/cp/ChangeLog
    branches/gcc-8-branch/gcc/cp/cp-gimplify.c
    branches/gcc-8-branch/gcc/testsuite/ChangeLog