On 1.04.2025 22:55, Martin Storsjö wrote:
On Tue, 1 Apr 2025, Jacek Caban wrote:
On ARM64EC, function declarations have additional nuances:
- Function names are mangled by prefixing them with "#"
- An unmangled symbol is defined as a weak anti-dependency alias to
the mangled
symbol
- An entry thunk is generated to convert from the x86_64 calling
convention to
the ARM64EC calling convention, used by the emulator
- A .hybmp section entry is generated to associate the function with
its entry
thunk
The compiler can handle all of this if provided with the necessary
information.
Naked functions are the most convenient way to achieve this.
Use naked functions only on Clang. GCC doesn’t support them on ARM
targets
Does this hold for GCC on e.g. aarch64 linux as well, or do you mean
the in-progress aarch64-mingw target?
It’s not supported on aarch64 at all, seems to be blocked by some
ideological concerns, see:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77882
and has broken behavior on x86_64 by emitting .seh_endprologue.
Right, that probably disqualifies using it overall.
Regarding me not liking global scope asm function definitions; I'm
ambivalent about whether I like naked functions more or less than
global scope asm. Using naked functions is better in the sense that it
is clear on a C level what the functions are. But it's also an even
more obscure feature which is even more of a tricky case to use - as
noted by your observations about which compilers support it above.
I mostly agree. I like the idea of naked functions, they provide a much
cleaner and more flexible approach (e.g., allowing static assembly
functions). However, compatibility concerns limit their usefulness.
On ARM64EC in particular, I think the benefits outweigh the drawbacks.
For entry thunks, we could also use _Arm64XGenerateThunk. It’s not yet
supported by Clang, but implementing it shouldn’t be too difficult. I
even started looking into it at one point but never finished the patch.
That said, I find its design unappealing. The idea of inserting a
built-in function in what otherwise looks like an implementation just to
prevent the compiler from emitting that implementation and doing
something else instead feels ugly. And, as I recall, I could crash the
MSVC compiler with my test cases (which isn’t uncommon with MSVC’s
ARM64EC behavior...).
Using naked functions instead provides entry thunks without needing to
deal with _Arm64XGenerateThunk while also handling mangling and aliases.
Anyway, again this was just a note for the record; I do agree that
it's reasonable to use the feature here for arm64ec.
Before proceeding with this direction, I'd like to get a grasp of how
many functions this change will cover. I'd guess that essentially any
arm64 function in a .S file will need to be wrapped in this form, at
least if we want to be able to call them from x86_64 code? Is this the
case for only these couple of fucntions covered in this series, or
will there be dozens of similar functions converted afterwards?
There isn’t much more than this, which is why I eliminated a bunch of
assembly files first. Aside from this series, the main remaining part is
setjmp/longjmp. I haven’t tackled that yet, I was waiting to see how
this series goes first. We could either place only ARM64EC versions in a
C file or move them for all architectures.
There’s also __argtos, but since it’s internal, I think it’s fine to
ignore x86_64 callers and just mangle the name in the .S file instead.
---
mingw-w64-crt/include/internal.h | 8 ++++++++
mingw-w64-crt/math/arm64/nearbyint.c | 7 +++++++
mingw-w64-crt/math/arm64/nearbyintf.c | 7 +++++++
mingw-w64-crt/math/arm64/nearbyintl.c | 7 +++++++
mingw-w64-crt/math/arm64/trunc.c | 7 +++++++
mingw-w64-crt/math/arm64/truncf.c | 7 +++++++
6 files changed, 43 insertions(+)
diff --git a/mingw-w64-crt/include/internal.h
b/mingw-w64-crt/include/internal.h
index b30ae0e5f..445928045 100644
--- a/mingw-w64-crt/include/internal.h
+++ b/mingw-w64-crt/include/internal.h
@@ -287,6 +287,8 @@ static inline unsigned int __mingw_statusfp(void)
return flags;
}
+#ifndef __clang__
+
#define __ASM_FUNC_CODE(name,code) \
asm(".text\n\t" \
".p2align 2\n\t" \
@@ -295,6 +297,12 @@ static inline unsigned int __mingw_statusfp(void)
__MINGW64_STRINGIFY(__MINGW_USYMBOL(trunc))
":\n\t" \
code "\n\t");
+#else
+
+#define __ASM_FUNC_CODE(name,code) asm(code "\n\t");
+
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/mingw-w64-crt/math/arm64/nearbyint.c
b/mingw-w64-crt/math/arm64/nearbyint.c
index 64ade2750..02e433722 100644
--- a/mingw-w64-crt/math/arm64/nearbyint.c
+++ b/mingw-w64-crt/math/arm64/nearbyint.c
@@ -7,8 +7,15 @@
#include <math.h>
#include <internal.h>
+#ifdef __clang__
+double __attribute__((naked)) nearbyint(double x)
+{
+#endif
__ASM_FUNC_CODE(nearbyint,
This ifdeffery feels quite ugly TBH. Ideally I wouldn't want to have
any such ifdefs within the implementation files. Is there any way that
we hide these details inside the macro?
Then we'd need to pass the function signature through the macro, which
is tricky, especially for the function arguments.
But I have a faint memory that it may be possible to pass such things
within parentheses, e.g. we could do __ASM_FUNC_CODE(pow, double,
(double x, double y), "<code>").
If I'm mistaken, then it's indeed more tricky, but perhaps we could at
least make a version for functions that take one argument and returns
the same type?
I think that would work. I do like the idea of having an explicit C
signature spelled out rather than buried in macro arguments, but I admit
the extra #ifdef usage isn’t great. I’ll make the change.
Thanks,
Jacek
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public