diff --git a/include/nlohmann/detail/view/macro_scope.hpp b/include/nlohmann/detail/view/macro_scope.hpp index bbe1a240b..f19492648 100644 --- a/include/nlohmann/detail/view/macro_scope.hpp +++ b/include/nlohmann/detail/view/macro_scope.hpp @@ -40,6 +40,15 @@ #define NLOHMANN_VIEW_NOINLINE #endif +// GCC: a function compiled as written, without the clones in which interprocedural +// constant propagation drops an argument that every caller passes as the same constant +// (calling such a clone changes the code GCC generates for the parse loop that calls it) +#if defined(__GNUC__) && !defined(__clang__) + #define NLOHMANN_VIEW_NOCLONE __attribute__((noclone)) +#else + #define NLOHMANN_VIEW_NOCLONE +#endif + #if defined(__GNUC__) || defined(__clang__) #define NLOHMANN_VIEW_NODISCARD __attribute__((warn_unused_result)) #elif defined(_MSC_VER) diff --git a/include/nlohmann/detail/view/macro_unscope.hpp b/include/nlohmann/detail/view/macro_unscope.hpp index a9aec867b..0c4fab4d2 100644 --- a/include/nlohmann/detail/view/macro_unscope.hpp +++ b/include/nlohmann/detail/view/macro_unscope.hpp @@ -15,6 +15,7 @@ #undef NLOHMANN_VIEW_UNLIKELY #undef NLOHMANN_VIEW_ALWAYS_INLINE #undef NLOHMANN_VIEW_NOINLINE +#undef NLOHMANN_VIEW_NOCLONE #undef NLOHMANN_VIEW_NODISCARD #undef NLOHMANN_VIEW_THROW #undef NLOHMANN_VIEW_LITTLE_ENDIAN diff --git a/include/nlohmann/detail/view/simd.hpp b/include/nlohmann/detail/view/simd.hpp index 9dede9b09..f4745f6b8 100644 --- a/include/nlohmann/detail/view/simd.hpp +++ b/include/nlohmann/detail/view/simd.hpp @@ -289,9 +289,12 @@ compares, and the UTF-8 check covers the bytes up to it. Returns where the string scan stops, like scan_string_run: before ill-formed UTF-8 and for the last bytes of the input, the bytes are checked one sequence at a time. Out of line, so that no constants of the check occupy registers in the parse loop. -On x86-64, it is compiled for SSSE3 (see cpu_has_ssse3()). +On x86-64, it is compiled for SSSE3 (see cpu_has_ssse3()). Not cloned: with +the calls of accept()'s builder, GCC 16 would clone it without plain (always +string_plain()), and calling the clone made json_document::parse() run 0.7-1.2 % +more instructions. */ -NLOHMANN_VIEW_SSSE3_TARGET NLOHMANN_VIEW_NOINLINE inline const unsigned char* scan_string_vector(const unsigned char* p, const unsigned char* e, const std::uint8_t* plain) noexcept +NLOHMANN_VIEW_SSSE3_TARGET NLOHMANN_VIEW_NOINLINE NLOHMANN_VIEW_NOCLONE inline const unsigned char* scan_string_vector(const unsigned char* p, const unsigned char* e, const std::uint8_t* plain) noexcept { using lookup = utf8_lookup4<>; const unsigned char* block = p; diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index ec18242ea..262795445 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -157,6 +157,15 @@ #define NLOHMANN_VIEW_NOINLINE #endif +// GCC: a function compiled as written, without the clones in which interprocedural +// constant propagation drops an argument that every caller passes as the same constant +// (calling such a clone changes the code GCC generates for the parse loop that calls it) +#if defined(__GNUC__) && !defined(__clang__) + #define NLOHMANN_VIEW_NOCLONE __attribute__((noclone)) +#else + #define NLOHMANN_VIEW_NOCLONE +#endif + #if defined(__GNUC__) || defined(__clang__) #define NLOHMANN_VIEW_NODISCARD __attribute__((warn_unused_result)) #elif defined(_MSC_VER) @@ -948,9 +957,12 @@ compares, and the UTF-8 check covers the bytes up to it. Returns where the string scan stops, like scan_string_run: before ill-formed UTF-8 and for the last bytes of the input, the bytes are checked one sequence at a time. Out of line, so that no constants of the check occupy registers in the parse loop. -On x86-64, it is compiled for SSSE3 (see cpu_has_ssse3()). +On x86-64, it is compiled for SSSE3 (see cpu_has_ssse3()). Not cloned: with +the calls of accept()'s builder, GCC 16 would clone it without plain (always +string_plain()), and calling the clone made json_document::parse() run 0.7-1.2 % +more instructions. */ -NLOHMANN_VIEW_SSSE3_TARGET NLOHMANN_VIEW_NOINLINE inline const unsigned char* scan_string_vector(const unsigned char* p, const unsigned char* e, const std::uint8_t* plain) noexcept +NLOHMANN_VIEW_SSSE3_TARGET NLOHMANN_VIEW_NOINLINE NLOHMANN_VIEW_NOCLONE inline const unsigned char* scan_string_vector(const unsigned char* p, const unsigned char* e, const std::uint8_t* plain) noexcept { using lookup = utf8_lookup4<>; const unsigned char* block = p; @@ -10927,6 +10939,7 @@ class tuple_element> // NOLINT(cert #undef NLOHMANN_VIEW_UNLIKELY #undef NLOHMANN_VIEW_ALWAYS_INLINE #undef NLOHMANN_VIEW_NOINLINE +#undef NLOHMANN_VIEW_NOCLONE #undef NLOHMANN_VIEW_NODISCARD #undef NLOHMANN_VIEW_THROW #undef NLOHMANN_VIEW_LITTLE_ENDIAN diff --git a/tests/src/unit-json_view_macros.cpp b/tests/src/unit-json_view_macros.cpp index 2cc8b7744..d2c9e197f 100644 --- a/tests/src/unit-json_view_macros.cpp +++ b/tests/src/unit-json_view_macros.cpp @@ -25,7 +25,7 @@ #endif // the view's own macros do not leak -#if defined(NLOHMANN_VIEW_LIKELY) || defined(NLOHMANN_VIEW_UNLIKELY) || defined(NLOHMANN_VIEW_ALWAYS_INLINE) || defined(NLOHMANN_VIEW_NOINLINE) \ +#if defined(NLOHMANN_VIEW_LIKELY) || defined(NLOHMANN_VIEW_UNLIKELY) || defined(NLOHMANN_VIEW_ALWAYS_INLINE) || defined(NLOHMANN_VIEW_NOINLINE) || defined(NLOHMANN_VIEW_NOCLONE) \ || defined(NLOHMANN_VIEW_NODISCARD) || defined(NLOHMANN_VIEW_THROW) || defined(NLOHMANN_VIEW_HAS_CPP_17) || defined(NLOHMANN_VIEW_LITTLE_ENDIAN) \ || defined(NLOHMANN_VIEW_REPEAT16) #error "json_view.hpp leaks a macro"