RFC: Support non-standard extension (call via casted function pointer)

2016-01-25 Thread Michael Karcher
Hello gcc developers,

as discussed in https://ghc.haskell.org/trac/ghc/ticket/11395 (and
forwarded as PR c/69221), ghc generates non-compliant C code that is not
compiled as intended on m68k. This is because its internal Cmm (a C--
dialect) to C compiler needs to declare external functions (no varargs)
without fixing the type. It currently uses "unspecified-function-type*
function();" which is a quite good fit, because the argument list is
still left open, but it fixes the return type to some pointer type.
Before calling that function, the function address is put into a
function pointer of the correct type and invoked via that pointer. This
model currently works fine (possibly by accident) on all architectures
ghc supports except m68k.

I developed a gcc patch that does not change the code generation for
conforming programs but fixes this non-conforming use-case by always
taking the actual return type in the call expression into account, even
if the function declaration to be called is known. Please comment
whether such a patch has any chance to be applied to either gcc upstream
or gcc in Debian.

Regards,
  Michael Karcher
>From 50c0af571f829e54cb3274c05d7be3da41114162 Mon Sep 17 00:00:00 2001
From: Michael Karcher 
Date: Mon, 25 Jan 2016 22:07:37 +0100
Subject: [PATCH 1/1] Extend gcc on m68k to allow calling incorrectly declared
 externals through a function pointer of the correct type.

There are users of gcc (one of them is ghc) that expect to be able to declare
external functions with a dummy type (ghc currently uses
"ptr-to-function fn();") and call them through a function pointer of the
actual type of this function. I reported a ticket on ghc[1] which was
forwarded as PR c/69221 and subsequently closed as INVALID. While I agree
that the use case is way off the standard, and gcc emits the deserved
warnings about incompatible types, this extension seems useful, and at
least for m68k does no harm for conforming code.
---
 gcc/config/m68k/m68k.c | 40 ++
 gcc/testsuite/gcc.target/m68k/cast-fptr1.c | 11 
 gcc/testsuite/gcc.target/m68k/cast-fptr2.c | 12 +
 3 files changed, 53 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/m68k/cast-fptr1.c
 create mode 100644 gcc/testsuite/gcc.target/m68k/cast-fptr2.c

diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c
index 03f474e..da7a5f0 100644
--- a/gcc/config/m68k/m68k.c
+++ b/gcc/config/m68k/m68k.c
@@ -5288,22 +5288,42 @@ m68k_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
 
   /* If the function returns a pointer, push that into %a0.  */
   if (func && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (func
+  {
 /* For compatibility with the large body of existing code which
does not always properly declare external functions returning
pointer types, the m68k/SVR4 convention is to copy the value
returned for pointer functions from a0 to d0 in the function
epilogue, so that callers that have neglected to properly
declare the callee can still find the correct return value in
-   d0.  */
-return gen_rtx_PARALLEL
-  (mode,
-   gen_rtvec (2,
-		  gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (mode, A0_REG),
- const0_rtx),
-		  gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (mode, D0_REG),
- const0_rtx)));
+   d0.
+   expand_call uses the first register in the PARALLEL rtx.  Order
+   the registers such that expand_call uses the same register as
+   if func was not given.  */
+if (POINTER_TYPE_P (valtype))
+{
+  return gen_rtx_PARALLEL
+(mode,
+ gen_rtvec (2,
+gen_rtx_EXPR_LIST (VOIDmode,
+   gen_rtx_REG (mode, A0_REG),
+   const0_rtx),
+gen_rtx_EXPR_LIST (VOIDmode,
+   gen_rtx_REG (mode, D0_REG),
+   const0_rtx)));
+}
+else
+{
+  return gen_rtx_PARALLEL
+(mode,
+ gen_rtvec (2,
+gen_rtx_EXPR_LIST (VOIDmode,
+   gen_rtx_REG (mode, D0_REG),
+   const0_rtx),
+gen_rtx_EXPR_LIST (VOIDmode,
+   gen_rtx_REG (mode, A0_REG),
+   const0_rtx)));
+}
+  }
   else if (POINTER_TYPE_P (valtype))
 return gen_rtx_REG (mode, A0_REG);
   else
diff --git a/gcc/testsuite/gcc.target/m68k/cast-fptr1.c b/gcc/testsuite/gcc.target/m68k/cast-fptr1.c
new file mode 100644
index 000..3116167
--- /dev/null
+++ b/gcc/testsuite/gcc.target/m68k/cast-fptr1.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O -Wno-incompatible-pointer-types" } */
+/* { dg-final { scan-assembler "cmp.l %d0,%d1" }

Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-25 Thread Michael Karcher
On 26.01.2016 08:25, Jeff Law wrote:
> I believe if they make the return type a pointer type, then the m68k
> backend ought to return the value in a0 and d0 to make broken code
> like this work.  It may also be the case that we're not doing this
> properly for indirect calls from a quick scan of m68k_function_arg.
It works properly on the callee side: A function declared to return a
pointer returns in both a0 and d0.
On the caller side, a the result of a function returning a pointer is
expected in a0 (obviously correct for the SVR4/m68k ABI), even if a
"pointer-to-function returning int" is used to call that function.
(which is obviously undefined behaviour. See the linked ghc problem
report for chapter and verse). My patch makes ghc retrieve the value
from d0 instead of a0, if the actually returned value in the
call-expression has a non-pointer type, even if the called function is
known and declared as returning a pointer.

Regards,
  Michael Karcher


Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-26 Thread Michael Karcher
On 26.01.2016 16:40, Richard Biener wrote:
> No, the patch looks somewhat broken to me.  A complete fix would replace
> the target macro FUNCTION_VALUE implementation by implementing the
> function_value hook instead (to also receive the function type called if no
> decl is available and to be able to distinguish caller vs. callee).
>
> I also don't see how changing the called function code will fix anything.
I definitely agree that the approach to move from the FUNCTION_VALUE
macro to the function_value hook is the most clean way to do it. If
there is a consensus among the gcc developers that a patch to support
this non-conforming code should be applied, I would be willing to
prepare a patch as suggested.

The current patch does not change the called code at all. The only time
at which my suggested patch changes gcc behaviour is if a function
declaration is given, that declaration has a pointer type as return
type, but valtype is no pointer type. This (unverified claim) can not
happen in the callee, as the valtype when compiling the return statement
or assembling the epilogue is taken from the function declaration.

> In fact the frobbing of the return value into %d0 should only happen
> if the 'outgoing' arg is true (in the hook implementation) and in the
> 'outgoing' == false case simply disregard 'func' and always use
> 'valtype'.
This frobbing of a pointer return value in %d0 only happens in the
outgoing case already now, because in the non-outgoing case,
m68k_function_value is (unverified claim) only called from expand_call,
which replaces the PARALLEL rtx by the first list element, i.e. %a0. (or
%d0 if !POINTER_TYPE_P(valtype) after my patch).

> So, hookize and change to
>
>   if (outgoing && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (func
> ...
>   else if (POINTER_TYPE_P (valtype))
>...
>  else
>    ...
Looks good and clean to me, but I expect my patch to have the same effect.

Regards,
  Michael Karcher


Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-26 Thread Michael Karcher
On 26.01.2016 21:47, Richard Biener wrote:
>>> So, hookize and change to
>>>
>>>   if (outgoing && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (func
>>> ...
>>>   else if (POINTER_TYPE_P (valtype))
>>>...
>>>  else
>>>...
>> Looks good and clean to me, but I expect my patch to have the same
>> effect.
> I would still prefer the more obvious approach of using the target hook 
> transition.
I intended to express in my last email: Me too, and I will prepare a
patch with a target hook if there is consensus that this patch can be
accepted. I do not want the current patch committed as-is, I just posted
it to get the idea across and start discussion.

Regards,
  Michael Karcher


Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-27 Thread Michael Karcher
On 27.01.2016 16:10, Thorsten Otto wrote:
> > This frobbing of a pointer return value in %d0 only happens in the
> > outgoing case already now, because in the non-outgoing case,
> > m68k_function_value is (unverified claim) only called from expand_call,
> > which replaces the PARALLEL rtx by the first list element, i.e. %a0. (or
> > %d0 if !POINTER_TYPE_P(valtype) after my patch).
>
> Wether pointer values are returned in d0 or a0 only depend on the ABI,
> not the machine type.
Well, this is kind-of correct. Of course the ABI defines whether
pointers are returned in d0, a0 or rax. But the ABI depends on the
machine type and is implemented by the machine-specific backend.

>
> BTW what does ghc expect on x86-64? The ABIs on linux and e.g Win64
> are also quite different with respect to which registers are used to
> pass/ parameters.
The translation from Cmm to C only happens in "unregisterized" builds of
ghc. In this kind of build, ghc has no specific expectations about what
registers will be used for passing or returning value. The only thing
ghc expects is that the code generated *by gcc* to retrieve the returned
value (from a temporary memory location, an integer register, a
floating-point register, an address register) for a function of type X
called through a pointer-to-function-of-type-X matches the code
generated *by gcc* to put that value into some registers.
I trust gcc on Win64 and gcc on linux to know the specific ABIs and
generate correct code.
> And in the m68k-case, you will have similar problems with double
> values. Depending on the ABI, they are returned in either fp0 or d0/d1.
I already checked that, first experimentally, after that by reading the
source code. There are no problems with returning double values, because
the register picked for reading the return value from is determined by
m68k_function_value using only the valtype parameter. This parameter
indicates the actual return type when m68k_function_value is used while
compiling the function and this parameter indicates the return type of
the function-expression that acutally got called when compiling the
caller. In the case of ghc, these types match, so a consistent register
is returned (which depends on the ABI, though).

> Conclusion: IMHO, If ghc fetches the return value from the wrong
> register, then ghc is broken, not gcc.
ghc does not fetch anything from any register. ghc generates C code that
is known to be non-conforming, and gcc decides what register to fetch from.

Regards,
  Michael Karcher


Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-27 Thread Michael Karcher
On 27.01.2016 16:40, Thorsten Otto wrote:
> > We are trying to support ... a direct call to a function with a
> (wrong) prototype via a function
> > pointer cast to the correct type and having a matching implementation of
> > that function elsewhere.
>
> Yes, i did understand that. But you are trying to do that by hacking
> gcc's m68k backend. I still think that's the wrong place to fix.
The non-support of "direct call to a function with a (wrong) prototype
via a function pointer cast" on m68k is not caused by a general issue in
gcc (gcc tracks the return type of the function pointer used to call the
function), but because of a pecularity of the m68k backend (the backend
ignores the return type of the function pointer in certain
circumstances). I don't see how this can be catered for outside of the
backend.

The effect of the patch I posted is reducing the effect of the "return
pointer in both a0 and d0" hack, which interferes with the usual
decision of the return value register. We do agree that the way the
patch achieves this result is ugly and a better way has been proposed.

Regards,
  Michael Karcher


Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-27 Thread Michael Karcher
On 27.01.2016 11:33, Richard Biener wrote:
> On Tue, Jan 26, 2016 at 9:54 PM, Michael Karcher
>  wrote:
>> On 26.01.2016 21:47, Richard Biener wrote:
>>
>>> I would still prefer the more obvious approach of using the target hook 
>>> transition.
>> I intended to express in my last email: Me too, and I will prepare a
>> patch with a target hook if there is consensus that this patch can be
>> accepted. I do not want the current patch committed as-is, I just posted
>> it to get the idea across and start discussion.
> Yes, I think such patch would be accepted.
Great, that is at least one "yes". I still see a lot of doubt on
adjusting the m68k backend to not disturb the call of a function
declared with a wrong prototype when casted to the right type. This
includes Thorsten Otto, who suggests that the m68k backend is the wrong
place to patch, and Andreas Schwab (I took this discussion part off
gcc@gcc.gnu.org, probably a bad idea in hindsight) who points out that
ghc is lying to the compiler (I agree with that) and thus the compiler
does not need to be made compatible with that lie. I don't know the
process of patch acceptence in the gcc project, but one positive vote
against two doubtful or negative votes is not what I would call "consensus".

Regards,
  Michael Karcher


Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-27 Thread Michael Karcher
On 27.01.2016 22:49, Andrew Pinski wrote:
> On Wed, Jan 27, 2016 at 7:17 AM, Richard Biener
>  wrote:
>>
>> We are trying to support
>>
>> t.c
>> ---
>> void *foo();
>>
>> int bar()
>> {
>>   return ((int (*)())foo) ();
>> }
> Why can't ghc produce code like:
> int bar ()
> {
>   int (*foo1)() = foo;
>   asm("":"+r"(foo1));
>   return foo1();
> }
Thank you for the first suggestion about what ghc can do to avoid the
problem without the need to change the internally used Cmm language (as
would be needed to avoid lying about the type of foo).
 
> Yes it no longer produces an direct function call but it might be
> better in the long run anyways.
I don't really care about the direct function call. Using ghc on a
target where it can't generate native machine code is doomed to be slow
anyway.
But I don't see how it "might be better in the long rung anyway". The
first thing I note is that this workaround is specific to gcc. I didn't
check the gcc internals manual, but I am unsure whether the constraint
"r" is portable to be used on function pointers on all architectures gcc
supports. Furthermore, this hides the fact that the use case is not
supported by playing games with the optimizer, whereas Jeff and Richard
try to get this use case supported.

If gcc decides that the m68k backend should not be adjusted to use the
parameter "valtype" to determine the register used by the currently
selected ABI for the return value on caller side, but keep using the
function declaration for that, I will nevertheless propose this change
to ghc.

> Thanks,
> Andrew
Regards,
  Michael Karcher


Re: RFC: Support non-standard extension (call via casted function pointer)

2016-01-28 Thread Michael Karcher
On 28.01.2016 11:04, Andreas Schwab wrote:
> John Paul Adrian Glaubitz  writes:
>
>> [suggestion to use "void" as dummy return type]
>>
>> Wait. Do you think this would actually allow ghc to determine the
>> return type later? If I remember correctly, ghc currently initially
>> declares the function prototype with return type void*, doesn't it?
> Replace a lie with a different lie.  Spot the pattern?
>
> Andreas.
I am sorry, I fail to spot any pattern. As I understood you, you are
opposed to changing gcc because a program that lies to gcc fails to get
the result this program expects. But I don't see any pattern in
"replacing a lie with a different lie". Please be more specific in what
your message should tell the recipients.

In case you refer to the ppc64el issue of ghc, and try to imply that
changing lies at it fits has precedence in ghc history (and thus ghc
either needs to stop lying or find a new lie that just happens to work),
I strongly disagree. In that case, ghc developers not "replace a lie
with a different lie", but replaced a lie "(empty parameter list)" with
the truth "(unknown parameter list)".

Regards,
  Michael Karcher