JDK-8345265 : Minor improvements for LTO across all compilers
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 24
  • Priority: P4
  • Status: In Progress
  • Resolution: Unresolved
  • Submitted: 2024-11-30
  • Updated: 2025-03-06
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
This is a general cleanup and improvement of LTO, as well as a quick fix to remove a workaround in the Makefiles that disabled LTO for g1ParScanThreadState.cpp due to the old poisoning mechanism causing trouble. The \-Wno\-attribute\-warning change here can be removed once Kim's new poisoning solution is integrated.

- -fno-omit-frame-pointer is added to gcc to stop the linker from emitting code without the frame pointer
- -flto is set to $(JOBS) instead of auto to better match what the user requested
- -Gy is passed to the Microsoft compiler. This does not fully fix LTO under Microsoft, but prevents warnings about -LTCG:INCREMENTAL at least
Comments
The way to achieve this would be to make SIZE the default value for OPTIMIZATION in SetupNativeCompilation (right now there is no default value, which is stupid in itself), and remove the OPTIMIZATION line for all calls to SetupNativeCompilation (or rather, SetupJdkLibrary, which is the front-end). Then build it and check size and performance against mainline.
06-03-2025

I did not notice the ping here until now, sorry. I have no love for the current system for optimizations. As I think I explained elsewhere, it was just created as some kind of "reverse engineering" of the actual compile lines that were duplicated and locally modified across a ton of recursive Makefiles in the old build system. I don't have a very good idea what to replace it with, though. I understand the idea that there should be a configure argument to say how you'd like to tweak the libraries, but frankly, I think there is likely to be a good "one size fits all" here, it's just not what we have right now. :-) To phrase it differently, I think we can e.g. change all native libraries to e.g. optimize for size, and then check if we see any measurable regressions, and if so trim the optimization level for that specific library. And I think the result we come up with will be much better than what we have now, and likely to be good enough for all parties, removing the need for any configure flag.
06-03-2025

Sorry for not getting back to you sooner. I'm not very fond of that approach, rather I think it would be better to implement LTO for native JDK code by passing the arguments to the CFLAGS/CXXFLAGS and LDFLAGS rather than adding a new parameter. LTO at least seems more compelling to me than a feature to tweak the opt flags to SIZE
28-02-2025

> Unlike with HotSpot, the optimization levels on non HotSpot native code are set in their individual module Makefiles; Trying to have an opt-size equivalent for them would not be a trivial task Additionally to parameters NAME OPTIMIZATION SRC etc. the SetupJdkLibrary make rule might have another parameter named e.g. ALLOW_OPTIMIZATION_FEATURE . If this is set to true (probably for libs compiled with OPTIMIZATION high or highest this would apply), we allow changing the opt level to LTO or SIZE optimization. This way we do not force other compile settings (like opt size) to all libs. What do you think ?
28-01-2025

> Unlike with HotSpot, the optimization levels on non HotSpot native code are set in their individual module Makefiles; > Trying to have an opt-size equivalent for them would not be a trivial task Yeah true it is really a bit strange ; for example for LIBNET i the optimization level is LOW , but for LIBNIO it is HIGH https://github.com/openjdk/jdk/blob/master/make/modules/java.base/Lib.gmk for LIBJAVA it is HIGH but for LIBZIP (an LIBJIMAGE) it is LOW https://github.com/openjdk/jdk/blob/master/make/modules/java.base/lib/CoreLibraries.gmk (but we can also use an external libzip and then it is whatever the providers used in the compilation) Is it really a must have to use those settings or more a historical thing ? maybe we should discuss with e.g. Magnus [~ihse] first ?
27-01-2025

I can open a Pull Request, but I'm not sure whether this is wanted or not by others, that's why I have not tried to upstream support for link time optimization on non HotSpot native code just yet. Unlike with HotSpot, the optimization levels on non HotSpot native code are set in their individual module Makefiles; Trying to have an opt-size equivalent for them would not be a trivial task
25-01-2025

> In my fork I use a separate configure option rather than --enable-jvm-feature-link-time-opt, since that is meant for the JVM only Makes probably sense to have a separate configure flag ; will you do a PR for this ? (maybe we also need then a similar flag for opt-size for the JDK libs ?)
23-01-2025

In my fork I use a separate configure option rather than --enable-jvm-feature-link-time-opt, since that is meant for the JVM only
22-01-2025

