Bug 33661 - template methods forget explicit local register asm vars
Summary: template methods forget explicit local register asm vars
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.2.2
: P3 normal
Target Milestone: 8.5
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
: 36080 58118 64951 66393 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-10-04 19:33 UTC by Vincent Riviere
Modified: 2021-09-11 14:29 UTC (History)
9 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 3.3.3, 4.1.0, 4.2.3, 4.3.0
Last reconfirmed: 2007-12-10 01:55:24


Attachments
Experimental fix (1.16 KB, patch)
2015-06-09 09:06 UTC, Andreas Krebbel
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Vincent Riviere 2007-10-04 19:33:19 UTC
Hello.

http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Local-Reg-Vars.html#Local-Reg-Vars
"However, using the variable as an asm operand guarantees that the specified register is used for the operand."

This does not always work if the code is inside a template method which gets inlined.

For this testcase, imagine a system call invoked by trap #0. It returns a pointer into d0, and clobbers d1/a0/a1.

$ cat a.cpp
template<class T>
class Tpl
{
public:
        static long* MySysCall()
        {
                register long retval __asm__("d0");

                asm
                (
                        "trap   #0\n\t"
                        "move.l %0,%0\n\t"
                : "=r"(retval)
                :
                : "d1", "a0", "a1"
                );

                return (long*)retval;
        }
};

void f()
{
        long* p = Tpl<char>::MySysCall();
        *p = 1;
}

Note that I added an extra "move.l %0,%0" to see which register is affected to %0.

$ g++ -S a.cpp -O1 -o -
#NO_APP
        .file   "a.cpp"
        .text
        .align  2
        .globl  _Z1fv
        .type   _Z1fv, @function
_Z1fv:
.LFB3:
        link.w %fp,#0
.LCFI0:
        move.l %a2,-(%sp)
.LCFI1:
#APP
        trap    #0
        move.l  %a2,%a2

#NO_APP
        moveq #1,%d0
        move.l %d0,(%a2)
        move.l (%sp)+,%a2
        unlk %fp
        rts
.LFE3:
        .size   _Z1fv, .-_Z1fv
        .globl  __gxx_personality_v0
        .ident  "GCC: (GNU) 4.2.1"
        .section        .note.GNU-stack,"",@progbits

We can see that even though we requested "retval" to be mapped to d0, it has been mapped to a2.

The problem does not appear if the code is in a method of a standard class (not a method of a template).
Another way to get it work is to add "volatile" to the register variable declaration.
Comment 1 Andrew Pinski 2007-12-10 01:55:22 UTC
Confirmed,
It also fails with 3.3 so this is not a regression.
A testcase for PPC:
template<class T>
class Tpl
{
public:
        static long* MySysCall()
        {
                register long retval __asm__("r3");

                asm
                (
                        "trap   #0\n\t"
                        "move.l %0,%0\n\t"
                : "=r"(retval)
                :
                : "r4"
                );

                return (long*)retval;
        }
};

void f()
{
        long* p = Tpl<char>::MySysCall();
        *p = 1;
}

