Bug 101326 - std::optional returns forced through stack
Summary: std::optional returns forced through stack
Status: RESOLVED DUPLICATE of bug 95405
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 12.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: missed-optimization
: 109631 (view as bug list)
Depends on:
Blocks: argument, return 109281
  Show dependency treegraph
 
Reported: 2021-07-05 16:37 UTC by Tamar Christina
Modified: 2024-01-30 16:00 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-07-05 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tamar Christina 2021-07-05 16:37:40 UTC
The simple example

#include <optional>

std::optional<long> foo() {
    return 0;
}

shows suboptimal codegen at -O3 -std=c++17.

The values seem to be forced through the stack.  On AArch64 we get

foo():
        sub     sp, sp, #16
        mov     w0, 1
        strb    w0, [sp, 8]
        mov     x0, 0
        ldr     x1, [sp, 8]
        add     sp, sp, 16
        ret

instead of (what clang gives)

foo():                                // @foo()
        mov     w1, #1
        mov     x0, xzr
        ret

On x86 it's the same:

foo():
        mov     QWORD PTR [rsp-24], 0
        mov     rax, QWORD PTR [rsp-24]
        mov     BYTE PTR [rsp-16], 1
        mov     rdx, QWORD PTR [rsp-16]
        ret

vs

foo():                                // @foo()
        mov     w1, #1
        mov     x0, xzr
        ret
Comment 1 Tamar Christina 2021-07-05 16:45:47 UTC
last example for x86 should have been

foo():                                # @foo()
        xor     eax, eax
        mov     dl, 1
        ret
Comment 2 Andrew Pinski 2021-07-05 17:07:46 UTC
Take:
struct g {
  unsigned long b;
};

struct a
{
  struct g g1;
  unsigned char c;
};

static inline void h(struct g *a)
{
  a->b = 0;
}

static inline void j(struct a *a)
{
  h(&a->g1);
  a->c = 1;
}

struct a f(void)
{
  struct a a;
  j(&a);
  return a;
}

---- CUT ----
It works, I suspect in the case of std::optional, std::optional is marked as BLKmode rather than TImode as the struct is marked as "ADDRESSABLE".
Comment 3 Richard Biener 2021-07-06 06:38:14 UTC
I think the issue is simply that RTL expansion forces the object to memory
since that is what GIMPLE does:

struct optional foo ()
{
  struct optional D.12374;

  <bb 2> [local count: 1073741824]:
  MEM <long int> [(struct optional *)&D.12374] = 0;
  MEM <unsigned char> [(struct optional *)&D.12374 + 8B] = 1;
  return D.12374;
}

and yes, if it would have had TImode we could expand it to a register pair
but it has BLKmode because it's TYPE_NEEDS_CONSTRUCTING(?), the type
isn't TREE_ADDRESSABLE for me.
Comment 4 Andrew Pinski 2021-07-06 07:40:55 UTC
(In reply to Richard Biener from comment #3)
> and yes, if it would have had TImode we could expand it to a register pair
> but it has BLKmode because it's TYPE_NEEDS_CONSTRUCTING(?), the type
> isn't TREE_ADDRESSABLE for me.

Yes TYPE_NEEDS_CONSTRUCTING, I had thought they were the same bit but I am wrong.
Comment 5 Andrew Pinski 2023-04-26 09:46:37 UTC
*** Bug 109631 has been marked as a duplicate of this bug. ***
Comment 6 Andrew Pinski 2023-07-12 21:35:03 UTC
Dup

*** This bug has been marked as a duplicate of bug 95405 ***