> Library size might not be the only measure of improvements for the compiled code, it would likely be faster as well The lib size is easily measurable so it is a nice metrics. The speed of the compiled code is often just similar compared to non-LTO but this of course depends a lot on the benchmark; for the JDK libs we probably do not have good benchmarks anyway but I might be wrong. While some JDK libs stay roughly the same of get slightly larger with lto, looking at all JDK libs we also see a ~ 10% size reduction (Linux x86_64, gcc 11.3.0 devkit) : du -sh lto_build 7.3M lto_build du -sh normal_build 8.3M normal_build So the question is, should we compile the JDK libs too with lto when setting --enable-jvm-feature-link-time-opt ? Or do we need a separate configure - switch because a 'jvm feature' is not for jdk libs ?
22-01-2025

I also regularly compile the non HotSpot native code of the JDK with LTO active as well, this generally works well on Windows too. I added the flags in LauncherCommon.gmk and LibCommon.gmk if memory serves me right. Library size might not be the only measure of improvements for the compiled code, it would likely be faster as well
21-01-2025

I tried to build the JDK shared libs as well with lto. This works on Linux x86_64 (gcc). Gives some lib size improvements for a couple of JDK libs, but not for all. Some additional warnings show up : src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h:91:6: warning: type of 'get_lwp_regs' does not match original declaration [-Wlto-type-mismatch] src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:407:6: note: return value type mismatch src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:407:6: note: type 'bool' should match type 'bool' src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:407:6: note: 'get_lwp_regs' was previously declared here src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:407:6: note: code may be misoptimized unless '-fno-strict-aliasing' is used src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h:82:1: warning: type of 'init_libproc' does not match original declaration [-Wlto-type-mismatch] src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:118:1: note: return value type mismatch src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:118:1: note: type 'bool' should match type 'bool' src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:118:1: note: 'init_libproc' was previously declared here src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c:118:1: note: code may be misoptimized unless '-fno-strict-aliasing' is used Might be related to the bool typedef in the header libproc.h : #ifndef __cplusplus typedef int bool; #define true 1 #define false 0 #endif // get regs for a given lwp bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lid, struct user_regs_struct* regs); src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c // get regs for a given lwp bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, struct user_regs_struct* regs) { return ph->ops->get_lwp_regs(ph, lwp_id, regs); }
21-01-2025

Sorry for forgetting about this, it should be ready now. The final assembly emitted by LTO on GodBolt is actually not assembly at all, it's gcc's GIMPLE representation that the compiler uses during link time to perform whole program optimization
09-01-2025

Regarding the LTO hang - it seems to have nothing to do with the -Wattribute-warning warnings. Simply remove the disable of LTO for g1ParScanThreadState.cpp from JvmOverrideFiles.gmk and comment out the gcc definitions for FORBID_C_FUNCTIONS and ALLOW_C_FUNCTIONS (so the default expansion to nothing will be used), and I still get the hang. This is with a release build, using gcc13.2.
08-12-2024

[~jwaters] Regarding "Additionally, a quick test with GodBolt reveals that at least some gcc versions do not emit the warnings when forbidden methods are called in the compile step, only in the link step: https://godbolt.org/z/3Y7nafKsq" It does seem like turning on LTO turns off the -Wattribute-warning warnings in some cases. That actually makes sense. The documentation for that attribute says "If the error or warning attribute is used on a function declaration and a call to such a function is not eliminated through dead code elimination or other optimizations, an error or warning (respectively) that includes message is diagnosed." Since before LTO is before "other optimizations" has completed, it would be inappropriate to warn before linking. In the final assembly code being generated with LTO I don't see any remaining reference to malloc. Actually, I don't see anything that looks like code, so I don't understand the output with LTO at all. This is similar to the problem discussed in the comments that -Wattribute-warning doesn't fire for calls rewritten by _FORTIFY_SOURCE.
08-12-2024

