This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Morello GNU g++ Compiler bug? - Inline assembler in template function ignores register declarations

Hi

Potential GCC bug? - please advise if this is a known issue, if not please inform how to raise an issue on the Gnu toolchain for Morello.

Issue: When defining register variables in a template function, the compiler ignores the directives.  Does not happen if not using templates.

Found on: ARM GNU Toolchain for Morello release.  Applies to Linux hybrid-cap or Linux pure-cap.
- Note: This behaviour does not apply when using clang.

Example Code Snippet

Note: This is based on Arm Morello compartment demo.
Intent is to call into a function following C calling convention, with C0...C6 set to passed in arguments:

#include <stdint.h>

extern uintcap_t AsUintcap(uint32_t);

template <typename Fn>
inline void TestCall(uint32_t id,
    uintcap_t arg0, uintcap_t arg1, uintcap_t arg2,
    uintcap_t arg3, uintcap_t arg4, uintcap_t arg5,
    Fn arg6)
{

    register uintcap_t c0 asm("c0") = AsUintcap(id);
    register uintcap_t c1 asm("c1") = arg0;
    register uintcap_t c2 asm("c2") = arg1;
    register uintcap_t c3 asm("c3") = arg2;
    register uintcap_t c4 asm("c4") = arg3;
    register uintcap_t c5 asm("c5") = arg4;
    register uintcap_t c6 asm("c6") = arg5;

    asm __volatile__("blr %[fn]\n"
        : : [fn] "C"(arg6), "C"(c0), "C"(c1), "C"(c2), "C"(c3), "C"(c4), "C"(c5), "C"(c6)
        );
}

void DoTestCall(uint32_t id,
    uintcap_t arg0, uintcap_t arg1, uintcap_t arg2,
    uintcap_t arg3, uintcap_t arg4, uintcap_t arg5, void *__capability arg6)
{
    TestCall(id, arg0, arg1, arg2, arg3, arg4, arg5, arg6);
}

Compile with g++ -march=morello -save-temps -Wall -Wextra -O3 -std=gnu++14 -c test.cpp  (Note: different optimisation levels give same failure behaviour).

Resulting generated assembler (tidied - comment added is mine):

    stp	x29, x30, [sp, -128]!
    mov	x29, sp
	str	c7, [sp, 16]
	stp	c6, c5, [sp, 32]
	str	c2, [sp, 96]
	str	c1, [sp, 112]
	stp	c4, c3, [sp, 64]
	bl	_Z9AsUintcapj
	ldr	c14, [sp, 112]
	ldp	c2, c6, [sp, 16]
	ldp	c8, c4, [sp, 48]
	ldp	c10, c12, [sp, 80]

// Args passed as c0, c14, c4, c6, c8, c10, c12
// c2 corrupted; being used for branch address
	blr c2
	ldp	x29, x30, [sp], 128
	ret

Compare result if not using a template function (modify example code by removing template statement and add using Fn=void *__capability;):

	stp	x29, x30, [sp, -128]!
	mov	x29, sp
	str	c7, [sp, 16]
	stp	c6, c5, [sp, 32]
	stp	c4, c3, [sp, 64]
	stp	c2, c1, [sp, 96]
	bl	_Z9AsUintcapj
	ldr	c8, [sp, 16]
	ldp	c6, c5, [sp, 32]
	ldp	c4, c3, [sp, 64]
	ldp	c2, c1, [sp, 96]

// c0..c6 set correctly as defined arguments.
// c8 is used for address; no corruption of registers we are using
	blr c8

	ldp	x29, x30, [sp], 128
	ret
 

Thanks
Pete