Note I did not change the inline-asm's string though so it will not assemble.

 
Comment 2 Vincent Riviere 2008-02-02 20:48:22 UTC
Still fails in GCC release 4.2.3
Comment 3 Richard Biener 2008-02-05 10:28:20 UTC
This works for me on x86_64 with 4.3.0.
Comment 4 Vincent Riviere 2008-02-05 19:56:37 UTC
Same problem with GCC 4.3-20080201 on target i686-pc-linux-gnu
Comment 5 Andrew Pinski 2008-04-29 17:09:17 UTC
*** Bug 36080 has been marked as a duplicate of this bug. ***
Comment 6 Adam Lackorzynski 2008-04-30 13:34:46 UTC
Even if this is not a regression it would be very helpful if g++ would emit a warning that the register allocation will be ignored. Those bugs are subtle.
Comment 7 Vincent Riviere 2008-04-30 15:32:52 UTC
This is not a regression, however it is a bug, it has to be fixed. Inline assembly coupled with templates is very powerful, however because of this bug it is unusable :-(
If I remember well, a workaround is to put the assembly in an inline global function, then call it from the template method.
Comment 8 Adam Lackorzynski 2008-04-30 20:22:11 UTC
(In reply to comment #7)
> This is not a regression, however it is a bug, it has to be fixed. Inline
> assembly coupled with templates is very powerful, however because of this bug
> it is unusable :-(

I fully agree. I don't know how hard this one is to fix so at least a warning/error would be good to have. Silently ignoring the allocation just causes bugs nobody wants to have.

Comment 9 Stefan Schuh 2011-08-24 10:00:48 UTC
I can confirm this bug in gcc version 4.5.2 on i686-linux-gnu 
and in gcc version 4.6.0 on s390x-ibm-linux-gnu
Comment 10 Aleksandrs Saveljevs 2012-05-31 08:16:41 UTC
Confirming that the bug still exists on ARM, x86 and x86_64 as of GCC 4.6.3.
Comment 11 Adam Lackorzynski 2014-10-26 13:37:27 UTC
Confirming issue still exists for 4.7.4, 4.8.4, 4.9.2 and 5.0 (tested on x86_64).
Comment 12 Andrew Pinski 2015-06-03 10:05:33 UTC
*** Bug 66393 has been marked as a duplicate of this bug. ***
Comment 13 Andreas Krebbel 2015-06-09 09:06:05 UTC
Created attachment 35723 [details]
Experimental fix

The attached patch fixes the problem for me.

There appear to be two problems:

1. When parsing a template function cp_finish_decl returns before the asmspec is set in a var decl.
2. When expanding the template function the assembler_name is zeroed out.
Comment 14 Andrew Pinski 2017-03-30 14:47:27 UTC
*** Bug 64951 has been marked as a duplicate of this bug. ***
Comment 15 Alexey Dobriyan 2019-04-14 11:45:32 UTC
I can only reconfirm this bug still exists with 8.2.0 after rediscovering it independently.

Linux system calls taking 4+ arguments can't be templatized as they require

    register T3 _a3 asm("r10") = a3;


But using

    "r10" (a3)

in assembly input constraints doesn't work either.
Comment 16 Guillaume Delugré 2019-05-18 22:28:34 UTC
Tested on g++ 9.1.0, the bug is still present. Is there any chance of having the Andreas' fix pushed upstream?
Comment 17 Martin Papik 2020-04-11 06:13:29 UTC
Hello, I found a bug, which I think is a duplicate of this one, but am not 100% sure.

Below is a minimal piece of code which triggers the bug. All versions of gcc seem to be affected, as seen on compiler explorer, https://godbolt.org/z/jFMj8b, which also shows a difference in gimple, the templated version is missing the explicit naming attributes.

Is this the same bug? If so, is there some technical reason why a clear miscompilation persists for as long as it seems to? What I mean is this, if a bug like this persists for this long, it could be taken to mean that the bug is too big for a casual volunteer. Would that be the case? Can someone familiar with the code base tell me what I'd need to know to fix this, e.g. what's wrong with the patch, is it better to fix the patch or start from scratch.


$ cat bug.cpp
#define DEMONSTRABLY_IDENTICAL                                  \
        long ret;                                               \
        register long r10 __asm__("r10") = (long)a4;            \
        __asm__ __volatile__ ("syscall"                         \
                : "=a"(ret)                                     \
                : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10)   \
                : "rcx", "r11", "memory"                        \
                );
enum class sysnr : long {
        // accept4 has enough parameters to require extra registers and trigger the bug
        accept4 = 0x120
};
static __inline long sys_01(long n, long a1, long a2, long a3, long a4)
{
        DEMONSTRABLY_IDENTICAL
        return ret;
}
template <sysnr SYS_NR, typename RET, typename T1, typename T2, typename T3, typename T4>
RET sys_02(T1 a1, T2 a2, T3 a3, T4 a4) {
        constexpr long n = (long) SYS_NR;
        DEMONSTRABLY_IDENTICAL
        return (RET)ret;
}
void test_01 () {
        sys_01( (long)sysnr::accept4, 0xfeed01, 0xfeed02, 0xfeed03, 0xfeed04 );
}
void test_02() {
        sys_02<sysnr::accept4, long>( 0xfeed01, 0xfeed02, 0xfeed03, 0xfeed04 );
}
void test_03() {
        sys_02<sysnr::accept4, long, long, long, long, long>( 0xfeed01, 0xfeed02, 0xfeed03, 0xfeed04 );
}

$ g++ -std=c++11 -O1 bug.cpp -c -o bug.c
$ objdump -Cd bug.o 

bug.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <test_01()>:
   0:	41 ba 04 ed fe 00    	mov    $0xfeed04,%r10d
   6:	b8 20 01 00 00       	mov    $0x120,%eax
   b:	bf 01 ed fe 00       	mov    $0xfeed01,%edi
  10:	be 02 ed fe 00       	mov    $0xfeed02,%esi
  15:	ba 03 ed fe 00       	mov    $0xfeed03,%edx
  1a:	0f 05                	syscall 
  1c:	c3                   	retq   

000000000000001d <test_02()>:
  1d:	b8 20 01 00 00       	mov    $0x120,%eax
  22:	bf 01 ed fe 00       	mov    $0xfeed01,%edi
  27:	be 02 ed fe 00       	mov    $0xfeed02,%esi
  2c:	ba 03 ed fe 00       	mov    $0xfeed03,%edx
  31:	41 b8 04 ed fe 00    	mov    $0xfeed04,%r8d
  37:	0f 05                	syscall 
  39:	c3                   	retq   

000000000000003a <test_03()>:
  3a:	b8 20 01 00 00       	mov    $0x120,%eax
  3f:	bf 01 ed fe 00       	mov    $0xfeed01,%edi
  44:	be 02 ed fe 00       	mov    $0xfeed02,%esi
  49:	ba 03 ed fe 00       	mov    $0xfeed03,%edx
  4e:	41 b8 04 ed fe 00    	mov    $0xfeed04,%r8d
  54:	0f 05                	syscall 
  56:	c3                   	retq
Comment 18 GCC Commits 2021-01-28 15:14:14 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:6bb207b468da36d9d99c63409dc4098514759c90

commit r11-6958-g6bb207b468da36d9d99c63409dc4098514759c90
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Jan 28 16:13:11 2021 +0100

    c++: Fix up handling of register ... asm ("...") vars in templates [PR33661, PR98847]
    
    As the testcase shows, for vars appearing in templates, we don't attach
    the asm spec string to the pattern decls, nor pass it back to cp_finish_decl
    during instantiation.
    
    The following patch does that.
    
    2021-01-28  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/33661
            PR c++/98847
            * decl.c (cp_finish_decl): For register vars with asmspec in templates
            call set_user_assembler_name and set DECL_HARD_REGISTER.
            * pt.c (tsubst_expr): When instantiating DECL_HARD_REGISTER vars,
            pass asmspec_tree to cp_finish_decl.
    
            * g++.target/i386/pr98847.C: New test.
Comment 19 GCC Commits 2021-01-29 19:20:12 UTC
The releases/gcc-10 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:cf93f94b3498f3925895fb0bbfd4b64232b9987a

commit r10-9323-gcf93f94b3498f3925895fb0bbfd4b64232b9987a
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Jan 28 16:13:11 2021 +0100

    c++: Fix up handling of register ... asm ("...") vars in templates [PR33661, PR98847]
    
    As the testcase shows, for vars appearing in templates, we don't attach
    the asm spec string to the pattern decls, nor pass it back to cp_finish_decl
    during instantiation.
    
    The following patch does that.
    
    2021-01-28  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/33661
            PR c++/98847
            * decl.c (cp_finish_decl): For register vars with asmspec in templates
            call set_user_assembler_name and set DECL_HARD_REGISTER.
            * pt.c (tsubst_expr): When instantiating DECL_HARD_REGISTER vars,
            pass asmspec_tree to cp_finish_decl.
    
            * g++.target/i386/pr98847.C: New test.
Comment 20 Florian Weimer 2021-01-30 12:40:05 UTC
*** Bug 58118 has been marked as a duplicate of this bug. ***
Comment 21 GCC Commits 2021-04-20 23:31:46 UTC
The releases/gcc-9 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:ef5db37cc4e80b229502bea7d6e2daa95ad6f805

commit r9-9412-gef5db37cc4e80b229502bea7d6e2daa95ad6f805
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Jan 28 16:13:11 2021 +0100

    c++: Fix up handling of register ... asm ("...") vars in templates [PR33661, PR98847]
    
    As the testcase shows, for vars appearing in templates, we don't attach
    the asm spec string to the pattern decls, nor pass it back to cp_finish_decl
    during instantiation.
    
    The following patch does that.
    
    2021-01-28  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/33661
            PR c++/98847
            * decl.c (cp_finish_decl): For register vars with asmspec in templates
            call set_user_assembler_name and set DECL_HARD_REGISTER.
            * pt.c (tsubst_expr): When instantiating DECL_HARD_REGISTER vars,
            pass asmspec_tree to cp_finish_decl.
    
            * g++.target/i386/pr98847.C: New test.
    
    (cherry picked from commit cf93f94b3498f3925895fb0bbfd4b64232b9987a)
Comment 22 GCC Commits 2021-04-22 16:50:15 UTC
The releases/gcc-8 branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:712ffc0ad150aadfa5b91f493075e88fd050189f

commit r8-10878-g712ffc0ad150aadfa5b91f493075e88fd050189f
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Jan 28 16:13:11 2021 +0100

    c++: Fix up handling of register ... asm ("...") vars in templates [PR33661, PR98847]
    
    As the testcase shows, for vars appearing in templates, we don't attach
    the asm spec string to the pattern decls, nor pass it back to cp_finish_decl
    during instantiation.
    
    The following patch does that.
    
    2021-01-28  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/33661
            PR c++/98847
            * decl.c (cp_finish_decl): For register vars with asmspec in templates
            call set_user_assembler_name and set DECL_HARD_REGISTER.
            * pt.c (tsubst_expr): When instantiating DECL_HARD_REGISTER vars,
            pass asmspec_tree to cp_finish_decl.
    
            * g++.dg/opt/pr98847.C: New test.
    
    (cherry picked from commit cf93f94b3498f3925895fb0bbfd4b64232b9987a)
Comment 23 Jakub Jelinek 2021-04-22 17:05:18 UTC
Fixed.