Skip to content

Conversation

@ivankra
Copy link

@ivankra ivankra commented Dec 23, 2025

It builds with clang 19-22 on Debian Trixie, both with and without ICU.
Passes test suite (except Intl tests) and benchmarks - on par with
JIT-less SpiderMonkey (on Mac M4):

Benchmark chakracore_jitless sm_jitless v8_jitless quickjs
Richards 566.27 ± 44.45 508.91 2050.18 1748.91
DeltaBlue 509.45 ± 34.98 524.45 2224.36 1594.91
Crypto 895.73 ± 42.25 809.82 1902.64 2277.55
RayTrace 1360.36 ± 49.25 1717.91 7105 3132.91
EarleyBoyer 2055.25 ± 77.63 2389 11803.75 3840
RegExp 2533.75 ± 25.57 1012.25 5484.75 859.50
Splay 4166 ± 905.75 3533.25 12368.50 4153.50
NavierStokes 1240.50 ± 12.71 1484.75 3473.25 4385.25
gmean 1324.31 1213.76 4510.95 2425.15

Key issues during port:

  • Different ARM64 vararg calling convention between macOS (DarwinPCS) and Linux (AAPCS64).

    Fixed CALL_ENTRYPOINT_NOASSERT and DECLARE_ARGS_VARARRAY using va_list.__stack from the official ABI - robust and compiler-independent.

    However, there is also JavascriptStackWalker.cpp which depends on the exact stack layout and magic constants, especially ArgOffsetFromFramePtr, this part is more fragile.

  • char is unsigned in Linux ARM64 ABI unlike macOS/Win, breaking int8 code. Make sure __int8 (=char) is always prefixed with signed/unsigned.

  • arm64/*.S files use Microsoft-style ; comments, unsupported by GNU assembler. These were already converted in amd64/*.S files to //, but I propose a simpler fix to just strip them on the fly during the build with sed.

  • Missing _GetNativeSigSimdContext based on https://github.com/dotnet/runtime/blob/main/src/coreclr/pal/src/thread/context.cpp#L927

  • Cpsr register is PState on Linux

  • wchar_t/char16_t mismatches when building with ICU. For now solved with #define wcslen PAL_wcslen etc in ChakraICU.h. It builds at least, though some Intl tests are failing.

Note: binary with JIT crashes - this must be built with ./build.sh --no-jit. cmake already prints a warning that ARM64 JIT is only supported on Windows.

Testing

$ cmake -Bdbg -GNinja -DCHAKRACORE_BUILD_SH=ON -DNO_JIT_SH=1 -DNO_ICU_SH=1 -DSTATIC_LIBRARY_SH=1 -DCC_USES_SYSTEM_ARCH_SH=1 -DCMAKE_BUILD_TYPE=Debug -DINTL_ICU_SH=0 && cmake --build dbg --target check
Results for disable_jit tests
Total: passed 1979, failed 0

One test failing in release:

$ cmake -Brel -GNinja -DDISABLE_JIT=1 -DCHAKRACORE_BUILD_SH=ON -DNO_ICU_SH=1 -DSTATIC_LIBRARY_SH=1 -DCC_USES_SYSTEM_ARCH_SH=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DINTL_ICU_SH=0 && cmake --build rel --target check
Total: passed 1926, failed 1

$ /home/ivan/src/ChakraCore/rel/ch -ExtendedErrorStackForTestHost -BaselineMode -WERExceptionSupport -nonative /home/ivan/src/ChakraCore/test/GlobalFunctions/ParseInt1.js
50:"String Testing :2^32 "	failed Actual:1 Excpected:4294967296
51:"String Testing :2^32 "	failed Actual:-1 Excpected:-4294967296
59:"Base 36 number - slow path "	failed Actual:839437431 Excpected:61019439797496

Intl compiles with some failing tests:

$ cmake -Bdbgi -GNinja -DDISABLE_JIT=1 -DCHAKRACORE_BUILD_SH=ON -DSTATIC_LIBRARY_SH=1 -DCC_USES_SYSTEM_ARCH_SH=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo && cmake --build dbgi --target check
$ cmake -Breli -GNinja -DDISABLE_JIT=1 -DCHAKRACORE_BUILD_SH=ON -DSTATIC_LIBRARY_SH=1 -DCC_USES_SYSTEM_ARCH_SH=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo && cmake --build reli --target check
DebuggerCommon: passed 204, failed 3
GlobalFunctions: passed 12, failed 1
Intl: passed 0, failed 14
Strings: passed 40, failed 3
es6: passed 232, failed 1
----------------------------
Total: passed 1933, failed 22

build.sh:

$ ./build.sh -j 12 -t && cmake --build out/Release --target check
Passed

$ ./build.sh -j 12 -t --debug --no-jit --no-icu && cmake --build out/Debug --target check
Total: passed 1979, failed 0

$ ./build.sh -j 12 -t --debug --no-icu && cmake --build out/Debug --target check
Total: passed 6, failed 2178
Crashes due to JavascriptStackWalker.
But "ARM64 Jit not yet functional on platforms other than windows." warning is printed to user.

Key issues during port:

* Different ARM64 vararg calling convention between macOS (DarwinPCS)
  and Linux (AAPCS64).

  Fixed CALL_ENTRYPOINT_NOASSERT and DECLARE_ARGS_VARARRAY using
  va_list.__stack from the official ABI - robust and compiler-independent.

  However, there is also JavascriptStackWalker.cpp which depends on the
  exact stack layout and magic constants, especially ArgOffsetFromFramePtr,
  this part is more fragile.

* char is unsigned in Linux ARM64 ABI unlike macOS/Win, breaking int8 code.
  Make sure __int8 (=char) is always prefixed with signed/unsigned.

* arm64/*.S files use Microsoft-style ; comments, unsupported by GNU
  assembler. These were already converted in amd64/*.S files to //,
  but I propose a simpler fix to just strip them on the fly during
  the build with sed.

* Missing _GetNativeSigSimdContext based on
  https://github.com/dotnet/runtime/blob/main/src/coreclr/pal/src/thread/context.cpp#L927

* Cpsr register is PState on Linux

* wchar_t/char16_t mismatches when building with ICU.
  For now solved with #define wcslen PAL_wcslen etc in ChakraICU.h.
  It builds at least, though some Intl tests are failing.

Note: binary with JIT crashes - this must be built with ./build.sh --no-jit.
cmake already prints a warning that ARM64 JIT is only supported on Windows.
@ppenzin ppenzin requested review from ppenzin and rhuanjl December 23, 2025 23:06
Copy link
Collaborator

@rhuanjl rhuanjl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work on this; it's certainly good to support another platform.

I've put a few initial comments above though I'm going to want to another slightly more detailed review.

Are you able to sign our Contributor agreement? https://github.com/chakra-core/ChakraCore/blob/master/ContributionAgreement.md

#include "unicode/upluralrules.h"
#endif // ifdef WINDOWS10_ICU

// Use PAL wrappers for Linux arm64 to fix wchar_t/char16_t mismatches.
Copy link
Collaborator

@rhuanjl rhuanjl Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed as pal.h should be doing these defines; needing to add them here suggests the include order is wrong, additionally I'd be surprised if there is anything arm specific about the include order.

Could there be an include order issue with a newer version of clang (vs what we use in CI) or with a different flavour of linux (CI only tests Ubuntu)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my testing, it looks like this needs to go anywhere inside lib/Runtime/Runtime.h between PlatformAgnostic/ChakraPlatform.h and Library/JavascriptFunction.h.

Putting it just before before ChakraPlatform.h (vs immediately after) breaks the build in a spooky action at a distance way:

In file included from /home/ivan/src/ChakraCore/lib/Jsrt/Core/JsrtCore.cpp:20:
/home/ivan/src/ChakraCore/lib/Common/Codex/Utf8Helper.h:223:27: error: no matching function for call to 'wcslen'
  223 |       [  7%] Building CXX object lib/Common/DataStructures/CMakeFiles/Chakra.Common.DataStructures.dir/DictionaryStats.cpp.o
      sourceString, wcslen(sourceString), destStringPtr, &unused);
      |                           ^~~~~~
/usr/include/wchar.h:247:15: note: candidate function not viable: no known conversion from 'LPCWSTR' (aka 'const char16_t *') to 'const wchar_t *' for 1st argument
  247 | extern size_t wcslen (const wchar_t *__s) __THROW __attribute_pure__;
      |               ^       ~~~~~~~~~~~~~~~~~~
/home/ivan/src/ChakraCore/lib/Jsrt/Core/JsrtCore.cpp:919:18: error: no matching function for call to 'wcslen'
  919 |         length = wcslen((const char16 *)content);
      |                  ^~~~~~
/usr/include/wchar.h:247:15: note: candidate function not viable: no known conversion from 'const char16 *' (aka 'const char16_t *') to 'const wchar_t *' for 1st argument
  247 | extern size_t wcslen (const wchar_t *__s) __THROW __attribute_pure__;
      |               ^       ~~~~~~~~~~~~~~~~~~

This is probably due to various #pragma once, Pch.h system and headers being generously over-included all over the place. There's probably a right place somewhere deep in pal where this can be properly fixed, but it is hard to identify it when the build behaves in this way.

ChakraPlatform.h also transitively includes ChakraICU.h and redefining wcs* functions before unicode headers breaks the build in STL headers (perhaps libstdc++ issue? macOS/Windows build I suppose default to clang's libc++):

In file included from /home/ivan/src/ChakraCore/lib/Runtime/PlatformAgnostic/Platform/Common/UnicodeText.Common.cpp:8:
In file included from /home/ivan/src/ChakraCore/lib/Runtime/PlatformAgnostic/UnicodeText.h:8:
In file included from /home/ivan/src/ChakraCore/lib/Runtime/PlatformAgnostic/ChakraICU.h:52:
In file included from /usr/include/unicode/ucol.h:1525:
In file included from /usr/lib/gcc/aarch64-linux-gnu/14/../../../../include/c++/14/string_view:48:
In file included from /usr/lib/gcc/aarch64-linux-gnu/14/../../../../include/c++/14/bits/char_traits.h:42:
In file included from /usr/lib/gcc/aarch64-linux-gnu/14/../../../../include/c++/14/bits/postypes.h:40:
In file included from /usr/lib/gcc/aarch64-linux-gnu/14/../../../../include/c++/14/cwchar:44:
/usr/include/wchar.h:130:12: error: exception specification in declaration does not match previous declaration
  130 | extern int wcscmp (const wchar_t *__s1, const wchar_t *__s2)
      |            ^
/home/ivan/src/ChakraCore/lib/Runtime/PlatformAgnostic/ChakraICU.h:14:17: note: expanded from macro 'wcscmp'
   14 | #define wcscmp  PAL_wcscmp
      |                 ^
/home/ivan/src/ChakraCore/pal/inc/pal.h:6242:23: note: previous declaration is here
 6242 | PALIMPORT int __cdecl PAL_wcscmp(const WCHAR*, const WCHAR*);
      |                       ^

Library/JavascriptFunction.h included in Runtime.h needs these wcs* defines already, so they need to be in place before that point:

In file included from /home/ivan/src/ChakraCore/lib/Runtime/Library/JavascriptFunction.cpp:5:
In file included from /home/ivan/src/ChakraCore/lib/Runtime/Library/RuntimeLibraryPch.h:10:
In file included from /home/ivan/src/ChakraCore/lib/Runtime/./Runtime.h:436:
/home/ivan/src/ChakraCore/lib/Runtime/./Library/JavascriptFunction.h:288:12: error: no matching function for call to 'wcscmp'
  288 |         if(wcscmp(displayName, Js::Constants::AnonymousFunction) == 0)
      |            ^~~~~~
/usr/include/wchar.h:130:12: note: candidate function not viable: no known conversion from 'PCWSTR' (aka 'const char16_t *') to 'const wchar_t *' for 1st argument
  130 | extern int wcscmp (const wchar_t *__s1, const wchar_t *__s2)
      |            ^       ~~~~~~~~~~~~~~~~~~~
1 error generated.

Copy link
Author

@ivankra ivankra Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I found the culprit in /usr/include/c++/14/cwchar:

$ rg def.*wcs /usr/include/
/usr/include/wchar.h
546:#  define wcstol __isoc23_wcstol
547:#  define wcstoul __isoc23_wcstoul
...
/usr/include/c++/14/cwchar
// Get rid of those macros defined in <wchar.h> in lieu of real functions.
...
81:#undef mbsrtowcs
100:#undef wcscat
101:#undef wcschr
102:#undef wcscmp
103:#undef wcscoll
...

So, looks like an issue caused by libstd++ in recent Debian versions.

Tested build on Ubuntu 22.04 - it works without these extra defines in ChakraICU.h.

endif()

if(CC_TARGETS_ARM64)
if(CC_TARGET_OS_LINUX)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see CI running before we remove this warning; I think we should be able to add linux on arm to the cirrus CI.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will bring it back.

// Linux ARM64 uses AAPCS64: first 8 args in x0-x7, rest via stack.
// Fill x2-x7 with nulls here to force the expected stack layout:
// [RetAddr] [function] [callInfo] [args...]
#define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect this to break the JavascriptStackWalker, I saw you said it was "fragile", what's the status?

Copy link
Author

@ivankra ivankra Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The single hard-coded constant ArgOffsetFromFramePtr that JavascriptStackWalker relies on feels rather fragile to me. For now, I set ArgOffsetFromFramePtr=4 - I see 8 extra bytes of what seems like padding or saved registers. But I'd expect it to break with different compilers and optimization flags by analogy with my experience with DECLARE_ARGS_VARARRAY - at first (before I found va_list.__stack) I was trying to fixing it by trying different offsets in _get_va(), but it just kept breaking between optimized and debug builds.

JavascriptStackWalker in JIT-less builds so far works for me with clang 19, 20, and 22, both optimized and debug builds. Perhaps because it only needs to deal with a few specific call sites in this mode. When JIT is enabled it is crashing, but because JIT wasn't supported on macOS, I didn't investigate these crashes further.

writeLine("n.toString(2): " + n.toString(2));
writeLine("n.toString(16): " + n.toString(16));
writeLine("n.toString(25): " + n.toString(25));
if (!numberToTestAsString.endsWith('e21')) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hit this same issue on macOS ARM, and disabled the test for mac see test/Number/rlexe.xml lines 17-24.

Awkwardly we don't have a mechanism for disabling a test for an OS and CPU combination.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll try reenabling that test for macOS then with this change.

@ivankra
Copy link
Author

ivankra commented Dec 24, 2025

Are you able to sign our Contributor agreement? https://github.com/chakra-core/ChakraCore/blob/master/ContributionAgreement.md

Yes, added my name there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants