Bug 95222 - [10/11 Regression] GCC 10.1 x86 issue with function pointers with calling convention attribute and template specialization
Summary: [10/11 Regression] GCC 10.1 x86 issue with function pointers with calling con...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 10.1.0
: P3 normal
Target Milestone: 10.2
Assignee: Jason Merrill
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2020-05-19 20:55 UTC by raptorfactor
Modified: 2022-01-03 22:01 UTC (History)
6 users (show)

See Also:
Host:
Target: x86_64-*-* i?86-*-*
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-05-23 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description raptorfactor 2020-05-19 20:55:32 UTC
The following code (which is a simplified repro from a much larger function introspection library) used to work with GCC 9.x, but does not with the update to 10.1. Is this a compiler/toolchain bug or is there something incorrect about the code that I'm missing (obviously it's non-standard and relying on the behavior of vendor extensions)?

>>>>>>>>>>>>>>>

#if defined(_MSC_VER)
#define CC_FASTCALL __fastcall
#define CC_STDCALL __stdcall
#else
#define CC_FASTCALL __attribute__((fastcall))
#define CC_STDCALL __attribute__((stdcall))
#endif

template <typename FuncT>
struct FuncResult;

template <typename R, typename... Args>
struct FuncResult<R(*)(Args...)>
{
    using type = R;
};

template <typename R, typename... Args>
struct FuncResult<R(CC_FASTCALL*)(Args...)>
{
    using type = R;
};

template <typename R, typename... Args>
struct FuncResult<R(CC_STDCALL*)(Args...)>
{
    using type = R;
};

template <typename FuncT>
auto wrap(FuncT f) -> typename FuncResult<FuncT>::type
{
    return f(1, 2, 3);
}

int CC_FASTCALL func1(int x, int y, int z)
{
    return x + y + z;
}

int CC_STDCALL func2(int x, int y, int z)
{
    return x + y + z;
}

int main()
{
    return wrap(&func1) + wrap(&func2);
}

>>>>>>>>>>>>>>>

With the calling convention specific specialization:

>>>>>>>>>>>>>>>

main.cpp:19:8: error: redefinition of 'struct FuncResult<R (*)(Args ...)>'
   19 | struct FuncResult<R(CC_FASTCALL*)(Args...)>
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:13:8: note: previous definition of 'struct FuncResult<R (*)(Args ...)>'
   13 | struct FuncResult<R(*)(Args...)>
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~

>>>>>>>>>>>>>>>

Without the calling convention specific specialization:

>>>>>>>>>>>>>>>

main.cpp:50:23: error: no matching function for call to 'wrap(int (__attribute__((fastcall)) *)(int, int, int))'
   50 |     return wrap(&func1) + wrap(&func2);
      |                       ^
main.cpp:33:6: note: candidate: 'template<class FuncT> typename FuncResult<FuncT>::type wrap(FuncT)'
   33 | auto wrap(FuncT f) -> typename FuncResult<FuncT>::type
      |      ^~~~
main.cpp:33:6: note:   template argument deduction/substitution failed:
main.cpp: In substitution of 'template<class FuncT> typename FuncResult<FuncT>::type wrap(FuncT) [with FuncT = int (__attribute__((fastcall)) *)(int, int, int)]':
main.cpp:50:23:   required from here
main.cpp:33:6: error: invalid use of incomplete type 'struct FuncResult<int (__attribute__((fastcall)) *)(int, int, int)>'
main.cpp:10:8: note: declaration of 'struct FuncResult<int (__attribute__((fastcall)) *)(int, int, int)>'
   10 | struct FuncResult;
      |        ^~~~~~~~~~

>>>>>>>>>>>>>>>

Damned if you do, damned if you don't. Is anyone aware of a workaround for this? It's blocking my build and I can't figure out an easy workaround.

