This is an archive of the discontinued LLVM Phabricator instance.

[MCU] PR26438: Fix assertion failure on function returning an empty struct or union
ClosedPublic

Authored by d.zobnin.bugzilla on Feb 2 2016, 9:01 AM.

Diff Detail

Repository
rL LLVM

Event Timeline

d.zobnin.bugzilla retitled this revision from to [MCU] PR26438: Fix assertion failure on function returning an empty struct or union.
d.zobnin.bugzilla updated this object.
d.zobnin.bugzilla added a subscriber: cfe-commits.

Why should empty structs and unions) be return in memory? I don't remember
I called for it in MCU psABI.

rnk edited edge metadata.Feb 2 2016, 10:47 AM

If the ABI is still open to small changes and clarifications, can we make sure that C and C++ empty structs are passed the same way? Unfortunately, on normal x86_64 SysV GCC doesn't pass them the same way and Clang has to follow suit.

DavidKreitzer edited edge metadata.Feb 3 2016, 9:07 AM

Denis, can you please explain your rationale for choosing to return 0-sized aggregates in memory for MCU? It doesn't match gcc behavior. For example,

int g;
union U {} u;
union U f(int a, int b, int c) { g = a + b + c; return u; }
void f1() { f(1, 2, 3); }

Produces this, where all three incoming arguments are in registers. With your patch, %eax will be used for the return value pointer.

f:
	addl	%eax, %edx
	subl	$4, %esp
	addl	%edx, %ecx
	movl	%ecx, g
	popl	%eax
	ret

(Using i586-intel-elfiamcu-gcc 5.2.1 20150820)

HJ, even though a strict reading of the ABI suggests that a 0-size aggregate should be returned in register, it would be a good idea to make this more explicit in the ABI here, by adding the underlined text, for example.

Returning Values
Table 2.4 lists the location used to return a value for each fundamental data type.
Aggregate types (structs and unions) are returned as follows:
• Short aggregate types no larger than 8 bytes, including 0-length aggregate types, are returned in %edx:%eax.
The most significant 32 bits are returned in %edx. The least significant 32
bits are returned in %eax.
• Other aggregate types are returned in memory.

I am planning to update i386, x86-64 and IA MCU psABIs to address how to
pass and return C++ empty class after resolving:

https://llvm.org/bugs/show_bug.cgi?id=26337

by updating C++ ABI to explicitly pass and return C++ empty class like C empty
structure.

Thank you for the review!

David, I started from the assertion failure and was going up the call stack trying to understand what should be changed to make the assertion go away. In shouldReturnTypeInRegister() function there is isRegisterSize(Size) check for i386, which returns false for 0-sized types, so I naively assumed that for MCU we missed the 0-case check. I must have read the ABI more carefully...

While searching for a correct solution for this, I saw that 'void' return type is handled in classifyReturnType() by returning ABIArgInfo::getIgnore(). According to the comments:

/// Ignore - Ignore the argument (treat as void). Useful for void and
/// empty structs.
Ignore,

Does it mean that we can treat empty structs and unions in return type as void in this case?

From an MCU ABI perspective, yes, returning an empty struct is equivalent to returning void.

Empty struct/union should be passed and returned as void for all IA psABs.

d.zobnin.bugzilla edited edge metadata.

Thank you for your reply!

Added a check for empty struct/union to treat them as void, updated the test accordingly.

Please take a look.

rnk accepted this revision.Feb 10 2016, 4:29 PM
rnk edited edge metadata.

lgtm

This revision is now accepted and ready to land.Feb 10 2016, 4:29 PM
This revision was automatically updated to reflect the committed changes.
alexanderkyte added a subscriber: alexanderkyte.EditedAug 22 2017, 10:15 AM

This change is causing mono's native interop with C code compiled from clang to break. After some investigation, we're tracked bitcode differences between tests that fail versus not. When we upgrade the compiler to Xcode 8.3, we see that code generated to call functions returning empty structs will now generate code that crashes.

This seems to be us trying to pass arguments assuming that clang exhibits the same behavior it used to. It is easy enough for us to generate code for what clang does now, but that code would break if forced to interact with C compiled by gcc and older clangs.

Is there a way for callers of clang-emitted code to detect which ABI clang is going to use when returning an empty struct? This ABI change does not seem to admit backwards compatibility.

xcode73's clang -emit-llvm generates:

  • define void @ret(%struct.AStruct* noalias sret %agg.result, i32 %a) #0 {

while xcode83's clang generates:

  • define void @ret(i32) #0 {