Other warnings and notes: ----------------- In function 'init_jvm', inlined from 'runUnitTestsInner' at ../../test/hotspot/gtest/gtestMain.cpp:280:24: ../../test/hotspot/gtest/gtestMain.cpp:72:59: warning: argument 1 value '18446744073709551615' exceeds maximum object size 9223372036854775807 [-Walloc-size-larger-than=] 72 | JavaVMOption* options = new JavaVMOption[num_jvm_options]; | ^ /usr/local/gxx_include/new: In function 'runUnitTestsInner': /usr/local/gxx_include/new:128:26: note: in a call to allocation function 'operator new []' declared here In function 'init_jvm', inlined from 'runUnitTestsInner' at ../../test/hotspot/gtest/gtestMain.cpp:280:24: ../../test/hotspot/gtest/gtestMain.cpp:72:59: warning: argument 1 value '18446744073709551615' exceeds maximum object size 9223372036854775807 [-Walloc-size-larger-than=] 72 | JavaVMOption* options = new JavaVMOption[num_jvm_options]; | ^ /usr/local/gxx_include/new: In function 'runUnitTestsInner': /usr/local/gxx_include/new:128:26: note: in a call to allocation function 'operator new []' declared here ----------------- In function 'make_unique', inlined from 'CreateArgvFromArgs' at /scratch/kab/devtools/jib-data/install/jpg/infra/builddeps/gtest/1.14.0+1.0/gtest-1.14.0+1.0.tar.gz/googletest-1.14.0/googletest/src/gtest-death-test.cc:633:58, inlined from 'AssumeRole' at /scratch/kab/devtools/jib-data/install/jpg/infra/builddeps/gtest/1.14.0+1.0/gtest-1.14.0+1.0.tar.gz/googletest-1.14.0/googletest/src/gtest-death-test.cc:1389:58: /usr/local/gxx_include/bits/unique_ptr.h:1085:30: warning: argument 1 value '18446744073709551615' exceeds maximum object size 9223372036854775807 [-Walloc-size-larger-than=] /usr/local/gxx_include/new: In member function 'AssumeRole': /usr/local/gxx_include/new:128:26: note: in a call to allocation function 'operator new []' declared here ---------------------- ../../src/hotspot/share/gc/g1/g1ParScanThreadState.cpp: In member function 'trim_queue_to_threshold.constprop': ../../src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:318:6: note: variable tracking size limit exceeded with '-fvar-tracking-assignments', retrying without 318 | void G1ParScanThreadState::trim_queue_to_threshold(uint threshold) { | ^ ../../src/hotspot/share/gc/g1/g1ParScanThreadState.cpp: In member function 'trim_queue_to_threshold': ../../src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:318:6: note: variable tracking size limit exceeded with '-fvar-tracking-assignments', retrying without 318 | void G1ParScanThreadState::trim_queue_to_threshold(uint threshold) { | ^ ../../src/hotspot/share/gc/g1/g1ParScanThreadState.cpp: In member function 'copy_to_survivor_space': ../../src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:588:5: note: variable tracking size limit exceeded with '-fvar-tracking-assignments', retrying without 588 | oop G1ParScanThreadState::copy_to_survivor_space(G1HeapRegionAttr region_attr, | ^
08-12-2024

The proposed solution doesn't seem to work on my end, as the warnings also appear during the compile step as well, and not just during the link step when proper LTO is being performed, and likewise also cannot be suppressed with ALLOW_C_FUNCTION. Additionally, a quick test with GodBolt reveals that at least some gcc versions do not emit the warnings when forbidden methods are called in the compile step, only in the link step: https://godbolt.org/z/3Y7nafKsq It seems like this might vary wildly between different gcc distributions and versions, making this issue very messy to solve. The link hanging on LTO is also rather concerning. What warnings and error messages do you get?
08-12-2024

The problem being encountered is that LTO doesn't honor the pragma used to locally suppress the warnings for functions we've marked with FORBID_C_FUNCTION. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80922 There is a much better and simpler way to suppress the -Wattribute-warning warnings from LTO than globally disabling FORBID_C_FUNCTION when building with LTO enabled. Just disable the warning during the LTO part, e.g. {code} diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index 09a48508eff..a26f10a47d5 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -172,8 +172,11 @@ ifeq ($(call check-jvm-feature, link-time-opt), true) ifeq ($(call isCompiler, gcc), true) JVM_CFLAGS_FEATURES += -flto=auto -fuse-linker-plugin -fno-strict-aliasing \ -fno-fat-lto-objects + # Disable attribute-warning because LTO doesn't honor the pragma used to + # suppress FORBID_C_FUNCTION. OK to do so, since check has already been + # done by normal compilation. JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) -flto=auto \ - -fuse-linker-plugin -fno-strict-aliasing + -fuse-linker-plugin -fno-strict-aliasing -Wno-attribute-warning else ifeq ($(call isCompiler, clang), true) JVM_CFLAGS_FEATURES += -flto -fno-strict-aliasing ifeq ($(call isBuildOs, aix), true) {code} And voila, those warnings are gone. However, there are some other warnings and concerning messages that should be investigated. And the link never seems to complete, just runs seemingly forever at 100% CPU (with gcc 13.2).
07-12-2024

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/22464 Date: 2024-11-30 00:35:59 +0000
30-11-2024