Thanks.
Comment 1 raptorfactor 2020-05-19 20:57:33 UTC
$ g++ -v
Using built-in specs.
COLLECT_GCC=C:\Redacted\msys2-x86_64-rolling\mingw32\bin\g++.exe
COLLECT_LTO_WRAPPER=C:/Redacted/msys2-x86_64-rolling/mingw32/bin/../lib/gcc/i686-w64-mingw32/10.1.0/lto-wrapper.exe
Target: i686-w64-mingw32
Configured with: ../gcc-10.1.0/configure --prefix=/mingw32 --with-local-prefix=/mingw32/local --build=i686-w64-mingw32 --host=i686-w64-mingw32 --target=i686-w64-mingw32 --with-native-system-header-dir=/mingw32/i686-w64-mingw32/include --libexecdir=/mingw32/lib --enable-bootstrap --with-arch=i686 --with-tune=generic --enable-languages=c,lto,c++,fortran,ada,objc,obj-c++ --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-filesystem-ts=yes --enable-libstdcxx-time=yes --disable-libstdcxx-pch --disable-libstdcxx-debug --disable-isl-version-check --enable-lto --enable-libgomp --disable-multilib --enable-checking=release --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --disable-plugin --with-libiconv --with-system-zlib --with-gmp=/mingw32 --with-mpfr=/mingw32 --with-mpc=/mingw32 --with-isl=/mingw32 --with-pkgversion='Rev2, Built by MSYS2 project' --with-bugurl=https://sourceforge.net/projects/msys2 --with-gnu-as --with-gnu-ld --disable-sjlj-exceptions --with-dwarf2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.1.0 (Rev2, Built by MSYS2 project)
Comment 2 Jakub Jelinek 2020-05-23 20:44:55 UTC
Rejected since r10-7998-g5f1cd1da1a805c3d00332da45c3ab78a3931af63 , on Linux needs -m32.
Comment 3 GCC Commits 2020-05-27 14:10:27 UTC
The master branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:7e7d9fcff56385812764cba63e1ebf6f4c6c0320

commit r11-662-g7e7d9fcff56385812764cba63e1ebf6f4c6c0320
Author: Jason Merrill <jason@redhat.com>
Date:   Mon May 25 19:04:05 2020 -0400

    c++: Fix stdcall attribute in template. [PR95222]
    
    Another case that breaks with my fix for PR90750: we shouldn't move type
    attributes in TYPENAME context either, as there's no decl for them to move
    to.
    
    gcc/cp/ChangeLog:
    
            PR c++/95222
            * decl.c (grokdeclarator): Don't shift attributes in TYPENAME
            context.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/95222
            * g++.dg/ext/tmplattr10.C: New test.
Comment 4 GCC Commits 2020-05-27 14:11:51 UTC
The releases/gcc-10 branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:0c473d8f32510fcc96d584ee5099b856cfd3d8d6

commit r10-8189-g0c473d8f32510fcc96d584ee5099b856cfd3d8d6
Author: Jason Merrill <jason@redhat.com>
Date:   Mon May 25 19:04:05 2020 -0400

    c++: Fix stdcall attribute in template. [PR95222]
    
    Another case that breaks with my fix for PR90750: we shouldn't move type
    attributes in TYPENAME context either, as there's no decl for them to move
    to.
    
    gcc/cp/ChangeLog:
    
            PR c++/95222
            * decl.c (grokdeclarator): Don't shift attributes in TYPENAME
            context.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/95222
            * g++.dg/ext/tmplattr10.C: New test.
    
    (cherry picked from commit 7e7d9fcff56385812764cba63e1ebf6f4c6c0320)
Comment 5 Jason Merrill 2020-05-27 14:12:57 UTC
Fixed for 10.2/11.
Comment 6 Rainer Orth 2020-05-28 07:46:40 UTC
The new testcase FAILs on 32-bit Solaris/x86:

+FAIL: g++.dg/ext/tmplattr10.C  -std=c++98 (test for excess errors)

Excess errors:
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:15:31: error: variadic templates only available with '-std=c++11' or '-std=gnu++11'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:16:28: error: variadic templates only available with '-std=c++11' or '-std=gnu++11'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:18:11: error: expected nested-name-specifier before 'type'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:21:31: error: variadic templates only available with '-std=c++11' or '-std=gnu++11'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:22:39: error: variadic templates only available with '-std=c++11' or '-std=gnu++11'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:24:11: error: expected nested-name-specifier before 'type'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:27:31: error: variadic templates only available with '-std=c++11' or '-std=gnu++11'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:28:38: error: variadic templates only available with '-std=c++11' or '-std=gnu++11'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:30:11: error: expected nested-name-specifier before 'type'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:34:6: error: ISO C++ forbids declaration of 'wrap' with no type [-fpermissive]
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:34:1: error: top-level declaration of 'wrap' specifies 'auto'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:34:51: error: trailing return type only available with '-std=c++11' or '-std=gnu++11'
/vol/gcc/src/hg/master/local/gcc/testsuite/g++.dg/ext/tmplattr10.C:51:12: error: 'wrap' was not declared in this scope
Comment 7 Jason Merrill 2020-05-29 16:24:12 UTC
Testcase fixed.
Comment 8 Kevin Puetz 2022-01-03 15:37:24 UTC
This bug can also manifest as wrong-code, e.g.

>>>>>>>>>>>>>>>

template<bool bIsStdcall, typename T> struct func_ptr_t;

template<typename T> struct func_ptr_t<false,T> {
	using type = void(*)(T);
};

template<typename T> struct func_ptr_t<true,T> {
#if 1
	using type = void(__attribute__((__stdcall__))*)(T);
#else
	using type = void(__attribute__((__stdcall__))*)(int); // this works, using T is important somehow
#endif
};

#if 1
	using foo_stdcall_ptr = func_ptr_t<true,int>::type;
	using foo_cdecl_ptr = func_ptr_t<false,int>::type;
#else
	using foo_stdcall_ptr = void(__attribute__((__stdcall__))*)(int,int);
	using foo_cdecl_ptr = void(*)(int,int);
#endif

foo_stdcall_ptr foo_stdcall;
foo_cdecl_ptr foo_cdecl;

void bar_stdcall() {
	foo_stdcall(1);
}

void bar_cdecl() {
	foo_cdecl(1);
}

>>>>>>>>>>>>>>>

Should compile so that the bar_stdcall versions pops fewer bytes off the stack (as foo_stdcall already cleaned up its own arguments).

>>>>>>>>>>>>>>>

--- bar_cdecl.S	2021-03-09 00:54:58.404022904 +0000
+++ bar_stdcall.S	2021-03-09 00:52:07.900015002 +0000
@@ -1,15 +1,15 @@
-<bar_cdecl()>:
+<bar_stdcall()>:
    0:	f3 0f 1e fb          	endbr32 
    4:	55                   	push   %ebp
    5:	89 e5                	mov    %esp,%ebp
    7:	83 ec 08             	sub    $0x8,%esp
-   a:	e8 fc ff ff ff       	call   32 <bar_cdecl()+0xb>
+   a:	e8 fc ff ff ff       	call   b <bar_stdcall()+0xb>
    f:	05 01 00 00 00       	add    $0x1,%eax
   14:	8b 80 00 00 00 00    	mov    0x0(%eax),%eax
   1a:	83 ec 0c             	sub    $0xc,%esp
   1d:	6a 01                	push   $0x1
   1f:	ff d0                	call   *%eax
-  21:	83 c4 10             	add    $0x10,%esp
+  21:	83 c4 0c             	add    $0xc,%esp
   24:	90                   	nop
   25:	c9                   	leave  
   26:	c3                   	ret    


>>>>>>>>>>>>>>>

But doesn't in 9.4, 10.0, and 10.1 (seems to be fixed again in 10.2+, presumably per this fix). I mention this only because I finally found this relevant PR, and perhaps knowing it might affect decisions about which branches should get fixed - e.g. 9.x is in stage 4 but this is a 9.3.0->9.4.0 regression (presumably PR090750), and also wrong-code. But we were able to work around it, and so I'm not directly arguing for (or against) a backport to 

Ubuntu 20.04 compiler **is** affected despite claiming to be 9.3.0 - Ubuntu has seemingly backported the gcc 9 branch through 4ad02cfb768 (git-updates.patch in gcc-9_9.3.0-17ubuntu1~20.04.debian.tar.xz). But I'll raise that separately in their bug tracker.
Comment 9 Kevin Puetz 2022-01-03 22:01:01 UTC
I accidentally dropped a word, meant to say:
I'm not directly arguing for (or against) a backport to (a hypothetical 9.5.0)

Just noting that this issue could also manifest as wrong-code, for consideration in case there's going to be one.